@tscircuit/parts-engine 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8869,11 +8869,11 @@ function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag,
8869
8869
  const midY = (startY + endY) / 2;
8870
8870
  const dx = endX - startX;
8871
8871
  const dy = endY - startY;
8872
- const distance2 = Math.sqrt(dx * dx + dy * dy);
8873
- if (distance2 === 0 || radius < distance2 / 2) {
8872
+ const distance3 = Math.sqrt(dx * dx + dy * dy);
8873
+ if (distance3 === 0 || radius < distance3 / 2) {
8874
8874
  return [start, end];
8875
8875
  }
8876
- const h = Math.sqrt(radius * radius - distance2 * distance2 / 4);
8876
+ const h = Math.sqrt(radius * radius - distance3 * distance3 / 4);
8877
8877
  const angle = Math.atan2(dy, dx);
8878
8878
  const centerX = midX + h * Math.sin(angle) * (sweepFlag ? -1 : 1);
8879
8879
  const centerY = midY - h * Math.cos(angle) * (sweepFlag ? -1 : 1);
@@ -8906,6 +8906,121 @@ function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag,
8906
8906
  return path;
8907
8907
  }
8908
8908
  var mil10ToMm = (value) => value * 10 * 0.0254;
8909
+ var getBoundsCenter = (bounds) => ({
8910
+ x: (bounds.minX + bounds.maxX) / 2,
8911
+ y: (bounds.minY + bounds.maxY) / 2
8912
+ });
8913
+ var getBoundsFromPoints = (points) => {
8914
+ if (points.length === 0) {
8915
+ return null;
8916
+ }
8917
+ let minX = points[0].x;
8918
+ let minY = points[0].y;
8919
+ let maxX = points[0].x;
8920
+ let maxY = points[0].y;
8921
+ for (let i = 1; i < points.length; i++) {
8922
+ const point2 = points[i];
8923
+ if (point2.x < minX) minX = point2.x;
8924
+ if (point2.y < minY) minY = point2.y;
8925
+ if (point2.x > maxX) maxX = point2.x;
8926
+ if (point2.y > maxY) maxY = point2.y;
8927
+ }
8928
+ return { minX, minY, maxX, maxY };
8929
+ };
8930
+ var getCadSvgNode = (easyEdaJson) => {
8931
+ const svgNode = easyEdaJson.packageDetail.dataStr.shape.find(
8932
+ (shape) => shape.type === "SVGNODE" && shape.svgData.attrs?.uuid
8933
+ );
8934
+ return svgNode?.type === "SVGNODE" ? svgNode : null;
8935
+ };
8936
+ var isSvgChildNodeWithAttrs = (childNode) => typeof childNode === "object" && childNode !== null;
8937
+ var getXyBoundsFromModelBounds = (bounds) => ({
8938
+ minX: bounds.min.x,
8939
+ minY: bounds.min.y,
8940
+ maxX: bounds.max.x,
8941
+ maxY: bounds.max.y
8942
+ });
8943
+ var rotateScenePoint = (point2, rotationDeg) => {
8944
+ switch (rotationDeg) {
8945
+ case 0:
8946
+ return point2;
8947
+ case 90:
8948
+ return { x: point2.y, y: -point2.x };
8949
+ case 180:
8950
+ return { x: -point2.x, y: -point2.y };
8951
+ case 270:
8952
+ return { x: -point2.y, y: point2.x };
8953
+ }
8954
+ };
8955
+ var getEasyEdaModelOriginOnBoardMm = ({
8956
+ svgNode,
8957
+ footprintCenterMm
8958
+ }) => {
8959
+ const points = [];
8960
+ for (const childNode of svgNode.svgData.childNodes ?? []) {
8961
+ if (!isSvgChildNodeWithAttrs(childNode)) continue;
8962
+ const rawPoints = childNode.attrs?.points;
8963
+ if (!rawPoints) continue;
8964
+ const values = String(rawPoints).trim().split(/\s+/).map(Number);
8965
+ for (let i = 0; i + 1 < values.length; i += 2) {
8966
+ const x = values[i];
8967
+ const y = values[i + 1];
8968
+ if (Number.isFinite(x) && Number.isFinite(y)) {
8969
+ points.push({ x: mil10ToMm(x), y: mil10ToMm(y) });
8970
+ }
8971
+ }
8972
+ }
8973
+ let modelOriginMm = null;
8974
+ if (points.length > 0) {
8975
+ const bounds = getBoundsFromPoints(points);
8976
+ modelOriginMm = bounds ? getBoundsCenter(bounds) : null;
8977
+ } else {
8978
+ const [originX, originY] = String(svgNode.svgData.attrs?.c_origin ?? "0,0").split(",").map((value) => Number(value.trim()));
8979
+ if (Number.isFinite(originX) && Number.isFinite(originY)) {
8980
+ modelOriginMm = { x: mil10ToMm(originX), y: mil10ToMm(originY) };
8981
+ }
8982
+ }
8983
+ if (!modelOriginMm) return null;
8984
+ return {
8985
+ x: modelOriginMm.x - footprintCenterMm.x,
8986
+ y: footprintCenterMm.y - modelOriginMm.y
8987
+ };
8988
+ };
8989
+ var snapZero = (value) => Math.abs(value) < 1e-6 ? 0 : value;
8990
+ var normalizePoint = (point2) => ({
8991
+ x: snapZero(point2.x),
8992
+ y: snapZero(point2.y)
8993
+ });
8994
+ var getCadModelOffsetMmFromBounds = (easyEdaJson, bounds, {
8995
+ footprintBoundsCenterMm
8996
+ } = {}) => {
8997
+ const svgNode = getCadSvgNode(easyEdaJson);
8998
+ if (!svgNode || !bounds) return null;
8999
+ const [, , rotationZRaw] = String(
9000
+ svgNode.svgData.attrs?.c_rotation ?? "0,0,0"
9001
+ ).split(",").map((value) => Number(value.trim()));
9002
+ const rotationDeg = (rotationZRaw % 360 + 360) % 360;
9003
+ if (![0, 90, 180, 270].includes(rotationDeg)) return null;
9004
+ const modelCenter = getBoundsCenter(getXyBoundsFromModelBounds(bounds));
9005
+ const footprintCenter = easyEdaJson.packageDetail.dataStr.head;
9006
+ const footprintCenterMm = footprintBoundsCenterMm ?? {
9007
+ x: mil10ToMm(footprintCenter.x),
9008
+ y: mil10ToMm(footprintCenter.y)
9009
+ };
9010
+ const targetOriginOnBoardMm = getEasyEdaModelOriginOnBoardMm({
9011
+ svgNode,
9012
+ footprintCenterMm
9013
+ });
9014
+ if (!targetOriginOnBoardMm) return null;
9015
+ const targetOriginInModelFrame = rotateScenePoint(
9016
+ targetOriginOnBoardMm,
9017
+ rotationDeg
9018
+ );
9019
+ return normalizePoint({
9020
+ x: modelCenter.x - targetOriginInModelFrame.x,
9021
+ y: modelCenter.y - targetOriginInModelFrame.y
9022
+ });
9023
+ };
8909
9024
  var normalizePinLabels = (inputPinLabels) => {
8910
9025
  const uniqueInputPinLabels = inputPinLabels.map((labels) => [
8911
9026
  ...new Set(labels)
@@ -9076,13 +9191,14 @@ var handleHoleCutout = (hole, index) => {
9076
9191
  });
9077
9192
  };
9078
9193
  var handleVia = (via, index) => {
9194
+ const holeDiameter = via.holeRadius * 2;
9079
9195
  return pcb_via.parse({
9080
9196
  type: "pcb_via",
9081
9197
  pcb_via_id: `pcb_via_${index + 1}`,
9082
9198
  x: milx10(via.center.x),
9083
9199
  y: milx10(via.center.y),
9084
9200
  outer_diameter: milx10(via.outerDiameter),
9085
- hole_diameter: milx10(via.holeDiameter),
9201
+ hole_diameter: milx10(holeDiameter),
9086
9202
  layers: ["top", "bottom"]
9087
9203
  });
9088
9204
  };
@@ -9097,6 +9213,10 @@ var handleCutout = (solidRegion, index) => {
9097
9213
  }))
9098
9214
  });
9099
9215
  };
9216
+ var LEAD_SHAPE_LAYER = 100;
9217
+ var isPcbSolidRegionCutout = (shape) => {
9218
+ return shape.fillStyle === "cutout" && shape.layermask !== LEAD_SHAPE_LAYER;
9219
+ };
9100
9220
  var getCadPositionZMmFromMetadata = (easyEdaJson) => {
9101
9221
  const svgNode = easyEdaJson.packageDetail.dataStr.shape.find(
9102
9222
  (shape) => shape.type === "SVGNODE" && shape.svgData.attrs?.uuid
@@ -9115,6 +9235,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9115
9235
  cadPositionXMm,
9116
9236
  cadPositionYMm,
9117
9237
  cadPositionZMm,
9238
+ cadModelBounds,
9118
9239
  showDesignator = false
9119
9240
  } = {}) => {
9120
9241
  const resolvedCadPositionZMm = cadPositionZMm ?? getCadPositionZMmFromMetadata(easyEdaJson);
@@ -9194,15 +9315,12 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9194
9315
  const innerWidth = largestOuterDimensionName === "width" ? largestInnerDimension : smallestInnerDimension;
9195
9316
  const innerHeight = largestOuterDimensionName === "height" ? largestInnerDimension : smallestInnerDimension;
9196
9317
  additionalPlatedHoleProps = {
9197
- shape: "pill_hole_with_rect_pad",
9198
- hole_shape: "pill",
9199
- pad_shape: "rect",
9318
+ shape: "pill",
9200
9319
  hole_width: innerWidth,
9201
9320
  hole_height: innerHeight,
9202
- rect_pad_width: mil2mm(pad.width),
9203
- rect_pad_height: mil2mm(pad.height),
9204
- hole_offset_x: 0,
9205
- hole_offset_y: 0
9321
+ outer_width: mil2mm(pad.width),
9322
+ outer_height: mil2mm(pad.height),
9323
+ ccw_rotation: pad.rotation || 0
9206
9324
  };
9207
9325
  } else if (pad.shape === "RECT") {
9208
9326
  const padWidth = mil2mm(pad.width);
@@ -9256,7 +9374,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9256
9374
  } else if (pad.shape === "ELLIPSE") {
9257
9375
  soupShape = "rect";
9258
9376
  } else if (pad.shape === "OVAL") {
9259
- soupShape = "rect";
9377
+ soupShape = "pill";
9260
9378
  } else if (pad.shape === "POLYGON") {
9261
9379
  soupShape = "polygon";
9262
9380
  }
@@ -9276,12 +9394,17 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9276
9394
  x: mil2mm(pad.center.x),
9277
9395
  y: mil2mm(pad.center.y)
9278
9396
  },
9279
- ...soupShape === "rect" ? rectSize : soupShape === "polygon" && pad.points ? {
9397
+ ...soupShape === "rect" ? rectSize : soupShape === "pill" ? {
9398
+ ...rectSize,
9399
+ radius: Math.min(rectSize.width, rectSize.height) / 2
9400
+ } : soupShape === "polygon" && pad.points ? {
9280
9401
  points: pad.points.map((p) => ({
9281
9402
  x: milx10(p.x),
9282
9403
  y: milx10(p.y)
9283
9404
  }))
9284
- } : { radius: Math.min(mil2mm(pad.width), mil2mm(pad.height)) / 2 },
9405
+ } : {
9406
+ radius: Math.min(mil2mm(pad.width), mil2mm(pad.height)) / 2
9407
+ },
9285
9408
  layer: "top",
9286
9409
  port_hints: [`pin${pinNumber}`],
9287
9410
  pcb_component_id: "pcb_component_1",
@@ -9300,7 +9423,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9300
9423
  circuitElements.push(handleVia(v, index));
9301
9424
  });
9302
9425
  easyEdaJson.packageDetail.dataStr.shape.filter(
9303
- (shape) => shape.type === "SOLIDREGION" && shape.fillStyle === "cutout"
9426
+ (shape) => shape.type === "SOLIDREGION" && isPcbSolidRegionCutout(shape)
9304
9427
  ).forEach((sr, index) => {
9305
9428
  circuitElements.push(handleCutout(sr, index));
9306
9429
  });
@@ -9352,7 +9475,7 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9352
9475
  circuitElements.push(
9353
9476
  pcb_silkscreen_text.parse({
9354
9477
  type: "pcb_silkscreen_text",
9355
- pcb_silkscreen_text_id: `pcb_silkscreen_text_designator_fallback`,
9478
+ pcb_silkscreen_text_id: "pcb_silkscreen_text_designator_fallback",
9356
9479
  pcb_component_id: "pcb_component_1",
9357
9480
  text: "{NAME}",
9358
9481
  anchor_position: {
@@ -9470,6 +9593,14 @@ var convertEasyEdaJsonToCircuitJson = (easyEdaJson, {
9470
9593
  if (!cad.model_origin_position) {
9471
9594
  cad.model_origin_position = { x: 0, y: 0, z: 0 };
9472
9595
  }
9596
+ const resolvedCadModelBounds = cadModelBounds ?? easyEdaJson._objMetadata?.bounds;
9597
+ const recenteredCadOffset = resolvedCadModelBounds && getCadModelOffsetMmFromBounds(easyEdaJson, resolvedCadModelBounds, {
9598
+ footprintBoundsCenterMm: bounds.center
9599
+ });
9600
+ if (recenteredCadOffset) {
9601
+ cad.model_origin_position.x = recenteredCadOffset.x;
9602
+ cad.model_origin_position.y = recenteredCadOffset.y;
9603
+ }
9473
9604
  const side = pcb_component2.layer ?? "top";
9474
9605
  const t = DEFAULT_PCB_THICKNESS_MM / 2;
9475
9606
  const attrs = svgNode?.svgData?.attrs ?? {};
@@ -9623,7 +9754,7 @@ var ViaSchema = BaseShapeSchema.extend({
9623
9754
  type: external_exports.literal("VIA"),
9624
9755
  center: PointSchema,
9625
9756
  outerDiameter: external_exports.number(),
9626
- holeDiameter: external_exports.number()
9757
+ holeRadius: external_exports.number()
9627
9758
  });
9628
9759
  var RectSchema = BaseShapeSchema.extend({
9629
9760
  type: external_exports.literal("RECT"),
@@ -9773,13 +9904,13 @@ var ShapeItemSchema = external_exports.object({
9773
9904
  });
9774
9905
  }
9775
9906
  case "VIA": {
9776
- const [x, y, outerDiameter, , holeDiameter, id] = shape.data.split("~");
9907
+ const [x, y, outerDiameter, , holeRadius, id] = shape.data.split("~");
9777
9908
  const center = [Number(x), Number(y)];
9778
9909
  return ViaSchema.parse({
9779
9910
  type: "VIA",
9780
9911
  center,
9781
9912
  outerDiameter: Number(outerDiameter),
9782
- holeDiameter: Number(holeDiameter),
9913
+ holeRadius: Number(holeRadius),
9783
9914
  id
9784
9915
  });
9785
9916
  }
@@ -10035,6 +10166,12 @@ var parsePath = (str) => {
10035
10166
  };
10036
10167
  };
10037
10168
  var PathShapeSchema = external_exports.string().startsWith("PT~").transform(parsePath).pipe(PathShapeOutputSchema);
10169
+ var optionalEasyEdaTextField = (fieldSchema) => external_exports.preprocess((textField) => {
10170
+ if (textField == null || textField === "" || textField === "undefined") {
10171
+ return void 0;
10172
+ }
10173
+ return textField;
10174
+ }, fieldSchema);
10038
10175
  var TextShapeOutputSchema = external_exports.object({
10039
10176
  type: external_exports.literal("TEXT"),
10040
10177
  alignment: external_exports.enum(["L", "C", "R"]),
@@ -10042,11 +10179,13 @@ var TextShapeOutputSchema = external_exports.object({
10042
10179
  y: external_exports.number(),
10043
10180
  rotation: external_exports.number(),
10044
10181
  fontColor: external_exports.string(),
10045
- backgroundColor: external_exports.string().optional(),
10182
+ backgroundColor: optionalEasyEdaTextField(external_exports.string().optional()),
10046
10183
  fontSize: external_exports.string(),
10047
- fontWeight: external_exports.string().optional().default("normal"),
10048
- fontStyle: external_exports.enum(["normal", "italic"]).optional().default("normal"),
10049
- fontDecoration: external_exports.string().optional().default(""),
10184
+ fontWeight: optionalEasyEdaTextField(external_exports.string().optional().default("normal")),
10185
+ fontStyle: optionalEasyEdaTextField(
10186
+ external_exports.enum(["normal", "italic"]).optional().default("normal")
10187
+ ),
10188
+ fontDecoration: optionalEasyEdaTextField(external_exports.string().optional().default("")),
10050
10189
  content: external_exports.string(),
10051
10190
  textType: external_exports.string(),
10052
10191
  visibility: external_exports.enum(["0", "1"]),
@@ -10079,11 +10218,11 @@ var parseText = (str) => {
10079
10218
  y: Number(y),
10080
10219
  rotation: Number(rotation2),
10081
10220
  fontColor,
10082
- backgroundColor: backgroundColor || void 0,
10221
+ backgroundColor,
10083
10222
  fontSize,
10084
- fontWeight: fontWeight || "normal",
10085
- fontStyle: fontStyle || "normal",
10086
- fontDecoration: fontDecoration || "",
10223
+ fontWeight,
10224
+ fontStyle,
10225
+ fontDecoration,
10087
10226
  content,
10088
10227
  textType,
10089
10228
  visibility,
@@ -10250,6 +10389,7 @@ var EasyEdaJsonSchema = external_exports.object({
10250
10389
  lcsc: LcscSchema,
10251
10390
  owner: OwnerSchema,
10252
10391
  tags: external_exports.array(external_exports.string()),
10392
+ category: external_exports.string().optional(),
10253
10393
  updateTime: external_exports.number(),
10254
10394
  updated_at: external_exports.string(),
10255
10395
  dataStr: DataStrSchema,