@tscircuit/core 0.0.707 → 0.0.708

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.
Files changed (2) hide show
  1. package/dist/index.js +309 -65
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ __export(components_exports, {
13
13
  BreakoutPoint: () => BreakoutPoint,
14
14
  Capacitor: () => Capacitor,
15
15
  Chip: () => Chip,
16
- Constraint: () => Constraint2,
16
+ Constraint: () => Constraint3,
17
17
  Crystal: () => Crystal,
18
18
  Cutout: () => Cutout,
19
19
  Diode: () => Diode,
@@ -9740,8 +9740,7 @@ function getPresetAutoroutingConfig(autorouterConfig) {
9740
9740
  }
9741
9741
  }
9742
9742
 
9743
- // lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack.ts
9744
- import "@tscircuit/circuit-json-util";
9743
+ // lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/Group_doInitialPcbLayoutPack.ts
9745
9744
  import {
9746
9745
  pack,
9747
9746
  convertCircuitJsonToPackOutput,
@@ -9749,64 +9748,263 @@ import {
9749
9748
  getGraphicsFromPackOutput
9750
9749
  } from "calculate-packing";
9751
9750
  import { length as length4 } from "circuit-json";
9752
- import {
9753
- transformPCBElements
9754
- } from "@tscircuit/circuit-json-util";
9755
- import { translate as translate5, rotate as rotate2, compose as compose4 } from "transformation-matrix";
9756
9751
  import Debug7 from "debug";
9757
- var DEFAULT_MIN_GAP = "1mm";
9758
- var debug6 = Debug7("Group_doInitialPcbLayoutPack");
9752
+
9753
+ // lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/applyComponentConstraintClusters.ts
9754
+ import * as kiwi2 from "@lume/kiwi";
9755
+ var applyComponentConstraintClusters = (group, packInput) => {
9756
+ const constraints = group.children.filter(
9757
+ (c) => c.componentName === "Constraint" && c._parsedProps.pcb
9758
+ );
9759
+ const clusterByRoot = /* @__PURE__ */ new Map();
9760
+ const parent = {};
9761
+ const find = (x) => {
9762
+ if (parent[x] !== x) parent[x] = find(parent[x]);
9763
+ return parent[x];
9764
+ };
9765
+ const union = (a, b) => {
9766
+ const ra = find(a);
9767
+ const rb = find(b);
9768
+ if (ra !== rb) parent[rb] = ra;
9769
+ };
9770
+ const makeSet = (x) => {
9771
+ if (!(x in parent)) parent[x] = x;
9772
+ };
9773
+ const getIdFromSelector = (sel2) => {
9774
+ const name = sel2.startsWith(".") ? sel2.slice(1) : sel2;
9775
+ const child = group.children.find((c) => c.name === name);
9776
+ return child?.pcb_component_id ?? void 0;
9777
+ };
9778
+ for (const constraint of constraints) {
9779
+ const props = constraint._parsedProps;
9780
+ if ("left" in props && "right" in props) {
9781
+ const a = getIdFromSelector(props.left);
9782
+ const b = getIdFromSelector(props.right);
9783
+ if (a && b) {
9784
+ makeSet(a);
9785
+ makeSet(b);
9786
+ union(a, b);
9787
+ }
9788
+ } else if ("top" in props && "bottom" in props) {
9789
+ const a = getIdFromSelector(props.top);
9790
+ const b = getIdFromSelector(props.bottom);
9791
+ if (a && b) {
9792
+ makeSet(a);
9793
+ makeSet(b);
9794
+ union(a, b);
9795
+ }
9796
+ } else if ("for" in props && Array.isArray(props.for)) {
9797
+ const ids = props.for.map((s) => getIdFromSelector(s)).filter((s) => !!s);
9798
+ for (const id of ids) makeSet(id);
9799
+ for (let i = 1; i < ids.length; i++) union(ids[0], ids[i]);
9800
+ }
9801
+ }
9802
+ for (const id of Object.keys(parent)) {
9803
+ const rootId = find(id);
9804
+ if (!clusterByRoot.has(rootId))
9805
+ clusterByRoot.set(rootId, { componentIds: [], constraints: [] });
9806
+ clusterByRoot.get(rootId).componentIds.push(id);
9807
+ }
9808
+ for (const constraint of constraints) {
9809
+ const props = constraint._parsedProps;
9810
+ let compId;
9811
+ if ("left" in props) compId = getIdFromSelector(props.left);
9812
+ else if ("top" in props) compId = getIdFromSelector(props.top);
9813
+ else if ("for" in props) compId = getIdFromSelector(props.for[0]);
9814
+ if (!compId) continue;
9815
+ const root = find(compId);
9816
+ clusterByRoot.get(root)?.constraints.push(constraint);
9817
+ }
9818
+ const clusterMap = {};
9819
+ const packCompById = Object.fromEntries(
9820
+ packInput.components.map((c) => [c.componentId, c])
9821
+ );
9822
+ for (const [rootId, info] of clusterByRoot.entries()) {
9823
+ if (info.componentIds.length <= 1) continue;
9824
+ const solver = new kiwi2.Solver();
9825
+ const kVars = {};
9826
+ const getVar = (id, axis) => {
9827
+ const key = `${id}_${axis}`;
9828
+ if (!kVars[key]) kVars[key] = new kiwi2.Variable(key);
9829
+ return kVars[key];
9830
+ };
9831
+ const anchor = info.componentIds[0];
9832
+ solver.addConstraint(
9833
+ new kiwi2.Constraint(
9834
+ getVar(anchor, "x"),
9835
+ kiwi2.Operator.Eq,
9836
+ 0,
9837
+ kiwi2.Strength.required
9838
+ )
9839
+ );
9840
+ solver.addConstraint(
9841
+ new kiwi2.Constraint(
9842
+ getVar(anchor, "y"),
9843
+ kiwi2.Operator.Eq,
9844
+ 0,
9845
+ kiwi2.Strength.required
9846
+ )
9847
+ );
9848
+ for (const constraint of info.constraints) {
9849
+ const props = constraint._parsedProps;
9850
+ if ("xDist" in props) {
9851
+ const left = getIdFromSelector(props.left);
9852
+ const right = getIdFromSelector(props.right);
9853
+ if (left && right) {
9854
+ solver.addConstraint(
9855
+ new kiwi2.Constraint(
9856
+ new kiwi2.Expression(getVar(right, "x"), [-1, getVar(left, "x")]),
9857
+ kiwi2.Operator.Eq,
9858
+ props.xDist,
9859
+ kiwi2.Strength.required
9860
+ )
9861
+ );
9862
+ }
9863
+ } else if ("yDist" in props) {
9864
+ const top = getIdFromSelector(props.top);
9865
+ const bottom = getIdFromSelector(props.bottom);
9866
+ if (top && bottom) {
9867
+ solver.addConstraint(
9868
+ new kiwi2.Constraint(
9869
+ new kiwi2.Expression(getVar(top, "y"), [-1, getVar(bottom, "y")]),
9870
+ kiwi2.Operator.Eq,
9871
+ props.yDist,
9872
+ kiwi2.Strength.required
9873
+ )
9874
+ );
9875
+ }
9876
+ } else if ("sameX" in props && Array.isArray(props.for)) {
9877
+ const ids = props.for.map((s) => getIdFromSelector(s)).filter((s) => !!s);
9878
+ if (ids.length > 1) {
9879
+ const base = getVar(ids[0], "x");
9880
+ for (let i = 1; i < ids.length; i++) {
9881
+ solver.addConstraint(
9882
+ new kiwi2.Constraint(
9883
+ new kiwi2.Expression(getVar(ids[i], "x"), [-1, base]),
9884
+ kiwi2.Operator.Eq,
9885
+ 0,
9886
+ kiwi2.Strength.required
9887
+ )
9888
+ );
9889
+ }
9890
+ }
9891
+ } else if ("sameY" in props && Array.isArray(props.for)) {
9892
+ const ids = props.for.map((s) => getIdFromSelector(s)).filter((s) => !!s);
9893
+ if (ids.length > 1) {
9894
+ const base = getVar(ids[0], "y");
9895
+ for (let i = 1; i < ids.length; i++) {
9896
+ solver.addConstraint(
9897
+ new kiwi2.Constraint(
9898
+ new kiwi2.Expression(getVar(ids[i], "y"), [-1, base]),
9899
+ kiwi2.Operator.Eq,
9900
+ 0,
9901
+ kiwi2.Strength.required
9902
+ )
9903
+ );
9904
+ }
9905
+ }
9906
+ }
9907
+ }
9908
+ solver.updateVariables();
9909
+ const positions = {};
9910
+ for (const id of info.componentIds) {
9911
+ positions[id] = {
9912
+ x: getVar(id, "x").value(),
9913
+ y: getVar(id, "y").value()
9914
+ };
9915
+ }
9916
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
9917
+ for (const id of info.componentIds) {
9918
+ const comp = packCompById[id];
9919
+ const pos = positions[id];
9920
+ if (!comp) continue;
9921
+ for (const pad of comp.pads) {
9922
+ const ax = pos.x + pad.offset.x;
9923
+ const ay = pos.y + pad.offset.y;
9924
+ minX = Math.min(minX, ax - pad.size.x / 2);
9925
+ maxX = Math.max(maxX, ax + pad.size.x / 2);
9926
+ minY = Math.min(minY, ay - pad.size.y / 2);
9927
+ maxY = Math.max(maxY, ay + pad.size.y / 2);
9928
+ }
9929
+ }
9930
+ const clusterCenter = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
9931
+ const mergedPads = [];
9932
+ const relCenters = {};
9933
+ for (const id of info.componentIds) {
9934
+ const comp = packCompById[id];
9935
+ const pos = positions[id];
9936
+ if (!comp) continue;
9937
+ relCenters[id] = {
9938
+ x: pos.x - clusterCenter.x,
9939
+ y: pos.y - clusterCenter.y
9940
+ };
9941
+ for (const pad of comp.pads) {
9942
+ mergedPads.push({
9943
+ padId: pad.padId,
9944
+ networkId: pad.networkId,
9945
+ type: pad.type,
9946
+ size: pad.size,
9947
+ offset: {
9948
+ x: pos.x + pad.offset.x - clusterCenter.x,
9949
+ y: pos.y + pad.offset.y - clusterCenter.y
9950
+ }
9951
+ });
9952
+ }
9953
+ }
9954
+ packInput.components = packInput.components.filter(
9955
+ (c) => !info.componentIds.includes(c.componentId)
9956
+ );
9957
+ packInput.components.push({
9958
+ componentId: info.componentIds[0],
9959
+ pads: mergedPads,
9960
+ availableRotationDegrees: [0]
9961
+ });
9962
+ info.relativeCenters = relCenters;
9963
+ clusterMap[info.componentIds[0]] = info;
9964
+ }
9965
+ return clusterMap;
9966
+ };
9967
+
9968
+ // lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/applyPackOutput.ts
9969
+ import { translate as translate5, rotate as rotate2, compose as compose4 } from "transformation-matrix";
9970
+ import { transformPCBElements } from "@tscircuit/circuit-json-util";
9759
9971
  var isDescendantGroup = (db, groupId, ancestorId) => {
9760
9972
  if (groupId === ancestorId) return true;
9761
9973
  const group = db.source_group.get(groupId);
9762
9974
  if (!group || !group.parent_source_group_id) return false;
9763
9975
  return isDescendantGroup(db, group.parent_source_group_id, ancestorId);
9764
9976
  };
9765
- var Group_doInitialPcbLayoutPack = (group) => {
9977
+ var applyPackOutput = (group, packOutput, clusterMap) => {
9766
9978
  const { db } = group.root;
9767
- const { _parsedProps: props } = group;
9768
- const {
9769
- packOrderStrategy,
9770
- packPlacementStrategy,
9771
- gap: gapProp,
9772
- pcbGap,
9773
- // @ts-expect-error remove when props introduces pcbPackGap
9774
- pcbPackGap
9775
- } = props;
9776
- const gap = pcbPackGap ?? pcbGap ?? gapProp;
9777
- const gapMm = length4.parse(gap ?? DEFAULT_MIN_GAP);
9778
- const packInput = {
9779
- ...convertPackOutputToPackInput(
9780
- convertCircuitJsonToPackOutput(db.toArray(), {
9781
- source_group_id: group.source_group_id,
9782
- shouldAddInnerObstacles: true
9783
- })
9784
- ),
9785
- // @ts-expect-error we're missing some pack order strategies
9786
- orderStrategy: packOrderStrategy ?? "largest_to_smallest",
9787
- placementStrategy: packPlacementStrategy ?? "minimum_sum_squared_distance_to_network",
9788
- minGap: gapMm
9789
- };
9790
- if (debug6.enabled) {
9791
- group.root?.emit("debug:logOutput", {
9792
- type: "debug:logOutput",
9793
- name: `packInput-circuitjson-${group.name}`,
9794
- content: JSON.stringify(db.toArray())
9795
- });
9796
- group.root?.emit("debug:logOutput", {
9797
- type: "debug:logOutput",
9798
- name: `packInput-${group.name}`,
9799
- content: packInput
9800
- });
9801
- }
9802
- const packOutput = pack(packInput);
9803
- if (debug6.enabled) {
9804
- const graphics = getGraphicsFromPackOutput(packOutput);
9805
- graphics.title = `packOutput-${group.name}`;
9806
- global.debugGraphics?.push(graphics);
9807
- }
9808
9979
  for (const packedComponent of packOutput.components) {
9809
9980
  const { center, componentId, ccwRotationOffset, ccwRotationDegrees } = packedComponent;
9981
+ const cluster = clusterMap[componentId];
9982
+ if (cluster) {
9983
+ const rotationDegrees2 = ccwRotationDegrees ?? ccwRotationOffset ?? 0;
9984
+ const angleRad = rotationDegrees2 * Math.PI / 180;
9985
+ for (const memberId of cluster.componentIds) {
9986
+ const rel = cluster.relativeCenters[memberId];
9987
+ if (!rel) continue;
9988
+ const rotatedRel = {
9989
+ x: rel.x * Math.cos(angleRad) - rel.y * Math.sin(angleRad),
9990
+ y: rel.x * Math.sin(angleRad) + rel.y * Math.cos(angleRad)
9991
+ };
9992
+ const member = db.pcb_component.get(memberId);
9993
+ if (!member) continue;
9994
+ const originalCenter2 = member.center;
9995
+ const transformMatrix2 = compose4(
9996
+ group._computePcbGlobalTransformBeforeLayout(),
9997
+ translate5(center.x + rotatedRel.x, center.y + rotatedRel.y),
9998
+ rotate2(angleRad),
9999
+ translate5(-originalCenter2.x, -originalCenter2.y)
10000
+ );
10001
+ const related = db.toArray().filter(
10002
+ (elm) => "pcb_component_id" in elm && elm.pcb_component_id === memberId
10003
+ );
10004
+ transformPCBElements(related, transformMatrix2);
10005
+ }
10006
+ continue;
10007
+ }
9810
10008
  const pcbComponent = db.pcb_component.get(componentId);
9811
10009
  if (pcbComponent) {
9812
10010
  const currentGroupId = group.source_group_id;
@@ -9862,20 +10060,16 @@ var Group_doInitialPcbLayoutPack = (group) => {
9862
10060
  }
9863
10061
  }
9864
10062
  if ("pcb_component_id" in elm && elm.pcb_component_id) {
9865
- const pcbComponent2 = db.pcb_component.get(elm.pcb_component_id);
9866
- if (pcbComponent2?.source_component_id) {
9867
- const sourceComponent = db.source_component.get(
9868
- pcbComponent2.source_component_id
10063
+ const pcbComp = db.pcb_component.get(elm.pcb_component_id);
10064
+ if (pcbComp?.source_component_id) {
10065
+ const sourceComp = db.source_component.get(
10066
+ pcbComp.source_component_id
9869
10067
  );
9870
- if (sourceComponent?.source_group_id) {
9871
- if (sourceComponent.source_group_id === componentId) {
10068
+ if (sourceComp?.source_group_id) {
10069
+ if (sourceComp.source_group_id === componentId) {
9872
10070
  return true;
9873
10071
  }
9874
- if (isDescendantGroup(
9875
- db,
9876
- sourceComponent.source_group_id,
9877
- componentId
9878
- )) {
10072
+ if (isDescendantGroup(db, sourceComp.source_group_id, componentId)) {
9879
10073
  return true;
9880
10074
  }
9881
10075
  }
@@ -9888,6 +10082,56 @@ var Group_doInitialPcbLayoutPack = (group) => {
9888
10082
  }
9889
10083
  };
9890
10084
 
10085
+ // lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/Group_doInitialPcbLayoutPack.ts
10086
+ var DEFAULT_MIN_GAP = "1mm";
10087
+ var debug6 = Debug7("Group_doInitialPcbLayoutPack");
10088
+ var Group_doInitialPcbLayoutPack = (group) => {
10089
+ const { db } = group.root;
10090
+ const { _parsedProps: props } = group;
10091
+ const {
10092
+ packOrderStrategy,
10093
+ packPlacementStrategy,
10094
+ gap: gapProp,
10095
+ pcbGap,
10096
+ // @ts-expect-error remove when props introduces pcbPackGap
10097
+ pcbPackGap
10098
+ } = props;
10099
+ const gap = pcbPackGap ?? pcbGap ?? gapProp;
10100
+ const gapMm = length4.parse(gap ?? DEFAULT_MIN_GAP);
10101
+ const packInput = {
10102
+ ...convertPackOutputToPackInput(
10103
+ convertCircuitJsonToPackOutput(db.toArray(), {
10104
+ source_group_id: group.source_group_id,
10105
+ shouldAddInnerObstacles: true
10106
+ })
10107
+ ),
10108
+ // @ts-expect-error we're missing some pack order strategies
10109
+ orderStrategy: packOrderStrategy ?? "largest_to_smallest",
10110
+ placementStrategy: packPlacementStrategy ?? "minimum_sum_squared_distance_to_network",
10111
+ minGap: gapMm
10112
+ };
10113
+ const clusterMap = applyComponentConstraintClusters(group, packInput);
10114
+ if (debug6.enabled) {
10115
+ group.root?.emit("debug:logOutput", {
10116
+ type: "debug:logOutput",
10117
+ name: `packInput-circuitjson-${group.name}`,
10118
+ content: JSON.stringify(db.toArray())
10119
+ });
10120
+ group.root?.emit("debug:logOutput", {
10121
+ type: "debug:logOutput",
10122
+ name: `packInput-${group.name}`,
10123
+ content: packInput
10124
+ });
10125
+ }
10126
+ const packOutput = pack(packInput);
10127
+ if (debug6.enabled) {
10128
+ const graphics = getGraphicsFromPackOutput(packOutput);
10129
+ graphics.title = `packOutput-${group.name}`;
10130
+ global.debugGraphics?.push(graphics);
10131
+ }
10132
+ applyPackOutput(group, packOutput, clusterMap);
10133
+ };
10134
+
9891
10135
  // lib/components/primitive-components/Group/Group_doInitialPcbLayoutFlex.ts
9892
10136
  import { getMinimumFlexContainer as getMinimumFlexContainer2 } from "@tscircuit/circuit-json-util";
9893
10137
  import { RootFlexBox as RootFlexBox2 } from "@tscircuit/miniflex";
@@ -12696,7 +12940,7 @@ var edgeSpecifiers = [
12696
12940
  "bottomedge",
12697
12941
  "center"
12698
12942
  ];
12699
- var Constraint2 = class extends PrimitiveComponent2 {
12943
+ var Constraint3 = class extends PrimitiveComponent2 {
12700
12944
  get config() {
12701
12945
  return {
12702
12946
  componentName: "Constraint",
@@ -14379,7 +14623,7 @@ import { identity as identity6 } from "transformation-matrix";
14379
14623
  var package_default = {
14380
14624
  name: "@tscircuit/core",
14381
14625
  type: "module",
14382
- version: "0.0.706",
14626
+ version: "0.0.707",
14383
14627
  types: "dist/index.d.ts",
14384
14628
  main: "dist/index.js",
14385
14629
  module: "dist/index.js",
@@ -14889,7 +15133,7 @@ export {
14889
15133
  Capacitor,
14890
15134
  Chip,
14891
15135
  Circuit,
14892
- Constraint2 as Constraint,
15136
+ Constraint3 as Constraint,
14893
15137
  Crystal,
14894
15138
  Cutout,
14895
15139
  Diode,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.707",
4
+ "version": "0.0.708",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",