@tscircuit/core 0.0.1507 → 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.
Files changed (2) hide show
  1. package/dist/index.js +784 -718
  2. package/package.json +1 -1
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,
@@ -21011,397 +21011,257 @@ function Group_doInitialSchematicLayoutSections(group) {
21011
21011
  import { SchematicTracePipelineSolver as SchematicTracePipelineSolver4 } from "@tscircuit/schematic-trace-solver";
21012
21012
  import Debug12 from "debug";
21013
21013
 
21014
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
21015
- import {
21016
- getBoundFromCenteredRect as getBoundFromCenteredRect4,
21017
- getBoundsCenter as getBoundsCenter2
21018
- } from "@tscircuit/math-utils";
21014
+ // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
21015
+ import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
21019
21016
  import "@tscircuit/schematic-trace-solver";
21017
+ import Debug10 from "debug";
21020
21018
 
21021
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
21022
- import {
21023
- getBoundFromCenteredRect as getBoundFromCenteredRect3,
21024
- getBoundsCenter
21025
- } from "@tscircuit/math-utils";
21026
-
21027
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicTextBounds.ts
21028
- import { getBoundsFromPoints as getBoundsFromPoints4 } from "@tscircuit/math-utils";
21029
- var DEFAULT_SCHEMATIC_TEXT_FONT_SIZE = 0.18;
21030
- function getSchematicTextBounds(text) {
21031
- const fontSize = text.font_size ?? DEFAULT_SCHEMATIC_TEXT_FONT_SIZE;
21032
- const width = getSchematicTextWidth({
21033
- text: text.text ?? "",
21034
- fontSize
21035
- });
21036
- const height = fontSize;
21037
- const anchor = text.anchor ?? "center";
21038
- let minX;
21039
- let maxX;
21040
- if (anchor.includes("left")) {
21041
- minX = 0;
21042
- maxX = width;
21043
- } else if (anchor.includes("right")) {
21044
- minX = -width;
21045
- maxX = 0;
21046
- } else {
21047
- minX = -width / 2;
21048
- maxX = width / 2;
21049
- }
21050
- let minY;
21051
- let maxY;
21052
- if (anchor.includes("top")) {
21053
- minY = -height;
21054
- maxY = 0;
21055
- } else if (anchor.includes("bottom")) {
21056
- minY = 0;
21057
- maxY = height;
21058
- } else {
21059
- minY = -height / 2;
21060
- maxY = height / 2;
21061
- }
21062
- const rotation4 = (text.rotation ?? 0) * Math.PI / 180;
21063
- const cos = Math.cos(rotation4);
21064
- const sin = Math.sin(rotation4);
21065
- const corners = [
21066
- { x: minX, y: minY },
21067
- { x: minX, y: maxY },
21068
- { x: maxX, y: minY },
21069
- { x: maxX, y: maxY }
21070
- ].map((corner) => ({
21071
- x: text.position.x + corner.x * cos - corner.y * sin,
21072
- y: text.position.y + corner.x * sin + corner.y * cos
21073
- }));
21074
- return getBoundsFromPoints4(corners);
21075
- }
21076
-
21077
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/schematicTextToTextBounds.ts
21078
- var REFDES_TEXT_BOX_CHIP_OVERLAP = 0.01;
21079
- var REFDES_TEXT_BOX_CLEARANCE = 0.06;
21080
- function isReferenceDesignatorText(text, sourceComponent) {
21081
- if (!sourceComponent) return false;
21082
- return text.text === sourceComponent.display_name || text.text === sourceComponent.name;
21083
- }
21084
- function schematicTextToTextBox(text, options = {}) {
21085
- if (!text.text) return null;
21086
- const bounds = getSchematicTextBounds(text);
21087
- if (isReferenceDesignatorText(text, options.sourceComponent) && options.schematicComponent?.center && options.schematicComponent?.size) {
21088
- const componentBounds = getBoundFromCenteredRect3({
21089
- center: options.schematicComponent.center,
21090
- width: options.schematicComponent.size.width,
21091
- height: options.schematicComponent.size.height
21092
- });
21093
- const textHeight = bounds.maxY - bounds.minY;
21094
- if (bounds.minY > componentBounds.maxY) {
21095
- bounds.minX -= REFDES_TEXT_BOX_CLEARANCE;
21096
- bounds.maxX += REFDES_TEXT_BOX_CLEARANCE;
21097
- bounds.minY = componentBounds.maxY - REFDES_TEXT_BOX_CHIP_OVERLAP;
21098
- bounds.maxY = componentBounds.maxY + textHeight + REFDES_TEXT_BOX_CLEARANCE;
21099
- }
21100
- }
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;
21101
21028
  return {
21102
- chipId: text.schematic_component_id,
21103
- center: getBoundsCenter(bounds),
21104
- width: bounds.maxX - bounds.minX,
21105
- height: bounds.maxY - bounds.minY,
21106
- text: text.text
21029
+ minX: center.x - halfWidth,
21030
+ maxX: center.x + halfWidth,
21031
+ minY: center.y - halfHeight,
21032
+ maxY: center.y + halfHeight
21107
21033
  };
21108
- }
21109
-
21110
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortSelector.ts
21111
- var getSchematicPortSelector = ({
21112
- componentName,
21113
- schematicPort,
21114
- sourcePort
21115
- }) => {
21116
- const pinIdentifier = schematicPort.pin_number ?? sourcePort?.name ?? sourcePort?.port_hints?.[0];
21117
- return `${componentName}.${pinIdentifier ?? schematicPort.schematic_port_id}`;
21118
21034
  };
21119
21035
 
21120
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/createSchematicTraceSolverInputProblem.ts
21121
- var DEFAULT_MAX_MSP_PAIR_DISTANCE = 2.4;
21122
- var SCHEMATIC_RAIL_NET_LABEL_HEIGHT = 0.42;
21123
- function createSchematicTraceSolverInputProblem(group, opts = {}) {
21124
- const { db } = group.root;
21125
- const connKeyToSourceNet = /* @__PURE__ */ new Map();
21126
- const childGroups = [];
21127
- const componentsToVisit = [...group.children].reverse();
21128
- while (componentsToVisit.length > 0) {
21129
- const child = componentsToVisit.pop();
21130
- if (child instanceof Group) {
21131
- if (child.isSubcircuit) {
21132
- if (child._parsedProps.showAsSchematicBox) childGroups.push(child);
21133
- 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 };
21134
21044
  }
21135
- childGroups.push(child);
21136
21045
  }
21137
- componentsToVisit.push(...[...child.children].reverse());
21138
21046
  }
21139
- const allSchematicGroupIds = [
21140
- group.schematic_group_id,
21141
- ...childGroups.map((a) => a.schematic_group_id)
21142
- ];
21143
- const schematicComponents = db.schematic_component.list().filter((a) => allSchematicGroupIds.includes(a.schematic_group_id)).filter(
21144
- (schematicComponent) => opts.schematicSheetId === void 0 || schematicComponent.schematic_sheet_id === opts.schematicSheetId
21145
- );
21146
- const schematicComponentIds = new Set(
21147
- schematicComponents.map((component) => component.schematic_component_id)
21148
- );
21149
- const schematicComponentsById = new Map(
21150
- schematicComponents.map((component) => [
21151
- component.schematic_component_id,
21152
- component
21153
- ])
21154
- );
21155
- const textBoxes = db.schematic_text.list().filter(
21156
- (text) => text.schematic_component_id && schematicComponentIds.has(text.schematic_component_id)
21157
- ).map((text) => {
21158
- const schematicComponent = schematicComponentsById.get(
21159
- text.schematic_component_id
21160
- );
21161
- if (!schematicComponent) return;
21162
- const sourceComponent = db.source_component.get(
21163
- schematicComponent.source_component_id
21164
- );
21165
- return schematicTextToTextBox(text, {
21166
- schematicComponent,
21167
- sourceComponent
21168
- });
21169
- }).filter((textBox) => Boolean(textBox));
21170
- const sectionIdBySchematicComponentId = /* @__PURE__ */ new Map();
21171
- for (const component of group.getDescendants()) {
21172
- const schematicComponentId = component.schematic_component_id;
21173
- const sectionId = component.getSchematicSectionName();
21174
- if (schematicComponentId && sectionId) {
21175
- sectionIdBySchematicComponentId.set(schematicComponentId, sectionId);
21176
- }
21177
- }
21178
- const chips = [];
21179
- const pinIdToSchematicPortId = /* @__PURE__ */ new Map();
21180
- const schematicPortIdToPinId = /* @__PURE__ */ new Map();
21181
- for (const schematicComponent of schematicComponents) {
21182
- const chipId = schematicComponent.schematic_component_id;
21183
- const pins = [];
21184
- const sourceComponent = db.source_component.getWhere({
21185
- source_component_id: schematicComponent.source_component_id
21186
- });
21187
- const schematicPorts = db.schematic_port.list({
21188
- schematic_component_id: schematicComponent.schematic_component_id
21189
- });
21190
- for (const schematicPort of schematicPorts) {
21191
- const sourcePort = db.source_port.get(schematicPort.source_port_id);
21192
- const selector = getSchematicPortSelector({
21193
- componentName: sourceComponent?.name ?? schematicComponent.schematic_component_id,
21194
- schematicPort,
21195
- sourcePort
21196
- });
21197
- pinIdToSchematicPortId.set(selector, schematicPort.schematic_port_id);
21198
- schematicPortIdToPinId.set(schematicPort.schematic_port_id, selector);
21199
- }
21200
- for (const schematicPort of schematicPorts) {
21201
- const pinId = schematicPortIdToPinId.get(schematicPort.schematic_port_id);
21202
- pins.push({
21203
- pinId,
21204
- x: schematicPort.center.x,
21205
- y: schematicPort.center.y,
21206
- // Pass the port's true facing direction (known from the schematic
21207
- // symbol). The chip box handed to the solver is text-inclusive, so for
21208
- // small parts with a large reference designator the pins sit inside the
21209
- // box. The solver snaps such pins to the box edge along this facing
21210
- // direction (rather than guessing from geometry, which would pick the
21211
- // wrong edge for a resistor whose ref text widened the box).
21212
- _facingDirection: convertFacingDirectionToElbowDirection(
21213
- schematicPort.facing_direction ?? null
21214
- )
21215
- });
21216
- }
21217
- const sectionId = sectionIdBySchematicComponentId.get(
21218
- schematicComponent.schematic_component_id
21219
- );
21220
- const layoutBounds = getSchematicComponentWithTextBounds({ db, schematicComponent }) ?? getBoundFromCenteredRect4({
21221
- center: schematicComponent.center,
21222
- width: schematicComponent.size.width,
21223
- height: schematicComponent.size.height
21224
- });
21225
- chips.push({
21226
- chipId,
21227
- center: getBoundsCenter2(layoutBounds),
21228
- width: layoutBounds.maxX - layoutBounds.minX,
21229
- height: layoutBounds.maxY - layoutBounds.minY,
21230
- pins,
21231
- sectionId
21232
- });
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";
21233
21074
  }
21234
- const allSourceAndSchematicPortIdsInScope = /* @__PURE__ */ new Set();
21235
- const schPortIdToSourcePortId = /* @__PURE__ */ new Map();
21236
- const sourcePortIdToSchPortId = /* @__PURE__ */ new Map();
21237
- const userNetIdToConnKey = /* @__PURE__ */ new Map();
21238
- for (const sc of schematicComponents) {
21239
- const ports = db.schematic_port.list({
21240
- schematic_component_id: sc.schematic_component_id
21241
- });
21242
- for (const sp of ports) {
21243
- allSourceAndSchematicPortIdsInScope.add(sp.schematic_port_id);
21244
- if (sp.source_port_id) {
21245
- schPortIdToSourcePortId.set(sp.schematic_port_id, sp.source_port_id);
21246
- sourcePortIdToSchPortId.set(sp.source_port_id, sp.schematic_port_id);
21247
- }
21248
- }
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;
21249
21083
  }
21250
- const allowedSubcircuitIds = /* @__PURE__ */ new Set();
21251
- if (group.subcircuit_id) allowedSubcircuitIds.add(group.subcircuit_id);
21252
- for (const cg of childGroups) {
21253
- if (cg.subcircuit_id) allowedSubcircuitIds.add(cg.subcircuit_id);
21084
+ if (label.schematicPortIds.some(
21085
+ (id) => solverPlacement.schematicPortIds.has(id)
21086
+ )) {
21087
+ return true;
21254
21088
  }
21255
- const tracesInScope = db.source_trace.list().filter((st) => {
21256
- if (st.subcircuit_id === group.subcircuit_id) return true;
21257
- for (const source_port_id of st.connected_source_port_ids) {
21258
- if (sourcePortIdToSchPortId.has(source_port_id)) return true;
21259
- }
21260
- return false;
21261
- });
21262
- const externalNetIds = tracesInScope.flatMap(
21263
- (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
21264
21093
  );
21265
- for (const netId of externalNetIds) {
21266
- const net = db.source_net.get(netId);
21267
- if (net?.subcircuit_id) {
21268
- 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;
21269
21118
  }
21119
+ netLabelPlacementKeys.add(key);
21120
+ dedupedNetLabelPlacements.push(placement);
21270
21121
  }
21271
- const directConnections = [];
21272
- const connectedPairKeys = /* @__PURE__ */ new Set();
21273
- const connKeysWithExplicitPortNetTraces = /* @__PURE__ */ new Set();
21274
- for (const sourceTrace of tracesInScope) {
21275
- if (sourceTrace?.subcircuit_connectivity_map_key && (sourceTrace.connected_source_port_ids?.length ?? 0) > 0 && (sourceTrace.connected_source_net_ids?.length ?? 0) > 0) {
21276
- connKeysWithExplicitPortNetTraces.add(
21277
- sourceTrace.subcircuit_connectivity_map_key
21278
- );
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);
21279
21138
  }
21280
21139
  }
21281
- for (const st of db.source_trace.list()) {
21282
- if (st.subcircuit_id && !allowedSubcircuitIds.has(st.subcircuit_id)) {
21283
- 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);
21284
21159
  }
21285
- const connected = (st.connected_source_port_ids ?? []).map((sourcePortId) => sourcePortIdToSchPortId.get(sourcePortId)).filter(
21286
- (schematicPortId) => Boolean(schematicPortId) && allSourceAndSchematicPortIdsInScope.has(schematicPortId)
21160
+ const schPortIds = placement.pinIds.map(
21161
+ (pinId) => pinIdToSchematicPortId.get(pinId)
21287
21162
  );
21288
- if (connected.length >= 2) {
21289
- const [a, b] = connected.slice(0, 2);
21290
- const pairKey = [a, b].sort().join("::");
21291
- if (!connectedPairKeys.has(pairKey)) {
21292
- connectedPairKeys.add(pairKey);
21293
- const traceLabel = st.name ?? st.display_name;
21294
- const userNetId = traceLabel ?? st.source_trace_id;
21295
- if (st.subcircuit_connectivity_map_key) {
21296
- userNetIdToConnKey.set(userNetId, st.subcircuit_connectivity_map_key);
21297
- }
21298
- const portA = db.schematic_port.get(a);
21299
- const portB = db.schematic_port.get(b);
21300
- let portDistance = 0;
21301
- if (portA && portB) {
21302
- portDistance = Math.sqrt(
21303
- (portA.center.x - portB.center.x) ** 2 + (portA.center.y - portB.center.y) ** 2
21304
- );
21305
- }
21306
- const maxMspDist = group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
21307
- let netLabelWidth;
21308
- if (traceLabel && !traceLabel.startsWith(".") && portDistance > maxMspDist) {
21309
- netLabelWidth = Number(
21310
- getSchematicNetLabelTextWidth({
21311
- text: String(traceLabel),
21312
- font_size: 0.14
21313
- }).toFixed(2)
21314
- );
21315
- }
21316
- directConnections.push({
21317
- pinIds: [a, b].map((id) => schematicPortIdToPinId.get(id)),
21318
- netId: userNetId,
21319
- netLabelWidth
21320
- });
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;
21321
21173
  }
21322
21174
  }
21323
- }
21324
- const netConnections = [];
21325
- for (const net of db.source_net.list().filter(
21326
- (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
21327
- )) {
21328
- if (net.subcircuit_connectivity_map_key) {
21329
- 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;
21330
21182
  }
21331
- }
21332
- const connKeyToPinIds = /* @__PURE__ */ new Map();
21333
- for (const [schId, srcPortId] of schPortIdToSourcePortId) {
21334
- const sp = db.source_port.get(srcPortId);
21335
- if (!sp?.subcircuit_connectivity_map_key) continue;
21336
- const connKey = sp.subcircuit_connectivity_map_key;
21337
- if (!connKeyToPinIds.has(connKey)) connKeyToPinIds.set(connKey, []);
21338
- connKeyToPinIds.get(connKey).push(schId);
21339
- }
21340
- for (const [connKey, schematicPortIds] of connKeyToPinIds) {
21341
- const sourceNet = connKeyToSourceNet.get(connKey);
21342
- if (sourceNet && schematicPortIds.length >= 1) {
21343
- const userNetId = String(
21344
- 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
21345
21187
  );
21346
- userNetIdToConnKey.set(userNetId, connKey);
21347
- const netLabelTextWidth = Number(
21348
- getSchematicNetLabelTextWidth({ text: String(userNetId) }).toFixed(2)
21188
+ const hasRoutedTraceForPlacementPort = schPortIds.some(
21189
+ (id) => schematicPortIdsWithRoutedTraces.has(id)
21349
21190
  );
21350
- let netLabelWidth = netLabelTextWidth;
21351
- let netLabelHeight;
21352
- if (sourceNet.is_ground || sourceNet.is_power) {
21353
- netLabelWidth = SCHEMATIC_RAIL_NET_LABEL_HEIGHT;
21354
- 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;
21355
21198
  }
21356
- netConnections.push({
21357
- netId: userNetId,
21358
- pinIds: schematicPortIds.map(
21359
- (portId) => schematicPortIdToPinId.get(portId)
21360
- ),
21361
- netLabelWidth,
21362
- netLabelHeight
21199
+ const text2 = sourceNet.name;
21200
+ const center2 = computeSchematicNetLabelCenter({
21201
+ anchor_position,
21202
+ anchor_side,
21203
+ text: text2
21363
21204
  });
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
+ }
21217
+ }
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;
21364
21228
  }
21365
- }
21366
- const availableNetLabelOrientations = (() => {
21367
- const netToAllowedOrientations = {};
21368
- const presentNetIds = new Set(netConnections.map((nc) => nc.netId));
21369
- for (const net of db.source_net.list().filter(
21370
- (n) => !n.subcircuit_id || allowedSubcircuitIds.has(n.subcircuit_id)
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)
21371
21239
  )) {
21372
- if (!net.name) continue;
21373
- if (!presentNetIds.has(net.name)) continue;
21374
- if (net.is_ground) {
21375
- netToAllowedOrientations[net.name] = ["y-"];
21376
- } else if (net.is_power) {
21377
- netToAllowedOrientations[net.name] = ["y+"];
21378
- } else {
21379
- netToAllowedOrientations[net.name] = ["x-", "x+"];
21380
- }
21240
+ debug8(
21241
+ `skipping net label placement for "${placement.netId}" REASON:schematic port has routed traces and no display label`
21242
+ );
21243
+ continue;
21381
21244
  }
21382
- return netToAllowedOrientations;
21383
- })();
21384
- const inputProblem = {
21385
- chips,
21386
- directConnections,
21387
- netConnections,
21388
- textBoxes,
21389
- availableNetLabelOrientations,
21390
- maxMspPairDistance: group._parsedProps.schMaxTraceDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE
21391
- };
21392
- return {
21393
- inputProblem,
21394
- pinIdToSchematicPortId,
21395
- connKeyToSourceNet,
21396
- userNetIdToConnKey,
21397
- connKeysWithExplicitPortNetTraces,
21398
- allSourceAndSchematicPortIdsInScope,
21399
- schPortIdToSourcePortId
21400
- };
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
+ }
21401
21260
  }
21402
21261
 
21403
21262
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyTracesFromSolverOutput.ts
21404
21263
  import "@tscircuit/schematic-trace-solver";
21264
+ import Debug11 from "debug";
21405
21265
 
21406
21266
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/segment-intersection.ts
21407
21267
  var cross = (ax, ay, bx, by) => {
@@ -21743,6 +21603,25 @@ function computeJunctions(traces, opts = {}) {
21743
21603
  return result;
21744
21604
  }
21745
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
+
21746
21625
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/remove-overlapping-same-net-crossing-segments.ts
21747
21626
  var TOL3 = 1e-6;
21748
21627
  function isCollinear(a, b, tol = TOL3) {
@@ -21849,8 +21728,7 @@ function removeOverlappingSameNetCrossingSegments(traces) {
21849
21728
  }
21850
21729
 
21851
21730
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyTracesFromSolverOutput.ts
21852
- import Debug10 from "debug";
21853
- var debug8 = Debug10("Group_doInitialSchematicTraceRender");
21731
+ var debug9 = Debug11("Group_doInitialSchematicTraceRender");
21854
21732
  var MAX_PIN_SNAP_GAP = 1.5;
21855
21733
  function extendTraceEndpointsToReachPinsInsideExpandedBoundingBox(params, db) {
21856
21734
  const { points, schematicPortIds, eligiblePortIds } = params;
@@ -21902,6 +21780,20 @@ function applyTracesFromSolverOutput(args) {
21902
21780
  schematicPortIdsWithPreExistingNetLabels
21903
21781
  } = args;
21904
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
+ }
21905
21797
  const eligiblePortIds = /* @__PURE__ */ new Set();
21906
21798
  for (const schematicComponent of db.schematic_component.list()) {
21907
21799
  if (!getSchematicComponentWithTextBounds({ db, schematicComponent })) {
@@ -21910,12 +21802,12 @@ function applyTracesFromSolverOutput(args) {
21910
21802
  for (const port of db.schematic_port.list({
21911
21803
  schematic_component_id: schematicComponent.schematic_component_id
21912
21804
  })) {
21913
- eligiblePortIds.add(port.schematic_port_id);
21805
+ eligiblePortIds.add(asSchematicPortId(port.schematic_port_id));
21914
21806
  }
21915
21807
  }
21916
21808
  const traces = solver.netLabelTraceCollisionSolver?.getOutput().traces ?? solver.traceCleanupSolver?.getOutput().traces ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().traces ?? solver.schematicTraceLinesSolver?.solvedTracePaths;
21917
21809
  const pendingTraces = [];
21918
- debug8(`Traces inside SchematicTraceSolver output: ${(traces ?? []).length}`);
21810
+ debug9(`Traces inside SchematicTraceSolver output: ${(traces ?? []).length}`);
21919
21811
  for (const solvedTracePath of traces ?? []) {
21920
21812
  const uniquePinIds = Array.from(new Set(solvedTracePath.pinIds ?? []));
21921
21813
  const solvedTraceSchematicPortIds = uniquePinIds.map((pinId) => pinIdToSchematicPortId.get(pinId)).filter((id) => Boolean(id));
@@ -21923,14 +21815,14 @@ function applyTracesFromSolverOutput(args) {
21923
21815
  (id) => schematicPortIdsWithPreExistingNetLabels.has(id)
21924
21816
  );
21925
21817
  if (isNetLabelCoveredTrace) {
21926
- debug8(
21818
+ debug9(
21927
21819
  `Skipping solver netlabel-covered trace ${solvedTracePath?.mspPairId} because all schematic ports already have netlabels`
21928
21820
  );
21929
21821
  continue;
21930
21822
  }
21931
21823
  const points = solvedTracePath?.tracePath;
21932
21824
  if (!Array.isArray(points) || points.length < 2) {
21933
- debug8(
21825
+ debug9(
21934
21826
  `Skipping trace ${solvedTracePath?.pinIds.join(",")} because it has less than 2 points`
21935
21827
  );
21936
21828
  continue;
@@ -21956,9 +21848,18 @@ function applyTracesFromSolverOutput(args) {
21956
21848
  const pA = pinIdToSchematicPortId.get(solvedTracePath.pins[0]?.pinId);
21957
21849
  const pB = pinIdToSchematicPortId.get(solvedTracePath.pins[1]?.pinId);
21958
21850
  if (pA && pB) {
21959
- for (const schPid of [pA, pB]) {
21960
- const existing = db.schematic_port.get(schPid);
21961
- 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
+ }
21962
21863
  }
21963
21864
  subcircuit_connectivity_map_key = userNetIdToConnKey.get(
21964
21865
  String(solvedTracePath.userNetId)
@@ -21993,322 +21894,526 @@ function applyTracesFromSolverOutput(args) {
21993
21894
  } else if (endpointSchematicSheetIds.size === 0) {
21994
21895
  schematicSheetId2 = group._resolveSchematicSheetId();
21995
21896
  }
21996
- pendingTraces.push({
21997
- source_trace_id,
21998
- edges,
21999
- subcircuit_connectivity_map_key,
22000
- schematic_sheet_id: schematicSheetId2
21897
+ pendingTraces.push({
21898
+ source_trace_id,
21899
+ edges,
21900
+ subcircuit_connectivity_map_key,
21901
+ schematic_sheet_id: schematicSheetId2
21902
+ });
21903
+ }
21904
+ debug9(
21905
+ `Applying ${pendingTraces.length} traces from SchematicTraceSolver output`
21906
+ );
21907
+ const withCrossings = computeCrossings(
21908
+ pendingTraces.map((t) => ({
21909
+ source_trace_id: t.source_trace_id,
21910
+ edges: t.edges,
21911
+ connectivity_key: t.subcircuit_connectivity_map_key
21912
+ }))
21913
+ );
21914
+ const existingTracesForJunctions = [];
21915
+ const schematicSheetId = group._resolveSchematicSheetId();
21916
+ for (const t of db.schematic_trace.list()) {
21917
+ if (t.edges.length === 0) continue;
21918
+ if (t.schematic_sheet_id !== schematicSheetId) continue;
21919
+ const sourceTrace = t.source_trace_id ? db.source_trace.get(t.source_trace_id) : void 0;
21920
+ existingTracesForJunctions.push({
21921
+ schematic_trace_id: t.schematic_trace_id,
21922
+ source_trace_id: t.source_trace_id ?? t.schematic_trace_id,
21923
+ edges: t.edges,
21924
+ connectivity_key: t.subcircuit_connectivity_map_key ?? sourceTrace?.subcircuit_connectivity_map_key
21925
+ });
21926
+ }
21927
+ const tracesWithTrimmedCrossingOverlaps = removeOverlappingSameNetCrossingSegments([
21928
+ ...withCrossings,
21929
+ ...existingTracesForJunctions
21930
+ ]);
21931
+ const visibleTraces = tracesWithTrimmedCrossingOverlaps.slice(
21932
+ 0,
21933
+ withCrossings.length
21934
+ );
21935
+ const visibleExistingTraces = tracesWithTrimmedCrossingOverlaps.slice(
21936
+ withCrossings.length
21937
+ );
21938
+ for (const trace of visibleExistingTraces) {
21939
+ if (!trace.schematic_trace_id) continue;
21940
+ db.schematic_trace.update(trace.schematic_trace_id, {
21941
+ edges: trace.edges
21942
+ });
21943
+ }
21944
+ const junctionsById = computeJunctions([
21945
+ ...visibleTraces,
21946
+ ...visibleExistingTraces
21947
+ ]);
21948
+ for (const t of visibleTraces) {
21949
+ const pendingTrace = pendingTraces.find(
21950
+ (pendingTrace2) => pendingTrace2.source_trace_id === t.source_trace_id
21951
+ );
21952
+ let traceSchematicSheetId = schematicSheetId;
21953
+ if (pendingTrace?.schematic_sheet_id) {
21954
+ traceSchematicSheetId = pendingTrace.schematic_sheet_id;
21955
+ }
21956
+ db.schematic_trace.insert({
21957
+ source_trace_id: t.source_trace_id,
21958
+ edges: t.edges,
21959
+ junctions: junctionsById[t.source_trace_id] ?? [],
21960
+ subcircuit_connectivity_map_key: pendingTrace?.subcircuit_connectivity_map_key,
21961
+ schematic_sheet_id: traceSchematicSheetId
21962
+ });
21963
+ }
21964
+ }
21965
+
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";
21971
+ import "@tscircuit/schematic-trace-solver";
21972
+
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}`;
21981
+ };
21982
+
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;
22011
+ }
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
+ }
22038
+
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
+ }
22063
+ return {
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
22069
+ };
22070
+ }
22071
+
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 = {}) {
22076
+ const { db } = group.root;
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);
22088
+ }
22089
+ componentsToVisit.push(...[...child.children].reverse());
22090
+ }
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
22112
+ );
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);
22128
+ }
22129
+ }
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);
22152
+ }
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
22174
+ );
22175
+ const layoutBounds = getSchematicComponentWithTextBounds({ db, schematicComponent }) ?? getBoundFromCenteredRect4({
22176
+ center: schematicComponent.center,
22177
+ width: schematicComponent.size.width,
22178
+ height: schematicComponent.size.height
22001
22179
  });
22002
- }
22003
- debug8(
22004
- `Applying ${pendingTraces.length} traces from SchematicTraceSolver output`
22005
- );
22006
- const withCrossings = computeCrossings(
22007
- pendingTraces.map((t) => ({
22008
- source_trace_id: t.source_trace_id,
22009
- edges: t.edges,
22010
- connectivity_key: t.subcircuit_connectivity_map_key
22011
- }))
22012
- );
22013
- const existingTracesForJunctions = [];
22014
- const schematicSheetId = group._resolveSchematicSheetId();
22015
- for (const t of db.schematic_trace.list()) {
22016
- if (t.edges.length === 0) continue;
22017
- if (t.schematic_sheet_id !== schematicSheetId) continue;
22018
- const sourceTrace = t.source_trace_id ? db.source_trace.get(t.source_trace_id) : void 0;
22019
- existingTracesForJunctions.push({
22020
- schematic_trace_id: t.schematic_trace_id,
22021
- source_trace_id: t.source_trace_id ?? t.schematic_trace_id,
22022
- edges: t.edges,
22023
- connectivity_key: t.subcircuit_connectivity_map_key ?? sourceTrace?.subcircuit_connectivity_map_key
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
22024
22187
  });
22025
22188
  }
22026
- const tracesWithTrimmedCrossingOverlaps = removeOverlappingSameNetCrossingSegments([
22027
- ...withCrossings,
22028
- ...existingTracesForJunctions
22029
- ]);
22030
- const visibleTraces = tracesWithTrimmedCrossingOverlaps.slice(
22031
- 0,
22032
- withCrossings.length
22033
- );
22034
- const visibleExistingTraces = tracesWithTrimmedCrossingOverlaps.slice(
22035
- withCrossings.length
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])
22036
22195
  );
22037
- for (const trace of visibleExistingTraces) {
22038
- if (!trace.schematic_trace_id) continue;
22039
- db.schematic_trace.update(trace.schematic_trace_id, {
22040
- edges: trace.edges
22196
+ for (const sc of schematicComponents) {
22197
+ const ports = db.schematic_port.list({
22198
+ schematic_component_id: sc.schematic_component_id
22041
22199
  });
22042
- }
22043
- const junctionsById = computeJunctions([
22044
- ...visibleTraces,
22045
- ...visibleExistingTraces
22046
- ]);
22047
- for (const t of visibleTraces) {
22048
- const pendingTrace = pendingTraces.find(
22049
- (pendingTrace2) => pendingTrace2.source_trace_id === t.source_trace_id
22050
- );
22051
- let traceSchematicSheetId = schematicSheetId;
22052
- if (pendingTrace?.schematic_sheet_id) {
22053
- traceSchematicSheetId = pendingTrace.schematic_sheet_id;
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
+ }
22054
22208
  }
22055
- db.schematic_trace.insert({
22056
- source_trace_id: t.source_trace_id,
22057
- edges: t.edges,
22058
- junctions: junctionsById[t.source_trace_id] ?? [],
22059
- subcircuit_connectivity_map_key: pendingTrace?.subcircuit_connectivity_map_key,
22060
- schematic_sheet_id: traceSchematicSheetId
22061
- });
22062
22209
  }
22063
- }
22064
-
22065
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
22066
- import "@tscircuit/schematic-trace-solver";
22067
-
22068
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/oppositeSide.ts
22069
- var oppositeSide = (input) => {
22070
- switch (input) {
22071
- case "x+":
22072
- return "left";
22073
- case "x-":
22074
- return "right";
22075
- case "y+":
22076
- return "bottom";
22077
- case "y-":
22078
- return "top";
22079
- case "left":
22080
- return "right";
22081
- case "top":
22082
- return "bottom";
22083
- case "right":
22084
- return "left";
22085
- case "bottom":
22086
- return "top";
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);
22087
22214
  }
22088
- };
22089
-
22090
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetNameFromPorts.ts
22091
- var getNetNameFromPorts = (ports) => {
22092
- for (const port of ports) {
22093
- const traces = port._getDirectlyConnectedTraces();
22094
- for (const trace of traces) {
22095
- const displayLabel = trace._getSchematicNetLabelText();
22096
- if (displayLabel) {
22097
- return { name: displayLabel, wasAssignedDisplayLabel: true };
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;
22098
22220
  }
22099
22221
  }
22100
- }
22101
- const db = ports.find((port) => port.root)?.root?.db;
22102
- const sourcePortIds = ports.map((port) => port.source_port_id).filter((sourcePortId) => Boolean(sourcePortId));
22103
- const netName = db ? getNetNameFromSourcePorts(db, sourcePortIds) : ports.map((port) => port._getNetLabelText()).filter((name) => Boolean(name)).sort(
22104
- (a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
22105
- )[0];
22106
- return { name: netName ?? "", wasAssignedDisplayLabel: false };
22107
- };
22108
-
22109
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getNetLabelTextBounds.ts
22110
- var NET_LABEL_TEXT_HEIGHT = 0.18;
22111
- var getNetLabelTextBounds = ({
22112
- center,
22113
- text
22114
- }) => {
22115
- const width = getSchematicNetLabelTextWidth({ text });
22116
- const halfWidth = width / 2;
22117
- const halfHeight = NET_LABEL_TEXT_HEIGHT / 2;
22118
- return {
22119
- minX: center.x - halfWidth,
22120
- maxX: center.x + halfWidth,
22121
- minY: center.y - halfHeight,
22122
- maxY: center.y + halfHeight
22123
- };
22124
- };
22125
-
22126
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/applyNetLabelPlacements.ts
22127
- import Debug11 from "debug";
22128
- import { doBoundsOverlap as doBoundsOverlap2 } from "@tscircuit/math-utils";
22129
- var debug9 = Debug11("Group_doInitialSchematicTraceRender");
22130
- var isSameNet = (label, sourceNet) => label.source_net_id != null && label.source_net_id === sourceNet.source_net_id;
22131
- var isUserDefinedNetLabelRedundantWithPlacement = (label, solverPlacement) => {
22132
- if (!isSameNet(label, solverPlacement.sourceNet)) {
22133
22222
  return false;
22134
- }
22135
- if (label.schematicPortIds.some(
22136
- (id) => solverPlacement.schematicPortIds.has(id)
22137
- )) {
22138
- return true;
22139
- }
22140
- if (!label.center) return false;
22141
- return doBoundsOverlap2(
22142
- getNetLabelTextBounds({ center: label.center, text: label.text }),
22143
- solverPlacement.bounds
22223
+ });
22224
+ const externalNetIds = tracesInScope.flatMap(
22225
+ (st) => st.connected_source_net_ids
22144
22226
  );
22145
- };
22146
- function applyNetLabelPlacements(args) {
22147
- const {
22148
- group,
22149
- solver,
22150
- connKeyToSourceNet,
22151
- userNetIdToConnKey,
22152
- pinIdToSchematicPortId,
22153
- connKeysWithExplicitPortNetTraces,
22154
- schematicPortIdsWithPreExistingNetLabels,
22155
- schematicPortIdsWithRoutedTraces
22156
- } = args;
22157
- const { db } = group.root;
22158
- const netLabelPlacements = solver.netLabelNetLabelCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelTraceCollisionSolver?.getOutput().netLabelPlacements ?? solver.netLabelPlacementSolver?.netLabelPlacements ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().netLabelPlacements ?? [];
22159
- const dedupedNetLabelPlacements = [];
22160
- const netLabelPlacementKeys = /* @__PURE__ */ new Set();
22161
- for (const placement of netLabelPlacements) {
22162
- const pinIds = [...placement.pinIds ?? []].sort();
22163
- const key = `${placement.globalConnNetId}|${pinIds.join("::")}|${placement.netId ?? ""}`;
22164
- if (netLabelPlacementKeys.has(key)) {
22165
- debug9(
22166
- `skipping duplicate placement for "${placement.netId}" REASON:identical net label placement`
22167
- );
22168
- continue;
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);
22169
22231
  }
22170
- netLabelPlacementKeys.add(key);
22171
- dedupedNetLabelPlacements.push(placement);
22172
22232
  }
22173
- const netLabelPlacementCountByGlobalNetId = /* @__PURE__ */ new Map();
22174
- const routedPairKeysByGlobalNetId = /* @__PURE__ */ new Map();
22175
- const globalNetIdsWithPortOnlyPlacements = /* @__PURE__ */ new Set();
22176
- for (const placement of dedupedNetLabelPlacements) {
22177
- netLabelPlacementCountByGlobalNetId.set(
22178
- placement.globalConnNetId,
22179
- (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) + 1
22180
- );
22181
- if ((placement.pinIds?.length ?? 0) > 1) {
22182
- if (!routedPairKeysByGlobalNetId.has(placement.globalConnNetId)) {
22183
- routedPairKeysByGlobalNetId.set(placement.globalConnNetId, /* @__PURE__ */ new Set());
22184
- }
22185
- routedPairKeysByGlobalNetId.get(placement.globalConnNetId).add(placement.pinIds.slice().sort().join("::"));
22186
- }
22187
- if ((placement.pinIds?.length ?? 0) <= 1) {
22188
- globalNetIdsWithPortOnlyPlacements.add(placement.globalConnNetId);
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
22240
+ );
22189
22241
  }
22190
22242
  }
22191
- const globalConnMap = solver.mspConnectionPairSolver.globalConnMap;
22192
- const userDefinedNetLabels = group.selectAll("netlabel").map((label) => {
22193
- if (!label.schematic_net_label_id) return null;
22194
- const dbLabel = db.schematic_net_label.get(label.schematic_net_label_id);
22195
- if (!dbLabel) return null;
22196
- return {
22197
- ...dbLabel,
22198
- schematicPortIds: label._getConnectedPorts().map((port) => port.schematic_port_id).filter((id) => Boolean(id))
22199
- };
22200
- }).filter((label) => label !== null);
22201
- for (const placement of dedupedNetLabelPlacements) {
22202
- debug9(`processing placement: ${placement.netId}`);
22203
- const placementUserNetId = globalConnMap.getIdsConnectedToNet(placement.globalConnNetId).find((id) => userNetIdToConnKey.get(id));
22204
- const placementConnKey = userNetIdToConnKey.get(placementUserNetId);
22205
- const orientation = placement.orientation;
22206
- const anchor_side = oppositeSide(orientation);
22207
- let sourceNet;
22208
- if (placementConnKey) {
22209
- sourceNet = connKeyToSourceNet.get(placementConnKey);
22243
+ for (const st of db.source_trace.list()) {
22244
+ if (st.subcircuit_id && !allowedSubcircuitIds.has(st.subcircuit_id)) {
22245
+ continue;
22210
22246
  }
22211
- const schPortIds = placement.pinIds.map(
22212
- (pinId) => pinIdToSchematicPortId.get(pinId)
22247
+ const connected = (st.connected_source_port_ids ?? []).map(
22248
+ (sourcePortId) => sourcePortIdToSchPortId.get(asSourcePortId(sourcePortId))
22249
+ ).filter(
22250
+ (schematicPortId) => Boolean(schematicPortId) && schematicPortIdsInScope.has(schematicPortId)
22213
22251
  );
22214
- let schematicSheetId = group._resolveSchematicSheetId();
22215
- const schematicPort = db.schematic_port.get(schPortIds[0]);
22216
- if (schematicPort?.schematic_sheet_id) {
22217
- schematicSheetId = schematicPort.schematic_sheet_id;
22218
- }
22219
- let anchor_position = placement.anchorPoint;
22220
- if (schPortIds.length === 1 && schPortIds[0]) {
22221
- const anchorSchPort = db.schematic_port.get(schPortIds[0]);
22222
- if (anchorSchPort?.center && !anchorSchPort.is_connected) {
22223
- anchor_position = anchorSchPort.center;
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
+ });
22224
22285
  }
22225
22286
  }
22226
- if (schPortIds.some(
22227
- (schPortId) => schematicPortIdsWithPreExistingNetLabels.has(schPortId)
22228
- )) {
22229
- debug9(
22230
- `skipping net label placement for "${placement.netId}" REASON:schematic port has pre-existing net label`
22231
- );
22232
- continue;
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);
22233
22294
  }
22234
- if (sourceNet) {
22235
- const isPowerOrGroundNet = sourceNet.is_ground || sourceNet.is_power;
22236
- const hasExplicitPortNetTrace = connKeysWithExplicitPortNetTraces.has(
22237
- placementConnKey
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
+ }
22238
22320
  );
22239
- const hasRoutedTraceForPlacementPort = schPortIds.some(
22240
- (id) => schematicPortIdsWithRoutedTraces.has(id)
22321
+ const userNetId = String(
22322
+ sourceNet.name || sourceNet.source_net_id || connKey
22241
22323
  );
22242
- const hasSingleLabelPlacement = (netLabelPlacementCountByGlobalNetId.get(placement.globalConnNetId) ?? 0) <= 1;
22243
- const shouldSkipImplicitSignalNetLabel = !hasExplicitPortNetTrace && !isPowerOrGroundNet && hasRoutedTraceForPlacementPort && hasSingleLabelPlacement;
22244
- if (shouldSkipImplicitSignalNetLabel) {
22245
- debug9(
22246
- `skipping net label placement for "${placement.netId}" REASON:schematic port has routed trace`
22247
- );
22248
- 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;
22249
22333
  }
22250
- const text2 = sourceNet.name;
22251
- const center2 = computeSchematicNetLabelCenter({
22252
- anchor_position,
22253
- anchor_side,
22254
- text: text2
22334
+ netConnections.push({
22335
+ netId: userNetId,
22336
+ pinIds: uniqueSchematicPortIds.map(
22337
+ (portId) => schematicPortIdToPinId.get(portId)
22338
+ ),
22339
+ netLabelWidth,
22340
+ netLabelHeight
22255
22341
  });
22256
- if (!isPowerOrGroundNet) {
22257
- const solverPlacement = {
22258
- sourceNet,
22259
- text: text2,
22260
- schematicPortIds: new Set(schPortIds),
22261
- bounds: getNetLabelTextBounds({ center: center2, text: text2 })
22262
- };
22263
- if (userDefinedNetLabels.some(
22264
- (label) => isUserDefinedNetLabelRedundantWithPlacement(label, solverPlacement)
22265
- )) {
22266
- continue;
22267
- }
22268
- }
22269
- const netLabel2 = {
22270
- text: text2,
22271
- source_net_id: sourceNet.source_net_id,
22272
- anchor_position,
22273
- center: center2,
22274
- anchor_side,
22275
- schematic_sheet_id: schematicSheetId
22276
- };
22277
- db.schematic_net_label.insert(netLabel2);
22278
- continue;
22279
22342
  }
22280
- const ports = group.selectAll("port").filter((p) => p._getSubcircuitConnectivityKey() === placementConnKey);
22281
- const { name: portInstanceText, wasAssignedDisplayLabel } = getNetNameFromPorts(ports);
22282
- const sourcePortIdsForConnection = placementConnKey ? db.source_port.list().filter(
22283
- (sourcePort) => sourcePort.subcircuit_connectivity_map_key === placementConnKey
22284
- ).map((sourcePort) => sourcePort.source_port_id) : [];
22285
- const text = portInstanceText || getNetNameFromSourcePorts(db, sourcePortIdsForConnection) || "";
22286
- const isRoutedPairPlacement = (placement.pinIds?.length ?? 0) > 1;
22287
- const shouldKeepRoutedPairLabel = isRoutedPairPlacement && (globalNetIdsWithPortOnlyPlacements.has(placement.globalConnNetId) || (routedPairKeysByGlobalNetId.get(placement.globalConnNetId)?.size ?? 0) > 1);
22288
- if (!wasAssignedDisplayLabel && !shouldKeepRoutedPairLabel && schPortIds.some(
22289
- (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)
22290
22349
  )) {
22291
- debug9(
22292
- `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)
22293
22391
  );
22294
- continue;
22295
22392
  }
22296
- const center = computeSchematicNetLabelCenter({
22297
- anchor_position,
22298
- anchor_side,
22299
- text
22300
- });
22301
- const netLabel = {
22302
- text,
22303
- source_net_id: placementConnKey ?? placement.netId ?? placement.globalConnNetId,
22304
- anchor_position,
22305
- center,
22306
- anchor_side,
22307
- schematic_sheet_id: schematicSheetId
22308
- };
22309
- db.schematic_net_label.insert(netLabel);
22310
22393
  }
22311
- }
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
+ };
22312
22417
 
22313
22418
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/insertNetLabelsForPortsMissingTrace.ts
22314
22419
  var NEAR_EXISTING_NET_LABEL_DISTANCE = 0.5;
@@ -22350,19 +22455,17 @@ var getDirectCrossSubcircuitConnectionLabelText = (db, sourcePortId) => {
22350
22455
  return getSourcePortNetLabelText(db, otherSourcePortId);
22351
22456
  };
22352
22457
  var insertNetLabelsForPortsMissingTrace = ({
22353
- allSourceAndSchematicPortIdsInScope,
22458
+ schematicPortIdsInScope,
22354
22459
  group,
22355
22460
  schPortIdToSourcePortId,
22356
22461
  connKeyToSourceNet
22357
22462
  }) => {
22358
22463
  const { db } = group.root;
22359
- for (const schOrSrcPortId of Array.from(
22360
- allSourceAndSchematicPortIdsInScope
22361
- )) {
22362
- const schPort = db.schematic_port.get(schOrSrcPortId);
22464
+ for (const schematicPortId of schematicPortIdsInScope) {
22465
+ const schPort = db.schematic_port.get(schematicPortId);
22363
22466
  if (!schPort) continue;
22364
22467
  if (schPort.is_connected) continue;
22365
- const srcPortId = schPortIdToSourcePortId.get(schOrSrcPortId);
22468
+ const srcPortId = schPortIdToSourcePortId.get(schematicPortId);
22366
22469
  if (!srcPortId) continue;
22367
22470
  const sourcePort = db.source_port.get(srcPortId);
22368
22471
  if (group._parsedProps.showAsSchematicBox && sourcePort?.subcircuit_id === group.subcircuit_id && !sourcePort.source_component_id && getDirectCrossSubcircuitConnectedSourcePortId(db, srcPortId)) {
@@ -22373,9 +22476,7 @@ var insertNetLabelsForPortsMissingTrace = ({
22373
22476
  const sourceNet = connKeyToSourceNet.get(connKey);
22374
22477
  const schComponent = schPort.schematic_component_id ? db.schematic_component.get(schPort.schematic_component_id) : void 0;
22375
22478
  const portSchematicSheetId = schComponent?.schematic_sheet_id ?? group._resolveSchematicSheetId();
22376
- const connectedSourcePortIdsForKey = Array.from(
22377
- allSourceAndSchematicPortIdsInScope
22378
- ).map((portId) => schPortIdToSourcePortId.get(portId)).filter((sourcePortId) => {
22479
+ const connectedSourcePortIdsForKey = Array.from(schematicPortIdsInScope).map((portId) => schPortIdToSourcePortId.get(portId)).filter((sourcePortId) => {
22379
22480
  if (!sourcePortId) return false;
22380
22481
  return db.source_port.get(sourcePortId)?.subcircuit_connectivity_map_key === connKey;
22381
22482
  });
@@ -22398,13 +22499,13 @@ var insertNetLabelsForPortsMissingTrace = ({
22398
22499
  );
22399
22500
  const hasExplicitLabel = Boolean(sourceNet || assignedPortNetLabelText);
22400
22501
  if (isCollapsedBoxPort && !hasExternalTrace && !hasExplicitLabel) continue;
22401
- const connectedPortCountForKey = Array.from(
22402
- allSourceAndSchematicPortIdsInScope
22403
- ).filter((portId) => {
22404
- const sourcePortId = schPortIdToSourcePortId.get(portId);
22405
- if (!sourcePortId) return false;
22406
- return db.source_port.get(sourcePortId)?.subcircuit_connectivity_map_key === connKey;
22407
- }).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;
22408
22509
  const isGndNet = sourceNet?.is_ground ?? false;
22409
22510
  const isPowerNet = !isGndNet && (sourceNet?.is_power ?? false);
22410
22511
  let side;
@@ -22528,41 +22629,6 @@ var insertNetLabelsForPortsMissingTrace = ({
22528
22629
  }
22529
22630
  };
22530
22631
 
22531
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithAssignedNetLabels.ts
22532
- var getSchematicPortIdsWithAssignedNetLabels = (group) => {
22533
- const schematicPortIdsWithNetLabels = /* @__PURE__ */ new Set();
22534
- const netLabels = group.selectAll("netlabel");
22535
- for (const netLabel of netLabels) {
22536
- const netLabelPorts = netLabel._getConnectedPorts();
22537
- for (const port of netLabelPorts) {
22538
- if (!port.schematic_port_id) continue;
22539
- schematicPortIdsWithNetLabels.add(port.schematic_port_id);
22540
- }
22541
- }
22542
- return schematicPortIdsWithNetLabels;
22543
- };
22544
-
22545
- // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/getSchematicPortIdsWithRoutedTraces.ts
22546
- var getSchematicPortIdsWithRoutedTraces = ({
22547
- solver,
22548
- pinIdToSchematicPortId
22549
- }) => {
22550
- const solvedTraces = solver.traceCleanupSolver?.getOutput().traces ?? solver.traceLabelOverlapAvoidanceSolver?.getOutput().traces ?? solver.schematicTraceLinesSolver?.solvedTracePaths ?? [];
22551
- const schematicPortIdsWithRoutedTraces = /* @__PURE__ */ new Set();
22552
- for (const solvedTrace of solvedTraces) {
22553
- const points = solvedTrace?.tracePath;
22554
- if (!Array.isArray(points) || points.length < 2) continue;
22555
- const pinIds = Array.isArray(solvedTrace.pins) ? solvedTrace.pins.map((pin) => pin.pinId) : solvedTrace.pinIds;
22556
- for (const pinId of pinIds) {
22557
- const schPortId = pinIdToSchematicPortId.get(pinId);
22558
- if (schPortId) {
22559
- schematicPortIdsWithRoutedTraces.add(schPortId);
22560
- }
22561
- }
22562
- }
22563
- return schematicPortIdsWithRoutedTraces;
22564
- };
22565
-
22566
22632
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/Group_doInitialSchematicTraceRender.ts
22567
22633
  var debug10 = Debug12("Group_doInitialSchematicTraceRender");
22568
22634
  var renderSchematicTracesForSheet = ({
@@ -22573,7 +22639,7 @@ var renderSchematicTracesForSheet = ({
22573
22639
  inputProblem,
22574
22640
  pinIdToSchematicPortId,
22575
22641
  connKeyToSourceNet,
22576
- allSourceAndSchematicPortIdsInScope,
22642
+ schematicPortIdsInScope,
22577
22643
  schPortIdToSourcePortId,
22578
22644
  userNetIdToConnKey,
22579
22645
  connKeysWithExplicitPortNetTraces
@@ -22584,7 +22650,7 @@ var renderSchematicTracesForSheet = ({
22584
22650
  if (!hasRouteableSchematicConnections) {
22585
22651
  insertNetLabelsForPortsMissingTrace({
22586
22652
  group,
22587
- allSourceAndSchematicPortIdsInScope,
22653
+ schematicPortIdsInScope,
22588
22654
  schPortIdToSourcePortId,
22589
22655
  connKeyToSourceNet
22590
22656
  });
@@ -22630,7 +22696,7 @@ var renderSchematicTracesForSheet = ({
22630
22696
  });
22631
22697
  insertNetLabelsForPortsMissingTrace({
22632
22698
  group,
22633
- allSourceAndSchematicPortIdsInScope,
22699
+ schematicPortIdsInScope,
22634
22700
  schPortIdToSourcePortId,
22635
22701
  connKeyToSourceNet
22636
22702
  });
@@ -22643,7 +22709,7 @@ var Group_doInitialSchematicTraceRender = (group) => {
22643
22709
  [
22644
22710
  group,
22645
22711
  ...group.getDescendants().filter(
22646
- (component) => component instanceof Group
22712
+ (component) => component instanceof Group3
22647
22713
  )
22648
22714
  ].map((schematicGroup) => schematicGroup.schematic_group_id).filter(
22649
22715
  (schematicGroupId) => Boolean(schematicGroupId)
@@ -23648,7 +23714,7 @@ function Group_getRoutingPhasePlans(group) {
23648
23714
  var package_default = {
23649
23715
  name: "@tscircuit/core",
23650
23716
  type: "module",
23651
- version: "0.0.1506",
23717
+ version: "0.0.1507",
23652
23718
  types: "dist/index.d.ts",
23653
23719
  main: "dist/index.js",
23654
23720
  module: "dist/index.js",
@@ -24672,7 +24738,7 @@ var ensureRouteStartsAtSourceTraceStart = ({
24672
24738
  const lastPoint = route[route.length - 1];
24673
24739
  return getDistanceToPoint(lastPoint, firstPcbPort) < getDistanceToPoint(firstPoint, firstPcbPort) ? reversePcbTraceRoute(route) : route;
24674
24740
  };
24675
- var Group = class extends NormalComponent3 {
24741
+ var Group3 = class extends NormalComponent3 {
24676
24742
  pcb_group_id = null;
24677
24743
  schematic_group_id = null;
24678
24744
  subcircuit_id = null;
@@ -26091,7 +26157,7 @@ function inflateSourceGroup(sourceGroup, inflatorContext) {
26091
26157
  if (pcbGroup?.anchor_alignment) {
26092
26158
  groupProps2.pcbAnchorAlignment = pcbGroup.anchor_alignment;
26093
26159
  }
26094
- const group = new Group(groupProps2);
26160
+ const group = new Group3(groupProps2);
26095
26161
  group._isInflatedFromCircuitJson = true;
26096
26162
  group.source_group_id = sourceGroup.source_group_id;
26097
26163
  if (groupsMap) {
@@ -27410,7 +27476,7 @@ var IsolatedCircuit = class {
27410
27476
  this.firstChild = this.children[0];
27411
27477
  return;
27412
27478
  }
27413
- const group = new Group({ subcircuit: true });
27479
+ const group = new Group3({ subcircuit: true });
27414
27480
  group.parent = this;
27415
27481
  group.addAll(this.children);
27416
27482
  this.children = [group];
@@ -27816,7 +27882,7 @@ var getRoundedRectOutline = (width, height, radius) => {
27816
27882
  }
27817
27883
  return outline;
27818
27884
  };
27819
- var Board = class extends Group {
27885
+ var Board = class extends Group3 {
27820
27886
  pcb_board_id = null;
27821
27887
  source_board_id = null;
27822
27888
  _drcChecksComplete = false;
@@ -28273,7 +28339,7 @@ import { mountedboardProps } from "@tscircuit/props";
28273
28339
 
28274
28340
  // lib/components/primitive-components/Group/Subcircuit/Subcircuit.ts
28275
28341
  import "@tscircuit/props";
28276
- var Subcircuit = class extends Group {
28342
+ var Subcircuit = class extends Group3 {
28277
28343
  constructor(props) {
28278
28344
  super({
28279
28345
  ...props,
@@ -28845,7 +28911,7 @@ function getItemDimensions(item, db) {
28845
28911
  }
28846
28912
 
28847
28913
  // lib/components/normal-components/Subpanel.ts
28848
- var Subpanel = class _Subpanel extends Group {
28914
+ var Subpanel = class _Subpanel extends Group3 {
28849
28915
  pcb_panel_id = null;
28850
28916
  _tabsAndMouseBitesGenerated = false;
28851
28917
  get config() {
@@ -31482,7 +31548,7 @@ var createBreakoutPointSolverInput = (breakout) => {
31482
31548
  };
31483
31549
 
31484
31550
  // lib/components/primitive-components/Breakout/Breakout.ts
31485
- var Breakout = class extends Group {
31551
+ var Breakout = class extends Group3 {
31486
31552
  constructor(props) {
31487
31553
  super({
31488
31554
  ...props,
@@ -35360,7 +35426,7 @@ export {
35360
35426
  Fiducial,
35361
35427
  Footprint,
35362
35428
  Fuse,
35363
- Group,
35429
+ Group3 as Group,
35364
35430
  Hole,
35365
35431
  Inductor,
35366
35432
  Interconnect,