@tscircuit/schematic-trace-solver 0.0.86 → 0.0.88
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/.agents/skills/schematic-trace-visualization/SKILL.md +115 -0
- package/.github/scripts/import-json-bug-report.ts +15 -1
- package/.github/workflows/json-bug-report.yml +3 -2
- package/AGENTS.md +22 -0
- package/dist/index.js +86 -10
- package/lib/solvers/Example28Solver/Example28Solver.ts +46 -10
- package/lib/solvers/Example28Solver/countCrossingsWithOtherTraces.ts +19 -0
- package/lib/solvers/Example28Solver/scoreSolutionTracePath.ts +26 -0
- package/lib/testing/PipelineStageDebugRunner.ts +344 -0
- package/package.json +3 -2
- package/scripts/debug-pipeline-stages.ts +146 -0
- package/site/bug-reports/bug-report-20260706T220324Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T020342Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T092615Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T134549Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T134722Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T140410Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T141025Z.page.tsx +4 -0
- package/site/bug-reports/bug-report-20260707T141421Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +357 -3448
- package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +64 -0
- package/tests/bug-reports/bug-report-20260707T020342Z/bug-report-20260707T020342Z.json +233 -0
- package/tests/bug-reports/bug-report-20260707T020342Z/bug-report-20260707T020342Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +2098 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/bug-report-20260707T092615Z.json +1392 -0
- package/tests/bug-reports/bug-report-20260707T092615Z/bug-report-20260707T092615Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +857 -0
- package/tests/bug-reports/bug-report-20260707T134549Z/bug-report-20260707T134549Z.json +652 -0
- package/tests/bug-reports/bug-report-20260707T134549Z/bug-report-20260707T134549Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +275 -0
- package/tests/bug-reports/bug-report-20260707T134722Z/bug-report-20260707T134722Z.json +265 -0
- package/tests/bug-reports/bug-report-20260707T134722Z/bug-report-20260707T134722Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +950 -0
- package/tests/bug-reports/bug-report-20260707T140410Z/bug-report-20260707T140410Z.json +610 -0
- package/tests/bug-reports/bug-report-20260707T140410Z/bug-report-20260707T140410Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +218 -0
- package/tests/bug-reports/bug-report-20260707T141025Z/bug-report-20260707T141025Z.json +185 -0
- package/tests/bug-reports/bug-report-20260707T141025Z/bug-report-20260707T141025Z.test.ts +12 -0
- package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +613 -0
- package/tests/bug-reports/bug-report-20260707T141421Z/bug-report-20260707T141421Z.json +499 -0
- package/tests/bug-reports/bug-report-20260707T141421Z/bug-report-20260707T141421Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example01.snap.svg +59 -162
- package/tests/examples/__snapshots__/example02.snap.svg +64 -273
- package/tests/examples/__snapshots__/example03.snap.svg +85 -290
- package/tests/examples/__snapshots__/example04.snap.svg +50 -94
- package/tests/examples/__snapshots__/example05.snap.svg +73 -191
- package/tests/examples/__snapshots__/example06.snap.svg +49 -95
- package/tests/examples/__snapshots__/example07.snap.svg +65 -218
- package/tests/examples/__snapshots__/example08.snap.svg +61 -143
- package/tests/examples/__snapshots__/example09.snap.svg +123 -493
- package/tests/examples/__snapshots__/example10.snap.svg +57 -136
- package/tests/examples/__snapshots__/example11.snap.svg +64 -173
- package/tests/examples/__snapshots__/example12.snap.svg +58 -159
- package/tests/examples/__snapshots__/example13.snap.svg +78 -348
- package/tests/examples/__snapshots__/example14.snap.svg +77 -293
- package/tests/examples/__snapshots__/example15.snap.svg +140 -984
- package/tests/examples/__snapshots__/example16.snap.svg +62 -162
- package/tests/examples/__snapshots__/example17.snap.svg +67 -198
- package/tests/examples/__snapshots__/example18.snap.svg +69 -249
- package/tests/examples/__snapshots__/example19.snap.svg +63 -202
- package/tests/examples/__snapshots__/example20.snap.svg +60 -148
- package/tests/examples/__snapshots__/example21.snap.svg +73 -298
- package/tests/examples/__snapshots__/example22.snap.svg +62 -156
- package/tests/examples/__snapshots__/example23.snap.svg +66 -172
- package/tests/examples/__snapshots__/example24.snap.svg +64 -164
- package/tests/examples/__snapshots__/example25.snap.svg +76 -299
- package/tests/examples/__snapshots__/example26.snap.svg +64 -164
- package/tests/examples/__snapshots__/example27.snap.svg +83 -300
- package/tests/examples/__snapshots__/example28.snap.svg +54 -115
- package/tests/examples/__snapshots__/example29.snap.svg +257 -1299
- package/tests/examples/__snapshots__/example30.snap.svg +91 -293
- package/tests/examples/__snapshots__/example31.snap.svg +47 -88
- package/tests/examples/__snapshots__/example32.snap.svg +107 -405
- package/tests/examples/__snapshots__/example33.snap.svg +71 -180
- package/tests/examples/__snapshots__/example34.snap.svg +55 -116
- package/tests/examples/__snapshots__/example35.snap.svg +69 -176
- package/tests/examples/__snapshots__/example36.snap.svg +65 -159
- package/tests/examples/__snapshots__/example37.snap.svg +75 -223
- package/tests/examples/__snapshots__/example38.snap.svg +46 -76
- package/tests/examples/__snapshots__/example39.snap.svg +51 -106
- package/tests/examples/__snapshots__/example40.snap.svg +83 -270
- package/tests/examples/__snapshots__/example41.snap.svg +54 -118
- package/tests/examples/__snapshots__/example42.snap.svg +67 -194
- package/tests/examples/__snapshots__/example43.snap.svg +62 -168
- package/tests/examples/__snapshots__/example44.snap.svg +86 -369
- package/tests/examples/__snapshots__/example45.snap.svg +60 -169
- package/tests/examples/__snapshots__/example46.snap.svg +80 -342
- package/tests/examples/__snapshots__/example47.snap.svg +82 -323
- package/tests/examples/__snapshots__/example48.snap.svg +64 -167
- package/tests/examples/__snapshots__/example49.snap.svg +120 -565
- package/tests/examples/__snapshots__/example50.snap.svg +71 -195
- package/tests/fixtures/getLastStepGraphicsObject.ts +38 -0
- package/tests/fixtures/getLastStepSvg.ts +9 -0
- package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +67 -218
- package/tests/repros/__snapshots__/manufacturePartNumber-text-box.snap.svg +47 -87
- package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +73 -272
- package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +284 -0
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +470 -0
- package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +94 -395
- package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +88 -467
- package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +109 -569
- package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +95 -459
- package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +64 -176
- package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +51 -112
- package/tests/repros/assets/repro-atmega328p-fault-pullup.input.json +239 -0
- package/tests/repros/assets/repro-atmega328p-missing-gnd-netlabel.input.json +432 -0
- package/tests/repros/repro-atmega328p-fault-pullup.test.ts +11 -0
- package/tests/repros/repro-atmega328p-missing-gnd-netlabel.test.ts +16 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.snap.svg +59 -159
- package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +54 -118
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +58 -245
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView01.snap.svg +73 -222
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView02.snap.svg +65 -204
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +65 -183
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +54 -139
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +61 -215
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +54 -124
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: schematic-trace-visualization
|
|
3
|
+
description: Work with schematic trace solver visualization in this repo. Use when changing solver `visualize()` or `preview()` output, adding debug graphics, creating SVG snapshots, inspecting pipeline stages, or generating graphics-debug artifacts for AI/headless review.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Schematic Trace Visualization
|
|
7
|
+
|
|
8
|
+
Use `GraphicsObject` from `graphics-debug` for solver debug views, SVG snapshots, and headless artifacts.
|
|
9
|
+
|
|
10
|
+
## Graphics Object Types
|
|
11
|
+
|
|
12
|
+
Import visualization types from `graphics-debug`:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import type {
|
|
16
|
+
GraphicsObject,
|
|
17
|
+
Point,
|
|
18
|
+
Line,
|
|
19
|
+
Rect,
|
|
20
|
+
Circle,
|
|
21
|
+
Text,
|
|
22
|
+
} from "graphics-debug"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Do not redefine these shapes in repo docs, tests, or utilities.
|
|
26
|
+
|
|
27
|
+
## Image Functions
|
|
28
|
+
|
|
29
|
+
This repo uses `graphics-debug` for SVG and PNG rendering:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import {
|
|
33
|
+
getPngBufferFromGraphicsObject,
|
|
34
|
+
getSvgFromGraphicsObject,
|
|
35
|
+
} from "graphics-debug"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Call them with a white background for stable artifacts:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const svg = getSvgFromGraphicsObject(graphics, { backgroundColor: "white" })
|
|
42
|
+
const png = await getPngBufferFromGraphicsObject(graphics, {
|
|
43
|
+
backgroundColor: "white",
|
|
44
|
+
pngWidth: 1536,
|
|
45
|
+
pngHeight: 1536,
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Existing Repo Helpers
|
|
50
|
+
|
|
51
|
+
- `tests/fixtures/matcher.ts`: `expect(solver).toMatchSolverSnapshot(import.meta.path)` converts `solver.visualize()` to SVG and snapshots it.
|
|
52
|
+
- `tests/fixtures/getLastStepGraphicsObject.ts`: filters a `GraphicsObject` to its highest numbered `step`.
|
|
53
|
+
- `tests/fixtures/getLastStepSvg.ts`: converts only the last step to SVG.
|
|
54
|
+
- `lib/testing/PipelineStageDebugRunner.ts`: runs `SchematicTracePipelineSolver` and writes per-stage PNG/SVG/graphics JSON artifacts.
|
|
55
|
+
- `scripts/debug-pipeline-stages.ts`: CLI wrapper around the stage debug runner.
|
|
56
|
+
|
|
57
|
+
## Minimal SVG Tests
|
|
58
|
+
|
|
59
|
+
Prefer focused solver SVG snapshots over broad dumps:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
63
|
+
solver.solve()
|
|
64
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For multi-step graphics where only the final stage matters:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
expect(getLastStepSvg(solver.visualize())).toMatchSvgSnapshot(import.meta.path)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Update snapshots with:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
BUN_UPDATE_SNAPSHOTS=1 bun test
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Headless Stage Debugging
|
|
80
|
+
|
|
81
|
+
To inspect a pipeline run without Cosmos:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bun run debug:pipeline tests/assets/example01.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Useful options:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bun run debug:pipeline tests/assets/example17.json --out tmp/schematic-trace-debug/example17
|
|
91
|
+
bun run debug:pipeline tests/assets/example17.json --stop-after schematicTraceLinesSolver
|
|
92
|
+
bun run debug:pipeline tests/assets/example17.json --no-json
|
|
93
|
+
bun run debug:pipeline tests/assets/example17.json --svg --step-pngs
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The runner writes:
|
|
97
|
+
|
|
98
|
+
- `logs.txt`
|
|
99
|
+
- `stageNN-stageName.png`
|
|
100
|
+
- `stageNN-stageName.graphics.json` unless `--no-json` is passed
|
|
101
|
+
|
|
102
|
+
Pass `--svg` when SVG companion artifacts are useful.
|
|
103
|
+
Pass `--step-pngs` to split graphics with `step` values into per-step PNGs.
|
|
104
|
+
|
|
105
|
+
Stage numbers are important because this pipeline can reuse the same solver name more than once, for example `netLabelPlacementSolver`.
|
|
106
|
+
|
|
107
|
+
## Choosing The View
|
|
108
|
+
|
|
109
|
+
Use solver-native `visualize()` for internal state, candidates, failed geometry, staged progress, net labels, and trace cleanup state.
|
|
110
|
+
|
|
111
|
+
Use `SchematicTracePipelineSolver.visualize()` for final combined output after `solve()`.
|
|
112
|
+
|
|
113
|
+
Use `visualizeInputProblem(inputProblem)` when the question is about the raw input geometry, schematic components, pins, direct connections, or net connection labels before routing.
|
|
114
|
+
|
|
115
|
+
Keep SVG backgrounds white. Treat colors, labels, line widths, opacity, and `step` values as snapshot-sensitive.
|
|
@@ -48,15 +48,19 @@ async function main() {
|
|
|
48
48
|
const reportId = `bug-report-${timestamp}`
|
|
49
49
|
const branchName = `json-bug-report/${reportId}`
|
|
50
50
|
const reportDir = path.join("tests", "bug-reports", reportId)
|
|
51
|
+
const siteReportDir = path.join("site", "bug-reports")
|
|
51
52
|
const reportDirAbs = path.join(repoRoot, reportDir)
|
|
52
53
|
const jsonPath = path.join(reportDir, `${reportId}.json`)
|
|
53
54
|
const testPath = path.join(reportDir, `${reportId}.test.ts`)
|
|
55
|
+
const pagePath = path.join(siteReportDir, `${reportId}.page.tsx`)
|
|
54
56
|
|
|
55
57
|
writeOutput("report_id", reportId)
|
|
56
58
|
writeOutput("branch_name", branchName)
|
|
57
59
|
writeOutput("report_dir", reportDir)
|
|
60
|
+
writeOutput("site_report_dir", siteReportDir)
|
|
58
61
|
writeOutput("json_path", jsonPath)
|
|
59
62
|
writeOutput("test_path", testPath)
|
|
63
|
+
writeOutput("page_path", pagePath)
|
|
60
64
|
|
|
61
65
|
if (fs.existsSync(reportDirAbs)) {
|
|
62
66
|
writeOutput("created", "false")
|
|
@@ -70,15 +74,17 @@ async function main() {
|
|
|
70
74
|
assertInputProblem(jsonAttachment.value)
|
|
71
75
|
|
|
72
76
|
fs.mkdirSync(reportDirAbs, { recursive: true })
|
|
77
|
+
fs.mkdirSync(path.join(repoRoot, siteReportDir), { recursive: true })
|
|
73
78
|
fs.writeFileSync(
|
|
74
79
|
path.join(repoRoot, jsonPath),
|
|
75
80
|
`${JSON.stringify(jsonAttachment.value, null, 2)}\n`,
|
|
76
81
|
)
|
|
77
82
|
fs.writeFileSync(path.join(repoRoot, testPath), getTestFile(reportId))
|
|
83
|
+
fs.writeFileSync(path.join(repoRoot, pagePath), getPageFile(reportId))
|
|
78
84
|
|
|
79
85
|
writeOutput("created", "true")
|
|
80
86
|
writeOutput("attachment_url", jsonAttachment.url)
|
|
81
|
-
console.log(`Generated ${jsonPath} and ${
|
|
87
|
+
console.log(`Generated ${jsonPath}, ${testPath}, and ${pagePath}`)
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
function isJsonBugReportIssue(issue: IssueEvent["issue"], body: string) {
|
|
@@ -458,6 +464,14 @@ test("${reportId}", () => {
|
|
|
458
464
|
`
|
|
459
465
|
}
|
|
460
466
|
|
|
467
|
+
function getPageFile(reportId: string) {
|
|
468
|
+
return `import { PipelineDebugger } from "site/components/PipelineDebugger"
|
|
469
|
+
import inputProblem from "../../tests/bug-reports/${reportId}/${reportId}.json"
|
|
470
|
+
|
|
471
|
+
export default () => <PipelineDebugger inputProblem={inputProblem as any} />
|
|
472
|
+
`
|
|
473
|
+
}
|
|
474
|
+
|
|
461
475
|
function writeOutput(key: string, value: string) {
|
|
462
476
|
if (!githubOutputPath) return
|
|
463
477
|
fs.appendFileSync(githubOutputPath, `${key}=${value}\n`)
|
|
@@ -64,7 +64,7 @@ jobs:
|
|
|
64
64
|
|
|
65
65
|
- name: Format generated test
|
|
66
66
|
if: steps.import.outputs.created == 'true'
|
|
67
|
-
run: ./node_modules/.bin/biome format --write "${{ steps.import.outputs.test_path }}"
|
|
67
|
+
run: ./node_modules/.bin/biome format --write "${{ steps.import.outputs.test_path }}" "${{ steps.import.outputs.page_path }}"
|
|
68
68
|
|
|
69
69
|
- name: Open pull request
|
|
70
70
|
id: pr
|
|
@@ -82,6 +82,7 @@ jobs:
|
|
|
82
82
|
git config user.email "tscircuit-bot@users.noreply.github.com"
|
|
83
83
|
git checkout -B "$BRANCH_NAME"
|
|
84
84
|
git add "${{ steps.import.outputs.report_dir }}"
|
|
85
|
+
git add "${{ steps.import.outputs.page_path }}"
|
|
85
86
|
|
|
86
87
|
if git diff --cached --quiet; then
|
|
87
88
|
echo "No changes to commit."
|
|
@@ -96,7 +97,7 @@ jobs:
|
|
|
96
97
|
{
|
|
97
98
|
echo "Generated from #${ISSUE_NUMBER}."
|
|
98
99
|
echo
|
|
99
|
-
echo "Adds a snapshot-only regression test for the attached JSON solver input."
|
|
100
|
+
echo "Adds a snapshot-only regression test and debugger page for the attached JSON solver input."
|
|
100
101
|
echo
|
|
101
102
|
echo "Workflow run: ${RUN_URL}"
|
|
102
103
|
} > "$pr_body_file"
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Agent Notes
|
|
2
|
+
|
|
3
|
+
- Use `bunx tsc --noEmit` to type check.
|
|
4
|
+
- Use `bun test path/to/test.ts` to run a test.
|
|
5
|
+
- Use `bun add <package>` to add a package.
|
|
6
|
+
|
|
7
|
+
## Solver Visualization
|
|
8
|
+
|
|
9
|
+
For schematic trace solver visualization work, use the repo-local skill:
|
|
10
|
+
|
|
11
|
+
- `.agents/skills/schematic-trace-visualization/SKILL.md`
|
|
12
|
+
|
|
13
|
+
This repo supports headless stage artifact generation without starting Cosmos:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun run debug:pipeline tests/assets/example01.json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The command writes per-stage PNG and graphics JSON artifacts under `tmp/schematic-trace-debug/...`.
|
|
20
|
+
Pass `--svg` when SVG companion files are needed.
|
|
21
|
+
|
|
22
|
+
Interactive debugging still exists through `site/**/*.page.tsx`, but agents should prefer the headless debug runner when they need to inspect solver state programmatically.
|
package/dist/index.js
CHANGED
|
@@ -5777,6 +5777,57 @@ var getPointDistanceFromRect = (point, rect) => {
|
|
|
5777
5777
|
return dx + dy;
|
|
5778
5778
|
};
|
|
5779
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
|
+
|
|
5780
5831
|
// lib/solvers/Example28Solver/visualize.ts
|
|
5781
5832
|
var CANDIDATE_SELECTED_COLOR = "blue";
|
|
5782
5833
|
var CANDIDATE_REJECTED_COLOR = "red";
|
|
@@ -5849,6 +5900,13 @@ ${label.netId ?? label.globalConnNetId}`
|
|
|
5849
5900
|
var LABEL_OUTWARD_STEP = 0.1;
|
|
5850
5901
|
var LABEL_MAX_OUTWARD_STEPS = 10;
|
|
5851
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
|
+
};
|
|
5852
5910
|
var Example28Solver = class extends BaseSolver {
|
|
5853
5911
|
inputProblem;
|
|
5854
5912
|
traces;
|
|
@@ -5951,7 +6009,7 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5951
6009
|
);
|
|
5952
6010
|
if (traceIndex === -1) return;
|
|
5953
6011
|
const currentTrace = this.outputTraces[traceIndex];
|
|
5954
|
-
|
|
6012
|
+
const labelMove = this.tryMoveLabelOutward(overlap.label);
|
|
5955
6013
|
const rerouteResult = findBestReroutePath({
|
|
5956
6014
|
trace: currentTrace,
|
|
5957
6015
|
obstacleLabel: overlap.label,
|
|
@@ -5961,6 +6019,26 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5961
6019
|
chipObstacles: this.chipObstacles
|
|
5962
6020
|
});
|
|
5963
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
|
+
}
|
|
5964
6042
|
if (!rerouteResult.bestPath) return;
|
|
5965
6043
|
const originalTracePath = currentTrace.tracePath;
|
|
5966
6044
|
this.outputTraces[traceIndex] = {
|
|
@@ -5978,17 +6056,17 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5978
6056
|
const labelIndex = this.outputNetLabelPlacements.findIndex(
|
|
5979
6057
|
(label2) => label2.globalConnNetId === labelToMove.globalConnNetId && label2.anchorPoint.x === labelToMove.anchorPoint.x && label2.anchorPoint.y === labelToMove.anchorPoint.y && label2.pinIds.join(",") === labelToMove.pinIds.join(",")
|
|
5980
6058
|
);
|
|
5981
|
-
if (labelIndex === -1) return
|
|
6059
|
+
if (labelIndex === -1) return null;
|
|
5982
6060
|
const label = this.outputNetLabelPlacements[labelIndex];
|
|
5983
|
-
if (label.mspConnectionPairIds.length > 0) return
|
|
5984
|
-
if (label.pinIds.length !== 1) return
|
|
6061
|
+
if (label.mspConnectionPairIds.length > 0) return null;
|
|
6062
|
+
if (label.pinIds.length !== 1) return null;
|
|
5985
6063
|
if (this.outputNetLabelPlacements.filter(
|
|
5986
6064
|
(otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId
|
|
5987
6065
|
).length !== 1) {
|
|
5988
|
-
return
|
|
6066
|
+
return null;
|
|
5989
6067
|
}
|
|
5990
6068
|
const outward = dir(label.orientation);
|
|
5991
|
-
if (outward.x === 0 && outward.y === 0) return
|
|
6069
|
+
if (outward.x === 0 && outward.y === 0) return null;
|
|
5992
6070
|
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
5993
6071
|
const distance3 = step * LABEL_OUTWARD_STEP;
|
|
5994
6072
|
const candidate = {
|
|
@@ -6024,11 +6102,9 @@ var Example28Solver = class extends BaseSolver {
|
|
|
6024
6102
|
}).length > 0) {
|
|
6025
6103
|
continue;
|
|
6026
6104
|
}
|
|
6027
|
-
|
|
6028
|
-
this.outputTraces.push(connectorTrace);
|
|
6029
|
-
return true;
|
|
6105
|
+
return { labelIndex, movedLabel: candidate, connectorTrace };
|
|
6030
6106
|
}
|
|
6031
|
-
return
|
|
6107
|
+
return null;
|
|
6032
6108
|
}
|
|
6033
6109
|
visualize() {
|
|
6034
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,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
|
+
}
|