@tscircuit/rectdiff 0.0.26 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/index.d.ts +21 -0
  2. package/dist/index.js +487 -160
  3. package/lib/RectDiffPipeline.ts +23 -0
  4. package/lib/solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver.ts +456 -0
  5. package/package.json +1 -1
  6. package/pages/bugreports/bugreport50-multi-support-layer-merge.page.tsx +19 -0
  7. package/tests/__snapshots__/board-outline.snap.svg +2 -2
  8. package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +1 -1
  9. package/tests/solver/both-points-equivalent/__snapshots__/both-points-equivalent.snap.svg +1 -1
  10. package/tests/solver/bugreport01-be84eb/__snapshots__/bugreport01-be84eb.snap.svg +1 -1
  11. package/tests/solver/bugreport02-bc4361/__snapshots__/bugreport02-bc4361.snap.svg +1 -1
  12. package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +1 -1
  13. package/tests/solver/bugreport07-d3f3be/__snapshots__/bugreport07-d3f3be.snap.svg +1 -1
  14. package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +1 -1
  15. package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +1 -1
  16. package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +1 -1
  17. package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +1 -1
  18. package/tests/solver/bugreport12-35ce1c/__snapshots__/bugreport12-35ce1c.snap.svg +1 -1
  19. package/tests/solver/bugreport13-b9a758/__snapshots__/bugreport13-b9a758.snap.svg +1 -1
  20. package/tests/solver/bugreport16-d95f38/__snapshots__/bugreport16-d95f38.snap.svg +1 -1
  21. package/tests/solver/bugreport19/__snapshots__/bugreport19.snap.svg +1 -1
  22. package/tests/solver/bugreport20-obstacle-clipping/__snapshots__/bugreport20-obstacle-clipping.snap.svg +1 -1
  23. package/tests/solver/bugreport21-board-outline/__snapshots__/bugreport21-board-outline.snap.svg +2 -2
  24. package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +1 -1
  25. package/tests/solver/bugreport23-LGA15x4/__snapshots__/bugreport23-LGA15x4.snap.svg +1 -1
  26. package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +1 -1
  27. package/tests/solver/bugreport25-4b1d55/__snapshots__/bugreport25-4b1d55.snap.svg +1 -1
  28. package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +1 -1
  29. package/tests/solver/bugreport50-multi-support-layer-merge/__snapshots__/bugreport50-multi-support-layer-merge.snap.svg +44 -0
  30. package/tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.json +972 -0
  31. package/tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.test.ts +125 -0
  32. package/tests/solver/interaction/__snapshots__/interaction.snap.svg +1 -1
  33. package/tests/solver/multi-point/__snapshots__/multi-point.snap.svg +1 -1
  34. package/tests/solver/no-better-path/__snapshots__/no-better-path.snap.svg +1 -1
  35. package/tests/solver/transitivity/__snapshots__/transitivity.snap.svg +2 -2
@@ -7,6 +7,7 @@ import type { SimpleRouteJson } from "./types/srj-types"
7
7
  import type { GridFill3DOptions, XYRect } from "./rectdiff-types"
8
8
  import type { CapacityMeshNode } from "./types/capacity-mesh-types"
9
9
  import type { GraphicsObject } from "graphics-debug"
10
+ import { AdjacentLayerContainmentMergeSolver } from "./solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver"
10
11
  import { GapFillSolverPipeline } from "./solvers/GapFillSolver/GapFillSolverPipeline"
11
12
  import { OuterLayerContainmentMergeSolver } from "./solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver"
12
13
  import { RectDiffGridSolverPipeline } from "./solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline"
@@ -27,6 +28,7 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
27
28
  rectDiffGridSolverPipeline?: RectDiffGridSolverPipeline
28
29
  gapFillSolver?: GapFillSolverPipeline
29
30
  outerLayerContainmentMergeSolver?: OuterLayerContainmentMergeSolver
31
+ adjacentLayerContainmentMergeSolver?: AdjacentLayerContainmentMergeSolver
30
32
  boardVoidRects: XYRect[] | undefined
31
33
  zIndexByName?: Map<string, number>
32
34
  layerNames?: string[]
@@ -87,6 +89,22 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
87
89
  },
88
90
  ],
89
91
  ),
92
+ definePipelineStep(
93
+ "adjacentLayerContainmentMergeSolver",
94
+ AdjacentLayerContainmentMergeSolver,
95
+ (rectDiffPipeline: RectDiffPipeline) => [
96
+ {
97
+ meshNodes:
98
+ rectDiffPipeline.outerLayerContainmentMergeSolver?.getOutput()
99
+ .outputNodes ??
100
+ rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ??
101
+ rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput()
102
+ .meshNodes ??
103
+ [],
104
+ simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
105
+ },
106
+ ],
107
+ ),
90
108
  ]
91
109
 
92
110
  override _setup(): void {
@@ -118,6 +136,11 @@ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput>
118
136
  }
119
137
 
120
138
  override getOutput(): { meshNodes: CapacityMeshNode[] } {
139
+ const adjacentLayerMergeOutput =
140
+ this.adjacentLayerContainmentMergeSolver?.getOutput()
141
+ if (adjacentLayerMergeOutput) {
142
+ return { meshNodes: adjacentLayerMergeOutput.outputNodes }
143
+ }
121
144
  const outerLayerMergeOutput =
122
145
  this.outerLayerContainmentMergeSolver?.getOutput()
123
146
  if (outerLayerMergeOutput) {
@@ -0,0 +1,456 @@
1
+ import { BaseSolver } from "@tscircuit/solver-utils"
2
+ import type { GraphicsObject } from "graphics-debug"
3
+ import type { XYRect } from "lib/rectdiff-types"
4
+ import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
5
+ import type { SimpleRouteJson } from "lib/types/srj-types"
6
+ import { getColorForZLayer } from "lib/utils/getColorForZLayer"
7
+ import { EPS, overlaps, subtractRect2D } from "lib/utils/rectdiff-geometry"
8
+
9
+ type AdjacentLayerContainmentMergeSolverInput = {
10
+ meshNodes: CapacityMeshNode[]
11
+ simpleRouteJson: SimpleRouteJson
12
+ minFragmentArea?: number
13
+ }
14
+
15
+ const DEFAULT_MIN_FRAGMENT_AREA = 0.2 ** 2
16
+
17
+ const nodeToRect = (node: CapacityMeshNode): XYRect => ({
18
+ x: node.center.x - node.width / 2,
19
+ y: node.center.y - node.height / 2,
20
+ width: node.width,
21
+ height: node.height,
22
+ })
23
+
24
+ const rectArea = (rect: XYRect) => rect.width * rect.height
25
+
26
+ const cloneNode = (node: CapacityMeshNode): CapacityMeshNode => ({
27
+ ...node,
28
+ center: { ...node.center },
29
+ availableZ: [...node.availableZ],
30
+ })
31
+
32
+ const cloneNodeWithRect = (
33
+ node: CapacityMeshNode,
34
+ rect: XYRect,
35
+ capacityMeshNodeId: string,
36
+ ): CapacityMeshNode => ({
37
+ ...node,
38
+ capacityMeshNodeId,
39
+ center: {
40
+ x: rect.x + rect.width / 2,
41
+ y: rect.y + rect.height / 2,
42
+ },
43
+ width: rect.width,
44
+ height: rect.height,
45
+ availableZ: [...node.availableZ],
46
+ layer: `z${node.availableZ.join(",")}`,
47
+ })
48
+
49
+ const clonePromotedNodeWithRect = (
50
+ node: CapacityMeshNode,
51
+ rect: XYRect,
52
+ capacityMeshNodeId: string,
53
+ availableZ: number[],
54
+ ): CapacityMeshNode => ({
55
+ ...node,
56
+ capacityMeshNodeId,
57
+ center: {
58
+ x: rect.x + rect.width / 2,
59
+ y: rect.y + rect.height / 2,
60
+ },
61
+ width: rect.width,
62
+ height: rect.height,
63
+ availableZ: [...availableZ],
64
+ layer: `z${availableZ.join(",")}`,
65
+ })
66
+
67
+ const isFreeNode = (node: CapacityMeshNode) =>
68
+ !node._containsObstacle && !node._containsTarget
69
+
70
+ const isSingletonNodeOnLayer = (node: CapacityMeshNode, z: number) =>
71
+ node.availableZ.length === 1 && node.availableZ[0] === z
72
+
73
+ const sameRect = (a: XYRect, b: XYRect) =>
74
+ Math.abs(a.x - b.x) <= EPS &&
75
+ Math.abs(a.y - b.y) <= EPS &&
76
+ Math.abs(a.width - b.width) <= EPS &&
77
+ Math.abs(a.height - b.height) <= EPS
78
+
79
+ const subtractRects = (target: XYRect, cutters: XYRect[]) => {
80
+ let remaining: XYRect[] = [target]
81
+
82
+ for (const cutter of cutters) {
83
+ if (remaining.length === 0) return remaining
84
+
85
+ const nextRemaining: XYRect[] = []
86
+ for (const piece of remaining) {
87
+ nextRemaining.push(...subtractRect2D(piece, cutter))
88
+ }
89
+ remaining = nextRemaining
90
+ }
91
+
92
+ return remaining
93
+ }
94
+
95
+ const isFullyCoveredByRects = (target: XYRect, coveringRects: XYRect[]) => {
96
+ return subtractRects(target, coveringRects).length === 0
97
+ }
98
+
99
+ const sortAndDedupeCuts = (values: number[]) => {
100
+ const sorted = [...values].sort((a, b) => a - b)
101
+ const out: number[] = []
102
+
103
+ for (const value of sorted) {
104
+ const last = out[out.length - 1]
105
+ if (last == null || Math.abs(last - value) > EPS) {
106
+ out.push(value)
107
+ }
108
+ }
109
+
110
+ return out
111
+ }
112
+
113
+ const partitionRectByRects = (target: XYRect, supportRects: XYRect[]) => {
114
+ const xCuts = [target.x, target.x + target.width]
115
+ const yCuts = [target.y, target.y + target.height]
116
+ const targetMaxX = target.x + target.width
117
+ const targetMaxY = target.y + target.height
118
+
119
+ for (const rect of supportRects) {
120
+ const x0 = Math.max(target.x, rect.x)
121
+ const x1 = Math.min(targetMaxX, rect.x + rect.width)
122
+ const y0 = Math.max(target.y, rect.y)
123
+ const y1 = Math.min(targetMaxY, rect.y + rect.height)
124
+
125
+ if (x1 <= x0 + EPS || y1 <= y0 + EPS) continue
126
+
127
+ xCuts.push(x0, x1)
128
+ yCuts.push(y0, y1)
129
+ }
130
+
131
+ const xs = sortAndDedupeCuts(xCuts)
132
+ const ys = sortAndDedupeCuts(yCuts)
133
+ const cells: XYRect[] = []
134
+
135
+ for (let xi = 0; xi < xs.length - 1; xi++) {
136
+ const x0 = xs[xi]!
137
+ const x1 = xs[xi + 1]!
138
+
139
+ if (x1 <= x0 + EPS) continue
140
+
141
+ for (let yi = 0; yi < ys.length - 1; yi++) {
142
+ const y0 = ys[yi]!
143
+ const y1 = ys[yi + 1]!
144
+
145
+ if (y1 <= y0 + EPS) continue
146
+
147
+ cells.push({
148
+ x: x0,
149
+ y: y0,
150
+ width: x1 - x0,
151
+ height: y1 - y0,
152
+ })
153
+ }
154
+ }
155
+
156
+ return cells
157
+ }
158
+
159
+ const canMergeHorizontally = (a: XYRect, b: XYRect) =>
160
+ Math.abs(a.y - b.y) <= EPS &&
161
+ Math.abs(a.height - b.height) <= EPS &&
162
+ Math.abs(a.x + a.width - b.x) <= EPS
163
+
164
+ const canMergeVertically = (a: XYRect, b: XYRect) =>
165
+ Math.abs(a.x - b.x) <= EPS &&
166
+ Math.abs(a.width - b.width) <= EPS &&
167
+ Math.abs(a.y + a.height - b.y) <= EPS
168
+
169
+ const mergeTouchingRects = (rects: XYRect[]) => {
170
+ const out = rects.map((rect) => ({ ...rect }))
171
+ let changed = true
172
+
173
+ while (changed) {
174
+ changed = false
175
+
176
+ outer: for (let i = 0; i < out.length; i++) {
177
+ for (let j = i + 1; j < out.length; j++) {
178
+ const a = out[i]!
179
+ const b = out[j]!
180
+
181
+ if (canMergeHorizontally(a, b) || canMergeHorizontally(b, a)) {
182
+ const merged: XYRect = {
183
+ x: Math.min(a.x, b.x),
184
+ y: a.y,
185
+ width: a.width + b.width,
186
+ height: a.height,
187
+ }
188
+ out.splice(j, 1)
189
+ out.splice(i, 1, merged)
190
+ changed = true
191
+ break outer
192
+ }
193
+
194
+ if (canMergeVertically(a, b) || canMergeVertically(b, a)) {
195
+ const merged: XYRect = {
196
+ x: a.x,
197
+ y: Math.min(a.y, b.y),
198
+ width: a.width,
199
+ height: a.height + b.height,
200
+ }
201
+ out.splice(j, 1)
202
+ out.splice(i, 1, merged)
203
+ changed = true
204
+ break outer
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ return out
211
+ }
212
+
213
+ const isPromotableRect = (params: {
214
+ rect: XYRect
215
+ viaMinSize: number
216
+ minFragmentArea: number
217
+ }) => {
218
+ const { rect, viaMinSize, minFragmentArea } = params
219
+
220
+ return (
221
+ rect.width > EPS &&
222
+ rect.height > EPS &&
223
+ rectArea(rect) + EPS >= minFragmentArea &&
224
+ rect.width + EPS >= viaMinSize &&
225
+ rect.height + EPS >= viaMinSize
226
+ )
227
+ }
228
+
229
+ const isResidualRect = (rect: XYRect, minFragmentArea: number) =>
230
+ rect.width > EPS &&
231
+ rect.height > EPS &&
232
+ rectArea(rect) + EPS >= minFragmentArea
233
+
234
+ const computePromotablePieces = (params: {
235
+ target: XYRect
236
+ supportRects: XYRect[]
237
+ viaMinSize: number
238
+ minFragmentArea: number
239
+ }) => {
240
+ const { target, supportRects, viaMinSize, minFragmentArea } = params
241
+ const overlappingSupports = supportRects.filter((rect) =>
242
+ overlaps(rect, target),
243
+ )
244
+
245
+ if (overlappingSupports.length === 0) return []
246
+
247
+ if (
248
+ isFullyCoveredByRects(target, overlappingSupports) &&
249
+ isPromotableRect({ rect: target, viaMinSize, minFragmentArea })
250
+ ) {
251
+ return [target]
252
+ }
253
+
254
+ const partitioned = partitionRectByRects(target, overlappingSupports)
255
+ const coveredPieces = partitioned.filter((piece) =>
256
+ isFullyCoveredByRects(piece, overlappingSupports),
257
+ )
258
+
259
+ if (coveredPieces.length === 0) return []
260
+
261
+ const mergedCoveredPieces = mergeTouchingRects(coveredPieces)
262
+
263
+ return mergedCoveredPieces.filter(
264
+ (piece) =>
265
+ isFullyCoveredByRects(piece, overlappingSupports) &&
266
+ isPromotableRect({ rect: piece, viaMinSize, minFragmentArea }),
267
+ )
268
+ }
269
+
270
+ export class AdjacentLayerContainmentMergeSolver extends BaseSolver {
271
+ private outputNodes: CapacityMeshNode[] = []
272
+ private promotedNodeIds = new Set<string>()
273
+ private residualNodeIds = new Set<string>()
274
+
275
+ constructor(private input: AdjacentLayerContainmentMergeSolverInput) {
276
+ super()
277
+ }
278
+
279
+ override _setup() {
280
+ this.outputNodes = this.input.meshNodes.map(cloneNode)
281
+ this.promotedNodeIds.clear()
282
+ this.residualNodeIds.clear()
283
+ }
284
+
285
+ override _step() {
286
+ this.outputNodes = this.processAdjacentLayerContainmentMerges()
287
+ this.solved = true
288
+ }
289
+
290
+ private processAdjacentLayerContainmentMerges(): CapacityMeshNode[] {
291
+ const srj = this.input.simpleRouteJson
292
+ const layerCount = Math.max(1, srj.layerCount || 1)
293
+
294
+ if (layerCount < 2) {
295
+ return this.input.meshNodes.map(cloneNode)
296
+ }
297
+
298
+ const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0)
299
+ const minFragmentArea = Math.max(
300
+ EPS,
301
+ this.input.minFragmentArea ?? DEFAULT_MIN_FRAGMENT_AREA,
302
+ )
303
+
304
+ let workingNodes = this.input.meshNodes.map(cloneNode)
305
+ let nextResidualId = 0
306
+ let nextPromotedId = 0
307
+
308
+ for (let lowerZ = 0; lowerZ < layerCount - 1; lowerZ++) {
309
+ const upperZ = lowerZ + 1
310
+ const mutableNodes = workingNodes.filter(
311
+ (node) =>
312
+ isFreeNode(node) &&
313
+ (isSingletonNodeOnLayer(node, lowerZ) ||
314
+ isSingletonNodeOnLayer(node, upperZ)),
315
+ )
316
+
317
+ if (mutableNodes.length === 0) continue
318
+
319
+ const immutableNodes = workingNodes.filter(
320
+ (node) => !mutableNodes.includes(node),
321
+ )
322
+ const supportRectsByLayer = new Map<number, XYRect[]>()
323
+
324
+ supportRectsByLayer.set(
325
+ lowerZ,
326
+ mutableNodes
327
+ .filter((node) => isSingletonNodeOnLayer(node, lowerZ))
328
+ .map(nodeToRect),
329
+ )
330
+ supportRectsByLayer.set(
331
+ upperZ,
332
+ mutableNodes
333
+ .filter((node) => isSingletonNodeOnLayer(node, upperZ))
334
+ .map(nodeToRect),
335
+ )
336
+
337
+ const promotedRects: XYRect[] = []
338
+ const promotedNodes: CapacityMeshNode[] = []
339
+ const candidateNodes = mutableNodes
340
+ .filter((node) =>
341
+ isPromotableRect({
342
+ rect: nodeToRect(node),
343
+ viaMinSize,
344
+ minFragmentArea,
345
+ }),
346
+ )
347
+ .sort((a, b) => rectArea(nodeToRect(b)) - rectArea(nodeToRect(a)))
348
+
349
+ for (const candidate of candidateNodes) {
350
+ const candidateRect = nodeToRect(candidate)
351
+ const candidatePieces = subtractRects(
352
+ candidateRect,
353
+ promotedRects,
354
+ ).filter((piece) => isResidualRect(piece, minFragmentArea))
355
+ const candidateZ = candidate.availableZ[0]!
356
+ const oppositeZ = candidateZ === lowerZ ? upperZ : lowerZ
357
+ const supportRects = supportRectsByLayer.get(oppositeZ) ?? []
358
+
359
+ for (const piece of candidatePieces) {
360
+ const promotablePieces = computePromotablePieces({
361
+ target: piece,
362
+ supportRects,
363
+ viaMinSize,
364
+ minFragmentArea,
365
+ })
366
+
367
+ for (const promotablePiece of promotablePieces) {
368
+ promotedRects.push(promotablePiece)
369
+
370
+ const promotedNode = clonePromotedNodeWithRect(
371
+ candidate,
372
+ promotablePiece,
373
+ `${candidate.capacityMeshNodeId}-adjacent-merge-${nextPromotedId++}`,
374
+ [lowerZ, upperZ],
375
+ )
376
+ promotedNodes.push(promotedNode)
377
+ this.promotedNodeIds.add(promotedNode.capacityMeshNodeId)
378
+ }
379
+ }
380
+ }
381
+
382
+ const residualNodes: CapacityMeshNode[] = []
383
+
384
+ for (const node of mutableNodes) {
385
+ const nodeRect = nodeToRect(node)
386
+ const remainingPieces = subtractRects(nodeRect, promotedRects).filter(
387
+ (piece) => isResidualRect(piece, minFragmentArea),
388
+ )
389
+
390
+ if (
391
+ remainingPieces.length === 1 &&
392
+ sameRect(remainingPieces[0]!, nodeRect)
393
+ ) {
394
+ residualNodes.push(node)
395
+ continue
396
+ }
397
+
398
+ for (const piece of remainingPieces) {
399
+ const residualNode = cloneNodeWithRect(
400
+ node,
401
+ piece,
402
+ `${node.capacityMeshNodeId}-adjacent-residual-${nextResidualId++}`,
403
+ )
404
+ residualNodes.push(residualNode)
405
+ this.residualNodeIds.add(residualNode.capacityMeshNodeId)
406
+ }
407
+ }
408
+
409
+ workingNodes = [...immutableNodes, ...promotedNodes, ...residualNodes]
410
+ }
411
+
412
+ return workingNodes
413
+ }
414
+
415
+ override getOutput(): { outputNodes: CapacityMeshNode[] } {
416
+ return { outputNodes: this.outputNodes }
417
+ }
418
+
419
+ override visualize(): GraphicsObject {
420
+ return {
421
+ title: "AdjacentLayerContainmentMergeSolver",
422
+ coordinateSystem: "cartesian",
423
+ rects: this.outputNodes.map((node) => {
424
+ const colors = getColorForZLayer(node.availableZ)
425
+ const isPromoted = this.promotedNodeIds.has(node.capacityMeshNodeId)
426
+ const isResidual = this.residualNodeIds.has(node.capacityMeshNodeId)
427
+
428
+ return {
429
+ center: node.center,
430
+ width: node.width,
431
+ height: node.height,
432
+ stroke: isPromoted
433
+ ? "rgba(245, 158, 11, 0.95)"
434
+ : isResidual
435
+ ? "rgba(37, 99, 235, 0.95)"
436
+ : colors.stroke,
437
+ fill: node._containsObstacle
438
+ ? "rgba(239, 68, 68, 0.35)"
439
+ : isPromoted
440
+ ? "rgba(251, 191, 36, 0.28)"
441
+ : isResidual
442
+ ? "rgba(59, 130, 246, 0.18)"
443
+ : colors.fill,
444
+ layer: `z${node.availableZ.join(",")}`,
445
+ label: [
446
+ `node ${node.capacityMeshNodeId}`,
447
+ `z:${node.availableZ.join(",")}`,
448
+ ].join("\n"),
449
+ }
450
+ }),
451
+ points: [],
452
+ lines: [],
453
+ texts: [],
454
+ }
455
+ }
456
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/rectdiff",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,19 @@
1
+ import { useMemo } from "react"
2
+ import { SolverDebugger3d } from "../../components/SolverDebugger3d"
3
+ import { RectDiffPipeline } from "../../lib/RectDiffPipeline"
4
+ import srjJson from "../../tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.json"
5
+
6
+ const simpleRouteJson =
7
+ srjJson.simpleRouteJson ?? srjJson.simple_route_json ?? srjJson
8
+
9
+ export default () => {
10
+ const solver = useMemo(
11
+ () =>
12
+ new RectDiffPipeline({
13
+ simpleRouteJson,
14
+ }),
15
+ [],
16
+ )
17
+
18
+ return <SolverDebugger3d solver={solver} simpleRouteJson={simpleRouteJson} />
19
+ }
@@ -1,4 +1,4 @@
1
- <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><rect data-type="rect" data-label="node" data-x="-5" data-y="1.1600000000000008" x="40" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="1.1600000000000008" x="390" y="110" width="210" height="338.80000000000007" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="0" data-y="4" x="250" y="110.00000000000006" width="140" height="140" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.48" data-y="-5.16" x="40" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.48" data-y="-5.16" x="423.6" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-2.4800000000000004" data-y="-4.84" x="216.39999999999998" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="2.4800000000000004" data-y="-4.84" x="390" y="448.80000000000007" width="33.60000000000002" height="81.19999999999993" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.48" data-y="-4" x="40" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.48" data-y="-4" x="423.6" y="448.80000000000007" width="176.39999999999998" height="22.399999999999977" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="6.890000000000001" data-y="-4" x="522.3000000000001" y="448.80000000000007" width="77.69999999999993" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-6.89" data-y="-4" x="40" y="448.80000000000007" width="77.70000000000002" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5" data-y="-4" x="136.6" y="448.80000000000007" width="16.80000000000001" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-3.5900000000000003" data-y="-4" x="172.3" y="448.80000000000007" width="44.099999999999966" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="3.5900000000000007" data-y="-4" x="423.6" y="448.80000000000007" width="44.10000000000002" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="-4" x="486.6" y="448.80000000000007" width="16.799999999999955" height="22.399999999999977" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.51" data-y="-4" x="117.70000000000002" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-4.49" data-y="-4" x="153.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="4.49" data-y="-4" x="467.70000000000005" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.51" data-y="-4" x="503.4" y="448.80000000000007" width="18.899999999999977" height="22.399999999999977" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><rect data-type="rect" data-label="node" data-x="-5" data-y="1.1600000000000001" x="40" y="110" width="210" height="338.79999999999995" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="1.1600000000000001" x="390" y="110" width="210" height="338.79999999999995" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="0" data-y="4" x="250" y="110" width="140" height="140" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.48" data-y="-5.16" x="40" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.48" data-y="-5.16" x="423.6" y="471.20000000000005" width="176.39999999999998" height="58.799999999999955" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-2.48" data-y="-4.84" x="216.4" y="448.79999999999995" width="33.599999999999994" height="81.20000000000005" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="2.48" data-y="-4.84" x="390" y="448.79999999999995" width="33.60000000000002" height="81.20000000000005" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-6.890000000000001" data-y="-3.999999999999999" x="40" y="448.79999999999995" width="77.69999999999996" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5" data-y="-3.999999999999999" x="136.6" y="448.79999999999995" width="16.80000000000001" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-3.59" data-y="-3.999999999999999" x="172.3" y="448.79999999999995" width="44.099999999999994" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="3.59" data-y="-3.999999999999999" x="423.6" y="448.79999999999995" width="44.099999999999966" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5" data-y="-3.999999999999999" x="486.6" y="448.79999999999995" width="16.799999999999955" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="6.889999999999999" data-y="-3.999999999999999" x="522.3" y="448.79999999999995" width="77.70000000000005" height="22.400000000000034" fill="#dbeafe" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.51" data-y="-4" x="117.70000000000002" y="448.79999999999995" width="18.899999999999977" height="22.40000000000009" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-4.49" data-y="-4" x="153.4" y="448.79999999999995" width="18.899999999999977" height="22.40000000000009" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="4.49" data-y="-4" x="467.70000000000005" y="448.79999999999995" width="18.899999999999977" height="22.40000000000009" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.51" data-y="-4" x="503.4" y="448.79999999999995" width="18.899999999999977" height="22.40000000000009" fill="red" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-5.51" data-y="-3.999999999999999" x="117.70000000000002" y="448.79999999999995" width="18.899999999999977" height="22.400000000000034" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="-4.49" data-y="-3.999999999999999" x="153.39999999999998" y="448.79999999999995" width="18.900000000000034" height="22.400000000000034" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="4.49" data-y="-3.999999999999999" x="467.70000000000005" y="448.79999999999995" width="18.899999999999977" height="22.400000000000034" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g><rect data-type="rect" data-label="node" data-x="5.51" data-y="-3.999999999999999" x="503.4" y="448.79999999999995" width="18.899999999999977" height="22.400000000000034" fill="#fef3c7" stroke="black" stroke-width="0.02857142857142857"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
2
2
  document.currentScript.parentElement.addEventListener('mousemove', (e) => {
3
3
  const svg = e.currentTarget;
4
4
  const rect = svg.getBoundingClientRect();
@@ -20,7 +20,7 @@
20
20
  v.setAttribute('y2', '640');
21
21
 
22
22
  // Calculate real coordinates using inverse transformation
23
- const matrix = {"a":35,"c":0,"e":320,"b":0,"d":-35,"f":320.00000000000006};
23
+ const matrix = {"a":35,"c":0,"e":320,"b":0,"d":-35,"f":320};
24
24
  // Manually invert and apply the affine transform
25
25
  // Since we only use translate and scale, we can directly compute:
26
26
  // x' = (x - tx) / sx