@tscircuit/checks 0.0.34 → 0.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +57 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -212,7 +212,10 @@ var getCollidableBounds = (collidable) => {
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
// lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
|
|
215
|
-
import {
|
|
215
|
+
import {
|
|
216
|
+
segmentToBoundsMinDistance,
|
|
217
|
+
segmentToCircleMinDistance
|
|
218
|
+
} from "@tscircuit/math-utils";
|
|
216
219
|
|
|
217
220
|
// lib/drc-defaults.ts
|
|
218
221
|
var DEFAULT_TRACE_MARGIN = 0.1;
|
|
@@ -399,6 +402,25 @@ var getCenterOfBounds = (bounds) => {
|
|
|
399
402
|
};
|
|
400
403
|
};
|
|
401
404
|
|
|
405
|
+
// lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.ts
|
|
406
|
+
var getRadiusOfCircuitJsonElement = (obj) => {
|
|
407
|
+
if (obj.type === "pcb_via") {
|
|
408
|
+
return obj.outer_diameter / 2;
|
|
409
|
+
}
|
|
410
|
+
if (obj.type === "pcb_plated_hole" && obj.shape === "circle") {
|
|
411
|
+
return obj.outer_diameter / 2;
|
|
412
|
+
}
|
|
413
|
+
if (obj.type === "pcb_hole" && obj.hole_shape === "circle") {
|
|
414
|
+
return obj.hole_diameter / 2;
|
|
415
|
+
}
|
|
416
|
+
if (obj.type === "pcb_smtpad" && obj.shape === "circle") {
|
|
417
|
+
return obj.radius;
|
|
418
|
+
}
|
|
419
|
+
throw new Error(
|
|
420
|
+
`Could not determine radius of element: ${JSON.stringify(obj)}`
|
|
421
|
+
);
|
|
422
|
+
};
|
|
423
|
+
|
|
402
424
|
// lib/check-each-pcb-trace-non-overlapping/check-each-pcb-trace-non-overlapping.ts
|
|
403
425
|
function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
404
426
|
connMap
|
|
@@ -490,15 +512,45 @@ function checkEachPcbTraceNonOverlapping(circuitJson, {
|
|
|
490
512
|
continue;
|
|
491
513
|
}
|
|
492
514
|
const primaryObjId = getPrimaryId(obj);
|
|
515
|
+
if (connMap.areIdsConnected(
|
|
516
|
+
segmentA.pcb_trace_id,
|
|
517
|
+
"pcb_trace_id" in obj ? obj.pcb_trace_id : primaryObjId
|
|
518
|
+
))
|
|
519
|
+
continue;
|
|
520
|
+
const isCircular = obj.type === "pcb_via" || obj.type === "pcb_plated_hole" && obj.shape === "circle" || obj.type === "pcb_hole" || obj.type === "pcb_smtpad" && obj.shape === "circle";
|
|
521
|
+
if (isCircular) {
|
|
522
|
+
const radius = getRadiusOfCircuitJsonElement(obj);
|
|
523
|
+
const gap2 = segmentToCircleMinDistance(
|
|
524
|
+
{ x: segmentA.x1, y: segmentA.y1 },
|
|
525
|
+
{ x: segmentA.x2, y: segmentA.y2 },
|
|
526
|
+
{ x: obj.x, y: obj.y, radius }
|
|
527
|
+
);
|
|
528
|
+
if (gap2 > DEFAULT_TRACE_MARGIN - EPSILON) continue;
|
|
529
|
+
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
530
|
+
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
531
|
+
errorIds.add(pcb_trace_error_id);
|
|
532
|
+
errors.push({
|
|
533
|
+
type: "pcb_trace_error",
|
|
534
|
+
error_type: "pcb_trace_error",
|
|
535
|
+
message: `PCB trace ${getReadableName(segmentA.pcb_trace_id)} overlaps with ${obj.type} "${getReadableName(getPrimaryId(obj))}" ${gap2 < 0 ? "(accidental contact)" : `(gap: ${gap2.toFixed(3)}mm)`}`,
|
|
536
|
+
pcb_trace_id: segmentA.pcb_trace_id,
|
|
537
|
+
source_trace_id: "",
|
|
538
|
+
pcb_trace_error_id,
|
|
539
|
+
pcb_component_ids: [
|
|
540
|
+
"pcb_component_id" in obj ? obj.pcb_component_id : void 0
|
|
541
|
+
].filter(Boolean),
|
|
542
|
+
pcb_port_ids: [
|
|
543
|
+
...getPcbPortIdsConnectedToTraces([segmentA._pcbTrace]),
|
|
544
|
+
"pcb_port_id" in obj ? obj.pcb_port_id : void 0
|
|
545
|
+
].filter(Boolean)
|
|
546
|
+
});
|
|
547
|
+
}
|
|
493
548
|
const gap = segmentToBoundsMinDistance(
|
|
494
549
|
{ x: segmentA.x1, y: segmentA.y1 },
|
|
495
550
|
{ x: segmentA.x2, y: segmentA.y2 },
|
|
496
551
|
getCollidableBounds(obj)
|
|
497
552
|
) - segmentA.thickness / 2;
|
|
498
|
-
if (
|
|
499
|
-
segmentA.pcb_trace_id,
|
|
500
|
-
"pcb_trace_id" in obj ? obj.pcb_trace_id : primaryObjId
|
|
501
|
-
) && gap + EPSILON < requiredMargin) {
|
|
553
|
+
if (gap + EPSILON < requiredMargin) {
|
|
502
554
|
const pcb_trace_error_id = `overlap_${segmentA.pcb_trace_id}_${primaryObjId}`;
|
|
503
555
|
if (errorIds.has(pcb_trace_error_id)) continue;
|
|
504
556
|
errorIds.add(pcb_trace_error_id);
|
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/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/getClosestPointBetweenSegments.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 { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\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 if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\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 layer: p1.layer,\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\n if (segmentA.layer !== segmentB.layer) continue\n\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 const gap =\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 if (gap > DEFAULT_TRACE_MARGIN - EPSILON) 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)} ${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 center: getClosestPointBetweenSegments(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(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n ) &&\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 layer: string\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\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\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":["t","closestOnB","closestOnA","distance","gap"]}
|
|
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/getClosestPointBetweenSegments.ts","../lib/check-each-pcb-trace-non-overlapping/getCenterOfBounds.ts","../lib/check-each-pcb-trace-non-overlapping/getRadiusOfCircuitJsonElement.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 {\n segmentToBoundsMinDistance,\n segmentToCircleMinDistance,\n} 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 { getClosestPointBetweenSegments } from \"./getClosestPointBetweenSegments\"\nimport { getCenterOfBounds } from \"./getCenterOfBounds\"\nimport { getRadiusOfCircuitJsonElement } from \"./getRadiusOfCircuitJsonElement\"\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 if (p1.route_type !== \"wire\") continue\n if (p2.route_type !== \"wire\") continue\n if (p1.layer !== p2.layer) continue\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 layer: p1.layer,\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\n if (segmentA.layer !== segmentB.layer) continue\n\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 const gap =\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 if (gap > DEFAULT_TRACE_MARGIN - EPSILON) 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)} ${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 center: getClosestPointBetweenSegments(segmentA, segmentB),\n pcb_port_ids: getPcbPortIdsConnectedToTraces([\n segmentA._pcbTrace,\n segmentB._pcbTrace,\n ]),\n })\n continue\n }\n\n const primaryObjId = getPrimaryId(obj as any)\n if (\n connMap.areIdsConnected(\n segmentA.pcb_trace_id,\n \"pcb_trace_id\" in obj ? (obj.pcb_trace_id as string) : primaryObjId,\n )\n )\n continue\n\n const isCircular =\n obj.type === \"pcb_via\" ||\n (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") ||\n obj.type === \"pcb_hole\" ||\n (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\")\n\n if (isCircular) {\n const radius = getRadiusOfCircuitJsonElement(obj)\n const gap = segmentToCircleMinDistance(\n { x: segmentA.x1, y: segmentA.y1 },\n { x: segmentA.x2, y: segmentA.y2 },\n { x: obj.x, y: obj.y, radius },\n )\n if (gap > DEFAULT_TRACE_MARGIN - EPSILON) continue\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\n ? (obj.pcb_component_id as string)\n : undefined,\n ].filter(Boolean) as string[],\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 // Handle generic case of hitting the bounds of any collidable obstacle\n // using the bounds of the collidable obstacle\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 (gap + EPSILON < requiredMargin) {\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 layer: string\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\nexport const getClosestPointBetweenSegments = (\n segmentA: PcbTraceSegment,\n segmentB: PcbTraceSegment,\n): { x: number; y: number } => {\n // Define points for each segment\n const a1 = { x: segmentA.x1, y: segmentA.y1 }\n const a2 = { x: segmentA.x2, y: segmentA.y2 }\n const b1 = { x: segmentB.x1, y: segmentB.y1 }\n const b2 = { x: segmentB.x2, y: segmentB.y2 }\n\n // Calculate direction vectors for each segment\n const va = { x: a2.x - a1.x, y: a2.y - a1.y }\n const vb = { x: b2.x - b1.x, y: b2.y - b1.y }\n\n // Calculate squared lengths of segments\n const lenSqrA = va.x * va.x + va.y * va.y\n const lenSqrB = vb.x * vb.x + vb.y * vb.y\n\n // If either segment is a point (zero length), handle as special case\n if (lenSqrA === 0 || lenSqrB === 0) {\n if (lenSqrA === 0 && lenSqrB === 0) {\n // Both segments are points, return the distance between them\n // Calculate the average point\n return {\n x: (a1.x + b1.x) / 2,\n y: (a1.y + b1.y) / 2,\n }\n }\n if (lenSqrA === 0) {\n // First segment is a point, find closest point on second segment\n const t = clamp(\n ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB,\n 0,\n 1,\n )\n const closestOnB = {\n x: b1.x + t * vb.x,\n y: b1.y + t * vb.y,\n }\n // Calculate the average point\n return {\n x: (a1.x + closestOnB.x) / 2,\n y: (a1.y + closestOnB.y) / 2,\n }\n }\n // Second segment is a point, find closest point on first segment\n const t = clamp(\n ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA,\n 0,\n 1,\n )\n const closestOnA = {\n x: a1.x + t * va.x,\n y: a1.y + t * va.y,\n }\n // Calculate the average point\n return {\n x: (closestOnA.x + b1.x) / 2,\n y: (closestOnA.y + b1.y) / 2,\n }\n }\n\n // Vector between segment starting points\n const w = { x: a1.x - b1.x, y: a1.y - b1.y }\n\n // Calculate dot products\n const dotAA = va.x * va.x + va.y * va.y\n const dotAB = va.x * vb.x + va.y * vb.y\n const dotAW = va.x * w.x + va.y * w.y\n const dotBB = vb.x * vb.x + vb.y * vb.y\n const dotBW = vb.x * w.x + vb.y * w.y\n\n // Calculate parametric positions (t values) along each segment\n const denominator = dotAA * dotBB - dotAB * dotAB\n\n // If segments are parallel, handle separately\n if (denominator < 1e-10) {\n return closestPointsParallelSegments(\n a1,\n a2,\n b1,\n b2,\n va,\n vb,\n lenSqrA,\n lenSqrB,\n )\n }\n\n // Calculate parameters for closest points\n let tA = (dotAB * dotBW - dotBB * dotAW) / denominator\n let tB = (dotAA * dotBW - dotAB * dotAW) / denominator\n\n // Clamp parameters to segment bounds\n tA = clamp(tA, 0, 1)\n tB = clamp(tB, 0, 1)\n\n // Recalculate tB if tA was clamped\n tB = (tA * dotAB + dotBW) / dotBB\n tB = clamp(tB, 0, 1)\n\n // Recalculate tA if tB was clamped\n tA = (tB * dotAB - dotAW) / dotAA\n tA = clamp(tA, 0, 1)\n\n // Calculate closest points on each segment\n const closestOnA = {\n x: a1.x + tA * va.x,\n y: a1.y + tA * va.y,\n }\n\n const closestOnB = {\n x: b1.x + tB * vb.x,\n y: b1.y + tB * vb.y,\n }\n\n // Calculate distance between closest points\n const dx = closestOnA.x - closestOnB.x\n const dy = closestOnA.y - closestOnB.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n // Calculate the average of the closest points\n const averagePoint = {\n x: (closestOnA.x + closestOnB.x) / 2,\n y: (closestOnA.y + closestOnB.y) / 2,\n }\n\n return averagePoint\n}\n\n// Helper function for handling parallel segments\nconst closestPointsParallelSegments = (\n a1: { x: number; y: number },\n a2: { x: number; y: number },\n b1: { x: number; y: number },\n b2: { x: number; y: number },\n va: { x: number; y: number },\n vb: { x: number; y: number },\n lenSqrA: number,\n lenSqrB: number,\n) => {\n // Project b1 onto segment A\n let tA = ((b1.x - a1.x) * va.x + (b1.y - a1.y) * va.y) / lenSqrA\n tA = clamp(tA, 0, 1)\n const pointOnA1 = { x: a1.x + tA * va.x, y: a1.y + tA * va.y }\n\n // Project b2 onto segment A\n let tA2 = ((b2.x - a1.x) * va.x + (b2.y - a1.y) * va.y) / lenSqrA\n tA2 = clamp(tA2, 0, 1)\n const pointOnA2 = { x: a1.x + tA2 * va.x, y: a1.y + tA2 * va.y }\n\n // Project a1 onto segment B\n let tB = ((a1.x - b1.x) * vb.x + (a1.y - b1.y) * vb.y) / lenSqrB\n tB = clamp(tB, 0, 1)\n const pointOnB1 = { x: b1.x + tB * vb.x, y: b1.y + tB * vb.y }\n\n // Project a2 onto segment B\n let tB2 = ((a2.x - b1.x) * vb.x + (a2.y - b1.y) * vb.y) / lenSqrB\n tB2 = clamp(tB2, 0, 1)\n const pointOnB2 = { x: b1.x + tB2 * vb.x, y: b1.y + tB2 * vb.y }\n\n // Calculate all possible distances between end points and their projections\n const distances = [\n {\n pointA: pointOnA1,\n pointB: b1,\n distance: Math.sqrt(\n (pointOnA1.x - b1.x) ** 2 + (pointOnA1.y - b1.y) ** 2,\n ),\n },\n {\n pointA: pointOnA2,\n pointB: b2,\n distance: Math.sqrt(\n (pointOnA2.x - b2.x) ** 2 + (pointOnA2.y - b2.y) ** 2,\n ),\n },\n {\n pointA: a1,\n pointB: pointOnB1,\n distance: Math.sqrt(\n (a1.x - pointOnB1.x) ** 2 + (a1.y - pointOnB1.y) ** 2,\n ),\n },\n {\n pointA: a2,\n pointB: pointOnB2,\n distance: Math.sqrt(\n (a2.x - pointOnB2.x) ** 2 + (a2.y - pointOnB2.y) ** 2,\n ),\n },\n ]\n\n // Find closest pair\n const closestPair = distances.reduce((closest, current) =>\n current.distance < closest.distance ? current : closest,\n )\n\n // Calculate the average of the closest points\n return {\n x: (closestPair.pointA.x + closestPair.pointB.x) / 2,\n y: (closestPair.pointA.y + closestPair.pointB.y) / 2,\n }\n}\n\n// Helper function to clamp a value between min and max\nconst clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value))\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","import type {\n AnyCircuitElement,\n PcbHole,\n PcbPlatedHole,\n PcbVia,\n PcbSmtPad,\n} from \"circuit-json\"\n\nexport const getRadiusOfCircuitJsonElement = (\n obj: PcbVia | PcbPlatedHole | PcbHole | PcbSmtPad,\n) => {\n if (obj.type === \"pcb_via\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_plated_hole\" && obj.shape === \"circle\") {\n return obj.outer_diameter / 2\n }\n if (obj.type === \"pcb_hole\" && obj.hole_shape === \"circle\") {\n return obj.hole_diameter / 2\n }\n if (obj.type === \"pcb_smtpad\" && obj.shape === \"circle\") {\n return obj.radius\n }\n throw new Error(\n `Could not determine radius of element: ${JSON.stringify(obj)}`,\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":["t","closestOnB","closestOnA","distance","gap"]}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tscircuit/checks",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.35",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"typescript": "^5.5.3"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@tscircuit/math-utils": "^0.0.
|
|
29
|
+
"@tscircuit/math-utils": "^0.0.13",
|
|
30
30
|
"circuit-json-to-connectivity-map": "^0.0.20"
|
|
31
31
|
}
|
|
32
32
|
}
|