@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 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 getPerimeterPoint(t, bounds) {
935
+ function getInwardPerpendicular(p, bounds) {
936
936
  const { minX, maxX, minY, maxY } = bounds;
937
- const W = maxX - minX;
938
- const H = maxY - minY;
939
- const perimeter = 2 * W + 2 * H;
940
- t = (t % perimeter + perimeter) % perimeter;
941
- if (t <= W) return { x: minX + t, y: maxY };
942
- if (t <= W + H) return { x: maxX, y: maxY - (t - W) };
943
- if (t <= 2 * W + H) return { x: maxX - (t - W - H), y: minY };
944
- return { x: minX, y: minY + (t - 2 * W - H) };
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
- let dt = t2 - t1;
1107
- if (dt > perimeter / 2) dt -= perimeter;
1108
- if (dt < -perimeter / 2) dt += perimeter;
1109
- const tCtrl1 = t1 + dt * 0.33;
1110
- const tCtrl2 = t1 + dt * 0.67;
1111
- const pPerim1 = getPerimeterPoint(tCtrl1, bounds);
1112
- const pPerim2 = getPerimeterPoint(tCtrl2, bounds);
1113
- const pLinear1 = {
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 depth = nestingDepth.get(idx) || 0;
1132
- const normalizedDepth = depth / maxDepth;
1133
- const basePull = 0.3 + spatialDepth * 0.4;
1134
- const pullAmount = Math.max(0.05, basePull - normalizedDepth * 0.2);
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
- x: pLinear1.x * (1 - pullAmount) + pPerim1.x * pullAmount,
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 SQRT1_2 = Math.SQRT1_2;
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 directions = [
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 bestCtrl1x = trace.ctrl1.x;
1355
- let bestCtrl1y = trace.ctrl1.y;
1356
- let bestCtrl2x = trace.ctrl2.x;
1357
- let bestCtrl2y = trace.ctrl2.y;
1358
- const origCtrl1x = trace.ctrl1.x;
1359
- const origCtrl1y = trace.ctrl1.y;
1360
- const origCtrl2x = trace.ctrl2.x;
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
- bestCtrl1x = trace.ctrl1.x;
1370
- bestCtrl1y = trace.ctrl1.y;
1371
- bestCtrl2x = origCtrl2x;
1372
- bestCtrl2y = origCtrl2y;
1378
+ bestD1 = trace.d1;
1379
+ bestD2 = origD2;
1373
1380
  }
1374
- trace.ctrl1.x = origCtrl1x;
1375
- trace.ctrl1.y = origCtrl1y;
1376
- trace.ctrl2.x = Math.max(minX, Math.min(maxX, origCtrl2x + dir.dx));
1377
- trace.ctrl2.y = Math.max(minY, Math.min(maxY, origCtrl2y + dir.dy));
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
- bestCtrl1x = origCtrl1x;
1383
- bestCtrl1y = origCtrl1y;
1384
- bestCtrl2x = trace.ctrl2.x;
1385
- bestCtrl2y = trace.ctrl2.y;
1389
+ bestD1 = origD1;
1390
+ bestD2 = trace.d2;
1386
1391
  }
1387
- trace.ctrl2.x = origCtrl2x;
1388
- trace.ctrl2.y = origCtrl2y;
1389
- trace.ctrl1.x = Math.max(minX, Math.min(maxX, origCtrl1x + dir.dx));
1390
- trace.ctrl1.y = Math.max(minY, Math.min(maxY, origCtrl1y + dir.dy));
1391
- trace.ctrl2.x = Math.max(minX, Math.min(maxX, origCtrl2x + dir.dx));
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
- bestCtrl1x = trace.ctrl1.x;
1398
- bestCtrl1y = trace.ctrl1.y;
1399
- bestCtrl2x = trace.ctrl2.x;
1400
- bestCtrl2y = trace.ctrl2.y;
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.ctrl1.x = origCtrl1x;
1403
- trace.ctrl1.y = origCtrl1y;
1404
- trace.ctrl2.x = origCtrl2x;
1405
- trace.ctrl2.y = origCtrl2y;
1417
+ trace.d1 = origD1;
1418
+ trace.d2 = origD2;
1419
+ this.updateControlPointsFromDistances(i);
1406
1420
  }
1407
- trace.ctrl1.x = bestCtrl1x;
1408
- trace.ctrl1.y = bestCtrl1y;
1409
- trace.ctrl2.x = bestCtrl2x;
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/curvy-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",