@tscircuit/curvy-trace-solver 0.0.6 → 0.0.7
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.d.ts +1 -0
- package/dist/index.js +102 -89
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ declare class CurvyTraceSolver extends BaseSolver {
|
|
|
45
45
|
private initializeTraces;
|
|
46
46
|
private updateSampledTraces;
|
|
47
47
|
private updateSingleTraceSample;
|
|
48
|
+
private updateControlPointsFromDistances;
|
|
48
49
|
private updateCollisionPairs;
|
|
49
50
|
private computeTotalCost;
|
|
50
51
|
private computeCostForTrace;
|
package/dist/index.js
CHANGED
|
@@ -932,16 +932,27 @@ function getPerimeterT(p, bounds) {
|
|
|
932
932
|
if (Math.abs(p.x - minX) < eps) return 2 * W + H + (p.y - minY);
|
|
933
933
|
return 0;
|
|
934
934
|
}
|
|
935
|
-
function
|
|
935
|
+
function getInwardPerpendicular(p, bounds) {
|
|
936
936
|
const { minX, maxX, minY, maxY } = bounds;
|
|
937
|
-
const
|
|
938
|
-
const
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
if (
|
|
943
|
-
if (
|
|
944
|
-
return { x:
|
|
937
|
+
const eps = 1e-6;
|
|
938
|
+
const onTop = Math.abs(p.y - maxY) < eps;
|
|
939
|
+
const onBottom = Math.abs(p.y - minY) < eps;
|
|
940
|
+
const onRight = Math.abs(p.x - maxX) < eps;
|
|
941
|
+
const onLeft = Math.abs(p.x - minX) < eps;
|
|
942
|
+
if (onTop && onRight) return { x: -Math.SQRT1_2, y: -Math.SQRT1_2 };
|
|
943
|
+
if (onTop && onLeft) return { x: Math.SQRT1_2, y: -Math.SQRT1_2 };
|
|
944
|
+
if (onBottom && onRight) return { x: -Math.SQRT1_2, y: Math.SQRT1_2 };
|
|
945
|
+
if (onBottom && onLeft) return { x: Math.SQRT1_2, y: Math.SQRT1_2 };
|
|
946
|
+
if (onTop) return { x: 0, y: -1 };
|
|
947
|
+
if (onBottom) return { x: 0, y: 1 };
|
|
948
|
+
if (onRight) return { x: -1, y: 0 };
|
|
949
|
+
if (onLeft) return { x: 1, y: 0 };
|
|
950
|
+
const cx = (minX + maxX) / 2;
|
|
951
|
+
const cy = (minY + maxY) / 2;
|
|
952
|
+
const dx = cx - p.x;
|
|
953
|
+
const dy = cy - p.y;
|
|
954
|
+
const len = Math.hypot(dx, dy);
|
|
955
|
+
return len > 0 ? { x: dx / len, y: dy / len } : { x: 0, y: -1 };
|
|
945
956
|
}
|
|
946
957
|
function sampleCubicBezierInline(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, points, numSamples) {
|
|
947
958
|
for (let i = 0; i <= numSamples; i++) {
|
|
@@ -1103,21 +1114,14 @@ var CurvyTraceSolver = class extends BaseSolver {
|
|
|
1103
1114
|
}
|
|
1104
1115
|
const maxDepth = Math.max(...Array.from(nestingDepth.values()), 1);
|
|
1105
1116
|
this.traces = tracesWithT.map(({ pair, t1, t2, idx }) => {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const
|
|
1113
|
-
const
|
|
1114
|
-
x: pair.start.x + (pair.end.x - pair.start.x) * 0.33,
|
|
1115
|
-
y: pair.start.y + (pair.end.y - pair.start.y) * 0.33
|
|
1116
|
-
};
|
|
1117
|
-
const pLinear2 = {
|
|
1118
|
-
x: pair.start.x + (pair.end.x - pair.start.x) * 0.67,
|
|
1119
|
-
y: pair.start.y + (pair.end.y - pair.start.y) * 0.67
|
|
1120
|
-
};
|
|
1117
|
+
const perpDir1 = getInwardPerpendicular(pair.start, bounds);
|
|
1118
|
+
const perpDir2 = getInwardPerpendicular(pair.end, bounds);
|
|
1119
|
+
const chordLength = Math.hypot(
|
|
1120
|
+
pair.end.x - pair.start.x,
|
|
1121
|
+
pair.end.y - pair.start.y
|
|
1122
|
+
);
|
|
1123
|
+
const depth = nestingDepth.get(idx) || 0;
|
|
1124
|
+
const normalizedDepth = depth / maxDepth;
|
|
1121
1125
|
const midPoint = {
|
|
1122
1126
|
x: (pair.start.x + pair.end.x) / 2,
|
|
1123
1127
|
y: (pair.start.y + pair.end.y) / 2
|
|
@@ -1128,23 +1132,31 @@ var CurvyTraceSolver = class extends BaseSolver {
|
|
|
1128
1132
|
);
|
|
1129
1133
|
const maxDist = Math.hypot(W / 2, H / 2);
|
|
1130
1134
|
const spatialDepth = 1 - distToCenter / maxDist;
|
|
1131
|
-
const
|
|
1132
|
-
const
|
|
1133
|
-
const
|
|
1134
|
-
const
|
|
1135
|
+
const baseFactor = 0.25 + spatialDepth * 0.15;
|
|
1136
|
+
const depthAdjustment = 1 - normalizedDepth * 0.3;
|
|
1137
|
+
const initialDist = chordLength * baseFactor * depthAdjustment;
|
|
1138
|
+
const minDist = Math.min(W, H) * 0.05;
|
|
1139
|
+
const d1 = Math.max(minDist, initialDist);
|
|
1140
|
+
const d2 = Math.max(minDist, initialDist);
|
|
1141
|
+
const ctrl1 = {
|
|
1142
|
+
x: pair.start.x + d1 * perpDir1.x,
|
|
1143
|
+
y: pair.start.y + d1 * perpDir1.y
|
|
1144
|
+
};
|
|
1145
|
+
const ctrl2 = {
|
|
1146
|
+
x: pair.end.x + d2 * perpDir2.x,
|
|
1147
|
+
y: pair.end.y + d2 * perpDir2.y
|
|
1148
|
+
};
|
|
1135
1149
|
return {
|
|
1136
1150
|
waypointPair: pair,
|
|
1137
|
-
ctrl1
|
|
1138
|
-
|
|
1139
|
-
y: pLinear1.y * (1 - pullAmount) + pPerim1.y * pullAmount
|
|
1140
|
-
},
|
|
1141
|
-
ctrl2: {
|
|
1142
|
-
x: pLinear2.x * (1 - pullAmount) + pPerim2.x * pullAmount,
|
|
1143
|
-
y: pLinear2.y * (1 - pullAmount) + pPerim2.y * pullAmount
|
|
1144
|
-
},
|
|
1151
|
+
ctrl1,
|
|
1152
|
+
ctrl2,
|
|
1145
1153
|
networkId: pair.networkId,
|
|
1146
1154
|
t1,
|
|
1147
|
-
t2
|
|
1155
|
+
t2,
|
|
1156
|
+
perpDir1,
|
|
1157
|
+
perpDir2,
|
|
1158
|
+
d1,
|
|
1159
|
+
d2
|
|
1148
1160
|
};
|
|
1149
1161
|
});
|
|
1150
1162
|
this.sampledPoints = this.traces.map(
|
|
@@ -1199,6 +1211,14 @@ var CurvyTraceSolver = class extends BaseSolver {
|
|
|
1199
1211
|
OPT_SAMPLES + 1
|
|
1200
1212
|
);
|
|
1201
1213
|
}
|
|
1214
|
+
// Update control points from perpendicular distances
|
|
1215
|
+
updateControlPointsFromDistances(i) {
|
|
1216
|
+
const trace = this.traces[i];
|
|
1217
|
+
trace.ctrl1.x = trace.waypointPair.start.x + trace.d1 * trace.perpDir1.x;
|
|
1218
|
+
trace.ctrl1.y = trace.waypointPair.start.y + trace.d1 * trace.perpDir1.y;
|
|
1219
|
+
trace.ctrl2.x = trace.waypointPair.end.x + trace.d2 * trace.perpDir2.x;
|
|
1220
|
+
trace.ctrl2.y = trace.waypointPair.end.y + trace.d2 * trace.perpDir2.y;
|
|
1221
|
+
}
|
|
1202
1222
|
// Determine which trace pairs could possibly collide based on bounding boxes
|
|
1203
1223
|
updateCollisionPairs() {
|
|
1204
1224
|
const { preferredTraceToTraceSpacing: preferredSpacing } = this.problem;
|
|
@@ -1327,9 +1347,11 @@ var CurvyTraceSolver = class extends BaseSolver {
|
|
|
1327
1347
|
optimizeStep() {
|
|
1328
1348
|
const { bounds } = this.problem;
|
|
1329
1349
|
const { minX, maxX, minY, maxY } = bounds;
|
|
1350
|
+
const minDim = Math.min(maxX - minX, maxY - minY);
|
|
1330
1351
|
const progress = this.optimizationStep / this.maxOptimizationSteps;
|
|
1331
1352
|
const baseStep = 3.5 * (1 - progress) + 0.5;
|
|
1332
|
-
const
|
|
1353
|
+
const minDist = minDim * 0.02;
|
|
1354
|
+
const maxDist = minDim * 1.5;
|
|
1333
1355
|
const traceCosts = [];
|
|
1334
1356
|
for (let i = 0; i < this.traces.length; i++) {
|
|
1335
1357
|
traceCosts.push({ idx: i, cost: this.computeCostForTrace(i) });
|
|
@@ -1340,74 +1362,65 @@ var CurvyTraceSolver = class extends BaseSolver {
|
|
|
1340
1362
|
const trace = this.traces[i];
|
|
1341
1363
|
const steps = [baseStep, baseStep * 1.5, baseStep * 0.5];
|
|
1342
1364
|
for (const step of steps) {
|
|
1343
|
-
const
|
|
1344
|
-
{ dx: step, dy: 0 },
|
|
1345
|
-
{ dx: -step, dy: 0 },
|
|
1346
|
-
{ dx: 0, dy: step },
|
|
1347
|
-
{ dx: 0, dy: -step },
|
|
1348
|
-
{ dx: step * SQRT1_2, dy: step * SQRT1_2 },
|
|
1349
|
-
{ dx: -step * SQRT1_2, dy: -step * SQRT1_2 },
|
|
1350
|
-
{ dx: step * SQRT1_2, dy: -step * SQRT1_2 },
|
|
1351
|
-
{ dx: -step * SQRT1_2, dy: step * SQRT1_2 }
|
|
1352
|
-
];
|
|
1365
|
+
const deltas = [step, -step, step * 2, -step * 2];
|
|
1353
1366
|
let bestCost = this.computeCostForTrace(i);
|
|
1354
|
-
let
|
|
1355
|
-
let
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
const origCtrl2y = trace.ctrl2.y;
|
|
1362
|
-
for (const dir of directions) {
|
|
1363
|
-
trace.ctrl1.x = Math.max(minX, Math.min(maxX, origCtrl1x + dir.dx));
|
|
1364
|
-
trace.ctrl1.y = Math.max(minY, Math.min(maxY, origCtrl1y + dir.dy));
|
|
1367
|
+
let bestD1 = trace.d1;
|
|
1368
|
+
let bestD2 = trace.d2;
|
|
1369
|
+
const origD1 = trace.d1;
|
|
1370
|
+
const origD2 = trace.d2;
|
|
1371
|
+
for (const delta of deltas) {
|
|
1372
|
+
trace.d1 = Math.max(minDist, Math.min(maxDist, origD1 + delta));
|
|
1373
|
+
this.updateControlPointsFromDistances(i);
|
|
1365
1374
|
this.updateSingleTraceSample(i);
|
|
1366
1375
|
const cost1 = this.computeCostForTrace(i);
|
|
1367
1376
|
if (cost1 < bestCost) {
|
|
1368
1377
|
bestCost = cost1;
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
bestCtrl2x = origCtrl2x;
|
|
1372
|
-
bestCtrl2y = origCtrl2y;
|
|
1378
|
+
bestD1 = trace.d1;
|
|
1379
|
+
bestD2 = origD2;
|
|
1373
1380
|
}
|
|
1374
|
-
trace.
|
|
1375
|
-
|
|
1376
|
-
trace.
|
|
1377
|
-
|
|
1381
|
+
trace.d1 = origD1;
|
|
1382
|
+
this.updateControlPointsFromDistances(i);
|
|
1383
|
+
trace.d2 = Math.max(minDist, Math.min(maxDist, origD2 + delta));
|
|
1384
|
+
this.updateControlPointsFromDistances(i);
|
|
1378
1385
|
this.updateSingleTraceSample(i);
|
|
1379
1386
|
const cost2 = this.computeCostForTrace(i);
|
|
1380
1387
|
if (cost2 < bestCost) {
|
|
1381
1388
|
bestCost = cost2;
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
bestCtrl2x = trace.ctrl2.x;
|
|
1385
|
-
bestCtrl2y = trace.ctrl2.y;
|
|
1389
|
+
bestD1 = origD1;
|
|
1390
|
+
bestD2 = trace.d2;
|
|
1386
1391
|
}
|
|
1387
|
-
trace.
|
|
1388
|
-
|
|
1389
|
-
trace.
|
|
1390
|
-
trace.
|
|
1391
|
-
|
|
1392
|
-
trace.ctrl2.y = Math.max(minY, Math.min(maxY, origCtrl2y + dir.dy));
|
|
1392
|
+
trace.d2 = origD2;
|
|
1393
|
+
this.updateControlPointsFromDistances(i);
|
|
1394
|
+
trace.d1 = Math.max(minDist, Math.min(maxDist, origD1 + delta));
|
|
1395
|
+
trace.d2 = Math.max(minDist, Math.min(maxDist, origD2 + delta));
|
|
1396
|
+
this.updateControlPointsFromDistances(i);
|
|
1393
1397
|
this.updateSingleTraceSample(i);
|
|
1394
1398
|
const cost3 = this.computeCostForTrace(i);
|
|
1395
1399
|
if (cost3 < bestCost) {
|
|
1396
1400
|
bestCost = cost3;
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
+
bestD1 = trace.d1;
|
|
1402
|
+
bestD2 = trace.d2;
|
|
1403
|
+
}
|
|
1404
|
+
trace.d1 = origD1;
|
|
1405
|
+
trace.d2 = origD2;
|
|
1406
|
+
this.updateControlPointsFromDistances(i);
|
|
1407
|
+
trace.d1 = Math.max(minDist, Math.min(maxDist, origD1 + delta));
|
|
1408
|
+
trace.d2 = Math.max(minDist, Math.min(maxDist, origD2 - delta));
|
|
1409
|
+
this.updateControlPointsFromDistances(i);
|
|
1410
|
+
this.updateSingleTraceSample(i);
|
|
1411
|
+
const cost4 = this.computeCostForTrace(i);
|
|
1412
|
+
if (cost4 < bestCost) {
|
|
1413
|
+
bestCost = cost4;
|
|
1414
|
+
bestD1 = trace.d1;
|
|
1415
|
+
bestD2 = trace.d2;
|
|
1401
1416
|
}
|
|
1402
|
-
trace.
|
|
1403
|
-
trace.
|
|
1404
|
-
|
|
1405
|
-
trace.ctrl2.y = origCtrl2y;
|
|
1417
|
+
trace.d1 = origD1;
|
|
1418
|
+
trace.d2 = origD2;
|
|
1419
|
+
this.updateControlPointsFromDistances(i);
|
|
1406
1420
|
}
|
|
1407
|
-
trace.
|
|
1408
|
-
trace.
|
|
1409
|
-
|
|
1410
|
-
trace.ctrl2.y = bestCtrl2y;
|
|
1421
|
+
trace.d1 = bestD1;
|
|
1422
|
+
trace.d2 = bestD2;
|
|
1423
|
+
this.updateControlPointsFromDistances(i);
|
|
1411
1424
|
this.updateSingleTraceSample(i);
|
|
1412
1425
|
if (bestCost < currentCost * 0.9) break;
|
|
1413
1426
|
}
|