@tscircuit/schematic-trace-solver 0.0.85 → 0.0.87
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 +99 -21
- package/lib/solvers/Example28Solver/Example28Solver.ts +46 -10
- package/lib/solvers/Example28Solver/countCrossingsWithOtherTraces.ts +19 -0
- package/lib/solvers/Example28Solver/doesPathRunAlongChipBoundary.ts +41 -0
- package/lib/solvers/Example28Solver/geometry.ts +1 -23
- package/lib/solvers/Example28Solver/scoreSolutionTracePath.ts +26 -0
- package/lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts +26 -0
- package/package.json +1 -1
- package/site/examples/example50.page.tsx +4 -0
- package/tests/assets/example50.json +149 -0
- package/tests/examples/__snapshots__/example50.snap.svg +190 -0
- package/tests/examples/example50.test.ts +12 -0
package/dist/index.js
CHANGED
|
@@ -5213,6 +5213,19 @@ var dir = (facingDirection) => {
|
|
|
5213
5213
|
// lib/solvers/Example28Solver/types.ts
|
|
5214
5214
|
var EPS7 = 1e-9;
|
|
5215
5215
|
|
|
5216
|
+
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
5217
|
+
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5218
|
+
const isVertical4 = Math.abs(start.x - end.x) < EPS7;
|
|
5219
|
+
const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
|
|
5220
|
+
if (isVertical4) {
|
|
5221
|
+
return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
|
|
5222
|
+
}
|
|
5223
|
+
if (isHorizontal3) {
|
|
5224
|
+
return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
|
|
5225
|
+
}
|
|
5226
|
+
return false;
|
|
5227
|
+
};
|
|
5228
|
+
|
|
5216
5229
|
// lib/solvers/Example28Solver/geometry.ts
|
|
5217
5230
|
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
5218
5231
|
var getPathLength = (path) => {
|
|
@@ -5350,17 +5363,6 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
|
5350
5363
|
}
|
|
5351
5364
|
return false;
|
|
5352
5365
|
};
|
|
5353
|
-
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5354
|
-
const isVertical4 = Math.abs(start.x - end.x) < EPS7;
|
|
5355
|
-
const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
|
|
5356
|
-
if (isVertical4) {
|
|
5357
|
-
return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
|
|
5358
|
-
}
|
|
5359
|
-
if (isHorizontal3) {
|
|
5360
|
-
return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
|
|
5361
|
-
}
|
|
5362
|
-
return false;
|
|
5363
|
-
};
|
|
5364
5366
|
|
|
5365
5367
|
// lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
|
|
5366
5368
|
var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
|
|
@@ -5775,6 +5777,57 @@ var getPointDistanceFromRect = (point, rect) => {
|
|
|
5775
5777
|
return dx + dy;
|
|
5776
5778
|
};
|
|
5777
5779
|
|
|
5780
|
+
// lib/solvers/Example28Solver/countCrossingsWithOtherTraces.ts
|
|
5781
|
+
var countCrossingsWithOtherTraces = (params) => {
|
|
5782
|
+
const { trace, outputTraces } = params;
|
|
5783
|
+
let crossingCount = 0;
|
|
5784
|
+
for (const otherTrace of outputTraces) {
|
|
5785
|
+
if (otherTrace.mspPairId === trace.mspPairId) continue;
|
|
5786
|
+
if (otherTrace.globalConnNetId === trace.globalConnNetId) continue;
|
|
5787
|
+
crossingCount += countPathIntersections(
|
|
5788
|
+
trace.tracePath,
|
|
5789
|
+
otherTrace.tracePath
|
|
5790
|
+
);
|
|
5791
|
+
}
|
|
5792
|
+
return crossingCount;
|
|
5793
|
+
};
|
|
5794
|
+
|
|
5795
|
+
// lib/solvers/Example28Solver/doesPathRunAlongChipBoundary.ts
|
|
5796
|
+
var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
|
|
5797
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
5798
|
+
const start = path[i];
|
|
5799
|
+
const end = path[i + 1];
|
|
5800
|
+
for (const obstacle of chipObstacles) {
|
|
5801
|
+
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5802
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS7) {
|
|
5803
|
+
return true;
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
return false;
|
|
5808
|
+
};
|
|
5809
|
+
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
5810
|
+
const isVertical4 = Math.abs(start.x - end.x) < EPS7;
|
|
5811
|
+
if (isVertical4) {
|
|
5812
|
+
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
5813
|
+
}
|
|
5814
|
+
return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
|
|
5815
|
+
};
|
|
5816
|
+
|
|
5817
|
+
// lib/solvers/Example28Solver/scoreSolutionTracePath.ts
|
|
5818
|
+
var scoreSolutionTracePath = (params) => {
|
|
5819
|
+
const { traces, outputTraces, chipObstacles } = params;
|
|
5820
|
+
let chipEdgeRuns = 0;
|
|
5821
|
+
let traceCrossings = 0;
|
|
5822
|
+
for (const trace of traces) {
|
|
5823
|
+
if (doesPathRunAlongChipBoundary(trace.tracePath, chipObstacles)) {
|
|
5824
|
+
chipEdgeRuns += 1;
|
|
5825
|
+
}
|
|
5826
|
+
traceCrossings += countCrossingsWithOtherTraces({ trace, outputTraces });
|
|
5827
|
+
}
|
|
5828
|
+
return { chipEdgeRuns, traceCrossings };
|
|
5829
|
+
};
|
|
5830
|
+
|
|
5778
5831
|
// lib/solvers/Example28Solver/visualize.ts
|
|
5779
5832
|
var CANDIDATE_SELECTED_COLOR = "blue";
|
|
5780
5833
|
var CANDIDATE_REJECTED_COLOR = "red";
|
|
@@ -5847,6 +5900,13 @@ ${label.netId ?? label.globalConnNetId}`
|
|
|
5847
5900
|
var LABEL_OUTWARD_STEP = 0.1;
|
|
5848
5901
|
var LABEL_MAX_OUTWARD_STEPS = 10;
|
|
5849
5902
|
var LABEL_TRACE_CLEARANCE = 0.1;
|
|
5903
|
+
var isScoreBetter = (rerouteScore, labelMoveScore) => {
|
|
5904
|
+
if (!rerouteScore) return false;
|
|
5905
|
+
if (rerouteScore.chipEdgeRuns !== labelMoveScore.chipEdgeRuns) {
|
|
5906
|
+
return rerouteScore.chipEdgeRuns < labelMoveScore.chipEdgeRuns;
|
|
5907
|
+
}
|
|
5908
|
+
return rerouteScore.traceCrossings < labelMoveScore.traceCrossings;
|
|
5909
|
+
};
|
|
5850
5910
|
var Example28Solver = class extends BaseSolver {
|
|
5851
5911
|
inputProblem;
|
|
5852
5912
|
traces;
|
|
@@ -5949,7 +6009,7 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5949
6009
|
);
|
|
5950
6010
|
if (traceIndex === -1) return;
|
|
5951
6011
|
const currentTrace = this.outputTraces[traceIndex];
|
|
5952
|
-
|
|
6012
|
+
const labelMove = this.tryMoveLabelOutward(overlap.label);
|
|
5953
6013
|
const rerouteResult = findBestReroutePath({
|
|
5954
6014
|
trace: currentTrace,
|
|
5955
6015
|
obstacleLabel: overlap.label,
|
|
@@ -5959,6 +6019,26 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5959
6019
|
chipObstacles: this.chipObstacles
|
|
5960
6020
|
});
|
|
5961
6021
|
this.currentCandidateResults = rerouteResult.candidateResults;
|
|
6022
|
+
let rerouteScore = null;
|
|
6023
|
+
if (rerouteResult.bestPath) {
|
|
6024
|
+
rerouteScore = scoreSolutionTracePath({
|
|
6025
|
+
traces: [{ ...currentTrace, tracePath: rerouteResult.bestPath }],
|
|
6026
|
+
outputTraces: this.outputTraces,
|
|
6027
|
+
chipObstacles: this.chipObstacles
|
|
6028
|
+
});
|
|
6029
|
+
}
|
|
6030
|
+
if (labelMove) {
|
|
6031
|
+
const labelMoveScore = scoreSolutionTracePath({
|
|
6032
|
+
traces: [labelMove.connectorTrace, currentTrace],
|
|
6033
|
+
outputTraces: this.outputTraces,
|
|
6034
|
+
chipObstacles: this.chipObstacles
|
|
6035
|
+
});
|
|
6036
|
+
if (!isScoreBetter(rerouteScore, labelMoveScore)) {
|
|
6037
|
+
this.outputNetLabelPlacements[labelMove.labelIndex] = labelMove.movedLabel;
|
|
6038
|
+
this.outputTraces.push(labelMove.connectorTrace);
|
|
6039
|
+
return;
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
5962
6042
|
if (!rerouteResult.bestPath) return;
|
|
5963
6043
|
const originalTracePath = currentTrace.tracePath;
|
|
5964
6044
|
this.outputTraces[traceIndex] = {
|
|
@@ -5976,17 +6056,17 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5976
6056
|
const labelIndex = this.outputNetLabelPlacements.findIndex(
|
|
5977
6057
|
(label2) => label2.globalConnNetId === labelToMove.globalConnNetId && label2.anchorPoint.x === labelToMove.anchorPoint.x && label2.anchorPoint.y === labelToMove.anchorPoint.y && label2.pinIds.join(",") === labelToMove.pinIds.join(",")
|
|
5978
6058
|
);
|
|
5979
|
-
if (labelIndex === -1) return
|
|
6059
|
+
if (labelIndex === -1) return null;
|
|
5980
6060
|
const label = this.outputNetLabelPlacements[labelIndex];
|
|
5981
|
-
if (label.mspConnectionPairIds.length > 0) return
|
|
5982
|
-
if (label.pinIds.length !== 1) return
|
|
6061
|
+
if (label.mspConnectionPairIds.length > 0) return null;
|
|
6062
|
+
if (label.pinIds.length !== 1) return null;
|
|
5983
6063
|
if (this.outputNetLabelPlacements.filter(
|
|
5984
6064
|
(otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId
|
|
5985
6065
|
).length !== 1) {
|
|
5986
|
-
return
|
|
6066
|
+
return null;
|
|
5987
6067
|
}
|
|
5988
6068
|
const outward = dir(label.orientation);
|
|
5989
|
-
if (outward.x === 0 && outward.y === 0) return
|
|
6069
|
+
if (outward.x === 0 && outward.y === 0) return null;
|
|
5990
6070
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
5991
6071
|
const distance3 = step * LABEL_OUTWARD_STEP;
|
|
5992
6072
|
const candidate = {
|
|
@@ -6022,11 +6102,9 @@ var Example28Solver = class extends BaseSolver {
|
|
|
6022
6102
|
}).length > 0) {
|
|
6023
6103
|
continue;
|
|
6024
6104
|
}
|
|
6025
|
-
|
|
6026
|
-
this.outputTraces.push(connectorTrace);
|
|
6027
|
-
return true;
|
|
6105
|
+
return { labelIndex, movedLabel: candidate, connectorTrace };
|
|
6028
6106
|
}
|
|
6029
|
-
return
|
|
6107
|
+
return null;
|
|
6030
6108
|
}
|
|
6031
6109
|
visualize() {
|
|
6032
6110
|
return visualizeExample28Solver({
|
|
@@ -20,6 +20,10 @@ import type {
|
|
|
20
20
|
import { dir } from "lib/utils/dir"
|
|
21
21
|
import { moveAttachedLabelsToReroutedTrace } from "./labelMovement"
|
|
22
22
|
import { findBestReroutePath } from "./reroute"
|
|
23
|
+
import {
|
|
24
|
+
scoreSolutionTracePath,
|
|
25
|
+
type TracePathScore,
|
|
26
|
+
} from "./scoreSolutionTracePath"
|
|
23
27
|
import type { Example28SolverParams, RerouteCandidateResult } from "./types"
|
|
24
28
|
import { visualizeExample28Solver } from "./visualize"
|
|
25
29
|
|
|
@@ -27,6 +31,17 @@ const LABEL_OUTWARD_STEP = 0.1
|
|
|
27
31
|
const LABEL_MAX_OUTWARD_STEPS = 10
|
|
28
32
|
const LABEL_TRACE_CLEARANCE = 0.1
|
|
29
33
|
|
|
34
|
+
const isScoreBetter = (
|
|
35
|
+
rerouteScore: TracePathScore | null,
|
|
36
|
+
labelMoveScore: TracePathScore,
|
|
37
|
+
) => {
|
|
38
|
+
if (!rerouteScore) return false
|
|
39
|
+
if (rerouteScore.chipEdgeRuns !== labelMoveScore.chipEdgeRuns) {
|
|
40
|
+
return rerouteScore.chipEdgeRuns < labelMoveScore.chipEdgeRuns
|
|
41
|
+
}
|
|
42
|
+
return rerouteScore.traceCrossings < labelMoveScore.traceCrossings
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
export class Example28Solver extends BaseSolver {
|
|
31
46
|
inputProblem: InputProblem
|
|
32
47
|
traces: SolvedTracePath[]
|
|
@@ -160,7 +175,7 @@ export class Example28Solver extends BaseSolver {
|
|
|
160
175
|
if (traceIndex === -1) return
|
|
161
176
|
|
|
162
177
|
const currentTrace = this.outputTraces[traceIndex]!
|
|
163
|
-
|
|
178
|
+
const labelMove = this.tryMoveLabelOutward(overlap.label)
|
|
164
179
|
|
|
165
180
|
const rerouteResult = findBestReroutePath({
|
|
166
181
|
trace: currentTrace,
|
|
@@ -172,6 +187,29 @@ export class Example28Solver extends BaseSolver {
|
|
|
172
187
|
})
|
|
173
188
|
this.currentCandidateResults = rerouteResult.candidateResults
|
|
174
189
|
|
|
190
|
+
let rerouteScore: TracePathScore | null = null
|
|
191
|
+
if (rerouteResult.bestPath) {
|
|
192
|
+
rerouteScore = scoreSolutionTracePath({
|
|
193
|
+
traces: [{ ...currentTrace, tracePath: rerouteResult.bestPath }],
|
|
194
|
+
outputTraces: this.outputTraces,
|
|
195
|
+
chipObstacles: this.chipObstacles,
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (labelMove) {
|
|
200
|
+
const labelMoveScore = scoreSolutionTracePath({
|
|
201
|
+
traces: [labelMove.connectorTrace, currentTrace],
|
|
202
|
+
outputTraces: this.outputTraces,
|
|
203
|
+
chipObstacles: this.chipObstacles,
|
|
204
|
+
})
|
|
205
|
+
if (!isScoreBetter(rerouteScore, labelMoveScore)) {
|
|
206
|
+
this.outputNetLabelPlacements[labelMove.labelIndex] =
|
|
207
|
+
labelMove.movedLabel
|
|
208
|
+
this.outputTraces.push(labelMove.connectorTrace)
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
175
213
|
if (!rerouteResult.bestPath) return
|
|
176
214
|
|
|
177
215
|
const originalTracePath = currentTrace.tracePath
|
|
@@ -195,21 +233,21 @@ export class Example28Solver extends BaseSolver {
|
|
|
195
233
|
label.anchorPoint.y === labelToMove.anchorPoint.y &&
|
|
196
234
|
label.pinIds.join(",") === labelToMove.pinIds.join(","),
|
|
197
235
|
)
|
|
198
|
-
if (labelIndex === -1) return
|
|
236
|
+
if (labelIndex === -1) return null
|
|
199
237
|
|
|
200
238
|
const label = this.outputNetLabelPlacements[labelIndex]!
|
|
201
|
-
if (label.mspConnectionPairIds.length > 0) return
|
|
202
|
-
if (label.pinIds.length !== 1) return
|
|
239
|
+
if (label.mspConnectionPairIds.length > 0) return null
|
|
240
|
+
if (label.pinIds.length !== 1) return null
|
|
203
241
|
if (
|
|
204
242
|
this.outputNetLabelPlacements.filter(
|
|
205
243
|
(otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId,
|
|
206
244
|
).length !== 1
|
|
207
245
|
) {
|
|
208
|
-
return
|
|
246
|
+
return null
|
|
209
247
|
}
|
|
210
248
|
|
|
211
249
|
const outward = dir(label.orientation)
|
|
212
|
-
if (outward.x === 0 && outward.y === 0) return
|
|
250
|
+
if (outward.x === 0 && outward.y === 0) return null
|
|
213
251
|
|
|
214
252
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
215
253
|
const distance = step * LABEL_OUTWARD_STEP
|
|
@@ -252,12 +290,10 @@ export class Example28Solver extends BaseSolver {
|
|
|
252
290
|
continue
|
|
253
291
|
}
|
|
254
292
|
|
|
255
|
-
|
|
256
|
-
this.outputTraces.push(connectorTrace)
|
|
257
|
-
return true
|
|
293
|
+
return { labelIndex, movedLabel: candidate, connectorTrace }
|
|
258
294
|
}
|
|
259
295
|
|
|
260
|
-
return
|
|
296
|
+
return null
|
|
261
297
|
}
|
|
262
298
|
|
|
263
299
|
override visualize(): GraphicsObject {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import { countPathIntersections } from "./geometry"
|
|
3
|
+
|
|
4
|
+
export const countCrossingsWithOtherTraces = (params: {
|
|
5
|
+
trace: SolvedTracePath
|
|
6
|
+
outputTraces: SolvedTracePath[]
|
|
7
|
+
}) => {
|
|
8
|
+
const { trace, outputTraces } = params
|
|
9
|
+
let crossingCount = 0
|
|
10
|
+
for (const otherTrace of outputTraces) {
|
|
11
|
+
if (otherTrace.mspPairId === trace.mspPairId) continue
|
|
12
|
+
if (otherTrace.globalConnNetId === trace.globalConnNetId) continue
|
|
13
|
+
crossingCount += countPathIntersections(
|
|
14
|
+
trace.tracePath,
|
|
15
|
+
otherTrace.tracePath,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
return crossingCount
|
|
19
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
+
import { segmentRunsAlongRectBoundary } from "./segmentRunsAlongRectBoundary"
|
|
3
|
+
import type { ChipObstacle } from "./types"
|
|
4
|
+
import { EPS } from "./types"
|
|
5
|
+
|
|
6
|
+
export const doesPathRunAlongChipBoundary = (
|
|
7
|
+
path: Point[],
|
|
8
|
+
chipObstacles: ChipObstacle[],
|
|
9
|
+
) => {
|
|
10
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
11
|
+
const start = path[i]!
|
|
12
|
+
const end = path[i + 1]!
|
|
13
|
+
for (const obstacle of chipObstacles) {
|
|
14
|
+
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue
|
|
15
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS) {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getSegmentOverlapWithRectSpan = (
|
|
24
|
+
start: Point,
|
|
25
|
+
end: Point,
|
|
26
|
+
rect: ChipObstacle,
|
|
27
|
+
) => {
|
|
28
|
+
const isVertical = Math.abs(start.x - end.x) < EPS
|
|
29
|
+
|
|
30
|
+
if (isVertical) {
|
|
31
|
+
return (
|
|
32
|
+
Math.min(Math.max(start.y, end.y), rect.maxY) -
|
|
33
|
+
Math.max(Math.min(start.y, end.y), rect.minY)
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
Math.min(Math.max(start.x, end.x), rect.maxX) -
|
|
39
|
+
Math.max(Math.min(start.x, end.x), rect.minX)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Point } from "@tscircuit/math-utils"
|
|
2
2
|
import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
3
|
+
import { segmentRunsAlongRectBoundary } from "./segmentRunsAlongRectBoundary"
|
|
3
4
|
import type { ChipObstacle, PathSegment, SegmentOrientation } from "./types"
|
|
4
5
|
import { EPS } from "./types"
|
|
5
6
|
|
|
@@ -221,26 +222,3 @@ export const isPathCollidingWithChipInterior = (
|
|
|
221
222
|
}
|
|
222
223
|
return false
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
-
const segmentRunsAlongRectBoundary = (
|
|
226
|
-
start: Point,
|
|
227
|
-
end: Point,
|
|
228
|
-
rect: ChipObstacle,
|
|
229
|
-
) => {
|
|
230
|
-
const isVertical = Math.abs(start.x - end.x) < EPS
|
|
231
|
-
const isHorizontal = Math.abs(start.y - end.y) < EPS
|
|
232
|
-
|
|
233
|
-
if (isVertical) {
|
|
234
|
-
return (
|
|
235
|
-
Math.abs(start.x - rect.minX) < EPS || Math.abs(start.x - rect.maxX) < EPS
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (isHorizontal) {
|
|
240
|
-
return (
|
|
241
|
-
Math.abs(start.y - rect.minY) < EPS || Math.abs(start.y - rect.maxY) < EPS
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return false
|
|
246
|
-
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import { countCrossingsWithOtherTraces } from "./countCrossingsWithOtherTraces"
|
|
3
|
+
import { doesPathRunAlongChipBoundary } from "./doesPathRunAlongChipBoundary"
|
|
4
|
+
import type { ChipObstacle } from "./types"
|
|
5
|
+
|
|
6
|
+
export type TracePathScore = {
|
|
7
|
+
chipEdgeRuns: number
|
|
8
|
+
traceCrossings: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const scoreSolutionTracePath = (params: {
|
|
12
|
+
traces: SolvedTracePath[]
|
|
13
|
+
outputTraces: SolvedTracePath[]
|
|
14
|
+
chipObstacles: ChipObstacle[]
|
|
15
|
+
}): TracePathScore => {
|
|
16
|
+
const { traces, outputTraces, chipObstacles } = params
|
|
17
|
+
let chipEdgeRuns = 0
|
|
18
|
+
let traceCrossings = 0
|
|
19
|
+
for (const trace of traces) {
|
|
20
|
+
if (doesPathRunAlongChipBoundary(trace.tracePath, chipObstacles)) {
|
|
21
|
+
chipEdgeRuns += 1
|
|
22
|
+
}
|
|
23
|
+
traceCrossings += countCrossingsWithOtherTraces({ trace, outputTraces })
|
|
24
|
+
}
|
|
25
|
+
return { chipEdgeRuns, traceCrossings }
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
+
import type { ChipObstacle } from "./types"
|
|
3
|
+
import { EPS } from "./types"
|
|
4
|
+
|
|
5
|
+
export const segmentRunsAlongRectBoundary = (
|
|
6
|
+
start: Point,
|
|
7
|
+
end: Point,
|
|
8
|
+
rect: ChipObstacle,
|
|
9
|
+
) => {
|
|
10
|
+
const isVertical = Math.abs(start.x - end.x) < EPS
|
|
11
|
+
const isHorizontal = Math.abs(start.y - end.y) < EPS
|
|
12
|
+
|
|
13
|
+
if (isVertical) {
|
|
14
|
+
return (
|
|
15
|
+
Math.abs(start.x - rect.minX) < EPS || Math.abs(start.x - rect.maxX) < EPS
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (isHorizontal) {
|
|
20
|
+
return (
|
|
21
|
+
Math.abs(start.y - rect.minY) < EPS || Math.abs(start.y - rect.maxY) < EPS
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return false
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_0",
|
|
5
|
+
"center": {
|
|
6
|
+
"x": -1.4,
|
|
7
|
+
"y": -6.6
|
|
8
|
+
},
|
|
9
|
+
"width": 1.1,
|
|
10
|
+
"height": 0.7789106999999991,
|
|
11
|
+
"pins": [
|
|
12
|
+
{
|
|
13
|
+
"pinId": "R7.1",
|
|
14
|
+
"x": -1.95,
|
|
15
|
+
"y": -6.6
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"pinId": "R7.2",
|
|
19
|
+
"x": -0.85,
|
|
20
|
+
"y": -6.6
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"sectionId": "qspi"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"chipId": "schematic_component_1",
|
|
27
|
+
"center": {
|
|
28
|
+
"x": 0,
|
|
29
|
+
"y": -8.4
|
|
30
|
+
},
|
|
31
|
+
"width": 2.245,
|
|
32
|
+
"height": 1,
|
|
33
|
+
"pins": [
|
|
34
|
+
{
|
|
35
|
+
"pinId": "U5.1",
|
|
36
|
+
"x": -1.5225,
|
|
37
|
+
"y": -8.1
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"pinId": "U5.2",
|
|
41
|
+
"x": -1.5225,
|
|
42
|
+
"y": -8.3
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"pinId": "U5.3",
|
|
46
|
+
"x": -1.5225,
|
|
47
|
+
"y": -8.5
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"pinId": "U5.4",
|
|
51
|
+
"x": -1.5225,
|
|
52
|
+
"y": -8.700000000000001
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"pinId": "U5.5",
|
|
56
|
+
"x": 1.5225,
|
|
57
|
+
"y": -8.1
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"pinId": "U5.6",
|
|
61
|
+
"x": 1.5225,
|
|
62
|
+
"y": -8.3
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"pinId": "U5.7",
|
|
66
|
+
"x": 1.5225,
|
|
67
|
+
"y": -8.700000000000001
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"pinId": "U5.8",
|
|
71
|
+
"x": 1.5225,
|
|
72
|
+
"y": -8.5
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"sectionId": "qspi"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"directConnections": [],
|
|
79
|
+
"netConnections": [
|
|
80
|
+
{
|
|
81
|
+
"netId": "V3V3",
|
|
82
|
+
"pinIds": [
|
|
83
|
+
"R7.1",
|
|
84
|
+
"U5.8"
|
|
85
|
+
],
|
|
86
|
+
"netLabelWidth": 0.6
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"netId": "QSPI_CS",
|
|
90
|
+
"pinIds": [
|
|
91
|
+
"R7.2",
|
|
92
|
+
"U5.4"
|
|
93
|
+
],
|
|
94
|
+
"netLabelWidth": 0.96
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"netId": "QSPI_SCK",
|
|
98
|
+
"pinIds": [
|
|
99
|
+
"U5.1"
|
|
100
|
+
],
|
|
101
|
+
"netLabelWidth": 1.08
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"netId": "QSPI_DATA0",
|
|
105
|
+
"pinIds": [
|
|
106
|
+
"U5.2"
|
|
107
|
+
],
|
|
108
|
+
"netLabelWidth": 1.32
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"netId": "QSPI_DATA1",
|
|
112
|
+
"pinIds": [
|
|
113
|
+
"U5.3"
|
|
114
|
+
],
|
|
115
|
+
"netLabelWidth": 1.32
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"netId": "QSPI_DATA2",
|
|
119
|
+
"pinIds": [
|
|
120
|
+
"U5.5"
|
|
121
|
+
],
|
|
122
|
+
"netLabelWidth": 1.32
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"netId": "QSPI_DATA3",
|
|
126
|
+
"pinIds": [
|
|
127
|
+
"U5.6"
|
|
128
|
+
],
|
|
129
|
+
"netLabelWidth": 1.32
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"netId": "GND",
|
|
133
|
+
"pinIds": [
|
|
134
|
+
"U5.7"
|
|
135
|
+
],
|
|
136
|
+
"netLabelWidth": 0.48
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"availableNetLabelOrientations": {
|
|
140
|
+
"V3V3": [
|
|
141
|
+
"y+"
|
|
142
|
+
],
|
|
143
|
+
"GND": [
|
|
144
|
+
"y-"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"maxMspPairDistance": 2.4
|
|
148
|
+
}
|
|
149
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="100%" height="100%" fill="white" />
|
|
3
|
+
<g>
|
|
4
|
+
<circle data-type="point" data-label="R7.1
|
|
5
|
+
x-" data-x="-1.95" data-y="-6.6" cx="126.46276136167273" cy="214.47382063245198" r="3" fill="hsl(232, 100%, 50%, 0.8)" />
|
|
6
|
+
</g>
|
|
7
|
+
<g>
|
|
8
|
+
<circle data-type="point" data-label="R7.2
|
|
9
|
+
x+" data-x="-0.8499999999999999" data-y="-6.6" cx="232.90824261275273" cy="214.47382063245198" r="3" fill="hsl(233, 100%, 50%, 0.8)" />
|
|
10
|
+
</g>
|
|
11
|
+
<g>
|
|
12
|
+
<circle data-type="point" data-label="U5.1
|
|
13
|
+
x-" data-x="-1.5225" data-y="-8.1" cx="167.83134612061517" cy="359.6267496111974" r="3" fill="hsl(203, 100%, 50%, 0.8)" />
|
|
14
|
+
</g>
|
|
15
|
+
<g>
|
|
16
|
+
<circle data-type="point" data-label="U5.2
|
|
17
|
+
x-" data-x="-1.5225" data-y="-8.3" cx="167.83134612061517" cy="378.9804734750303" r="3" fill="hsl(204, 100%, 50%, 0.8)" />
|
|
18
|
+
</g>
|
|
19
|
+
<g>
|
|
20
|
+
<circle data-type="point" data-label="U5.3
|
|
21
|
+
x-" data-x="-1.5225" data-y="-8.5" cx="167.83134612061517" cy="398.33419733886296" r="3" fill="hsl(205, 100%, 50%, 0.8)" />
|
|
22
|
+
</g>
|
|
23
|
+
<g>
|
|
24
|
+
<circle data-type="point" data-label="U5.4
|
|
25
|
+
x-" data-x="-1.5225" data-y="-8.700000000000001" cx="167.83134612061517" cy="417.68792120269575" r="3" fill="hsl(206, 100%, 50%, 0.8)" />
|
|
26
|
+
</g>
|
|
27
|
+
<g>
|
|
28
|
+
<circle data-type="point" data-label="U5.5
|
|
29
|
+
x+" data-x="1.5225" data-y="-8.1" cx="462.4917919474684" cy="359.6267496111974" r="3" fill="hsl(207, 100%, 50%, 0.8)" />
|
|
30
|
+
</g>
|
|
31
|
+
<g>
|
|
32
|
+
<circle data-type="point" data-label="U5.6
|
|
33
|
+
x+" data-x="1.5225" data-y="-8.3" cx="462.4917919474684" cy="378.9804734750303" r="3" fill="hsl(208, 100%, 50%, 0.8)" />
|
|
34
|
+
</g>
|
|
35
|
+
<g>
|
|
36
|
+
<circle data-type="point" data-label="U5.7
|
|
37
|
+
x+" data-x="1.5225" data-y="-8.700000000000001" cx="462.4917919474684" cy="417.68792120269575" r="3" fill="hsl(209, 100%, 50%, 0.8)" />
|
|
38
|
+
</g>
|
|
39
|
+
<g>
|
|
40
|
+
<circle data-type="point" data-label="U5.8
|
|
41
|
+
x+" data-x="1.5225" data-y="-8.5" cx="462.4917919474684" cy="398.33419733886296" r="3" fill="hsl(210, 100%, 50%, 0.8)" />
|
|
42
|
+
</g>
|
|
43
|
+
<g>
|
|
44
|
+
<circle data-type="point" data-label="anchorPoint
|
|
45
|
+
orientation: y+" data-x="-2.15" data-y="-6.6" cx="107.10903749784" cy="214.47382063245198" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
46
|
+
</g>
|
|
47
|
+
<g>
|
|
48
|
+
<circle data-type="point" data-label="anchorPoint
|
|
49
|
+
orientation: x+" data-x="-0.8499999999999999" data-y="-6.6" cx="232.90824261275273" cy="214.47382063245198" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
50
|
+
</g>
|
|
51
|
+
<g>
|
|
52
|
+
<circle data-type="point" data-label="anchorPoint
|
|
53
|
+
orientation: x-" data-x="-1.5225" data-y="-8.700000000000001" cx="167.83134612061517" cy="417.68792120269575" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
54
|
+
</g>
|
|
55
|
+
<g>
|
|
56
|
+
<circle data-type="point" data-label="anchorPoint
|
|
57
|
+
orientation: x-" data-x="-1.5225" data-y="-8.1" cx="167.83134612061517" cy="359.6267496111974" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
58
|
+
</g>
|
|
59
|
+
<g>
|
|
60
|
+
<circle data-type="point" data-label="anchorPoint
|
|
61
|
+
orientation: x-" data-x="-1.5225" data-y="-8.3" cx="167.83134612061517" cy="378.9804734750303" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
62
|
+
</g>
|
|
63
|
+
<g>
|
|
64
|
+
<circle data-type="point" data-label="anchorPoint
|
|
65
|
+
orientation: x-" data-x="-1.5225" data-y="-8.5" cx="167.83134612061517" cy="398.33419733886296" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
66
|
+
</g>
|
|
67
|
+
<g>
|
|
68
|
+
<circle data-type="point" data-label="anchorPoint
|
|
69
|
+
orientation: x+" data-x="1.5225" data-y="-8.1" cx="462.4917919474684" cy="359.6267496111974" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
70
|
+
</g>
|
|
71
|
+
<g>
|
|
72
|
+
<circle data-type="point" data-label="anchorPoint
|
|
73
|
+
orientation: x+" data-x="1.5225" data-y="-8.3" cx="462.4917919474684" cy="378.9804734750303" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
74
|
+
</g>
|
|
75
|
+
<g>
|
|
76
|
+
<circle data-type="point" data-label="anchorPoint
|
|
77
|
+
orientation: y-" data-x="1.6235" data-y="-8.901" cx="472.26542249870397" cy="437.1384136858476" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
|
|
78
|
+
</g>
|
|
79
|
+
<g>
|
|
80
|
+
<polyline data-points="-1.95,-6.6 1.5225,-8.5" data-type="line" data-label="" points="126.46276136167273,214.47382063245198 462.4917919474684,398.33419733886296" fill="none" stroke="hsl(154, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
81
|
+
</g>
|
|
82
|
+
<g>
|
|
83
|
+
<polyline data-points="-0.8499999999999999,-6.6 -1.5225,-8.700000000000001" data-type="line" data-label="" points="232.90824261275273,214.47382063245198 167.83134612061517,417.68792120269575" fill="none" stroke="hsl(172, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
|
|
84
|
+
</g>
|
|
85
|
+
<g>
|
|
86
|
+
<polyline data-points="-1.95,-6.6 -2.15,-6.6 -2.15,-7.55 2.9435000000000002,-7.55 2.9435000000000002,-8.5 1.5225000000000002,-8.5" data-type="line" data-label="" points="126.46276136167273,214.47382063245198 107.10903749784,214.47382063245198 107.10903749784,306.40400898565747 600,306.40400898565747 600,398.33419733886296 462.4917919474684,398.33419733886296" fill="none" stroke="purple" stroke-width="1" />
|
|
87
|
+
</g>
|
|
88
|
+
<g>
|
|
89
|
+
<polyline data-points="1.5225,-8.700000000000001 1.6235,-8.700000000000001 1.6235,-8.901" data-type="line" data-label="" points="462.4917919474684,417.68792120269575 472.26542249870397,417.68792120269575 472.26542249870397,437.1384136858476" fill="none" stroke="purple" stroke-width="1" />
|
|
90
|
+
</g>
|
|
91
|
+
<g>
|
|
92
|
+
<rect data-type="rect" data-label="schematic_component_0" data-x="-1.4" data-y="-6.6" x="126.46276136167273" y="176.78676412649043" width="106.44548125108" height="75.37411301192321" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010333928571428572" />
|
|
93
|
+
</g>
|
|
94
|
+
<g>
|
|
95
|
+
<rect data-type="rect" data-label="schematic_component_1" data-x="0" data-y="-8.4" x="167.83134612061517" y="340.27302574736484" width="294.6604458268532" height="96.76861931916358" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010333928571428572" />
|
|
96
|
+
</g>
|
|
97
|
+
<g>
|
|
98
|
+
<rect data-type="rect" data-label="netId: V3V3
|
|
99
|
+
globalConnNetId: connectivity_net0" data-x="-2.15" data-y="-6.3" x="97.43217556592361" y="156.41264904095385" width="19.353723863832755" height="58.06117159149812" fill="#ef444466" stroke="#ef4444" stroke-width="0.010333928571428572" />
|
|
100
|
+
</g>
|
|
101
|
+
<g>
|
|
102
|
+
<rect data-type="rect" data-label="netId: QSPI_CS
|
|
103
|
+
globalConnNetId: connectivity_net1" data-x="-0.3689999999999999" data-y="-6.6" x="233.0050112320719" y="204.7969587005357" width="92.8978745463971" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
104
|
+
</g>
|
|
105
|
+
<g>
|
|
106
|
+
<rect data-type="rect" data-label="netId: QSPI_CS
|
|
107
|
+
globalConnNetId: connectivity_net1" data-x="-2.0035" data-y="-8.700000000000001" x="74.83670295489893" y="408.01105927077947" width="92.8978745463971" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
108
|
+
</g>
|
|
109
|
+
<g>
|
|
110
|
+
<rect data-type="rect" data-label="netId: QSPI_SCK
|
|
111
|
+
globalConnNetId: connectivity_net2" data-x="-2.0635" data-y="-8.1" x="63.22446863659928" y="349.9498876792811" width="104.51010886469675" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
112
|
+
</g>
|
|
113
|
+
<g>
|
|
114
|
+
<rect data-type="rect" data-label="netId: QSPI_DATA0
|
|
115
|
+
globalConnNetId: connectivity_net3" data-x="-2.1835" data-y="-8.3" x="40" y="369.3036115431139" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
116
|
+
</g>
|
|
117
|
+
<g>
|
|
118
|
+
<rect data-type="rect" data-label="netId: QSPI_DATA1
|
|
119
|
+
globalConnNetId: connectivity_net4" data-x="-2.1835" data-y="-8.5" x="40" y="388.6573354069466" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
120
|
+
</g>
|
|
121
|
+
<g>
|
|
122
|
+
<rect data-type="rect" data-label="netId: QSPI_DATA2
|
|
123
|
+
globalConnNetId: connectivity_net5" data-x="2.1835" data-y="-8.1" x="462.5885605667876" y="349.9498876792811" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
124
|
+
</g>
|
|
125
|
+
<g>
|
|
126
|
+
<rect data-type="rect" data-label="netId: QSPI_DATA3
|
|
127
|
+
globalConnNetId: connectivity_net6" data-x="2.1835" data-y="-8.3" x="462.5885605667876" y="369.3036115431139" width="127.73457750129603" height="19.35372386383267" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.010333928571428572" />
|
|
128
|
+
</g>
|
|
129
|
+
<g>
|
|
130
|
+
<rect data-type="rect" data-label="netId: GND
|
|
131
|
+
globalConnNetId: connectivity_net7" data-x="1.6235" data-y="-9.141" x="462.5885605667876" y="437.1384136858476" width="19.353723863832784" height="46.44893727319857" fill="#00000066" stroke="#000000" stroke-width="0.010333928571428572" />
|
|
132
|
+
</g>
|
|
133
|
+
<g id="crosshair" style="display: none">
|
|
134
|
+
<line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
|
|
135
|
+
<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>
|
|
136
|
+
</g>
|
|
137
|
+
<script>
|
|
138
|
+
<![CDATA[
|
|
139
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
140
|
+
const svg = e.currentTarget;
|
|
141
|
+
const rect = svg.getBoundingClientRect();
|
|
142
|
+
const x = e.clientX - rect.left;
|
|
143
|
+
const y = e.clientY - rect.top;
|
|
144
|
+
const crosshair = svg.getElementById('crosshair');
|
|
145
|
+
const h = svg.getElementById('crosshair-h');
|
|
146
|
+
const v = svg.getElementById('crosshair-v');
|
|
147
|
+
const coords = svg.getElementById('coordinates');
|
|
148
|
+
|
|
149
|
+
crosshair.style.display = 'block';
|
|
150
|
+
h.setAttribute('x1', '0');
|
|
151
|
+
h.setAttribute('x2', '640');
|
|
152
|
+
h.setAttribute('y1', y);
|
|
153
|
+
h.setAttribute('y2', y);
|
|
154
|
+
v.setAttribute('x1', x);
|
|
155
|
+
v.setAttribute('x2', x);
|
|
156
|
+
v.setAttribute('y1', '0');
|
|
157
|
+
v.setAttribute('y2', '640');
|
|
158
|
+
|
|
159
|
+
// Calculate real coordinates using inverse transformation
|
|
160
|
+
const matrix = {
|
|
161
|
+
"a": 96.76861931916363,
|
|
162
|
+
"c": 0,
|
|
163
|
+
"e": 315.1615690340418,
|
|
164
|
+
"b": 0,
|
|
165
|
+
"d": -96.76861931916363,
|
|
166
|
+
"f": -424.19906687402795
|
|
167
|
+
};
|
|
168
|
+
// Manually invert and apply the affine transform
|
|
169
|
+
// Since we only use translate and scale, we can directly compute:
|
|
170
|
+
// x' = (x - tx) / sx
|
|
171
|
+
// y' = (y - ty) / sy
|
|
172
|
+
const sx = matrix.a;
|
|
173
|
+
const sy = matrix.d;
|
|
174
|
+
const tx = matrix.e;
|
|
175
|
+
const ty = matrix.f;
|
|
176
|
+
const realPoint = {
|
|
177
|
+
x: (x - tx) / sx,
|
|
178
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
182
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
183
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
184
|
+
});
|
|
185
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
186
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
187
|
+
});
|
|
188
|
+
]]>
|
|
189
|
+
</script>
|
|
190
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
3
|
+
import inputProblem from "../assets/example50.json"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
|
|
6
|
+
test("example49", () => {
|
|
7
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
8
|
+
|
|
9
|
+
solver.solve()
|
|
10
|
+
|
|
11
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
12
|
+
})
|