@tscircuit/checks 0.0.28 → 0.0.30

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/README.md CHANGED
@@ -26,7 +26,7 @@ and pcb traces here are the relevant elements (the types are produced below)
26
26
 
27
27
  ```ts
28
28
  // You can import these types from the @tscircuit/soup package e.g.
29
- // import type { PCBPort, PCBTrace, AnySoupElement } from "@tscircuit/soup"
29
+ // import type { PCBPort, PCBTrace, AnySoupElement } from "circuit-json"
30
30
 
31
31
  import { z } from "zod"
32
32
  import { distance } from "../units"
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  import { AnyCircuitElement, PcbTraceError } from 'circuit-json';
2
+ import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
2
3
 
3
4
  declare function checkEachPcbPortConnected(soup: AnyCircuitElement[]): PcbTraceError[];
4
5
 
5
- declare function checkEachPcbTraceNonOverlapping(soup: AnyCircuitElement[]): PcbTraceError[];
6
+ declare function checkEachPcbTraceNonOverlapping(circuitJson: AnyCircuitElement[], { connMap, }?: {
7
+ connMap?: ConnectivityMap;
8
+ }): PcbTraceError[];
6
9
 
7
10
  declare class NetManager {
8
11
  private networks;
package/dist/index.js CHANGED
@@ -55,7 +55,7 @@ var addStartAndEndPortIdsIfMissing = (soup) => {
55
55
  };
56
56
 
57
57
  // lib/check-each-pcb-port-connected.ts
58
- import { getReadableNameForPcbPort } from "@tscircuit/soup-util";
58
+ import { getReadableNameForPcbPort } from "@tscircuit/circuit-json-util";
59
59
  function checkEachPcbPortConnected(soup) {
60
60
  addStartAndEndPortIdsIfMissing(soup);
61
61
  const pcbPorts = soup.filter((item) => item.type === "pcb_port");
@@ -90,99 +90,136 @@ function checkEachPcbPortConnected(soup) {
90
90
  return errors;
91
91
  }
92
92
 
93
- // lib/check-each-pcb-trace-non-overlapping.ts
94
- import Debug from "debug";
95
- import { getReadableNameForElement } from "@tscircuit/soup-util";
96
- import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
97
- var debug = Debug("tscircuit:checks:check-each-pcb-trace-non-overlapping");
98
- function lineIntersects(x1, y1, x2, y2, x3, y3, x4, y4) {
99
- const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
100
- if (denom === 0) return false;
101
- const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
102
- const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
103
- return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1;
104
- }
105
- function tracesOverlap(trace1, trace2) {
106
- for (let i = 0; i < trace1.route.length - 1; i++) {
107
- for (let j = 0; j < trace2.route.length - 1; j++) {
108
- const seg1 = trace1.route[i];
109
- const seg2 = trace1.route[i + 1];
110
- const seg3 = trace2.route[j];
111
- const seg4 = trace2.route[j + 1];
112
- if (seg1.route_type === "wire" && seg2.route_type === "wire" && seg3.route_type === "wire" && seg4.route_type === "wire" && seg1.layer === seg3.layer) {
113
- const areLinesIntersecting = lineIntersects(
114
- seg1.x,
115
- seg1.y,
116
- seg2.x,
117
- seg2.y,
118
- seg3.x,
119
- seg3.y,
120
- seg4.x,
121
- seg4.y
122
- );
123
- if (areLinesIntersecting)
124
- return {
125
- // return the intersection point
126
- x: (seg1.x * seg2.y - seg2.x * seg1.y) / (seg2.y - seg1.y),
127
- y: (seg1.x * seg2.y - seg2.x * seg1.y) / (seg2.y - seg1.y)
128
- };
93
+ // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
94
+ import { getReadableNameForElement, cju } from "@tscircuit/circuit-json-util";
95
+
96
+ // lib/data-structures/SpatialIndex.ts
97
+ var SpatialObjectIndex = class {
98
+ buckets;
99
+ objectsById;
100
+ getBounds;
101
+ getId;
102
+ CELL_SIZE = 0.4;
103
+ constructor({
104
+ objects,
105
+ getBounds,
106
+ getId,
107
+ CELL_SIZE
108
+ }) {
109
+ this.buckets = /* @__PURE__ */ new Map();
110
+ this.objectsById = /* @__PURE__ */ new Map();
111
+ this.getBounds = getBounds;
112
+ this.getId = getId ?? (() => this._getNextId());
113
+ this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE;
114
+ for (const obj of objects) {
115
+ this.addObject(obj);
116
+ }
117
+ }
118
+ _idCounter = 0;
119
+ _getNextId() {
120
+ return `${this._idCounter++}`;
121
+ }
122
+ addObject(obj) {
123
+ const bounds = this.getBounds(obj);
124
+ const spatialIndexId = this.getId(obj);
125
+ const objWithId = { ...obj, spatialIndexId };
126
+ this.objectsById.set(spatialIndexId, objWithId);
127
+ const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE);
128
+ const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE);
129
+ const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE);
130
+ const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE);
131
+ for (let bx = minBucketX; bx <= maxBucketX; bx++) {
132
+ for (let by = minBucketY; by <= maxBucketY; by++) {
133
+ const bucketKey = `${bx}x${by}`;
134
+ const bucket = this.buckets.get(bucketKey);
135
+ if (!bucket) {
136
+ this.buckets.set(bucketKey, [objWithId]);
137
+ } else {
138
+ bucket.push(objWithId);
139
+ }
129
140
  }
130
141
  }
131
142
  }
132
- return false;
133
- }
134
- function traceOverlapsWithPad(trace, pad) {
135
- for (let i = 0; i < trace.route.length - 1; i++) {
136
- const seg1 = trace.route[i];
137
- const seg2 = trace.route[i + 1];
138
- if (seg1.route_type === "wire" && seg2.route_type === "wire" && seg1.layer === pad.layer && pad.shape === "rect") {
139
- const padLeft = pad.x - pad.width / 2;
140
- const padRight = pad.x + pad.width / 2;
141
- const padTop = pad.y - pad.height / 2;
142
- const padBottom = pad.y + pad.height / 2;
143
- if (lineIntersects(
144
- seg1.x,
145
- seg1.y,
146
- seg2.x,
147
- seg2.y,
148
- padLeft,
149
- padTop,
150
- padRight,
151
- padTop
152
- ) || lineIntersects(
153
- seg1.x,
154
- seg1.y,
155
- seg2.x,
156
- seg2.y,
157
- padRight,
158
- padTop,
159
- padRight,
160
- padBottom
161
- ) || lineIntersects(
162
- seg1.x,
163
- seg1.y,
164
- seg2.x,
165
- seg2.y,
166
- padRight,
167
- padBottom,
168
- padLeft,
169
- padBottom
170
- ) || lineIntersects(
171
- seg1.x,
172
- seg1.y,
173
- seg2.x,
174
- seg2.y,
175
- padLeft,
176
- padBottom,
177
- padLeft,
178
- padTop
179
- )) {
180
- return true;
143
+ removeObject(id) {
144
+ const obj = this.objectsById.get(id);
145
+ if (!obj) return false;
146
+ this.objectsById.delete(id);
147
+ const bounds = this.getBounds(obj);
148
+ const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE);
149
+ const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE);
150
+ const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE);
151
+ const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE);
152
+ for (let bx = minBucketX; bx <= maxBucketX; bx++) {
153
+ for (let by = minBucketY; by <= maxBucketY; by++) {
154
+ const bucketKey = `${bx}x${by}`;
155
+ const bucket = this.buckets.get(bucketKey);
156
+ if (bucket) {
157
+ const index = bucket.findIndex((item) => item.spatialIndexId === id);
158
+ if (index !== -1) {
159
+ bucket.splice(index, 1);
160
+ if (bucket.length === 0) {
161
+ this.buckets.delete(bucketKey);
162
+ }
163
+ }
164
+ }
181
165
  }
182
166
  }
167
+ return true;
183
168
  }
184
- return false;
185
- }
169
+ getBucketKey(x, y) {
170
+ return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`;
171
+ }
172
+ getObjectsInBounds(bounds, margin = 0) {
173
+ const objects = [];
174
+ const addedIds = /* @__PURE__ */ new Set();
175
+ const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE);
176
+ const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE);
177
+ const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE);
178
+ const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE);
179
+ for (let bx = minBucketX; bx <= maxBucketX; bx++) {
180
+ for (let by = minBucketY; by <= maxBucketY; by++) {
181
+ const bucketKey = `${bx}x${by}`;
182
+ const bucket = this.buckets.get(bucketKey) || [];
183
+ for (const obj of bucket) {
184
+ const id = obj.spatialIndexId;
185
+ if (addedIds.has(id)) continue;
186
+ addedIds.add(id);
187
+ objects.push(obj);
188
+ }
189
+ }
190
+ }
191
+ return objects;
192
+ }
193
+ };
194
+
195
+ // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
196
+ import {
197
+ getFullConnectivityMapFromCircuitJson
198
+ } from "circuit-json-to-connectivity-map";
199
+
200
+ // lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts
201
+ import { getBoundsOfPcbElements } from "@tscircuit/circuit-json-util";
202
+ var getCollidableBounds = (collidable) => {
203
+ if (collidable.type === "pcb_trace_segment") {
204
+ return {
205
+ minX: Math.min(collidable.x1, collidable.x2),
206
+ minY: Math.min(collidable.y1, collidable.y2),
207
+ maxX: Math.max(collidable.x1, collidable.x2),
208
+ maxY: Math.max(collidable.y1, collidable.y2)
209
+ };
210
+ }
211
+ return getBoundsOfPcbElements([collidable]);
212
+ };
213
+
214
+ // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
215
+ import { segmentToBoundsMinDistance } from "@tscircuit/math-utils";
216
+
217
+ // lib/drc-defaults.ts
218
+ var DEFAULT_TRACE_MARGIN = 0.1;
219
+ var DEFAULT_TRACE_THICKNESS = 0.15;
220
+ var EPSILON = 5e-3;
221
+
222
+ // lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts
186
223
  function getPcbPortIdsConnectedToTrace(trace) {
187
224
  const connectedPcbPorts = /* @__PURE__ */ new Set();
188
225
  for (const segment of trace.route) {
@@ -204,72 +241,137 @@ function getPcbPortIdsConnectedToTraces(traces) {
204
241
  }
205
242
  return Array.from(connectedPorts);
206
243
  }
207
- function checkEachPcbTraceNonOverlapping(soup) {
208
- addStartAndEndPortIdsIfMissing(soup);
209
- const connMap = getFullConnectivityMapFromCircuitJson(soup);
210
- const pcbTraces = soup.filter(
211
- (item) => item.type === "pcb_trace"
212
- );
213
- const pcbSMTPads = soup.filter(
214
- (item) => item.type === "pcb_smtpad"
215
- );
244
+
245
+ // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
246
+ import { segmentToSegmentMinDistance } from "@tscircuit/math-utils";
247
+ import { getPrimaryId } from "@tscircuit/circuit-json-util";
248
+
249
+ // lib/check-each-pcb-trace-non-overlapping/getCenterOfPcbTraceSegments.ts
250
+ var getCenterOfPcbTraceSegments = (segmentA, segmentB) => {
251
+ return {
252
+ x: (Math.min(segmentA.x1, segmentA.x2, segmentB.x1, segmentB.x2) + Math.max(segmentA.x1, segmentA.x2, segmentB.x1, segmentB.x2)) / 2,
253
+ y: (Math.min(segmentA.y1, segmentA.y2, segmentB.y1, segmentB.y2) + Math.max(segmentA.y1, segmentA.y2, segmentB.y1, segmentB.y2)) / 2
254
+ };
255
+ };
256
+
257
+ // lib/check-each-pcb-trace-non-overlapping/getCenterOfBounds.ts
258
+ var getCenterOfBounds = (bounds) => {
259
+ return {
260
+ x: (bounds.minX + bounds.maxX) / 2,
261
+ y: (bounds.minY + bounds.maxY) / 2
262
+ };
263
+ };
264
+
265
+ // lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
266
+ function checkEachPcbTraceNonOverlapping(circuitJson, {
267
+ connMap
268
+ } = {}) {
216
269
  const errors = [];
217
- for (let i = 0; i < pcbTraces.length; i++) {
218
- for (let j = i + 1; j < pcbTraces.length; j++) {
219
- debug(
220
- `Checking overlap for ${pcbTraces[i].pcb_trace_id} and ${pcbTraces[j].pcb_trace_id}`
221
- );
222
- const connectedPorts = getPcbPortIdsConnectedToTraces([
223
- pcbTraces[i],
224
- pcbTraces[j]
225
- ]);
226
- debug(`Connected ports: ${connectedPorts.join(",")}`);
227
- if (connectedPorts.length === 0) {
228
- debug("No ports connected to trace, skipping");
229
- continue;
230
- }
231
- if (connectedPorts.length === 1) {
232
- debug("Only one port connected, skipping");
233
- continue;
234
- }
235
- if (connMap.areAllIdsConnected(connectedPorts)) {
236
- continue;
237
- }
238
- const overlapPoint = tracesOverlap(pcbTraces[i], pcbTraces[j]);
239
- if (overlapPoint) {
270
+ connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson);
271
+ const pcbTraces = cju(circuitJson).pcb_trace.list();
272
+ const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {
273
+ const segments = [];
274
+ for (let i = 0; i < pcbTrace.route.length - 1; i++) {
275
+ const p1 = pcbTrace.route[i];
276
+ const p2 = pcbTrace.route[i + 1];
277
+ segments.push({
278
+ type: "pcb_trace_segment",
279
+ pcb_trace_id: pcbTrace.pcb_trace_id,
280
+ _pcbTrace: pcbTrace,
281
+ thickness: "width" in p1 ? p1.width : "width" in p2 ? p2.width : DEFAULT_TRACE_THICKNESS,
282
+ x1: p1.x,
283
+ y1: p1.y,
284
+ x2: p2.x,
285
+ y2: p2.y
286
+ });
287
+ }
288
+ return segments;
289
+ });
290
+ const pcbSmtPads = cju(circuitJson).pcb_smtpad.list();
291
+ const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list();
292
+ const pcbHoles = cju(circuitJson).pcb_hole.list();
293
+ const pcbVias = cju(circuitJson).pcb_via.list();
294
+ const pcbKeepouts = cju(circuitJson).pcb_keepout.list();
295
+ const allObjects = [
296
+ ...pcbTraceSegments,
297
+ ...pcbSmtPads,
298
+ ...pcbPlatedHoles,
299
+ ...pcbHoles,
300
+ ...pcbVias,
301
+ ...pcbKeepouts
302
+ ];
303
+ const spatialIndex = new SpatialObjectIndex({
304
+ objects: allObjects,
305
+ getBounds: getCollidableBounds
306
+ });
307
+ const getReadableName = (id) => getReadableNameForElement(circuitJson, id);
308
+ const errorIds = /* @__PURE__ */ new Set();
309
+ for (const segmentA of pcbTraceSegments) {
310
+ const requiredMargin = DEFAULT_TRACE_MARGIN;
311
+ const bounds = getCollidableBounds(segmentA);
312
+ const nearbyObjects = spatialIndex.getObjectsInBounds(
313
+ bounds,
314
+ requiredMargin + segmentA.thickness / 2
315
+ );
316
+ if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue;
317
+ for (const obj of nearbyObjects) {
318
+ if (obj.type === "pcb_trace_segment") {
319
+ const segmentB = obj;
320
+ if (connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id))
321
+ continue;
322
+ if (segmentToSegmentMinDistance(
323
+ { x: segmentA.x1, y: segmentA.y1 },
324
+ { x: segmentA.x2, y: segmentA.y2 },
325
+ { x: segmentB.x1, y: segmentB.y1 },
326
+ { x: segmentB.x2, y: segmentB.y2 }
327
+ ) - segmentA.thickness / 2 - segmentB.thickness / 2 > DEFAULT_TRACE_MARGIN - EPSILON)
328
+ continue;
329
+ const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`;
330
+ const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`;
331
+ if (errorIds.has(pcb_trace_error_id)) continue;
332
+ if (errorIds.has(pcb_trace_error_id_reverse)) continue;
333
+ errorIds.add(pcb_trace_error_id);
240
334
  errors.push({
241
335
  type: "pcb_trace_error",
242
336
  error_type: "pcb_trace_error",
243
- message: `PCB trace ${pcbTraces[i].pcb_trace_id} overlaps with ${pcbTraces[j].pcb_trace_id}`,
244
- pcb_trace_id: pcbTraces[i].pcb_trace_id,
337
+ message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)}`,
338
+ pcb_trace_id: segmentA.pcb_trace_id,
245
339
  source_trace_id: "",
246
- pcb_trace_error_id: `overlap_${pcbTraces[i].pcb_trace_id}_${pcbTraces[j].pcb_trace_id}`,
340
+ pcb_trace_error_id,
247
341
  pcb_component_ids: [],
248
- // @ts-ignore this is available in a future version of @tscircuit/soup
249
- center: overlapPoint,
342
+ center: getCenterOfPcbTraceSegments(segmentA, segmentB),
250
343
  pcb_port_ids: getPcbPortIdsConnectedToTraces([
251
- pcbTraces[i],
252
- pcbTraces[j]
344
+ segmentA._pcbTrace,
345
+ segmentB._pcbTrace
253
346
  ])
254
347
  });
255
- }
256
- }
257
- for (const pad of pcbSMTPads) {
258
- if (pad.pcb_port_id && connMap.areAllIdsConnected(
259
- getPcbPortIdsConnectedToTrace(pcbTraces[i]).concat([pad.pcb_port_id])
260
- )) {
261
348
  continue;
262
349
  }
263
- if (traceOverlapsWithPad(pcbTraces[i], pad)) {
350
+ const primaryObjId = getPrimaryId(obj);
351
+ const gap = segmentToBoundsMinDistance(
352
+ { x: segmentA.x1, y: segmentA.y1 },
353
+ { x: segmentA.x2, y: segmentA.y2 },
354
+ getCollidableBounds(obj)
355
+ ) - segmentA.thickness / 2;
356
+ if (!connMap.areIdsConnected(segmentA.pcb_trace_id, primaryObjId) && gap + EPSILON < requiredMargin) {
357
+ const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
358
+ if (errorIds.has(pcb_trace_error_id)) continue;
359
+ errorIds.add(pcb_trace_error_id);
264
360
  errors.push({
265
361
  type: "pcb_trace_error",
266
362
  error_type: "pcb_trace_error",
267
- message: `PCB trace ${getReadableNameForElement(soup, pcbTraces[i].pcb_trace_id)} overlaps with ${getReadableNameForElement(soup, pad.pcb_smtpad_id)}`,
268
- pcb_trace_id: pcbTraces[i].pcb_trace_id,
363
+ message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} "${getReadableName(getPrimaryId(obj))}" ${gap < 0 ? "(accidental contact)" : `(gap: ${gap.toFixed(3)}mm)`}`,
364
+ pcb_trace_id: segmentA.pcb_trace_id,
269
365
  source_trace_id: "",
270
- pcb_trace_error_id: `overlap_${pcbTraces[i].pcb_trace_id}_${pad.pcb_smtpad_id}`,
271
- pcb_component_ids: [],
272
- pcb_port_ids: getPcbPortIdsConnectedToTrace(pcbTraces[i])
366
+ pcb_trace_error_id,
367
+ pcb_component_ids: [
368
+ "pcb_component_id" in obj ? obj.pcb_component_id : void 0
369
+ ].filter(Boolean),
370
+ center: getCenterOfBounds(getCollidableBounds(obj)),
371
+ pcb_port_ids: [
372
+ ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),
373
+ "pcb_port_id" in obj ? obj.pcb_port_id : void 0
374
+ ].filter(Boolean)
273
375
  });
274
376
  }
275
377
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected.ts","../lib/check-each-pcb-trace-non-overlapping.ts","../lib/net-manager.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n PcbTrace,\n SourceTrace,\n AnyCircuitElement,\n PcbTraceError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/soup-util\"\n\nfunction checkEachPcbPortConnected(soup: AnyCircuitElement[]): PcbTraceError[] {\n addStartAndEndPortIdsIfMissing(soup)\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n const sourceTraces: SourceTrace[] = soup.filter(\n (item) => item.type === \"source_trace\",\n )\n const errors: PcbTraceError[] = []\n\n for (const port of pcbPorts) {\n const connectedTraces = pcbTraces.filter((trace) =>\n trace.route.some(\n (segment: any) =>\n segment.route_type === \"wire\" &&\n (segment.start_pcb_port_id === port.pcb_port_id ||\n segment.end_pcb_port_id === port.pcb_port_id),\n ),\n )\n\n const sourceTrace = sourceTraces.find((trace) =>\n trace.connected_source_port_ids?.includes(port.source_port_id),\n )\n\n const hasSourceTraceWithConnections =\n sourceTrace && sourceTrace.connected_source_port_ids?.length > 0\n\n if (connectedTraces.length === 0 && hasSourceTraceWithConnections) {\n errors.push({\n type: \"pcb_trace_error\",\n message: `pcb_trace_error: PCB port ${getReadableNameForPcbPort(soup, port.pcb_port_id)} is not connected by a PCB trace`,\n source_trace_id: sourceTrace.source_trace_id,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: \"\",\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnected }\n","import type {\n PcbTrace,\n PcbSmtPad,\n AnyCircuitElement,\n PcbTraceError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport Debug from \"debug\"\nimport { getReadableNameForElement } from \"@tscircuit/soup-util\"\nimport { getFullConnectivityMapFromCircuitJson } from \"circuit-json-to-connectivity-map\"\n\nconst debug = Debug(\"tscircuit:checks:check-each-pcb-trace-non-overlapping\")\n\n/**\n * Checks if lines given by (x1, y1) and (x2, y2) intersect with line\n * given by (x3, y3) and (x4, y4)\n */\nfunction lineIntersects(\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n x3: number,\n y3: number,\n x4: number,\n y4: number,\n): boolean {\n const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)\n if (denom === 0) return false // parallel lines\n\n const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom\n const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom\n\n return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1\n}\n\nfunction tracesOverlap(\n trace1: PcbTrace,\n trace2: PcbTrace,\n): { x: number; y: number } | false {\n for (let i = 0; i < trace1.route.length - 1; i++) {\n for (let j = 0; j < trace2.route.length - 1; j++) {\n const seg1 = trace1.route[i]\n const seg2 = trace1.route[i + 1]\n const seg3 = trace2.route[j]\n const seg4 = trace2.route[j + 1]\n\n if (\n seg1.route_type === \"wire\" &&\n seg2.route_type === \"wire\" &&\n seg3.route_type === \"wire\" &&\n seg4.route_type === \"wire\" &&\n seg1.layer === seg3.layer\n ) {\n const areLinesIntersecting = lineIntersects(\n seg1.x,\n seg1.y,\n seg2.x,\n seg2.y,\n seg3.x,\n seg3.y,\n seg4.x,\n seg4.y,\n )\n if (areLinesIntersecting)\n return {\n // return the intersection point\n x: (seg1.x * seg2.y - seg2.x * seg1.y) / (seg2.y - seg1.y),\n y: (seg1.x * seg2.y - seg2.x * seg1.y) / (seg2.y - seg1.y),\n }\n }\n }\n }\n return false\n}\n\nfunction traceOverlapsWithPad(trace: PcbTrace, pad: PcbSmtPad): boolean {\n for (let i = 0; i < trace.route.length - 1; i++) {\n const seg1 = trace.route[i]\n const seg2 = trace.route[i + 1]\n\n if (\n seg1.route_type === \"wire\" &&\n seg2.route_type === \"wire\" &&\n seg1.layer === pad.layer &&\n pad.shape === \"rect\"\n ) {\n const padLeft = pad.x - pad.width / 2\n const padRight = pad.x + pad.width / 2\n const padTop = pad.y - pad.height / 2\n const padBottom = pad.y + pad.height / 2\n\n if (\n lineIntersects(\n seg1.x,\n seg1.y,\n seg2.x,\n seg2.y,\n padLeft,\n padTop,\n padRight,\n padTop,\n ) ||\n lineIntersects(\n seg1.x,\n seg1.y,\n seg2.x,\n seg2.y,\n padRight,\n padTop,\n padRight,\n padBottom,\n ) ||\n lineIntersects(\n seg1.x,\n seg1.y,\n seg2.x,\n seg2.y,\n padRight,\n padBottom,\n padLeft,\n padBottom,\n ) ||\n lineIntersects(\n seg1.x,\n seg1.y,\n seg2.x,\n seg2.y,\n padLeft,\n padBottom,\n padLeft,\n padTop,\n )\n ) {\n return true\n }\n }\n }\n return false\n}\n\nfunction getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nfunction getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n\nfunction checkEachPcbTraceNonOverlapping(\n soup: AnyCircuitElement[],\n): PcbTraceError[] {\n addStartAndEndPortIdsIfMissing(soup)\n const connMap = getFullConnectivityMapFromCircuitJson(soup)\n const pcbTraces: PcbTrace[] = soup.filter(\n (item): item is PcbTrace => item.type === \"pcb_trace\",\n )\n const pcbSMTPads: PcbSmtPad[] = soup.filter(\n (item): item is PcbSmtPad => item.type === \"pcb_smtpad\",\n )\n const errors: PcbTraceError[] = []\n\n for (let i = 0; i < pcbTraces.length; i++) {\n for (let j = i + 1; j < pcbTraces.length; j++) {\n debug(\n `Checking overlap for ${pcbTraces[i].pcb_trace_id} and ${pcbTraces[j].pcb_trace_id}`,\n )\n const connectedPorts = getPcbPortIdsConnectedToTraces([\n pcbTraces[i],\n pcbTraces[j],\n ])\n debug(`Connected ports: ${connectedPorts.join(\",\")}`)\n\n if (connectedPorts.length === 0) {\n debug(\"No ports connected to trace, skipping\")\n continue\n }\n\n if (connectedPorts.length === 1) {\n debug(\"Only one port connected, skipping\")\n continue\n }\n\n if (connMap.areAllIdsConnected(connectedPorts)) {\n continue\n }\n const overlapPoint = tracesOverlap(pcbTraces[i], pcbTraces[j])\n if (overlapPoint) {\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${pcbTraces[i].pcb_trace_id} overlaps with ${pcbTraces[j].pcb_trace_id}`,\n pcb_trace_id: pcbTraces[i].pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id: `overlap_${pcbTraces[i].pcb_trace_id}_${pcbTraces[j].pcb_trace_id}`,\n pcb_component_ids: [],\n // @ts-ignore this is available in a future version of @tscircuit/soup\n center: overlapPoint,\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n pcbTraces[i],\n pcbTraces[j],\n ]),\n })\n }\n }\n\n for (const pad of pcbSMTPads) {\n if (\n pad.pcb_port_id &&\n connMap.areAllIdsConnected(\n getPcbPortIdsConnectedToTrace(pcbTraces[i]).concat([pad.pcb_port_id]),\n )\n ) {\n continue\n }\n if (traceOverlapsWithPad(pcbTraces[i], pad)) {\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableNameForElement(soup, pcbTraces[i].pcb_trace_id)} overlaps with ${getReadableNameForElement(soup, pad.pcb_smtpad_id)}`,\n pcb_trace_id: pcbTraces[i].pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id: `overlap_${pcbTraces[i].pcb_trace_id}_${pad.pcb_smtpad_id}`,\n pcb_component_ids: [],\n pcb_port_ids: getPcbPortIdsConnectedToTrace(pcbTraces[i]),\n })\n }\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbTraceNonOverlapping }\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../lib/add-start-and-end-port-ids-if-missing.ts","../lib/check-each-pcb-port-connected.ts","../lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts","../lib/data-structures/SpatialIndex.ts","../lib/check-each-pcb-trace-non-overlapping/getCollidableBounds.ts","../lib/drc-defaults.ts","../lib/check-each-pcb-trace-non-overlapping/getPcbPortIdsConnectedToTraces.ts","../lib/check-each-pcb-trace-non-overlapping/getCenterOfPcbTraceSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getCenterOfBounds.ts","../lib/net-manager.ts"],"sourcesContent":["import type {\n PcbPort,\n PcbTrace,\n AnyCircuitElement,\n PcbSmtPad,\n} from \"circuit-json\"\n\nfunction distance(x1: number, y1: number, x2: number, y2: number): number {\n return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n}\n\n/**\n * HACK: this whole method and all usage of it is a hack because of this issue:\n * https://github.com/tscircuit/tscircuit/issues/291\n */\nexport const addStartAndEndPortIdsIfMissing = (\n soup: AnyCircuitElement[],\n): void => {\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbSmtPads: PcbSmtPad[] = soup.filter(\n (item) => item.type === \"pcb_smtpad\",\n )\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n\n function findPortIdOverlappingPoint(\n point: {\n x: number\n y: number\n },\n options: { isFirstOrLastPoint?: boolean; traceWidth?: number } = {},\n ): string | null {\n const traceWidth = options.traceWidth || 0\n const directPort = pcbPorts.find(\n (port) => distance(port.x, port.y, point.x, point.y) < 0.01,\n )\n if (directPort) return directPort.pcb_port_id\n\n // If it starts or ends inside an smtpad, we'll connect it to the por\n if (options.isFirstOrLastPoint) {\n const smtPad = pcbSmtPads.find((pad) => {\n if (pad.shape === \"rect\") {\n return (\n Math.abs(point.x - pad.x) < pad.width / 2 + traceWidth / 2 &&\n Math.abs(point.y - pad.y) < pad.height / 2 + traceWidth / 2\n )\n // biome-ignore lint/style/noUselessElse: <explanation>\n } else if (pad.shape === \"circle\") {\n return distance(point.x, point.y, pad.x, pad.y) < pad.radius\n }\n })\n if (smtPad) return smtPad.pcb_port_id ?? null\n }\n\n return null\n }\n\n // Add start_pcb_port_id and end_pcb_port_id if not present\n for (const trace of pcbTraces) {\n for (let index = 0; index < trace.route.length; index++) {\n const segment = trace.route[index]\n const isFirstOrLastPoint = index === 0 || index === trace.route.length - 1\n if (segment.route_type === \"wire\") {\n if (!segment.start_pcb_port_id && index === 0) {\n const startPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (startPortId) {\n segment.start_pcb_port_id = startPortId\n }\n }\n if (!segment.end_pcb_port_id && index === trace.route.length - 1) {\n const endPortId = findPortIdOverlappingPoint(segment, {\n isFirstOrLastPoint,\n traceWidth: segment.width,\n })\n if (endPortId) {\n segment.end_pcb_port_id = endPortId\n }\n }\n }\n }\n }\n}\n","import type {\n PcbPort,\n PcbTrace,\n SourceTrace,\n AnyCircuitElement,\n PcbTraceError,\n} from \"circuit-json\"\nimport { addStartAndEndPortIdsIfMissing } from \"./add-start-and-end-port-ids-if-missing\"\nimport { getReadableNameForPcbPort } from \"@tscircuit/circuit-json-util\"\n\nfunction checkEachPcbPortConnected(soup: AnyCircuitElement[]): PcbTraceError[] {\n addStartAndEndPortIdsIfMissing(soup)\n const pcbPorts: PcbPort[] = soup.filter((item) => item.type === \"pcb_port\")\n const pcbTraces: PcbTrace[] = soup.filter((item) => item.type === \"pcb_trace\")\n const sourceTraces: SourceTrace[] = soup.filter(\n (item) => item.type === \"source_trace\",\n )\n const errors: PcbTraceError[] = []\n\n for (const port of pcbPorts) {\n const connectedTraces = pcbTraces.filter((trace) =>\n trace.route.some(\n (segment: any) =>\n segment.route_type === \"wire\" &&\n (segment.start_pcb_port_id === port.pcb_port_id ||\n segment.end_pcb_port_id === port.pcb_port_id),\n ),\n )\n\n const sourceTrace = sourceTraces.find((trace) =>\n trace.connected_source_port_ids?.includes(port.source_port_id),\n )\n\n const hasSourceTraceWithConnections =\n sourceTrace && sourceTrace.connected_source_port_ids?.length > 0\n\n if (connectedTraces.length === 0 && hasSourceTraceWithConnections) {\n errors.push({\n type: \"pcb_trace_error\",\n message: `pcb_trace_error: PCB port ${getReadableNameForPcbPort(soup, port.pcb_port_id)} is not connected by a PCB trace`,\n source_trace_id: sourceTrace.source_trace_id,\n error_type: \"pcb_trace_error\",\n pcb_trace_id: \"\",\n pcb_trace_error_id: \"\",\n pcb_component_ids: [],\n pcb_port_ids: [port.pcb_port_id],\n })\n }\n }\n\n return errors\n}\n\nexport { checkEachPcbPortConnected }\n","import type { AnyCircuitElement, PcbTraceError } from \"circuit-json\"\nimport { getReadableNameForElement, cju } from \"@tscircuit/circuit-json-util\"\nimport {\n SpatialObjectIndex,\n type Bounds,\n} from \"lib/data-structures/SpatialIndex\"\nimport {\n getFullConnectivityMapFromCircuitJson,\n type ConnectivityMap,\n} from \"circuit-json-to-connectivity-map\"\nimport {\n getCollidableBounds,\n type Collidable,\n type PcbTraceSegment,\n} from \"./getCollidableBounds\"\nimport { segmentToBoundsMinDistance } from \"@tscircuit/math-utils\"\nimport {\n DEFAULT_TRACE_MARGIN,\n DEFAULT_TRACE_THICKNESS,\n EPSILON,\n} from \"lib/drc-defaults\"\nimport { getPcbPortIdsConnectedToTraces } from \"./getPcbPortIdsConnectedToTraces\"\nimport { segmentToSegmentMinDistance } from \"@tscircuit/math-utils\"\nimport { areBoundsOverlapping } from \"./areBoundsOverlapping\"\nimport { getPrimaryId } from \"@tscircuit/circuit-json-util\"\nimport { getCenterOfBoundsPair } from \"./getCenterOfBoundsPair\"\nimport { getCenterOfPcbTraceSegments } from \"./getCenterOfPcbTraceSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\n\nexport function checkEachPcbTraceNonOverlapping(\n circuitJson: AnyCircuitElement[],\n {\n connMap,\n }: {\n connMap?: ConnectivityMap\n } = {},\n): PcbTraceError[] {\n const errors: PcbTraceError[] = []\n connMap ??= getFullConnectivityMapFromCircuitJson(circuitJson)\n\n const pcbTraces = cju(circuitJson).pcb_trace.list()\n const pcbTraceSegments = pcbTraces.flatMap((pcbTrace) => {\n const segments: PcbTraceSegment[] = []\n for (let i = 0; i < pcbTrace.route.length - 1; i++) {\n const p1 = pcbTrace.route[i]\n const p2 = pcbTrace.route[i + 1]\n segments.push({\n type: \"pcb_trace_segment\",\n pcb_trace_id: pcbTrace.pcb_trace_id,\n _pcbTrace: pcbTrace,\n thickness:\n \"width\" in p1\n ? p1.width\n : \"width\" in p2\n ? p2.width\n : DEFAULT_TRACE_THICKNESS,\n x1: p1.x,\n y1: p1.y,\n x2: p2.x,\n y2: p2.y,\n } as PcbTraceSegment)\n }\n return segments\n })\n const pcbSmtPads = cju(circuitJson).pcb_smtpad.list()\n const pcbPlatedHoles = cju(circuitJson).pcb_plated_hole.list()\n const pcbHoles = cju(circuitJson).pcb_hole.list()\n const pcbVias = cju(circuitJson).pcb_via.list()\n const pcbKeepouts = cju(circuitJson).pcb_keepout.list()\n\n const allObjects: Collidable[] = [\n ...pcbTraceSegments,\n ...pcbSmtPads,\n ...pcbPlatedHoles,\n ...pcbHoles,\n ...pcbVias,\n ...pcbKeepouts,\n ]\n\n const spatialIndex = new SpatialObjectIndex<Collidable>({\n objects: allObjects,\n getBounds: getCollidableBounds,\n })\n\n const getReadableName = (id: string) =>\n getReadableNameForElement(circuitJson, id)\n\n const errorIds = new Set<string>()\n\n // For each segment, check it if overlaps with anything collidable\n for (const segmentA of pcbTraceSegments) {\n const requiredMargin = DEFAULT_TRACE_MARGIN\n const bounds = getCollidableBounds(segmentA)\n const nearbyObjects = spatialIndex.getObjectsInBounds(\n bounds,\n requiredMargin + segmentA.thickness / 2,\n )\n if (segmentA.x1 === segmentA.x2 && segmentA.y1 === segmentA.y2) continue\n\n for (const obj of nearbyObjects) {\n if (obj.type === \"pcb_trace_segment\") {\n const segmentB = obj\n // Check if the segments are overlapping\n if (\n connMap.areIdsConnected(segmentA.pcb_trace_id, segmentB.pcb_trace_id)\n )\n continue\n\n if (\n segmentToSegmentMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: segmentB.x1, y: segmentB.y1 },\n { x: segmentB.x2, y: segmentB.y2 },\n ) -\n segmentA.thickness / 2 -\n segmentB.thickness / 2 >\n DEFAULT_TRACE_MARGIN - EPSILON\n )\n continue\n\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${segmentB.pcb_trace_id}`\n const pcb_trace_error_id_reverse = `overlap_${segmentB.pcb_trace_id}_${segmentA.pcb_trace_id}`\n if (errorIds.has(pcb_trace_error_id)) continue\n if (errorIds.has(pcb_trace_error_id_reverse)) continue\n\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${getReadableName(segmentB.pcb_trace_id)}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [],\n center: getCenterOfPcbTraceSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n const primaryObjId = getPrimaryId(obj as any)\n const gap =\n segmentToBoundsMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n getCollidableBounds(obj),\n ) -\n segmentA.thickness / 2\n if (\n !connMap.areIdsConnected(segmentA.pcb_trace_id, primaryObjId) &&\n gap + EPSILON < requiredMargin\n ) {\n const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`\n if (errorIds.has(pcb_trace_error_id)) continue\n errorIds.add(pcb_trace_error_id)\n errors.push({\n type: \"pcb_trace_error\",\n error_type: \"pcb_trace_error\",\n message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} \"${getReadableName(getPrimaryId(obj as any))}\" ${gap < 0 ? \"(accidental contact)\" : `(gap: ${gap.toFixed(3)}mm)`}`,\n pcb_trace_id: segmentA.pcb_trace_id,\n source_trace_id: \"\",\n pcb_trace_error_id,\n pcb_component_ids: [\n \"pcb_component_id\" in obj ? obj.pcb_component_id : undefined,\n ].filter(Boolean) as string[],\n center: getCenterOfBounds(getCollidableBounds(obj)),\n pcb_port_ids: [\n ...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),\n \"pcb_port_id\" in obj ? obj.pcb_port_id : undefined,\n ].filter(Boolean) as string[],\n })\n }\n }\n }\n return errors\n}\n","export type BucketCoordinate = `${number}x${number}`\n\nexport interface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type GetBoundsFn<T> = (obj: T) => Bounds\nexport type GetIdFn<T> = (obj: T) => string\n\nexport class SpatialObjectIndex<T> {\n buckets: Map<BucketCoordinate, Array<T & { spatialIndexId: string }>>\n objectsById: Map<string, T & { spatialIndexId: string }>\n getBounds: GetBoundsFn<T>\n getId: GetIdFn<T>\n CELL_SIZE = 0.4\n\n constructor({\n objects,\n getBounds,\n getId,\n CELL_SIZE,\n }: {\n objects: T[]\n getBounds: GetBoundsFn<T>\n getId?: GetIdFn<T>\n CELL_SIZE?: number\n }) {\n this.buckets = new Map()\n this.objectsById = new Map()\n this.getBounds = getBounds\n this.getId = getId ?? (() => this._getNextId())\n this.CELL_SIZE = CELL_SIZE ?? this.CELL_SIZE\n\n for (const obj of objects) {\n this.addObject(obj)\n }\n }\n\n _idCounter = 0\n _getNextId(): string {\n return `${this._idCounter++}`\n }\n\n addObject(obj: T): void {\n const bounds = this.getBounds(obj)\n const spatialIndexId = this.getId(obj)\n const objWithId = { ...obj, spatialIndexId } as T & {\n spatialIndexId: string\n }\n\n // Store in objectsById for quick lookup\n this.objectsById.set(spatialIndexId, objWithId)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Add the object to all buckets it intersects with\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (!bucket) {\n this.buckets.set(bucketKey, [objWithId])\n } else {\n bucket.push(objWithId)\n }\n }\n }\n }\n\n removeObject(id: string): boolean {\n const obj = this.objectsById.get(id)\n if (!obj) return false\n\n // Remove from objectsById\n this.objectsById.delete(id)\n\n // Calculate the bucket coordinates that cover the object's bounds\n const bounds = this.getBounds(obj)\n const minBucketX = Math.floor(bounds.minX / this.CELL_SIZE)\n const minBucketY = Math.floor(bounds.minY / this.CELL_SIZE)\n const maxBucketX = Math.floor(bounds.maxX / this.CELL_SIZE)\n const maxBucketY = Math.floor(bounds.maxY / this.CELL_SIZE)\n\n // Remove the object from all buckets it might be in\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey)\n if (bucket) {\n const index = bucket.findIndex((item) => item.spatialIndexId === id)\n if (index !== -1) {\n bucket.splice(index, 1)\n if (bucket.length === 0) {\n this.buckets.delete(bucketKey)\n }\n }\n }\n }\n }\n\n return true\n }\n\n getBucketKey(x: number, y: number): BucketCoordinate {\n return `${Math.floor(x / this.CELL_SIZE)}x${Math.floor(y / this.CELL_SIZE)}`\n }\n\n getObjectsInBounds(bounds: Bounds, margin = 0): T[] {\n const objects: T[] = []\n const addedIds = new Set<string>()\n\n // Calculate the bucket coordinates that cover the requested bounds with margin\n const minBucketX = Math.floor((bounds.minX - margin) / this.CELL_SIZE)\n const minBucketY = Math.floor((bounds.minY - margin) / this.CELL_SIZE)\n const maxBucketX = Math.floor((bounds.maxX + margin) / this.CELL_SIZE)\n const maxBucketY = Math.floor((bounds.maxY + margin) / this.CELL_SIZE)\n\n // Collect objects from all buckets that intersect the bounds\n for (let bx = minBucketX; bx <= maxBucketX; bx++) {\n for (let by = minBucketY; by <= maxBucketY; by++) {\n const bucketKey = `${bx}x${by}` as BucketCoordinate\n const bucket = this.buckets.get(bucketKey) || []\n\n for (const obj of bucket) {\n const id = obj.spatialIndexId\n if (addedIds.has(id)) continue\n\n addedIds.add(id)\n objects.push(obj)\n }\n }\n }\n\n return objects\n }\n}\n","import { getBoundsOfPcbElements } from \"@tscircuit/circuit-json-util\"\nimport type {\n AnyCircuitElement,\n PcbHole,\n PCBKeepout,\n PcbPlatedHole,\n PcbSmtPad,\n PcbTrace,\n PcbTraceError,\n PcbVia,\n} from \"circuit-json\"\n\ninterface Bounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport type PcbTraceSegment = {\n type: \"pcb_trace_segment\"\n _pcbTrace: PcbTrace\n pcb_trace_id: string\n thickness: number\n x1: number\n y1: number\n x2: number\n y2: number\n}\n\nexport type Collidable =\n | PcbTraceSegment\n | PcbSmtPad\n | PcbPlatedHole\n | PcbHole\n | PcbVia\n | PCBKeepout\n\nexport const getCollidableBounds = (collidable: Collidable): Bounds => {\n if (collidable.type === \"pcb_trace_segment\") {\n return {\n minX: Math.min(collidable.x1, collidable.x2),\n minY: Math.min(collidable.y1, collidable.y2),\n maxX: Math.max(collidable.x1, collidable.x2),\n maxY: Math.max(collidable.y1, collidable.y2),\n }\n }\n return getBoundsOfPcbElements([collidable as any])\n}\n","export const DEFAULT_TRACE_MARGIN = 0.1\nexport const DEFAULT_TRACE_THICKNESS = 0.15\nexport const DEFAULT_VIA_DIAMETER = 0.6\n\nexport const EPSILON = 0.005\n","import type { PcbTrace } from \"circuit-json\"\n\nexport function getPcbPortIdsConnectedToTrace(trace: PcbTrace) {\n const connectedPcbPorts = new Set<string>()\n for (const segment of trace.route) {\n if (segment.route_type === \"wire\") {\n if (segment.start_pcb_port_id)\n connectedPcbPorts.add(segment.start_pcb_port_id)\n if (segment.end_pcb_port_id)\n connectedPcbPorts.add(segment.end_pcb_port_id)\n }\n }\n\n return Array.from(connectedPcbPorts)\n}\n\nexport function getPcbPortIdsConnectedToTraces(traces: PcbTrace[]) {\n const connectedPorts = new Set<string>()\n for (const trace of traces) {\n for (const portId of getPcbPortIdsConnectedToTrace(trace)) {\n connectedPorts.add(portId)\n }\n }\n return Array.from(connectedPorts)\n}\n","import type { PcbTraceSegment } from \"./getCollidableBounds\"\n\n/**\n * Get the center point between two PCB trace segments\n */\nexport const getCenterOfPcbTraceSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n) => {\n return {\n x:\n (Math.min(segmentA.x1, segmentA.x2, segmentB.x1, segmentB.x2) +\n Math.max(segmentA.x1, segmentA.x2, segmentB.x1, segmentB.x2)) /\n 2,\n y:\n (Math.min(segmentA.y1, segmentA.y2, segmentB.y1, segmentB.y2) +\n Math.max(segmentA.y1, segmentA.y2, segmentB.y1, segmentB.y2)) /\n 2,\n }\n}\n","import type { Bounds } from \"lib/data-structures/SpatialIndex\"\n\n/**\n * Get the center point of a bounds object\n */\nexport const getCenterOfBounds = (bounds: Bounds) => {\n return {\n x: (bounds.minX + bounds.maxX) / 2,\n y: (bounds.minY + bounds.maxY) / 2,\n }\n}\n","export class NetManager {\n private networks: Set<Set<string>> = new Set()\n\n setConnected(nodes: string[]): void {\n if (nodes.length < 2) return\n\n let targetNetwork: Set<string> | null = null\n\n // Check if any of the nodes are already in a network\n for (const network of this.networks) {\n for (const node of nodes) {\n if (network.has(node)) {\n if (targetNetwork === null) {\n targetNetwork = network\n } else if (targetNetwork !== network) {\n // Merge networks\n for (const mergeNode of network) {\n targetNetwork.add(mergeNode)\n }\n this.networks.delete(network)\n }\n break\n }\n }\n if (targetNetwork !== null && targetNetwork !== network) break\n }\n\n // If no existing network found, create a new one\n if (targetNetwork === null) {\n targetNetwork = new Set(nodes)\n this.networks.add(targetNetwork)\n } else {\n // Add all nodes to the target network\n for (const node of nodes) {\n targetNetwork.add(node)\n }\n }\n }\n\n isConnected(nodes: string[]): boolean {\n if (nodes.length < 2) return true\n\n for (const network of this.networks) {\n if (nodes.every((node) => network.has(node))) {\n return true\n }\n }\n\n return false\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@tscircuit/checks",
3
- "module": "index.ts",
4
3
  "type": "module",
5
4
  "main": "./dist/index.js",
6
- "version": "0.0.28",
5
+ "version": "0.0.30",
7
6
  "files": [
8
7
  "dist"
9
8
  ],
@@ -14,19 +13,20 @@
14
13
  },
15
14
  "devDependencies": {
16
15
  "@biomejs/biome": "^1.9.0",
16
+ "@tscircuit/circuit-json-util": "^0.0.45",
17
17
  "@tscircuit/log-soup": "^1.0.2",
18
18
  "@types/bun": "latest",
19
- "circuit-json": "^0.0.85",
20
19
  "@types/debug": "^4.1.12",
20
+ "circuit-json": "^0.0.153",
21
21
  "debug": "^4.3.5",
22
22
  "tsup": "^8.2.3"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "circuit-json": "*",
26
- "typescript": "^5.5.3",
27
- "@tscircuit/soup-util": "*"
26
+ "typescript": "^5.5.3"
28
27
  },
29
28
  "dependencies": {
30
- "circuit-json-to-connectivity-map": "^0.0.10"
29
+ "@tscircuit/math-utils": "^0.0.12",
30
+ "circuit-json-to-connectivity-map": "^0.0.19"
31
31
  }
32
32
  }