@tscircuit/core 0.0.1506 → 0.0.1508

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
@@ -48,7 +48,7 @@ __export(components_exports, {
48
48
  Fiducial: () => Fiducial,
49
49
  Footprint: () => Footprint,
50
50
  Fuse: () => Fuse,
51
- Group: () => Group,
51
+ Group: () => Group3,
52
52
  Hole: () => Hole,
53
53
  Inductor: () => Inductor,
54
54
  Interconnect: () => Interconnect,
@@ -5527,12 +5527,8 @@ var getMaxLengthFromConnectedComponents = (ports, { db }) => {
5527
5527
  if (sourceComponent?.ftype === "simple_capacitor") {
5528
5528
  return sourceComponent.max_decoupling_trace_length;
5529
5529
  }
5530
- if (sourceComponent?.ftype === "simple_crystal") {
5531
- const crystalProps2 = port.parent?._parsedProps;
5532
- return crystalProps2?.maxTraceLength ?? 10;
5533
- }
5534
5530
  return null;
5535
- }).filter((length7) => length7 !== null);
5531
+ }).filter((length7) => typeof length7 === "number");
5536
5532
  if (componentMaxLengths.length === 0) return void 0;
5537
5533
  return Math.min(...componentMaxLengths);
5538
5534
  };
@@ -21015,397 +21011,257 @@ function Group_doInitialSchematicLayoutSections(group) {
21015
21011
  import { SchematicTracePipelineSolver as SchematicTracePipelineSolver4 } from "@tscircuit/schematic-trace-solver";
21016
21012
  import Debug12 from "debug";
21017
21013
 
21018
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
21019
- import {
21020
- getBoundFromCenteredRect as getBoundFromCenteredRect4,
21021
- getBoundsCenter as getBoundsCenter2
21022
- } from "@tscircuit/math-utils";
21014
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
21015
+ import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
21023
21016
  import "@tscircuit/schematic-trace-solver";
21017
+ import Debug10 from "debug";
21024
21018
 
21025
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
21026
- import {
21027
- getBoundFromCenteredRect as getBoundFromCenteredRect3,
21028
- getBoundsCenter
21029
- } from "@tscircuit/math-utils";
21030
-
21031
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicTextBounds.ts
21032
- import { getBoundsFromPoints as getBoundsFromPoints4 } from "@tscircuit/math-utils";
21033
- var DEFAULT_SCHEMATIC_TEXT_FONT_SIZE = 0.18;
21034
- function getSchematicTextBounds(text) {
21035
- const fontSize = text.font_size ?? DEFAULT_SCHEMATIC_TEXT_FONT_SIZE;
21036
- const width = getSchematicTextWidth({
21037
- text: text.text ?? "",
21038
- fontSize
21039
- });
21040
- const height = fontSize;
21041
- const anchor = text.anchor ?? "center";
21042
- let minX;
21043
- let maxX;
21044
- if (anchor.includes("left")) {
21045
- minX = 0;
21046
- maxX = width;
21047
- } else if (anchor.includes("right")) {
21048
- minX = -width;
21049
- maxX = 0;
21050
- } else {
21051
- minX = -width / 2;
21052
- maxX = width / 2;
21053
- }
21054
- let minY;
21055
- let maxY;
21056
- if (anchor.includes("top")) {
21057
- minY = -height;
21058
- maxY = 0;
21059
- } else if (anchor.includes("bottom")) {
21060
- minY = 0;
21061
- maxY = height;
21062
- } else {
21063
- minY = -height / 2;
21064
- maxY = height / 2;
21065
- }
21066
- const rotation4 = (text.rotation ?? 0) * Math.PI / 180;
21067
- const cos = Math.cos(rotation4);
21068
- const sin = Math.sin(rotation4);
21069
- const corners = [
21070
- { x: minX, y: minY },
21071
- { x: minX, y: maxY },
21072
- { x: maxX, y: minY },
21073
- { x: maxX, y: maxY }
21074
- ].map((corner) => ({
21075
- x: text.position.x + corner.x * cos - corner.y * sin,
21076
- y: text.position.y + corner.x * sin + corner.y * cos
21077
- }));
21078
- return getBoundsFromPoints4(corners);
21079
- }
21080
-
21081
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
21082
- var REFDES_TEXT_BOX_CHIP_OVERLAP = 0.01;
21083
- var REFDES_TEXT_BOX_CLEARANCE = 0.06;
21084
- function isReferenceDesignatorText(text, sourceComponent) {
21085
- if (!sourceComponent) return false;
21086
- return text.text === sourceComponent.display_name || text.text === sourceComponent.name;
21087
- }
21088
- function schematicTextToTextBox(text, options = {}) {
21089
- if (!text.text) return null;
21090
- const bounds = getSchematicTextBounds(text);
21091
- if (isReferenceDesignatorText(text, options.sourceComponent) && options.schematicComponent?.center && options.schematicComponent?.size) {
21092
- const componentBounds = getBoundFromCenteredRect3({
21093
- center: options.schematicComponent.center,
21094
- width: options.schematicComponent.size.width,
21095
- height: options.schematicComponent.size.height
21096
- });
21097
- const textHeight = bounds.maxY - bounds.minY;
21098
- if (bounds.minY > componentBounds.maxY) {
21099
- bounds.minX -= REFDES_TEXT_BOX_CLEARANCE;
21100
- bounds.maxX += REFDES_TEXT_BOX_CLEARANCE;
21101
- bounds.minY = componentBounds.maxY - REFDES_TEXT_BOX_CHIP_OVERLAP;
21102
- bounds.maxY = componentBounds.maxY + textHeight + REFDES_TEXT_BOX_CLEARANCE;
21103
- }
21104
- }
21019
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetLabelTextBounds.ts
21020
+ var NET_LABEL_TEXT_HEIGHT = 0.18;
21021
+ var getNetLabelTextBounds = ({
21022
+ center,
21023
+ text
21024
+ }) => {
21025
+ const width = getSchematicNetLabelTextWidth({ text });
21026
+ const halfWidth = width / 2;
21027
+ const halfHeight = NET_LABEL_TEXT_HEIGHT / 2;
21105
21028
  return {
21106
- chipId: text.schematic_component_id,
21107
- center: getBoundsCenter(bounds),
21108
- width: bounds.maxX - bounds.minX,
21109
- height: bounds.maxY - bounds.minY,
21110
- text: text.text
21029
+ minX: center.x - halfWidth,
21030
+ maxX: center.x + halfWidth,
21031
+ minY: center.y - halfHeight,
21032
+ maxY: center.y + halfHeight
21111
21033
  };
21112
- }
21113
-
21114
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortSelector.ts
21115
- var getSchematicPortSelector = ({
21116
- componentName,
21117
- schematicPort,
21118
- sourcePort
21119
- }) => {
21120
- const pinIdentifier = schematicPort.pin_number ?? sourcePort?.name ?? sourcePort?.port_hints?.[0];
21121
- return `${componentName}.${pinIdentifier ?? schematicPort.schematic_port_id}`;
21122
21034
  };
21123
21035
 
21124
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
21125
- var DEFAULT_MAX_MSP_PAIR_DISTANCE = 2.4;
21126
- var SCHEMATIC_RAIL_NET_LABEL_HEIGHT = 0.42;
21127
- function createSchematicTraceSolverInputProblem(group, opts = {}) {
21128
- const { db } = group.root;
21129
- const connKeyToSourceNet = /* @__PURE__ */ new Map();
21130
- const childGroups = [];
21131
- const componentsToVisit = [...group.children].reverse();
21132
- while (componentsToVisit.length > 0) {
21133
- const child = componentsToVisit.pop();
21134
- if (child instanceof Group) {
21135
- if (child.isSubcircuit) {
21136
- if (child._parsedProps.showAsSchematicBox) childGroups.push(child);
21137
- continue;
21036
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetNameFromPorts.ts
21037
+ var getNetNameFromPorts = (ports) => {
21038
+ for (const port of ports) {
21039
+ const traces = port._getDirectlyConnectedTraces();
21040
+ for (const trace of traces) {
21041
+ const displayLabel = trace._getSchematicNetLabelText();
21042
+ if (displayLabel) {
21043
+ return { name: displayLabel, wasAssignedDisplayLabel: true };
21138
21044
  }
21139
- childGroups.push(child);
21140
- }
21141
- componentsToVisit.push(...[...child.children].reverse());
21142
- }
21143
- const allSchematicGroupIds = [
21144
- group.schematic_group_id,
21145
- ...childGroups.map((a) => a.schematic_group_id)
21146
- ];
21147
- const schematicComponents = db.schematic_component.list().filter((a) => allSchematicGroupIds.includes(a.schematic_group_id)).filter(
21148
- (schematicComponent) => opts.schematicSheetId === void 0 || schematicComponent.schematic_sheet_id === opts.schematicSheetId
21149
- );
21150
- const schematicComponentIds = new Set(
21151
- schematicComponents.map((component) => component.schematic_component_id)
21152
- );
21153
- const schematicComponentsById = new Map(
21154
- schematicComponents.map((component) => [
21155
- component.schematic_component_id,
21156
- component
21157
- ])
21158
- );
21159
- const textBoxes = db.schematic_text.list().filter(
21160
- (text) => text.schematic_component_id && schematicComponentIds.has(text.schematic_component_id)
21161
- ).map((text) => {
21162
- const schematicComponent = schematicComponentsById.get(
21163
- text.schematic_component_id
21164
- );
21165
- if (!schematicComponent) return;
21166
- const sourceComponent = db.source_component.get(
21167
- schematicComponent.source_component_id
21168
- );
21169
- return schematicTextToTextBox(text, {
21170
- schematicComponent,
21171
- sourceComponent
21172
- });
21173
- }).filter((textBox) => Boolean(textBox));
21174
- const sectionIdBySchematicComponentId = /* @__PURE__ */ new Map();
21175
- for (const component of group.getDescendants()) {
21176
- const schematicComponentId = component.schematic_component_id;
21177
- const sectionId = component.getSchematicSectionName();
21178
- if (schematicComponentId && sectionId) {
21179
- sectionIdBySchematicComponentId.set(schematicComponentId, sectionId);
21180
21045
  }
21181
21046
  }
21182
- const chips = [];
21183
- const pinIdToSchematicPortId = /* @__PURE__ */ new Map();
21184
- const schematicPortIdToPinId = /* @__PURE__ */ new Map();
21185
- for (const schematicComponent of schematicComponents) {
21186
- const chipId = schematicComponent.schematic_component_id;
21187
- const pins = [];
21188
- const sourceComponent = db.source_component.getWhere({
21189
- source_component_id: schematicComponent.source_component_id
21190
- });
21191
- const schematicPorts = db.schematic_port.list({
21192
- schematic_component_id: schematicComponent.schematic_component_id
21193
- });
21194
- for (const schematicPort of schematicPorts) {
21195
- const sourcePort = db.source_port.get(schematicPort.source_port_id);
21196
- const selector = getSchematicPortSelector({
21197
- componentName: sourceComponent?.name ?? schematicComponent.schematic_component_id,
21198
- schematicPort,
21199
- sourcePort
21200
- });
21201
- pinIdToSchematicPortId.set(selector, schematicPort.schematic_port_id);
21202
- schematicPortIdToPinId.set(schematicPort.schematic_port_id, selector);
21203
- }
21204
- for (const schematicPort of schematicPorts) {
21205
- const pinId = schematicPortIdToPinId.get(schematicPort.schematic_port_id);
21206
- pins.push({
21207
- pinId,
21208
- x: schematicPort.center.x,
21209
- y: schematicPort.center.y,
21210
- // Pass the port's true facing direction (known from the schematic
21211
- // symbol). The chip box handed to the solver is text-inclusive, so for
21212
- // small parts with a large reference designator the pins sit inside the
21213
- // box. The solver snaps such pins to the box edge along this facing
21214
- // direction (rather than guessing from geometry, which would pick the
21215
- // wrong edge for a resistor whose ref text widened the box).
21216
- _facingDirection: convertFacingDirectionToElbowDirection(
21217
- schematicPort.facing_direction ?? null
21218
- )
21219
- });
21220
- }
21221
- const sectionId = sectionIdBySchematicComponentId.get(
21222
- schematicComponent.schematic_component_id
21223
- );
21224
- const layoutBounds = getSchematicComponentWithTextBounds({ db, schematicComponent }) ?? getBoundFromCenteredRect4({
21225
- center: schematicComponent.center,
21226
- width: schematicComponent.size.width,
21227
- height: schematicComponent.size.height
21228
- });
21229
- chips.push({
21230
- chipId,
21231
- center: getBoundsCenter2(layoutBounds),
21232
- width: layoutBounds.maxX - layoutBounds.minX,
21233
- height: layoutBounds.maxY - layoutBounds.minY,
21234
- pins,
21235
- sectionId
21236
- });
21047
+ const db = ports.find((port) => port.root)?.root?.db;
21048
+ const sourcePortIds = ports.map((port) => port.source_port_id).filter((sourcePortId) => Boolean(sourcePortId));
21049
+ const netName = db ? getNetNameFromSourcePorts(db, sourcePortIds) : ports.map((port) => port._getNetLabelText()).filter((name) => Boolean(name)).sort(
21050
+ (a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
21051
+ )[0];
21052
+ return { name: netName ?? "", wasAssignedDisplayLabel: false };
21053
+ };
21054
+
21055
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/oppositeSide.ts
21056
+ var oppositeSide = (input) => {
21057
+ switch (input) {
21058
+ case "x+":
21059
+ return "left";
21060
+ case "x-":
21061
+ return "right";
21062
+ case "y+":
21063
+ return "bottom";
21064
+ case "y-":
21065
+ return "top";
21066
+ case "left":
21067
+ return "right";
21068
+ case "top":
21069
+ return "bottom";
21070
+ case "right":
21071
+ return "left";
21072
+ case "bottom":
21073
+ return "top";
21237
21074
  }
21238
- const allSourceAndSchematicPortIdsInScope = /* @__PURE__ */ new Set();
21239
- const schPortIdToSourcePortId = /* @__PURE__ */ new Map();
21240
- const sourcePortIdToSchPortId = /* @__PURE__ */ new Map();
21241
- const userNetIdToConnKey = /* @__PURE__ */ new Map();
21242
- for (const sc of schematicComponents) {
21243
- const ports = db.schematic_port.list({
21244
- schematic_component_id: sc.schematic_component_id
21245
- });
21246
- for (const sp of ports) {
21247
- allSourceAndSchematicPortIdsInScope.add(sp.schematic_port_id);
21248
- if (sp.source_port_id) {
21249
- schPortIdToSourcePortId.set(sp.schematic_port_id, sp.source_port_id);
21250
- sourcePortIdToSchPortId.set(sp.source_port_id, sp.schematic_port_id);
21251
- }
21252
- }
21075
+ };
21076
+
21077
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
21078
+ var debug8 = Debug10("Group_doInitialSchematicTraceRender");
21079
+ var isSameNet = (label, sourceNet) => label.source_net_id != null && label.source_net_id === sourceNet.source_net_id;
21080
+ var isUserDefinedNetLabelRedundantWithPlacement = (label, solverPlacement) => {
21081
+ if (!isSameNet(label, solverPlacement.sourceNet)) {
21082
+ return false;
21253
21083
  }
21254
- const allowedSubcircuitIds = /* @__PURE__ */ new Set();
21255
- if (group.subcircuit_id) allowedSubcircuitIds.add(group.subcircuit_id);
21256
- for (const cg of childGroups) {
21257
- if (cg.subcircuit_id) allowedSubcircuitIds.add(cg.subcircuit_id);
21084
+ if (label.schematicPortIds.some(
21085
+ (id) => solverPlacement.schematicPortIds.has(id)
21086
+ )) {
21087
+ return true;
21258
21088
  }
21259
- const tracesInScope = db.source_trace.list().filter((st) => {
21260
- if (st.subcircuit_id === group.subcircuit_id) return true;
21261
- for (const source_port_id of st.connected_source_port_ids) {
21262
- if (sourcePortIdToSchPortId.has(source_port_id)) return true;
21263
- }
21264
- return false;
21265
- });
21266
- const externalNetIds = tracesInScope.flatMap(
21267
- (st) => st.connected_source_net_ids
21089
+ if (!label.center) return false;
21090
+ return doBoundsOverlap2(
21091
+ getNetLabelTextBounds({ center: label.center, text: label.text }),
21092
+ solverPlacement.bounds
21268
21093
  );
21269
- for (const netId of externalNetIds) {
21270
- const net = db.source_net.get(netId);
21271
- if (net?.subcircuit_id) {
21272
- allowedSubcircuitIds.add(net.subcircuit_id);
21094
+ };
21095
+ function applyNetLabelPlacements(args) {
21096
+ const {
21097
+ group,
21098
+ solver,
21099
+ connKeyToSourceNet,
21100
+ userNetIdToConnKey,
21101
+ pinIdToSchematicPortId,
21102
+ connKeysWithExplicitPortNetTraces,
21103
+ schematicPortIdsWithPreExistingNetLabels,
21104
+ schematicPortIdsWithRoutedTraces
21105
+ } = args;
21106
+ const { db } = group.root;
21107
+ const netLabelPlacements = solver.netLabelNetLabelCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelTraceCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelPlacementSolver?.netLabelPlacements ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().netLabelPlacements ?? [];
21108
+ const dedupedNetLabelPlacements = [];
21109
+ const netLabelPlacementKeys = /* @__PURE__ */ new Set();
21110
+ for (const placement of netLabelPlacements) {
21111
+ const pinIds = [...placement.pinIds ?? []].sort();
21112
+ const key = `${placement.globalConnNetId}|${pinIds.join("::")}|${placement.netId ?? ""}`;
21113
+ if (netLabelPlacementKeys.has(key)) {
21114
+ debug8(
21115
+ `skipping duplicate placement for "${placement.netId}" REASON:identical net label placement`
21116
+ );
21117
+ continue;
21273
21118
  }
21119
+ netLabelPlacementKeys.add(key);
21120
+ dedupedNetLabelPlacements.push(placement);
21274
21121
  }
21275
- const directConnections = [];
21276
- const connectedPairKeys = /* @__PURE__ */ new Set();
21277
- const connKeysWithExplicitPortNetTraces = /* @__PURE__ */ new Set();
21278
- for (const sourceTrace of tracesInScope) {
21279
- if (sourceTrace?.subcircuit_connectivity_map_key && (sourceTrace.connected_source_port_ids?.length ?? 0) > 0 && (sourceTrace.connected_source_net_ids?.length ?? 0) > 0) {
21280
- connKeysWithExplicitPortNetTraces.add(
21281
- sourceTrace.subcircuit_connectivity_map_key
21282
- );
21122
+ const netLabelPlacementCountByGlobalNetId = /* @__PURE__ */ new Map();
21123
+ const routedPairKeysByGlobalNetId = /* @__PURE__ */ new Map();
21124
+ const globalNetIdsWithPortOnlyPlacements = /* @__PURE__ */ new Set();
21125
+ for (const placement of dedupedNetLabelPlacements) {
21126
+ netLabelPlacementCountByGlobalNetId.set(
21127
+ placement.globalConnNetId,
21128
+ (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) + 1
21129
+ );
21130
+ if ((placement.pinIds?.length ?? 0) > 1) {
21131
+ if (!routedPairKeysByGlobalNetId.has(placement.globalConnNetId)) {
21132
+ routedPairKeysByGlobalNetId.set(placement.globalConnNetId, /* @__PURE__ */ new Set());
21133
+ }
21134
+ routedPairKeysByGlobalNetId.get(placement.globalConnNetId).add(placement.pinIds.slice().sort().join("::"));
21135
+ }
21136
+ if ((placement.pinIds?.length ?? 0) <= 1) {
21137
+ globalNetIdsWithPortOnlyPlacements.add(placement.globalConnNetId);
21283
21138
  }
21284
21139
  }
21285
- for (const st of db.source_trace.list()) {
21286
- if (st.subcircuit_id && !allowedSubcircuitIds.has(st.subcircuit_id)) {
21287
- continue;
21140
+ const globalConnMap = solver.mspConnectionPairSolver.globalConnMap;
21141
+ const userDefinedNetLabels = group.selectAll("netlabel").map((label) => {
21142
+ if (!label.schematic_net_label_id) return null;
21143
+ const dbLabel = db.schematic_net_label.get(label.schematic_net_label_id);
21144
+ if (!dbLabel) return null;
21145
+ return {
21146
+ ...dbLabel,
21147
+ schematicPortIds: label._getConnectedPorts().map((port) => port.schematic_port_id).filter((id) => Boolean(id))
21148
+ };
21149
+ }).filter((label) => label !== null);
21150
+ for (const placement of dedupedNetLabelPlacements) {
21151
+ debug8(`processing placement: ${placement.netId}`);
21152
+ const placementUserNetId = globalConnMap.getIdsConnectedToNet(placement.globalConnNetId).find((id) => userNetIdToConnKey.get(id));
21153
+ const placementConnKey = userNetIdToConnKey.get(placementUserNetId);
21154
+ const orientation = placement.orientation;
21155
+ const anchor_side = oppositeSide(orientation);
21156
+ let sourceNet;
21157
+ if (placementConnKey) {
21158
+ sourceNet = connKeyToSourceNet.get(placementConnKey);
21288
21159
  }
21289
- const connected = (st.connected_source_port_ids ?? []).map((sourcePortId) => sourcePortIdToSchPortId.get(sourcePortId)).filter(
21290
- (schematicPortId) => Boolean(schematicPortId) && allSourceAndSchematicPortIdsInScope.has(schematicPortId)
21160
+ const schPortIds = placement.pinIds.map(
21161
+ (pinId) => pinIdToSchematicPortId.get(pinId)
21291
21162
  );
21292
- if (connected.length >= 2) {
21293
- const [a, b] = connected.slice(0, 2);
21294
- const pairKey = [a, b].sort().join("::");
21295
- if (!connectedPairKeys.has(pairKey)) {
21296
- connectedPairKeys.add(pairKey);
21297
- const traceLabel = st.name ?? st.display_name;
21298
- const userNetId = traceLabel ?? st.source_trace_id;
21299
- if (st.subcircuit_connectivity_map_key) {
21300
- userNetIdToConnKey.set(userNetId, st.subcircuit_connectivity_map_key);
21301
- }
21302
- const portA = db.schematic_port.get(a);
21303
- const portB = db.schematic_port.get(b);
21304
- let portDistance = 0;
21305
- if (portA && portB) {
21306
- portDistance = Math.sqrt(
21307
- (portA.center.x - portB.center.x) ** 2 + (portA.center.y - portB.center.y) ** 2
21308
- );
21309
- }
21310
- const maxMspDist = group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
21311
- let netLabelWidth;
21312
- if (traceLabel && !traceLabel.startsWith(".") && portDistance > maxMspDist) {
21313
- netLabelWidth = Number(
21314
- getSchematicNetLabelTextWidth({
21315
- text: String(traceLabel),
21316
- font_size: 0.14
21317
- }).toFixed(2)
21318
- );
21319
- }
21320
- directConnections.push({
21321
- pinIds: [a, b].map((id) => schematicPortIdToPinId.get(id)),
21322
- netId: userNetId,
21323
- netLabelWidth
21324
- });
21163
+ let schematicSheetId = group._resolveSchematicSheetId();
21164
+ const schematicPort = db.schematic_port.get(schPortIds[0]);
21165
+ if (schematicPort?.schematic_sheet_id) {
21166
+ schematicSheetId = schematicPort.schematic_sheet_id;
21167
+ }
21168
+ let anchor_position = placement.anchorPoint;
21169
+ if (schPortIds.length === 1 && schPortIds[0]) {
21170
+ const anchorSchPort = db.schematic_port.get(schPortIds[0]);
21171
+ if (anchorSchPort?.center && !anchorSchPort.is_connected) {
21172
+ anchor_position = anchorSchPort.center;
21325
21173
  }
21326
21174
  }
21327
- }
21328
- const netConnections = [];
21329
- for (const net of db.source_net.list().filter(
21330
- (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
21331
- )) {
21332
- if (net.subcircuit_connectivity_map_key) {
21333
- connKeyToSourceNet.set(net.subcircuit_connectivity_map_key, net);
21175
+ if (schPortIds.some(
21176
+ (schPortId) => schematicPortIdsWithPreExistingNetLabels.has(schPortId)
21177
+ )) {
21178
+ debug8(
21179
+ `skipping net label placement for "${placement.netId}" REASON:schematic port has pre-existing net label`
21180
+ );
21181
+ continue;
21334
21182
  }
21335
- }
21336
- const connKeyToPinIds = /* @__PURE__ */ new Map();
21337
- for (const [schId, srcPortId] of schPortIdToSourcePortId) {
21338
- const sp = db.source_port.get(srcPortId);
21339
- if (!sp?.subcircuit_connectivity_map_key) continue;
21340
- const connKey = sp.subcircuit_connectivity_map_key;
21341
- if (!connKeyToPinIds.has(connKey)) connKeyToPinIds.set(connKey, []);
21342
- connKeyToPinIds.get(connKey).push(schId);
21343
- }
21344
- for (const [connKey, schematicPortIds] of connKeyToPinIds) {
21345
- const sourceNet = connKeyToSourceNet.get(connKey);
21346
- if (sourceNet && schematicPortIds.length >= 1) {
21347
- const userNetId = String(
21348
- sourceNet.name || sourceNet.source_net_id || connKey
21183
+ if (sourceNet) {
21184
+ const isPowerOrGroundNet = sourceNet.is_ground || sourceNet.is_power;
21185
+ const hasExplicitPortNetTrace = connKeysWithExplicitPortNetTraces.has(
21186
+ placementConnKey
21349
21187
  );
21350
- userNetIdToConnKey.set(userNetId, connKey);
21351
- const netLabelTextWidth = Number(
21352
- getSchematicNetLabelTextWidth({ text: String(userNetId) }).toFixed(2)
21188
+ const hasRoutedTraceForPlacementPort = schPortIds.some(
21189
+ (id) => schematicPortIdsWithRoutedTraces.has(id)
21353
21190
  );
21354
- let netLabelWidth = netLabelTextWidth;
21355
- let netLabelHeight;
21356
- if (sourceNet.is_ground || sourceNet.is_power) {
21357
- netLabelWidth = SCHEMATIC_RAIL_NET_LABEL_HEIGHT;
21358
- netLabelHeight = netLabelTextWidth;
21191
+ const hasSingleLabelPlacement = (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) <= 1;
21192
+ const shouldSkipImplicitSignalNetLabel = !hasExplicitPortNetTrace && !isPowerOrGroundNet && hasRoutedTraceForPlacementPort && hasSingleLabelPlacement;
21193
+ if (shouldSkipImplicitSignalNetLabel) {
21194
+ debug8(
21195
+ `skipping net label placement for "${placement.netId}" REASON:schematic port has routed trace`
21196
+ );
21197
+ continue;
21359
21198
  }
21360
- netConnections.push({
21361
- netId: userNetId,
21362
- pinIds: schematicPortIds.map(
21363
- (portId) => schematicPortIdToPinId.get(portId)
21364
- ),
21365
- netLabelWidth,
21366
- netLabelHeight
21199
+ const text2 = sourceNet.name;
21200
+ const center2 = computeSchematicNetLabelCenter({
21201
+ anchor_position,
21202
+ anchor_side,
21203
+ text: text2
21367
21204
  });
21368
- }
21369
- }
21370
- const availableNetLabelOrientations = (() => {
21371
- const netToAllowedOrientations = {};
21372
- const presentNetIds = new Set(netConnections.map((nc) => nc.netId));
21373
- for (const net of db.source_net.list().filter(
21374
- (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
21375
- )) {
21376
- if (!net.name) continue;
21377
- if (!presentNetIds.has(net.name)) continue;
21378
- if (net.is_ground) {
21379
- netToAllowedOrientations[net.name] = ["y-"];
21380
- } else if (net.is_power) {
21381
- netToAllowedOrientations[net.name] = ["y+"];
21382
- } else {
21383
- netToAllowedOrientations[net.name] = ["x-", "x+"];
21205
+ if (!isPowerOrGroundNet) {
21206
+ const solverPlacement = {
21207
+ sourceNet,
21208
+ text: text2,
21209
+ schematicPortIds: new Set(schPortIds),
21210
+ bounds: getNetLabelTextBounds({ center: center2, text: text2 })
21211
+ };
21212
+ if (userDefinedNetLabels.some(
21213
+ (label) => isUserDefinedNetLabelRedundantWithPlacement(label, solverPlacement)
21214
+ )) {
21215
+ continue;
21216
+ }
21384
21217
  }
21385
- }
21386
- return netToAllowedOrientations;
21387
- })();
21388
- const inputProblem = {
21389
- chips,
21390
- directConnections,
21391
- netConnections,
21392
- textBoxes,
21393
- availableNetLabelOrientations,
21394
- maxMspPairDistance: group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE
21395
- };
21396
- return {
21397
- inputProblem,
21398
- pinIdToSchematicPortId,
21399
- connKeyToSourceNet,
21400
- userNetIdToConnKey,
21401
- connKeysWithExplicitPortNetTraces,
21402
- allSourceAndSchematicPortIdsInScope,
21403
- schPortIdToSourcePortId
21404
- };
21218
+ const netLabel2 = {
21219
+ text: text2,
21220
+ source_net_id: sourceNet.source_net_id,
21221
+ anchor_position,
21222
+ center: center2,
21223
+ anchor_side,
21224
+ schematic_sheet_id: schematicSheetId
21225
+ };
21226
+ db.schematic_net_label.insert(netLabel2);
21227
+ continue;
21228
+ }
21229
+ const ports = group.selectAll("port").filter((p) => p._getSubcircuitConnectivityKey() === placementConnKey);
21230
+ const { name: portInstanceText, wasAssignedDisplayLabel } = getNetNameFromPorts(ports);
21231
+ const sourcePortIdsForConnection = placementConnKey ? db.source_port.list().filter(
21232
+ (sourcePort) => sourcePort.subcircuit_connectivity_map_key === placementConnKey
21233
+ ).map((sourcePort) => sourcePort.source_port_id) : [];
21234
+ const text = portInstanceText || getNetNameFromSourcePorts(db, sourcePortIdsForConnection) || "";
21235
+ const isRoutedPairPlacement = (placement.pinIds?.length ?? 0) > 1;
21236
+ const shouldKeepRoutedPairLabel = isRoutedPairPlacement && (globalNetIdsWithPortOnlyPlacements.has(placement.globalConnNetId) || (routedPairKeysByGlobalNetId.get(placement.globalConnNetId)?.size ?? 0) > 1);
21237
+ if (!wasAssignedDisplayLabel && !shouldKeepRoutedPairLabel && schPortIds.some(
21238
+ (schPortId) => schematicPortIdsWithRoutedTraces.has(schPortId)
21239
+ )) {
21240
+ debug8(
21241
+ `skipping net label placement for "${placement.netId}" REASON:schematic port has routed traces and no display label`
21242
+ );
21243
+ continue;
21244
+ }
21245
+ const center = computeSchematicNetLabelCenter({
21246
+ anchor_position,
21247
+ anchor_side,
21248
+ text
21249
+ });
21250
+ const netLabel = {
21251
+ text,
21252
+ source_net_id: placementConnKey ?? placement.netId ?? placement.globalConnNetId,
21253
+ anchor_position,
21254
+ center,
21255
+ anchor_side,
21256
+ schematic_sheet_id: schematicSheetId
21257
+ };
21258
+ db.schematic_net_label.insert(netLabel);
21259
+ }
21405
21260
  }
21406
21261
 
21407
21262
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyTracesFromSolverOutput.ts
21408
21263
  import "@tscircuit/schematic-trace-solver";
21264
+ import Debug11 from "debug";
21409
21265
 
21410
21266
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/segment-intersection.ts
21411
21267
  var cross = (ax, ay, bx, by) => {
@@ -21747,6 +21603,25 @@ function computeJunctions(traces, opts = {}) {
21747
21603
  return result;
21748
21604
  }
21749
21605
 
21606
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getPortForSchematicSymbolPort.ts
21607
+ var getPortForSchematicSymbolPort = (port) => {
21608
+ const { schX, schY } = port._parsedProps;
21609
+ if (schX !== void 0 && schY !== void 0) return port;
21610
+ if (!port.getParentNormalComponent()?.getSchematicSymbol()) return port;
21611
+ const internallyConnectedPorts = port._getPortsInternallyConnectedToThisPort();
21612
+ const schematicSymbolPortDef = port.schematicSymbolPortDef ?? internallyConnectedPorts.find(
21613
+ (connectedPort) => connectedPort.schematicSymbolPortDef
21614
+ )?.schematicSymbolPortDef;
21615
+ if (!schematicSymbolPortDef) return port;
21616
+ return internallyConnectedPorts.find(
21617
+ (connectedPort) => connectedPort.schematicSymbolPortDef === schematicSymbolPortDef
21618
+ ) ?? port;
21619
+ };
21620
+
21621
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/port-id-types.ts
21622
+ var asSchematicPortId = (id) => id;
21623
+ var asSourcePortId = (id) => id;
21624
+
21750
21625
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/remove-overlapping-same-net-crossing-segments.ts
21751
21626
  var TOL3 = 1e-6;
21752
21627
  function isCollinear(a, b, tol = TOL3) {
@@ -21853,8 +21728,7 @@ function removeOverlappingSameNetCrossingSegments(traces) {
21853
21728
  }
21854
21729
 
21855
21730
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyTracesFromSolverOutput.ts
21856
- import Debug10 from "debug";
21857
- var debug8 = Debug10("Group_doInitialSchematicTraceRender");
21731
+ var debug9 = Debug11("Group_doInitialSchematicTraceRender");
21858
21732
  var MAX_PIN_SNAP_GAP = 1.5;
21859
21733
  function extendTraceEndpointsToReachPinsInsideExpandedBoundingBox(params, db) {
21860
21734
  const { points, schematicPortIds, eligiblePortIds } = params;
@@ -21906,6 +21780,20 @@ function applyTracesFromSolverOutput(args) {
21906
21780
  schematicPortIdsWithPreExistingNetLabels
21907
21781
  } = args;
21908
21782
  const { db } = group.root;
21783
+ const schematicPortIdsByRoutedSchematicPortId = /* @__PURE__ */ new Map();
21784
+ for (const port of group.selectAll("port")) {
21785
+ const routedSchematicPortIdValue = getPortForSchematicSymbolPort(port).schematic_port_id;
21786
+ const schematicPortIdValue = port.schematic_port_id;
21787
+ if (!routedSchematicPortIdValue || !schematicPortIdValue) continue;
21788
+ const routedSchematicPortId = asSchematicPortId(routedSchematicPortIdValue);
21789
+ const schematicPortId = asSchematicPortId(schematicPortIdValue);
21790
+ const schematicPortIds = schematicPortIdsByRoutedSchematicPortId.get(routedSchematicPortId) ?? [];
21791
+ schematicPortIds.push(schematicPortId);
21792
+ schematicPortIdsByRoutedSchematicPortId.set(
21793
+ routedSchematicPortId,
21794
+ schematicPortIds
21795
+ );
21796
+ }
21909
21797
  const eligiblePortIds = /* @__PURE__ */ new Set();
21910
21798
  for (const schematicComponent of db.schematic_component.list()) {
21911
21799
  if (!getSchematicComponentWithTextBounds({ db, schematicComponent })) {
@@ -21914,12 +21802,12 @@ function applyTracesFromSolverOutput(args) {
21914
21802
  for (const port of db.schematic_port.list({
21915
21803
  schematic_component_id: schematicComponent.schematic_component_id
21916
21804
  })) {
21917
- eligiblePortIds.add(port.schematic_port_id);
21805
+ eligiblePortIds.add(asSchematicPortId(port.schematic_port_id));
21918
21806
  }
21919
21807
  }
21920
21808
  const traces = solver.netLabelTraceCollisionSolver?.getOutput().traces ?? solver.traceCleanupSolver?.getOutput().traces ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().traces ?? solver.schematicTraceLinesSolver?.solvedTracePaths;
21921
21809
  const pendingTraces = [];
21922
- debug8(`Traces inside SchematicTraceSolver output: ${(traces ?? []).length}`);
21810
+ debug9(`Traces inside SchematicTraceSolver output: ${(traces ?? []).length}`);
21923
21811
  for (const solvedTracePath of traces ?? []) {
21924
21812
  const uniquePinIds = Array.from(new Set(solvedTracePath.pinIds ?? []));
21925
21813
  const solvedTraceSchematicPortIds = uniquePinIds.map((pinId) => pinIdToSchematicPortId.get(pinId)).filter((id) => Boolean(id));
@@ -21927,14 +21815,14 @@ function applyTracesFromSolverOutput(args) {
21927
21815
  (id) => schematicPortIdsWithPreExistingNetLabels.has(id)
21928
21816
  );
21929
21817
  if (isNetLabelCoveredTrace) {
21930
- debug8(
21818
+ debug9(
21931
21819
  `Skipping solver netlabel-covered trace ${solvedTracePath?.mspPairId} because all schematic ports already have netlabels`
21932
21820
  );
21933
21821
  continue;
21934
21822
  }
21935
21823
  const points = solvedTracePath?.tracePath;
21936
21824
  if (!Array.isArray(points) || points.length < 2) {
21937
- debug8(
21825
+ debug9(
21938
21826
  `Skipping trace ${solvedTracePath?.pinIds.join(",")} because it has less than 2 points`
21939
21827
  );
21940
21828
  continue;
@@ -21960,9 +21848,18 @@ function applyTracesFromSolverOutput(args) {
21960
21848
  const pA = pinIdToSchematicPortId.get(solvedTracePath.pins[0]?.pinId);
21961
21849
  const pB = pinIdToSchematicPortId.get(solvedTracePath.pins[1]?.pinId);
21962
21850
  if (pA && pB) {
21963
- for (const schPid of [pA, pB]) {
21964
- const existing = db.schematic_port.get(schPid);
21965
- if (existing) db.schematic_port.update(schPid, { is_connected: true });
21851
+ const routedSchematicPortIds = /* @__PURE__ */ new Set([pA, pB]);
21852
+ for (const routedSchematicPortId of routedSchematicPortIds) {
21853
+ db.schematic_port.update(routedSchematicPortId, {
21854
+ is_connected: true
21855
+ });
21856
+ for (const schematicPortId of schematicPortIdsByRoutedSchematicPortId.get(
21857
+ routedSchematicPortId
21858
+ ) ?? []) {
21859
+ db.schematic_port.update(schematicPortId, {
21860
+ is_connected: true
21861
+ });
21862
+ }
21966
21863
  }
21967
21864
  subcircuit_connectivity_map_key = userNetIdToConnKey.get(
21968
21865
  String(solvedTracePath.userNetId)
@@ -22004,7 +21901,7 @@ function applyTracesFromSolverOutput(args) {
22004
21901
  schematic_sheet_id: schematicSheetId2
22005
21902
  });
22006
21903
  }
22007
- debug8(
21904
+ debug9(
22008
21905
  `Applying ${pendingTraces.length} traces from SchematicTraceSolver output`
22009
21906
  );
22010
21907
  const withCrossings = computeCrossings(
@@ -22066,253 +21963,457 @@ function applyTracesFromSolverOutput(args) {
22066
21963
  }
22067
21964
  }
22068
21965
 
22069
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
21966
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
21967
+ import {
21968
+ getBoundFromCenteredRect as getBoundFromCenteredRect4,
21969
+ getBoundsCenter as getBoundsCenter2
21970
+ } from "@tscircuit/math-utils";
22070
21971
  import "@tscircuit/schematic-trace-solver";
22071
21972
 
22072
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/oppositeSide.ts
22073
- var oppositeSide = (input) => {
22074
- switch (input) {
22075
- case "x+":
22076
- return "left";
22077
- case "x-":
22078
- return "right";
22079
- case "y+":
22080
- return "bottom";
22081
- case "y-":
22082
- return "top";
22083
- case "left":
22084
- return "right";
22085
- case "top":
22086
- return "bottom";
22087
- case "right":
22088
- return "left";
22089
- case "bottom":
22090
- return "top";
22091
- }
21973
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortSelector.ts
21974
+ var getSchematicPortSelector = ({
21975
+ componentName,
21976
+ schematicPort,
21977
+ sourcePort
21978
+ }) => {
21979
+ const pinIdentifier = schematicPort.pin_number ?? sourcePort?.name ?? sourcePort?.port_hints?.[0];
21980
+ return `${componentName}.${pinIdentifier ?? schematicPort.schematic_port_id}`;
22092
21981
  };
22093
21982
 
22094
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetNameFromPorts.ts
22095
- var getNetNameFromPorts = (ports) => {
22096
- for (const port of ports) {
22097
- const traces = port._getDirectlyConnectedTraces();
22098
- for (const trace of traces) {
22099
- const displayLabel = trace._getSchematicNetLabelText();
22100
- if (displayLabel) {
22101
- return { name: displayLabel, wasAssignedDisplayLabel: true };
22102
- }
22103
- }
21983
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
21984
+ import {
21985
+ getBoundFromCenteredRect as getBoundFromCenteredRect3,
21986
+ getBoundsCenter
21987
+ } from "@tscircuit/math-utils";
21988
+
21989
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicTextBounds.ts
21990
+ import { getBoundsFromPoints as getBoundsFromPoints4 } from "@tscircuit/math-utils";
21991
+ var DEFAULT_SCHEMATIC_TEXT_FONT_SIZE = 0.18;
21992
+ function getSchematicTextBounds(text) {
21993
+ const fontSize = text.font_size ?? DEFAULT_SCHEMATIC_TEXT_FONT_SIZE;
21994
+ const width = getSchematicTextWidth({
21995
+ text: text.text ?? "",
21996
+ fontSize
21997
+ });
21998
+ const height = fontSize;
21999
+ const anchor = text.anchor ?? "center";
22000
+ let minX;
22001
+ let maxX;
22002
+ if (anchor.includes("left")) {
22003
+ minX = 0;
22004
+ maxX = width;
22005
+ } else if (anchor.includes("right")) {
22006
+ minX = -width;
22007
+ maxX = 0;
22008
+ } else {
22009
+ minX = -width / 2;
22010
+ maxX = width / 2;
22104
22011
  }
22105
- const db = ports.find((port) => port.root)?.root?.db;
22106
- const sourcePortIds = ports.map((port) => port.source_port_id).filter((sourcePortId) => Boolean(sourcePortId));
22107
- const netName = db ? getNetNameFromSourcePorts(db, sourcePortIds) : ports.map((port) => port._getNetLabelText()).filter((name) => Boolean(name)).sort(
22108
- (a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
22109
- )[0];
22110
- return { name: netName ?? "", wasAssignedDisplayLabel: false };
22111
- };
22012
+ let minY;
22013
+ let maxY;
22014
+ if (anchor.includes("top")) {
22015
+ minY = -height;
22016
+ maxY = 0;
22017
+ } else if (anchor.includes("bottom")) {
22018
+ minY = 0;
22019
+ maxY = height;
22020
+ } else {
22021
+ minY = -height / 2;
22022
+ maxY = height / 2;
22023
+ }
22024
+ const rotation4 = (text.rotation ?? 0) * Math.PI / 180;
22025
+ const cos = Math.cos(rotation4);
22026
+ const sin = Math.sin(rotation4);
22027
+ const corners = [
22028
+ { x: minX, y: minY },
22029
+ { x: minX, y: maxY },
22030
+ { x: maxX, y: minY },
22031
+ { x: maxX, y: maxY }
22032
+ ].map((corner) => ({
22033
+ x: text.position.x + corner.x * cos - corner.y * sin,
22034
+ y: text.position.y + corner.x * sin + corner.y * cos
22035
+ }));
22036
+ return getBoundsFromPoints4(corners);
22037
+ }
22112
22038
 
22113
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetLabelTextBounds.ts
22114
- var NET_LABEL_TEXT_HEIGHT = 0.18;
22115
- var getNetLabelTextBounds = ({
22116
- center,
22117
- text
22118
- }) => {
22119
- const width = getSchematicNetLabelTextWidth({ text });
22120
- const halfWidth = width / 2;
22121
- const halfHeight = NET_LABEL_TEXT_HEIGHT / 2;
22039
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
22040
+ var REFDES_TEXT_BOX_CHIP_OVERLAP = 0.01;
22041
+ var REFDES_TEXT_BOX_CLEARANCE = 0.06;
22042
+ function isReferenceDesignatorText(text, sourceComponent) {
22043
+ if (!sourceComponent) return false;
22044
+ return text.text === sourceComponent.display_name || text.text === sourceComponent.name;
22045
+ }
22046
+ function schematicTextToTextBox(text, options = {}) {
22047
+ if (!text.text) return null;
22048
+ const bounds = getSchematicTextBounds(text);
22049
+ if (isReferenceDesignatorText(text, options.sourceComponent) && options.schematicComponent?.center && options.schematicComponent?.size) {
22050
+ const componentBounds = getBoundFromCenteredRect3({
22051
+ center: options.schematicComponent.center,
22052
+ width: options.schematicComponent.size.width,
22053
+ height: options.schematicComponent.size.height
22054
+ });
22055
+ const textHeight = bounds.maxY - bounds.minY;
22056
+ if (bounds.minY > componentBounds.maxY) {
22057
+ bounds.minX -= REFDES_TEXT_BOX_CLEARANCE;
22058
+ bounds.maxX += REFDES_TEXT_BOX_CLEARANCE;
22059
+ bounds.minY = componentBounds.maxY - REFDES_TEXT_BOX_CHIP_OVERLAP;
22060
+ bounds.maxY = componentBounds.maxY + textHeight + REFDES_TEXT_BOX_CLEARANCE;
22061
+ }
22062
+ }
22122
22063
  return {
22123
- minX: center.x - halfWidth,
22124
- maxX: center.x + halfWidth,
22125
- minY: center.y - halfHeight,
22126
- maxY: center.y + halfHeight
22064
+ chipId: text.schematic_component_id,
22065
+ center: getBoundsCenter(bounds),
22066
+ width: bounds.maxX - bounds.minX,
22067
+ height: bounds.maxY - bounds.minY,
22068
+ text: text.text
22127
22069
  };
22128
- };
22070
+ }
22129
22071
 
22130
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
22131
- import Debug11 from "debug";
22132
- import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
22133
- var debug9 = Debug11("Group_doInitialSchematicTraceRender");
22134
- var isSameNet = (label, sourceNet) => label.source_net_id != null && label.source_net_id === sourceNet.source_net_id;
22135
- var isUserDefinedNetLabelRedundantWithPlacement = (label, solverPlacement) => {
22136
- if (!isSameNet(label, solverPlacement.sourceNet)) {
22137
- return false;
22138
- }
22139
- if (label.schematicPortIds.some(
22140
- (id) => solverPlacement.schematicPortIds.has(id)
22141
- )) {
22142
- return true;
22143
- }
22144
- if (!label.center) return false;
22145
- return doBoundsOverlap2(
22146
- getNetLabelTextBounds({ center: label.center, text: label.text }),
22147
- solverPlacement.bounds
22148
- );
22149
- };
22150
- function applyNetLabelPlacements(args) {
22151
- const {
22152
- group,
22153
- solver,
22154
- connKeyToSourceNet,
22155
- userNetIdToConnKey,
22156
- pinIdToSchematicPortId,
22157
- connKeysWithExplicitPortNetTraces,
22158
- schematicPortIdsWithPreExistingNetLabels,
22159
- schematicPortIdsWithRoutedTraces
22160
- } = args;
22072
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
22073
+ var DEFAULT_MAX_MSP_PAIR_DISTANCE = 2.4;
22074
+ var SCHEMATIC_RAIL_NET_LABEL_HEIGHT = 0.42;
22075
+ function createSchematicTraceSolverInputProblem(group, opts = {}) {
22161
22076
  const { db } = group.root;
22162
- const netLabelPlacements = solver.netLabelNetLabelCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelTraceCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelPlacementSolver?.netLabelPlacements ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().netLabelPlacements ?? [];
22163
- const dedupedNetLabelPlacements = [];
22164
- const netLabelPlacementKeys = /* @__PURE__ */ new Set();
22165
- for (const placement of netLabelPlacements) {
22166
- const pinIds = [...placement.pinIds ?? []].sort();
22167
- const key = `${placement.globalConnNetId}|${pinIds.join("::")}|${placement.netId ?? ""}`;
22168
- if (netLabelPlacementKeys.has(key)) {
22169
- debug9(
22170
- `skipping duplicate placement for "${placement.netId}" REASON:identical net label placement`
22171
- );
22172
- continue;
22077
+ const connKeyToSourceNet = /* @__PURE__ */ new Map();
22078
+ const childGroups = [];
22079
+ const componentsToVisit = [...group.children].reverse();
22080
+ while (componentsToVisit.length > 0) {
22081
+ const child = componentsToVisit.pop();
22082
+ if (child instanceof Group3) {
22083
+ if (child.isSubcircuit) {
22084
+ if (child._parsedProps.showAsSchematicBox) childGroups.push(child);
22085
+ continue;
22086
+ }
22087
+ childGroups.push(child);
22173
22088
  }
22174
- netLabelPlacementKeys.add(key);
22175
- dedupedNetLabelPlacements.push(placement);
22089
+ componentsToVisit.push(...[...child.children].reverse());
22176
22090
  }
22177
- const netLabelPlacementCountByGlobalNetId = /* @__PURE__ */ new Map();
22178
- const routedPairKeysByGlobalNetId = /* @__PURE__ */ new Map();
22179
- const globalNetIdsWithPortOnlyPlacements = /* @__PURE__ */ new Set();
22180
- for (const placement of dedupedNetLabelPlacements) {
22181
- netLabelPlacementCountByGlobalNetId.set(
22182
- placement.globalConnNetId,
22183
- (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) + 1
22091
+ const allSchematicGroupIds = [
22092
+ group.schematic_group_id,
22093
+ ...childGroups.map((a) => a.schematic_group_id)
22094
+ ];
22095
+ const schematicComponents = db.schematic_component.list().filter((a) => allSchematicGroupIds.includes(a.schematic_group_id)).filter(
22096
+ (schematicComponent) => opts.schematicSheetId === void 0 || schematicComponent.schematic_sheet_id === opts.schematicSheetId
22097
+ );
22098
+ const schematicComponentIds = new Set(
22099
+ schematicComponents.map((component) => component.schematic_component_id)
22100
+ );
22101
+ const schematicComponentsById = new Map(
22102
+ schematicComponents.map((component) => [
22103
+ component.schematic_component_id,
22104
+ component
22105
+ ])
22106
+ );
22107
+ const textBoxes = db.schematic_text.list().filter(
22108
+ (text) => text.schematic_component_id && schematicComponentIds.has(text.schematic_component_id)
22109
+ ).map((text) => {
22110
+ const schematicComponent = schematicComponentsById.get(
22111
+ text.schematic_component_id
22184
22112
  );
22185
- if ((placement.pinIds?.length ?? 0) > 1) {
22186
- if (!routedPairKeysByGlobalNetId.has(placement.globalConnNetId)) {
22187
- routedPairKeysByGlobalNetId.set(placement.globalConnNetId, /* @__PURE__ */ new Set());
22188
- }
22189
- routedPairKeysByGlobalNetId.get(placement.globalConnNetId).add(placement.pinIds.slice().sort().join("::"));
22190
- }
22191
- if ((placement.pinIds?.length ?? 0) <= 1) {
22192
- globalNetIdsWithPortOnlyPlacements.add(placement.globalConnNetId);
22113
+ if (!schematicComponent) return;
22114
+ const sourceComponent = db.source_component.get(
22115
+ schematicComponent.source_component_id
22116
+ );
22117
+ return schematicTextToTextBox(text, {
22118
+ schematicComponent,
22119
+ sourceComponent
22120
+ });
22121
+ }).filter((textBox) => Boolean(textBox));
22122
+ const sectionIdBySchematicComponentId = /* @__PURE__ */ new Map();
22123
+ for (const component of group.getDescendants()) {
22124
+ const schematicComponentId = component.schematic_component_id;
22125
+ const sectionId = component.getSchematicSectionName();
22126
+ if (schematicComponentId && sectionId) {
22127
+ sectionIdBySchematicComponentId.set(schematicComponentId, sectionId);
22193
22128
  }
22194
22129
  }
22195
- const globalConnMap = solver.mspConnectionPairSolver.globalConnMap;
22196
- const userDefinedNetLabels = group.selectAll("netlabel").map((label) => {
22197
- if (!label.schematic_net_label_id) return null;
22198
- const dbLabel = db.schematic_net_label.get(label.schematic_net_label_id);
22199
- if (!dbLabel) return null;
22200
- return {
22201
- ...dbLabel,
22202
- schematicPortIds: label._getConnectedPorts().map((port) => port.schematic_port_id).filter((id) => Boolean(id))
22203
- };
22204
- }).filter((label) => label !== null);
22205
- for (const placement of dedupedNetLabelPlacements) {
22206
- debug9(`processing placement: ${placement.netId}`);
22207
- const placementUserNetId = globalConnMap.getIdsConnectedToNet(placement.globalConnNetId).find((id) => userNetIdToConnKey.get(id));
22208
- const placementConnKey = userNetIdToConnKey.get(placementUserNetId);
22209
- const orientation = placement.orientation;
22210
- const anchor_side = oppositeSide(orientation);
22211
- let sourceNet;
22212
- if (placementConnKey) {
22213
- sourceNet = connKeyToSourceNet.get(placementConnKey);
22130
+ const chips = [];
22131
+ const pinIdToSchematicPortId = /* @__PURE__ */ new Map();
22132
+ const schematicPortIdToPinId = /* @__PURE__ */ new Map();
22133
+ for (const schematicComponent of schematicComponents) {
22134
+ const chipId = schematicComponent.schematic_component_id;
22135
+ const pins = [];
22136
+ const sourceComponent = db.source_component.getWhere({
22137
+ source_component_id: schematicComponent.source_component_id
22138
+ });
22139
+ const schematicPorts = db.schematic_port.list({
22140
+ schematic_component_id: schematicComponent.schematic_component_id
22141
+ });
22142
+ for (const schematicPort of schematicPorts) {
22143
+ const sourcePort = db.source_port.get(schematicPort.source_port_id);
22144
+ const selector = getSchematicPortSelector({
22145
+ componentName: sourceComponent?.name ?? schematicComponent.schematic_component_id,
22146
+ schematicPort,
22147
+ sourcePort
22148
+ });
22149
+ const schematicPortId = asSchematicPortId(schematicPort.schematic_port_id);
22150
+ pinIdToSchematicPortId.set(selector, schematicPortId);
22151
+ schematicPortIdToPinId.set(schematicPortId, selector);
22214
22152
  }
22215
- const schPortIds = placement.pinIds.map(
22216
- (pinId) => pinIdToSchematicPortId.get(pinId)
22153
+ for (const schematicPort of schematicPorts) {
22154
+ const pinId = schematicPortIdToPinId.get(
22155
+ asSchematicPortId(schematicPort.schematic_port_id)
22156
+ );
22157
+ pins.push({
22158
+ pinId,
22159
+ x: schematicPort.center.x,
22160
+ y: schematicPort.center.y,
22161
+ // Pass the port's true facing direction (known from the schematic
22162
+ // symbol). The chip box handed to the solver is text-inclusive, so for
22163
+ // small parts with a large reference designator the pins sit inside the
22164
+ // box. The solver snaps such pins to the box edge along this facing
22165
+ // direction (rather than guessing from geometry, which would pick the
22166
+ // wrong edge for a resistor whose ref text widened the box).
22167
+ _facingDirection: convertFacingDirectionToElbowDirection(
22168
+ schematicPort.facing_direction ?? null
22169
+ )
22170
+ });
22171
+ }
22172
+ const sectionId = sectionIdBySchematicComponentId.get(
22173
+ schematicComponent.schematic_component_id
22217
22174
  );
22218
- let schematicSheetId = group._resolveSchematicSheetId();
22219
- const schematicPort = db.schematic_port.get(schPortIds[0]);
22220
- if (schematicPort?.schematic_sheet_id) {
22221
- schematicSheetId = schematicPort.schematic_sheet_id;
22175
+ const layoutBounds = getSchematicComponentWithTextBounds({ db, schematicComponent }) ?? getBoundFromCenteredRect4({
22176
+ center: schematicComponent.center,
22177
+ width: schematicComponent.size.width,
22178
+ height: schematicComponent.size.height
22179
+ });
22180
+ chips.push({
22181
+ chipId,
22182
+ center: getBoundsCenter2(layoutBounds),
22183
+ width: layoutBounds.maxX - layoutBounds.minX,
22184
+ height: layoutBounds.maxY - layoutBounds.minY,
22185
+ pins,
22186
+ sectionId
22187
+ });
22188
+ }
22189
+ const schematicPortIdsInScope = /* @__PURE__ */ new Set();
22190
+ const schPortIdToSourcePortId = /* @__PURE__ */ new Map();
22191
+ const sourcePortIdToSchPortId = /* @__PURE__ */ new Map();
22192
+ const userNetIdToConnKey = /* @__PURE__ */ new Map();
22193
+ const componentPortBySourcePortId = new Map(
22194
+ group.selectAll("port").filter((port) => port.source_port_id).map((port) => [asSourcePortId(port.source_port_id), port])
22195
+ );
22196
+ for (const sc of schematicComponents) {
22197
+ const ports = db.schematic_port.list({
22198
+ schematic_component_id: sc.schematic_component_id
22199
+ });
22200
+ for (const sp of ports) {
22201
+ const schematicPortId = asSchematicPortId(sp.schematic_port_id);
22202
+ schematicPortIdsInScope.add(schematicPortId);
22203
+ if (sp.source_port_id) {
22204
+ const sourcePortId = asSourcePortId(sp.source_port_id);
22205
+ schPortIdToSourcePortId.set(schematicPortId, sourcePortId);
22206
+ sourcePortIdToSchPortId.set(sourcePortId, schematicPortId);
22207
+ }
22222
22208
  }
22223
- let anchor_position = placement.anchorPoint;
22224
- if (schPortIds.length === 1 && schPortIds[0]) {
22225
- const anchorSchPort = db.schematic_port.get(schPortIds[0]);
22226
- if (anchorSchPort?.center && !anchorSchPort.is_connected) {
22227
- anchor_position = anchorSchPort.center;
22209
+ }
22210
+ const allowedSubcircuitIds = /* @__PURE__ */ new Set();
22211
+ if (group.subcircuit_id) allowedSubcircuitIds.add(group.subcircuit_id);
22212
+ for (const cg of childGroups) {
22213
+ if (cg.subcircuit_id) allowedSubcircuitIds.add(cg.subcircuit_id);
22214
+ }
22215
+ const tracesInScope = db.source_trace.list().filter((st) => {
22216
+ if (st.subcircuit_id === group.subcircuit_id) return true;
22217
+ for (const source_port_id of st.connected_source_port_ids) {
22218
+ if (sourcePortIdToSchPortId.has(asSourcePortId(source_port_id))) {
22219
+ return true;
22228
22220
  }
22229
22221
  }
22230
- if (schPortIds.some(
22231
- (schPortId) => schematicPortIdsWithPreExistingNetLabels.has(schPortId)
22232
- )) {
22233
- debug9(
22234
- `skipping net label placement for "${placement.netId}" REASON:schematic port has pre-existing net label`
22222
+ return false;
22223
+ });
22224
+ const externalNetIds = tracesInScope.flatMap(
22225
+ (st) => st.connected_source_net_ids
22226
+ );
22227
+ for (const netId of externalNetIds) {
22228
+ const net = db.source_net.get(netId);
22229
+ if (net?.subcircuit_id) {
22230
+ allowedSubcircuitIds.add(net.subcircuit_id);
22231
+ }
22232
+ }
22233
+ const directConnections = [];
22234
+ const connectedPairKeys = /* @__PURE__ */ new Set();
22235
+ const connKeysWithExplicitPortNetTraces = /* @__PURE__ */ new Set();
22236
+ for (const sourceTrace of tracesInScope) {
22237
+ if (sourceTrace?.subcircuit_connectivity_map_key && (sourceTrace.connected_source_port_ids?.length ?? 0) > 0 && (sourceTrace.connected_source_net_ids?.length ?? 0) > 0) {
22238
+ connKeysWithExplicitPortNetTraces.add(
22239
+ sourceTrace.subcircuit_connectivity_map_key
22235
22240
  );
22241
+ }
22242
+ }
22243
+ for (const st of db.source_trace.list()) {
22244
+ if (st.subcircuit_id && !allowedSubcircuitIds.has(st.subcircuit_id)) {
22236
22245
  continue;
22237
22246
  }
22238
- if (sourceNet) {
22239
- const isPowerOrGroundNet = sourceNet.is_ground || sourceNet.is_power;
22240
- const hasExplicitPortNetTrace = connKeysWithExplicitPortNetTraces.has(
22241
- placementConnKey
22247
+ const connected = (st.connected_source_port_ids ?? []).map(
22248
+ (sourcePortId) => sourcePortIdToSchPortId.get(asSourcePortId(sourcePortId))
22249
+ ).filter(
22250
+ (schematicPortId) => Boolean(schematicPortId) && schematicPortIdsInScope.has(schematicPortId)
22251
+ );
22252
+ if (connected.length >= 2) {
22253
+ const [a, b] = connected.slice(0, 2);
22254
+ const pairKey = [a, b].sort().join("::");
22255
+ if (!connectedPairKeys.has(pairKey)) {
22256
+ connectedPairKeys.add(pairKey);
22257
+ const traceLabel = st.name ?? st.display_name;
22258
+ const userNetId = traceLabel ?? st.source_trace_id;
22259
+ if (st.subcircuit_connectivity_map_key) {
22260
+ userNetIdToConnKey.set(userNetId, st.subcircuit_connectivity_map_key);
22261
+ }
22262
+ const portA = db.schematic_port.get(a);
22263
+ const portB = db.schematic_port.get(b);
22264
+ let portDistance = 0;
22265
+ if (portA && portB) {
22266
+ portDistance = Math.sqrt(
22267
+ (portA.center.x - portB.center.x) ** 2 + (portA.center.y - portB.center.y) ** 2
22268
+ );
22269
+ }
22270
+ const maxMspDist = group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
22271
+ let netLabelWidth;
22272
+ if (traceLabel && !traceLabel.startsWith(".") && portDistance > maxMspDist) {
22273
+ netLabelWidth = Number(
22274
+ getSchematicNetLabelTextWidth({
22275
+ text: String(traceLabel),
22276
+ font_size: 0.14
22277
+ }).toFixed(2)
22278
+ );
22279
+ }
22280
+ directConnections.push({
22281
+ pinIds: [a, b].map((id) => schematicPortIdToPinId.get(id)),
22282
+ netId: userNetId,
22283
+ netLabelWidth
22284
+ });
22285
+ }
22286
+ }
22287
+ }
22288
+ const netConnections = [];
22289
+ for (const net of db.source_net.list().filter(
22290
+ (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
22291
+ )) {
22292
+ if (net.subcircuit_connectivity_map_key) {
22293
+ connKeyToSourceNet.set(net.subcircuit_connectivity_map_key, net);
22294
+ }
22295
+ }
22296
+ const connKeyToPinIds = /* @__PURE__ */ new Map();
22297
+ for (const [schId, srcPortId] of schPortIdToSourcePortId) {
22298
+ const sp = db.source_port.get(srcPortId);
22299
+ if (!sp?.subcircuit_connectivity_map_key) continue;
22300
+ const connKey = sp.subcircuit_connectivity_map_key;
22301
+ if (!connKeyToPinIds.has(connKey)) connKeyToPinIds.set(connKey, []);
22302
+ connKeyToPinIds.get(connKey).push(schId);
22303
+ }
22304
+ for (const [connKey, schematicPortIds] of connKeyToPinIds) {
22305
+ const sourceNet = connKeyToSourceNet.get(connKey);
22306
+ if (sourceNet && schematicPortIds.length >= 1) {
22307
+ const seenRoutedSchematicPortIds = /* @__PURE__ */ new Set();
22308
+ const uniqueSchematicPortIds = schematicPortIds.filter(
22309
+ (schematicPortId) => {
22310
+ const sourcePortId = schPortIdToSourcePortId.get(schematicPortId);
22311
+ const componentPort = sourcePortId ? componentPortBySourcePortId.get(sourcePortId) : void 0;
22312
+ const routedSchematicPortIdValue = componentPort ? getPortForSchematicSymbolPort(componentPort).schematic_port_id : null;
22313
+ const routedSchematicPortId = routedSchematicPortIdValue ? asSchematicPortId(routedSchematicPortIdValue) : schematicPortId;
22314
+ if (seenRoutedSchematicPortIds.has(routedSchematicPortId)) {
22315
+ return false;
22316
+ }
22317
+ seenRoutedSchematicPortIds.add(routedSchematicPortId);
22318
+ return true;
22319
+ }
22242
22320
  );
22243
- const hasRoutedTraceForPlacementPort = schPortIds.some(
22244
- (id) => schematicPortIdsWithRoutedTraces.has(id)
22321
+ const userNetId = String(
22322
+ sourceNet.name || sourceNet.source_net_id || connKey
22245
22323
  );
22246
- const hasSingleLabelPlacement = (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) <= 1;
22247
- const shouldSkipImplicitSignalNetLabel = !hasExplicitPortNetTrace && !isPowerOrGroundNet && hasRoutedTraceForPlacementPort && hasSingleLabelPlacement;
22248
- if (shouldSkipImplicitSignalNetLabel) {
22249
- debug9(
22250
- `skipping net label placement for "${placement.netId}" REASON:schematic port has routed trace`
22251
- );
22252
- continue;
22324
+ userNetIdToConnKey.set(userNetId, connKey);
22325
+ const netLabelTextWidth = Number(
22326
+ getSchematicNetLabelTextWidth({ text: String(userNetId) }).toFixed(2)
22327
+ );
22328
+ let netLabelWidth = netLabelTextWidth;
22329
+ let netLabelHeight;
22330
+ if (sourceNet.is_ground || sourceNet.is_power) {
22331
+ netLabelWidth = SCHEMATIC_RAIL_NET_LABEL_HEIGHT;
22332
+ netLabelHeight = netLabelTextWidth;
22253
22333
  }
22254
- const text2 = sourceNet.name;
22255
- const center2 = computeSchematicNetLabelCenter({
22256
- anchor_position,
22257
- anchor_side,
22258
- text: text2
22334
+ netConnections.push({
22335
+ netId: userNetId,
22336
+ pinIds: uniqueSchematicPortIds.map(
22337
+ (portId) => schematicPortIdToPinId.get(portId)
22338
+ ),
22339
+ netLabelWidth,
22340
+ netLabelHeight
22259
22341
  });
22260
- if (!isPowerOrGroundNet) {
22261
- const solverPlacement = {
22262
- sourceNet,
22263
- text: text2,
22264
- schematicPortIds: new Set(schPortIds),
22265
- bounds: getNetLabelTextBounds({ center: center2, text: text2 })
22266
- };
22267
- if (userDefinedNetLabels.some(
22268
- (label) => isUserDefinedNetLabelRedundantWithPlacement(label, solverPlacement)
22269
- )) {
22270
- continue;
22271
- }
22272
- }
22273
- const netLabel2 = {
22274
- text: text2,
22275
- source_net_id: sourceNet.source_net_id,
22276
- anchor_position,
22277
- center: center2,
22278
- anchor_side,
22279
- schematic_sheet_id: schematicSheetId
22280
- };
22281
- db.schematic_net_label.insert(netLabel2);
22282
- continue;
22283
22342
  }
22284
- const ports = group.selectAll("port").filter((p) => p._getSubcircuitConnectivityKey() === placementConnKey);
22285
- const { name: portInstanceText, wasAssignedDisplayLabel } = getNetNameFromPorts(ports);
22286
- const sourcePortIdsForConnection = placementConnKey ? db.source_port.list().filter(
22287
- (sourcePort) => sourcePort.subcircuit_connectivity_map_key === placementConnKey
22288
- ).map((sourcePort) => sourcePort.source_port_id) : [];
22289
- const text = portInstanceText || getNetNameFromSourcePorts(db, sourcePortIdsForConnection) || "";
22290
- const isRoutedPairPlacement = (placement.pinIds?.length ?? 0) > 1;
22291
- const shouldKeepRoutedPairLabel = isRoutedPairPlacement && (globalNetIdsWithPortOnlyPlacements.has(placement.globalConnNetId) || (routedPairKeysByGlobalNetId.get(placement.globalConnNetId)?.size ?? 0) > 1);
22292
- if (!wasAssignedDisplayLabel && !shouldKeepRoutedPairLabel && schPortIds.some(
22293
- (schPortId) => schematicPortIdsWithRoutedTraces.has(schPortId)
22343
+ }
22344
+ const availableNetLabelOrientations = (() => {
22345
+ const netToAllowedOrientations = {};
22346
+ const presentNetIds = new Set(netConnections.map((nc) => nc.netId));
22347
+ for (const net of db.source_net.list().filter(
22348
+ (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
22294
22349
  )) {
22295
- debug9(
22296
- `skipping net label placement for "${placement.netId}" REASON:schematic port has routed traces and no display label`
22350
+ if (!net.name) continue;
22351
+ if (!presentNetIds.has(net.name)) continue;
22352
+ if (net.is_ground) {
22353
+ netToAllowedOrientations[net.name] = ["y-"];
22354
+ } else if (net.is_power) {
22355
+ netToAllowedOrientations[net.name] = ["y+"];
22356
+ } else {
22357
+ netToAllowedOrientations[net.name] = ["x-", "x+"];
22358
+ }
22359
+ }
22360
+ return netToAllowedOrientations;
22361
+ })();
22362
+ const inputProblem = {
22363
+ chips,
22364
+ directConnections,
22365
+ netConnections,
22366
+ textBoxes,
22367
+ availableNetLabelOrientations,
22368
+ maxMspPairDistance: group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE
22369
+ };
22370
+ return {
22371
+ inputProblem,
22372
+ pinIdToSchematicPortId,
22373
+ connKeyToSourceNet,
22374
+ userNetIdToConnKey,
22375
+ connKeysWithExplicitPortNetTraces,
22376
+ schematicPortIdsInScope,
22377
+ schPortIdToSourcePortId
22378
+ };
22379
+ }
22380
+
22381
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithAssignedNetLabels.ts
22382
+ var getSchematicPortIdsWithAssignedNetLabels = (group) => {
22383
+ const schematicPortIdsWithNetLabels = /* @__PURE__ */ new Set();
22384
+ const netLabels = group.selectAll("netlabel");
22385
+ for (const netLabel of netLabels) {
22386
+ const netLabelPorts = netLabel._getConnectedPorts();
22387
+ for (const port of netLabelPorts) {
22388
+ if (!port.schematic_port_id) continue;
22389
+ schematicPortIdsWithNetLabels.add(
22390
+ asSchematicPortId(port.schematic_port_id)
22297
22391
  );
22298
- continue;
22299
22392
  }
22300
- const center = computeSchematicNetLabelCenter({
22301
- anchor_position,
22302
- anchor_side,
22303
- text
22304
- });
22305
- const netLabel = {
22306
- text,
22307
- source_net_id: placementConnKey ?? placement.netId ?? placement.globalConnNetId,
22308
- anchor_position,
22309
- center,
22310
- anchor_side,
22311
- schematic_sheet_id: schematicSheetId
22312
- };
22313
- db.schematic_net_label.insert(netLabel);
22314
22393
  }
22315
- }
22394
+ return schematicPortIdsWithNetLabels;
22395
+ };
22396
+
22397
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithRoutedTraces.ts
22398
+ var getSchematicPortIdsWithRoutedTraces = ({
22399
+ solver,
22400
+ pinIdToSchematicPortId
22401
+ }) => {
22402
+ const solvedTraces = solver.traceCleanupSolver?.getOutput().traces ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().traces ?? solver.schematicTraceLinesSolver?.solvedTracePaths ?? [];
22403
+ const schematicPortIdsWithRoutedTraces = /* @__PURE__ */ new Set();
22404
+ for (const solvedTrace of solvedTraces) {
22405
+ const points = solvedTrace?.tracePath;
22406
+ if (!Array.isArray(points) || points.length < 2) continue;
22407
+ const pinIds = Array.isArray(solvedTrace.pins) ? solvedTrace.pins.map((pin) => pin.pinId) : solvedTrace.pinIds;
22408
+ for (const pinId of pinIds) {
22409
+ const schPortId = pinIdToSchematicPortId.get(pinId);
22410
+ if (schPortId) {
22411
+ schematicPortIdsWithRoutedTraces.add(schPortId);
22412
+ }
22413
+ }
22414
+ }
22415
+ return schematicPortIdsWithRoutedTraces;
22416
+ };
22316
22417
 
22317
22418
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/insertNetLabelsForPortsMissingTrace.ts
22318
22419
  var NEAR_EXISTING_NET_LABEL_DISTANCE = 0.5;
@@ -22354,19 +22455,17 @@ var getDirectCrossSubcircuitConnectionLabelText = (db, sourcePortId) => {
22354
22455
  return getSourcePortNetLabelText(db, otherSourcePortId);
22355
22456
  };
22356
22457
  var insertNetLabelsForPortsMissingTrace = ({
22357
- allSourceAndSchematicPortIdsInScope,
22458
+ schematicPortIdsInScope,
22358
22459
  group,
22359
22460
  schPortIdToSourcePortId,
22360
22461
  connKeyToSourceNet
22361
22462
  }) => {
22362
22463
  const { db } = group.root;
22363
- for (const schOrSrcPortId of Array.from(
22364
- allSourceAndSchematicPortIdsInScope
22365
- )) {
22366
- const schPort = db.schematic_port.get(schOrSrcPortId);
22464
+ for (const schematicPortId of schematicPortIdsInScope) {
22465
+ const schPort = db.schematic_port.get(schematicPortId);
22367
22466
  if (!schPort) continue;
22368
22467
  if (schPort.is_connected) continue;
22369
- const srcPortId = schPortIdToSourcePortId.get(schOrSrcPortId);
22468
+ const srcPortId = schPortIdToSourcePortId.get(schematicPortId);
22370
22469
  if (!srcPortId) continue;
22371
22470
  const sourcePort = db.source_port.get(srcPortId);
22372
22471
  if (group._parsedProps.showAsSchematicBox && sourcePort?.subcircuit_id === group.subcircuit_id && !sourcePort.source_component_id && getDirectCrossSubcircuitConnectedSourcePortId(db, srcPortId)) {
@@ -22377,9 +22476,7 @@ var insertNetLabelsForPortsMissingTrace = ({
22377
22476
  const sourceNet = connKeyToSourceNet.get(connKey);
22378
22477
  const schComponent = schPort.schematic_component_id ? db.schematic_component.get(schPort.schematic_component_id) : void 0;
22379
22478
  const portSchematicSheetId = schComponent?.schematic_sheet_id ?? group._resolveSchematicSheetId();
22380
- const connectedSourcePortIdsForKey = Array.from(
22381
- allSourceAndSchematicPortIdsInScope
22382
- ).map((portId) => schPortIdToSourcePortId.get(portId)).filter((sourcePortId) => {
22479
+ const connectedSourcePortIdsForKey = Array.from(schematicPortIdsInScope).map((portId) => schPortIdToSourcePortId.get(portId)).filter((sourcePortId) => {
22383
22480
  if (!sourcePortId) return false;
22384
22481
  return db.source_port.get(sourcePortId)?.subcircuit_connectivity_map_key === connKey;
22385
22482
  });
@@ -22402,13 +22499,13 @@ var insertNetLabelsForPortsMissingTrace = ({
22402
22499
  );
22403
22500
  const hasExplicitLabel = Boolean(sourceNet || assignedPortNetLabelText);
22404
22501
  if (isCollapsedBoxPort && !hasExternalTrace && !hasExplicitLabel) continue;
22405
- const connectedPortCountForKey = Array.from(
22406
- allSourceAndSchematicPortIdsInScope
22407
- ).filter((portId) => {
22408
- const sourcePortId = schPortIdToSourcePortId.get(portId);
22409
- if (!sourcePortId) return false;
22410
- return db.source_port.get(sourcePortId)?.subcircuit_connectivity_map_key === connKey;
22411
- }).length;
22502
+ const connectedPortCountForKey = Array.from(schematicPortIdsInScope).filter(
22503
+ (portId) => {
22504
+ const sourcePortId = schPortIdToSourcePortId.get(portId);
22505
+ if (!sourcePortId) return false;
22506
+ return db.source_port.get(sourcePortId)?.subcircuit_connectivity_map_key === connKey;
22507
+ }
22508
+ ).length;
22412
22509
  const isGndNet = sourceNet?.is_ground ?? false;
22413
22510
  const isPowerNet = !isGndNet && (sourceNet?.is_power ?? false);
22414
22511
  let side;
@@ -22532,41 +22629,6 @@ var insertNetLabelsForPortsMissingTrace = ({
22532
22629
  }
22533
22630
  };
22534
22631
 
22535
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithAssignedNetLabels.ts
22536
- var getSchematicPortIdsWithAssignedNetLabels = (group) => {
22537
- const schematicPortIdsWithNetLabels = /* @__PURE__ */ new Set();
22538
- const netLabels = group.selectAll("netlabel");
22539
- for (const netLabel of netLabels) {
22540
- const netLabelPorts = netLabel._getConnectedPorts();
22541
- for (const port of netLabelPorts) {
22542
- if (!port.schematic_port_id) continue;
22543
- schematicPortIdsWithNetLabels.add(port.schematic_port_id);
22544
- }
22545
- }
22546
- return schematicPortIdsWithNetLabels;
22547
- };
22548
-
22549
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithRoutedTraces.ts
22550
- var getSchematicPortIdsWithRoutedTraces = ({
22551
- solver,
22552
- pinIdToSchematicPortId
22553
- }) => {
22554
- const solvedTraces = solver.traceCleanupSolver?.getOutput().traces ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().traces ?? solver.schematicTraceLinesSolver?.solvedTracePaths ?? [];
22555
- const schematicPortIdsWithRoutedTraces = /* @__PURE__ */ new Set();
22556
- for (const solvedTrace of solvedTraces) {
22557
- const points = solvedTrace?.tracePath;
22558
- if (!Array.isArray(points) || points.length < 2) continue;
22559
- const pinIds = Array.isArray(solvedTrace.pins) ? solvedTrace.pins.map((pin) => pin.pinId) : solvedTrace.pinIds;
22560
- for (const pinId of pinIds) {
22561
- const schPortId = pinIdToSchematicPortId.get(pinId);
22562
- if (schPortId) {
22563
- schematicPortIdsWithRoutedTraces.add(schPortId);
22564
- }
22565
- }
22566
- }
22567
- return schematicPortIdsWithRoutedTraces;
22568
- };
22569
-
22570
22632
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/Group_doInitialSchematicTraceRender.ts
22571
22633
  var debug10 = Debug12("Group_doInitialSchematicTraceRender");
22572
22634
  var renderSchematicTracesForSheet = ({
@@ -22577,7 +22639,7 @@ var renderSchematicTracesForSheet = ({
22577
22639
  inputProblem,
22578
22640
  pinIdToSchematicPortId,
22579
22641
  connKeyToSourceNet,
22580
- allSourceAndSchematicPortIdsInScope,
22642
+ schematicPortIdsInScope,
22581
22643
  schPortIdToSourcePortId,
22582
22644
  userNetIdToConnKey,
22583
22645
  connKeysWithExplicitPortNetTraces
@@ -22588,7 +22650,7 @@ var renderSchematicTracesForSheet = ({
22588
22650
  if (!hasRouteableSchematicConnections) {
22589
22651
  insertNetLabelsForPortsMissingTrace({
22590
22652
  group,
22591
- allSourceAndSchematicPortIdsInScope,
22653
+ schematicPortIdsInScope,
22592
22654
  schPortIdToSourcePortId,
22593
22655
  connKeyToSourceNet
22594
22656
  });
@@ -22634,7 +22696,7 @@ var renderSchematicTracesForSheet = ({
22634
22696
  });
22635
22697
  insertNetLabelsForPortsMissingTrace({
22636
22698
  group,
22637
- allSourceAndSchematicPortIdsInScope,
22699
+ schematicPortIdsInScope,
22638
22700
  schPortIdToSourcePortId,
22639
22701
  connKeyToSourceNet
22640
22702
  });
@@ -22647,7 +22709,7 @@ var Group_doInitialSchematicTraceRender = (group) => {
22647
22709
  [
22648
22710
  group,
22649
22711
  ...group.getDescendants().filter(
22650
- (component) => component instanceof Group
22712
+ (component) => component instanceof Group3
22651
22713
  )
22652
22714
  ].map((schematicGroup) => schematicGroup.schematic_group_id).filter(
22653
22715
  (schematicGroupId) => Boolean(schematicGroupId)
@@ -23652,7 +23714,7 @@ function Group_getRoutingPhasePlans(group) {
23652
23714
  var package_default = {
23653
23715
  name: "@tscircuit/core",
23654
23716
  type: "module",
23655
- version: "0.0.1505",
23717
+ version: "0.0.1507",
23656
23718
  types: "dist/index.d.ts",
23657
23719
  main: "dist/index.js",
23658
23720
  module: "dist/index.js",
@@ -23687,7 +23749,7 @@ var package_default = {
23687
23749
  "@tscircuit/alphabet": "0.0.25",
23688
23750
  "@tscircuit/breakout-point-solver": "github:tscircuit/breakout-point-solver#bac9629",
23689
23751
  "@tscircuit/capacity-autorouter": "^0.0.710",
23690
- "@tscircuit/checks": "0.0.145",
23752
+ "@tscircuit/checks": "0.0.146",
23691
23753
  "@tscircuit/circuit-json-util": "^0.0.101",
23692
23754
  "@tscircuit/common": "^0.0.20",
23693
23755
  "@tscircuit/copper-pour-solver": "0.0.39",
@@ -23769,7 +23831,7 @@ var package_default = {
23769
23831
  "@flatten-js/core": "^1.6.2",
23770
23832
  "@lume/kiwi": "^0.4.3",
23771
23833
  "calculate-cell-boundaries": "^0.0.13",
23772
- "calculate-packing": "^0.0.79",
23834
+ "calculate-packing": "^0.0.82",
23773
23835
  "css-select": "5.1.0",
23774
23836
  "format-si-unit": "^0.0.7",
23775
23837
  nanoid: "^5.0.7",
@@ -24437,38 +24499,6 @@ function insertAutoplacedJumpers(params) {
24437
24499
  }
24438
24500
  }
24439
24501
 
24440
- // lib/components/primitive-components/Group/insert-pcb-trace-too-long-warnings.ts
24441
- var insertPcbTraceTooLongWarnings = ({
24442
- db,
24443
- subcircuitId
24444
- }) => {
24445
- for (const warning of db.pcb_trace_too_long_warning.list()) {
24446
- if (warning.subcircuit_id === subcircuitId) {
24447
- db.pcb_trace_too_long_warning.delete(
24448
- warning.pcb_trace_too_long_warning_id
24449
- );
24450
- }
24451
- }
24452
- const pcbTraces = db.pcb_trace.list().filter((pcbTrace) => pcbTrace.subcircuit_id === subcircuitId);
24453
- for (const pcbTrace of pcbTraces) {
24454
- if (!pcbTrace.source_trace_id) continue;
24455
- const sourceTrace = db.source_trace.get(pcbTrace.source_trace_id);
24456
- const maximumTraceLength = sourceTrace?.max_length;
24457
- if (maximumTraceLength === void 0) continue;
24458
- const actualTraceLength = pcbTrace.trace_length ?? getTraceLength(pcbTrace.route);
24459
- if (actualTraceLength <= maximumTraceLength) continue;
24460
- db.pcb_trace_too_long_warning.insert({
24461
- warning_type: "pcb_trace_too_long_warning",
24462
- message: `PCB trace is ${actualTraceLength.toFixed(2)}mm long, exceeding the ${maximumTraceLength}mm maximum`,
24463
- pcb_trace_id: pcbTrace.pcb_trace_id,
24464
- source_trace_id: pcbTrace.source_trace_id,
24465
- actual_trace_length: actualTraceLength,
24466
- maximum_trace_length: maximumTraceLength,
24467
- subcircuit_id: subcircuitId
24468
- });
24469
- }
24470
- };
24471
-
24472
24502
  // lib/components/primitive-components/Group/region-replacement/convert-pcb-trace-to-simplified-pcb-trace.ts
24473
24503
  function convertPcbTraceToSimplifiedPcbTrace(trace) {
24474
24504
  return {
@@ -24708,7 +24738,7 @@ var ensureRouteStartsAtSourceTraceStart = ({
24708
24738
  const lastPoint = route[route.length - 1];
24709
24739
  return getDistanceToPoint(lastPoint, firstPcbPort) < getDistanceToPoint(firstPoint, firstPcbPort) ? reversePcbTraceRoute(route) : route;
24710
24740
  };
24711
- var Group = class extends NormalComponent3 {
24741
+ var Group3 = class extends NormalComponent3 {
24712
24742
  pcb_group_id = null;
24713
24743
  schematic_group_id = null;
24714
24744
  subcircuit_id = null;
@@ -26047,10 +26077,6 @@ var Group = class extends NormalComponent3 {
26047
26077
  });
26048
26078
  }
26049
26079
  }
26050
- insertPcbTraceTooLongWarnings({
26051
- db,
26052
- subcircuitId: this.subcircuit_id
26053
- });
26054
26080
  }
26055
26081
  }
26056
26082
  doInitialSchematicReplaceNetLabelsWithSymbols() {
@@ -26131,7 +26157,7 @@ function inflateSourceGroup(sourceGroup, inflatorContext) {
26131
26157
  if (pcbGroup?.anchor_alignment) {
26132
26158
  groupProps2.pcbAnchorAlignment = pcbGroup.anchor_alignment;
26133
26159
  }
26134
- const group = new Group(groupProps2);
26160
+ const group = new Group3(groupProps2);
26135
26161
  group._isInflatedFromCircuitJson = true;
26136
26162
  group.source_group_id = sourceGroup.source_group_id;
26137
26163
  if (groupsMap) {
@@ -27450,7 +27476,7 @@ var IsolatedCircuit = class {
27450
27476
  this.firstChild = this.children[0];
27451
27477
  return;
27452
27478
  }
27453
- const group = new Group({ subcircuit: true });
27479
+ const group = new Group3({ subcircuit: true });
27454
27480
  group.parent = this;
27455
27481
  group.addAll(this.children);
27456
27482
  this.children = [group];
@@ -27856,7 +27882,7 @@ var getRoundedRectOutline = (width, height, radius) => {
27856
27882
  }
27857
27883
  return outline;
27858
27884
  };
27859
- var Board = class extends Group {
27885
+ var Board = class extends Group3 {
27860
27886
  pcb_board_id = null;
27861
27887
  source_board_id = null;
27862
27888
  _drcChecksComplete = false;
@@ -28313,7 +28339,7 @@ import { mountedboardProps } from "@tscircuit/props";
28313
28339
 
28314
28340
  // lib/components/primitive-components/Group/Subcircuit/Subcircuit.ts
28315
28341
  import "@tscircuit/props";
28316
- var Subcircuit = class extends Group {
28342
+ var Subcircuit = class extends Group3 {
28317
28343
  constructor(props) {
28318
28344
  super({
28319
28345
  ...props,
@@ -28885,7 +28911,7 @@ function getItemDimensions(item, db) {
28885
28911
  }
28886
28912
 
28887
28913
  // lib/components/normal-components/Subpanel.ts
28888
- var Subpanel = class _Subpanel extends Group {
28914
+ var Subpanel = class _Subpanel extends Group3 {
28889
28915
  pcb_panel_id = null;
28890
28916
  _tabsAndMouseBitesGenerated = false;
28891
28917
  get config() {
@@ -31522,7 +31548,7 @@ var createBreakoutPointSolverInput = (breakout) => {
31522
31548
  };
31523
31549
 
31524
31550
  // lib/components/primitive-components/Breakout/Breakout.ts
31525
- var Breakout = class extends Group {
31551
+ var Breakout = class extends Group3 {
31526
31552
  constructor(props) {
31527
31553
  super({
31528
31554
  ...props,
@@ -33428,6 +33454,7 @@ var Potentiometer = class extends NormalComponent3 {
33428
33454
  // lib/components/normal-components/Crystal.ts
33429
33455
  import { crystalProps } from "@tscircuit/props";
33430
33456
  import { formatSiUnit as formatSiUnit9 } from "format-si-unit";
33457
+ var DEFAULT_CRYSTAL_MAX_TRACE_LENGTH_MM = 10;
33431
33458
  var Crystal = class extends NormalComponent3 {
33432
33459
  get config() {
33433
33460
  const symbolName = this.props.symbolName ?? (this.props.pinVariant === "four_pin" ? "crystal_4pin" : "crystal");
@@ -33475,6 +33502,38 @@ var Crystal = class extends NormalComponent3 {
33475
33502
  });
33476
33503
  this.source_component_id = source_component.source_component_id;
33477
33504
  }
33505
+ doInitialSourceDesignRuleChecks() {
33506
+ super.doInitialSourceDesignRuleChecks();
33507
+ if (!this.source_component_id) return;
33508
+ const { db } = this.root;
33509
+ const maximumTraceLength = this._parsedProps.maxTraceLength ?? DEFAULT_CRYSTAL_MAX_TRACE_LENGTH_MM;
33510
+ const crystalSourcePortIds = /* @__PURE__ */ new Set();
33511
+ const crystalConnectivityMapKeys = /* @__PURE__ */ new Set();
33512
+ for (const sourcePort of db.source_port.list()) {
33513
+ if (sourcePort.source_component_id !== this.source_component_id) continue;
33514
+ crystalSourcePortIds.add(sourcePort.source_port_id);
33515
+ if (sourcePort.subcircuit_connectivity_map_key) {
33516
+ crystalConnectivityMapKeys.add(
33517
+ sourcePort.subcircuit_connectivity_map_key
33518
+ );
33519
+ }
33520
+ }
33521
+ for (const sourceTrace of db.source_trace.list()) {
33522
+ const isDirectlyConnectedToCrystal = sourceTrace.connected_source_port_ids.some(
33523
+ (sourcePortId) => crystalSourcePortIds.has(sourcePortId)
33524
+ );
33525
+ const isOnCrystalNet = sourceTrace.subcircuit_connectivity_map_key !== void 0 && crystalConnectivityMapKeys.has(
33526
+ sourceTrace.subcircuit_connectivity_map_key
33527
+ );
33528
+ if (!isDirectlyConnectedToCrystal && !isOnCrystalNet) continue;
33529
+ if (typeof sourceTrace.max_length === "number" && sourceTrace.max_length <= maximumTraceLength) {
33530
+ continue;
33531
+ }
33532
+ db.source_trace.update(sourceTrace.source_trace_id, {
33533
+ max_length: maximumTraceLength
33534
+ });
33535
+ }
33536
+ }
33478
33537
  };
33479
33538
 
33480
33539
  // lib/components/normal-components/Mosfet.ts
@@ -35367,7 +35426,7 @@ export {
35367
35426
  Fiducial,
35368
35427
  Footprint,
35369
35428
  Fuse,
35370
- Group,
35429
+ Group3 as Group,
35371
35430
  Hole,
35372
35431
  Inductor,
35373
35432
  Interconnect,