@mark1russell7/highways 0.9.5 → 0.9.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/build/index.js CHANGED
@@ -351,10 +351,20 @@ function portOffset(box, port) {
351
351
  along: port.along
352
352
  });
353
353
  }
354
- /** This function throws a `RangeError` when the `along` of a plug is bad: not finite, outside 0 to 1, without `side` or with `at`. */
354
+ /**
355
+ * This function throws a `RangeError` when the `along` of a plug is bad: not finite, outside 0 to 1, without `side` or with `at`.
356
+ * It also throws a `RangeError` for a bad `range` of a plug. A bad range has a value that is not finite or is outside 0 to 1, or a start after its end.
357
+ */
355
358
  function checkPort(node, port) {
356
- if (port.along === void 0) return;
357
359
  const at = `The plug "${port.id}" of the node "${node.id}"`;
360
+ if (port.range !== void 0) {
361
+ const r = port.range;
362
+ if (!Array.isArray(r) || r.length !== 2 || !r.every((v) => typeof v === "number" && Number.isFinite(v))) throw new RangeError(`${at} has the range ${JSON.stringify(r)}. It must be two finite numbers.`);
363
+ const [lo, hi] = r;
364
+ if (lo < 0 || hi > 1) throw new RangeError(`${at} has the range [${lo}, ${hi}]. It must be in the side, from 0 to 1.`);
365
+ if (lo > hi) throw new RangeError(`${at} has the range [${lo}, ${hi}]. Its start must not be after its end.`);
366
+ }
367
+ if (port.along === void 0) return;
358
368
  if (typeof port.along !== "number" || !Number.isFinite(port.along) || port.along < 0 || port.along > 1) throw new RangeError(`${at} has the along value ${String(port.along)}. It must be a finite number from 0 to 1.`);
359
369
  if (port.side === void 0) throw new RangeError(`${at} has an along value and no side. A plug with along needs a side.`);
360
370
  if (port.at !== void 0) throw new RangeError(`${at} has an along value and an at point. Give one of the two.`);
@@ -362,7 +372,7 @@ function checkPort(node, port) {
362
372
  /**
363
373
  * This function checks the plugs of a graph, and gives each plug with `side` and `along` a fixed `at`. The plug keeps its `side`.
364
374
  * Each router calls it at the start of `route`, so all routers agree. A graph without `along` comes back as the same object.
365
- * It throws a `RangeError` for a bad `along` (`checkPort`).
375
+ * It throws a `RangeError` for a bad `along` or a bad `range` (`checkPort`).
366
376
  */
367
377
  function resolvePorts(graph) {
368
378
  let found = false;
@@ -913,6 +923,115 @@ const CASES = [
913
923
  make: () => manyGroupsBoard()
914
924
  }
915
925
  ];
926
+ // src/plugs/range.ts
927
+ /** The tolerance of the comparisons of places along a side. */
928
+ const EPS$9 = 1e-9;
929
+ /** This function gives the axis along a side. */
930
+ function alongAxis(side) {
931
+ return side === "east" || side === "west" ? "y" : "x";
932
+ }
933
+ /** This function gives the port of an end, or `undefined` when the end has no port or the node has no such port. */
934
+ function portOfEnd(node, end) {
935
+ return end.port === void 0 ? void 0 : node.ports?.find((p) => p.id === end.port);
936
+ }
937
+ /**
938
+ * This function gives the interval of a plug with a range, on the axis along its side, or `undefined` for a port without a range.
939
+ * The interval is the range of the port, without the corner insets of the side.
940
+ * When the range is all in a corner inset, the interval is the range.
941
+ */
942
+ function rangeInterval(box, side, port, inset = 6) {
943
+ const range = port?.range;
944
+ if (range === void 0) return void 0;
945
+ const along = alongAxis(side);
946
+ const start = along === "y" ? box.y : box.x;
947
+ const len = along === "y" ? box.h : box.w;
948
+ const m = Math.min(inset, len / 4);
949
+ const lo = start + range[0] * len;
950
+ const hi = start + range[1] * len;
951
+ const a = Math.max(lo, start + m);
952
+ const b = Math.min(hi, start + len - m);
953
+ return a <= b ? [a, b] : [lo, hi];
954
+ }
955
+ /** This function moves a point of a side into the interval of its range, on the axis along the side. A point of a port without a range stays. */
956
+ function clampToRange(box, side, port, point, inset = 6) {
957
+ const r = rangeInterval(box, side, port, inset);
958
+ if (!r) return point;
959
+ const along = alongAxis(side);
960
+ const v = Math.max(r[0], Math.min(r[1], point[along]));
961
+ return along === "y" ? {
962
+ x: point.x,
963
+ y: v
964
+ } : {
965
+ x: v,
966
+ y: point.y
967
+ };
968
+ }
969
+ /**
970
+ * This function places `ideal.length` plugs of one side in their given order. The plug `i` stays in the interval from `lo[i]` to `hi[i]`.
971
+ * Two neighbours are at least `gap` apart. When the intervals are too short for the gap, the gap shrinks evenly to the largest gap that they let.
972
+ * The sum of the squares of the moves from the ideal places is the least.
973
+ *
974
+ * When the order cannot keep each plug in its interval, the plugs take the order of their ideal places in their intervals. Thus the intervals win over the order.
975
+ * The result is in the order of `ideal`.
976
+ */
977
+ function placeInIntervals(ideal, lo, hi, gap) {
978
+ const k = ideal.length;
979
+ if (k === 0) return [];
980
+ let order = ideal.map((_, i) => i);
981
+ let most = -Infinity;
982
+ let broken = false;
983
+ for (const i of order) {
984
+ if (hi[i] < most - EPS$9) broken = true;
985
+ most = Math.max(most, lo[i]);
986
+ }
987
+ if (broken) {
988
+ const c = order.map((i) => Math.max(lo[i], Math.min(hi[i], ideal[i])));
989
+ order = [...order].sort((a, b) => c[a] - c[b] || a - b);
990
+ }
991
+ let s = Math.max(0, gap);
992
+ for (let i = 1; i < k; i++) {
993
+ const hiI = hi[order[i]];
994
+ for (let j = 0; j < i; j++) s = Math.min(s, (hiI - lo[order[j]]) / (i - j));
995
+ }
996
+ s = Math.max(0, s);
997
+ const lower = new Array(k);
998
+ const upper = new Array(k);
999
+ let run = -Infinity;
1000
+ for (let i = 0; i < k; i++) lower[i] = run = Math.max(run, lo[order[i]] - i * s);
1001
+ run = Infinity;
1002
+ for (let i = k - 1; i >= 0; i--) upper[i] = run = Math.min(run, hi[order[i]] - i * s);
1003
+ const sum = [];
1004
+ const size = [];
1005
+ const blockLo = [];
1006
+ const blockHi = [];
1007
+ const value = [];
1008
+ const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
1009
+ for (let i = 0; i < k; i++) {
1010
+ sum.push(ideal[order[i]] - i * s);
1011
+ size.push(1);
1012
+ blockLo.push(lower[i]);
1013
+ blockHi.push(upper[i]);
1014
+ value.push(clamp(sum[sum.length - 1], lower[i], upper[i]));
1015
+ while (value.length > 1 && value[value.length - 2] > value[value.length - 1]) {
1016
+ const t = value.length - 1;
1017
+ sum[t - 1] += sum[t];
1018
+ size[t - 1] += size[t];
1019
+ blockLo[t - 1] = blockLo[t];
1020
+ value[t - 1] = clamp(sum[t - 1] / size[t - 1], blockLo[t - 1], blockHi[t - 1]);
1021
+ sum.pop();
1022
+ size.pop();
1023
+ blockLo.pop();
1024
+ blockHi.pop();
1025
+ value.pop();
1026
+ }
1027
+ }
1028
+ const out = new Array(k);
1029
+ let i = 0;
1030
+ value.forEach((v, b) => {
1031
+ for (let j = 0; j < size[b]; j++, i++) out[order[i]] = v + i * s;
1032
+ });
1033
+ return out;
1034
+ }
916
1035
  // src/plugs.ts
917
1036
  /** The outward normal of each side, in a frame with the y axis down. */
918
1037
  const SIDE_NORMAL = {
@@ -983,7 +1102,7 @@ function pointOnSide(box, side, toward, inset = 6) {
983
1102
  }
984
1103
  /**
985
1104
  * This function places the plug of one end of an edge. A fixed plug stays at its point. A plug with a side stays on that side.
986
- * The router places a free plug on the side that faces `toward`.
1105
+ * The router places a free plug on the side that faces `toward`. A free plug of a port with a `range` stays in that range (`clampToRange`).
987
1106
  */
988
1107
  function placePlug(node, end, toward) {
989
1108
  const fixed = fixedPortPoint(node, end);
@@ -993,8 +1112,9 @@ function placePlug(node, end, toward) {
993
1112
  normal: SIDE_NORMAL[port?.side ?? nearestSide(node.box, fixed)]
994
1113
  };
995
1114
  const side = port?.side ?? sideToward(node.box, toward);
1115
+ const point = pointOnSide(node.box, side, toward);
996
1116
  return {
997
- point: pointOnSide(node.box, side, toward),
1117
+ point: port?.range === void 0 ? point : clampToRange(node.box, side, port, point),
998
1118
  normal: SIDE_NORMAL[side]
999
1119
  };
1000
1120
  }
@@ -4953,6 +5073,7 @@ function isotonic(v) {
4953
5073
  * This function moves the free plugs of each side of each node, so that two of them are at least `gap` apart (`justifyPlugs`).
4954
5074
  * The plugs keep their order on the side. Two plugs at one place take the order of their other ends. A fixed plug does not move.
4955
5075
  * A plug stays `inset` from the corners of its side, or a quarter of the side when the side is short.
5076
+ * A plug of a port with a `range` stays in its range (PORT-RANGE), and the plugs keep their order and the gap (`placeInIntervals`).
4956
5077
  */
4957
5078
  function spacePlugs(ends, gap, inset = 6) {
4958
5079
  const sides = /* @__PURE__ */ new Map();
@@ -4985,18 +5106,21 @@ function spacePlugs(ends, gap, inset = 6) {
4985
5106
  side.items.push([
4986
5107
  i,
4987
5108
  k,
4988
- other.point
5109
+ other.point,
5110
+ portOfEnd(node, end)
4989
5111
  ]);
4990
5112
  }
4991
5113
  });
4992
5114
  for (const { node, normal, items } of sides.values()) {
4993
- if (items.length < 2) continue;
5115
+ const ranged = items.some((x) => x[3]?.range !== void 0);
5116
+ if (items.length < 2 && !ranged) continue;
4994
5117
  const along = normal.x !== 0 ? "y" : "x";
4995
5118
  const b = node.box;
4996
5119
  const start = along === "y" ? b.y : b.x;
4997
5120
  const len = along === "y" ? b.h : b.w;
4998
5121
  const m = Math.min(inset, len / 4);
4999
- const placed = justifyPlugs(items.map(([i, k]) => ends[i][k].point[along]), start + m, start + len - m, gap, (j) => items[j][2][along]);
5122
+ const ideal = items.map(([i, k]) => ends[i][k].point[along]);
5123
+ const placed = ranged ? spaceInRanges(b, normal, items, ideal, start + m, start + len - m, gap, inset) : justifyPlugs(ideal, start + m, start + len - m, gap, (j) => items[j][2][along]);
5000
5124
  items.forEach(([i, k], j) => {
5001
5125
  const old = ends[i][k];
5002
5126
  ends[i][k] = {
@@ -5013,6 +5137,28 @@ function spacePlugs(ends, gap, inset = 6) {
5013
5137
  }
5014
5138
  }
5015
5139
  /**
5140
+ * This function places the free plugs of one side with ranges (PORT-RANGE). The order is the order of `justifyPlugs`.
5141
+ * Each plug stays in the interval of its range, or in the room of the side without a range.
5142
+ */
5143
+ function spaceInRanges(box, normal, items, ideal, lo, hi, gap, inset) {
5144
+ const side = sideOfNormal(normal);
5145
+ const along = alongAxis(side);
5146
+ const order = ideal.map((_, i) => i).sort((a, b) => ideal[a] - ideal[b] || items[a][2][along] - items[b][2][along] || a - b);
5147
+ const span = order.map((j) => rangeInterval(box, side, items[j][3], inset) ?? [lo, hi]);
5148
+ const at = placeInIntervals(order.map((j) => ideal[j]), span.map((r) => r[0]), span.map((r) => r[1]), gap);
5149
+ const out = new Array(ideal.length);
5150
+ order.forEach((j, i) => {
5151
+ out[j] = at[i];
5152
+ });
5153
+ return out;
5154
+ }
5155
+ /** This function gives the side of an outward normal. */
5156
+ function sideOfNormal(n) {
5157
+ if (n.x > 0) return "east";
5158
+ if (n.x < 0) return "west";
5159
+ return n.y > 0 ? "south" : "north";
5160
+ }
5161
+ /**
5016
5162
  * This function gives the plugs of the plug strategy `justify` (WILD-002): the plugs of `facing`, then the gaps of `spacePlugs`.
5017
5163
  * The router `corners` with the option `plugGap` gives the same plugs.
5018
5164
  */
@@ -5048,7 +5194,8 @@ function justifyEnds(input, options = {}) {
5048
5194
  /** This function gives the free side of an end, or `undefined` when the plug of the end is fixed. */
5049
5195
  function freeSide(node, end, toward, inset) {
5050
5196
  if (fixedPortPoint(node, end)) return void 0;
5051
- const side = (end.port === void 0 ? void 0 : node.ports?.find((p) => p.id === end.port))?.side ?? sideToward(node.box, toward);
5197
+ const port = end.port === void 0 ? void 0 : node.ports?.find((p) => p.id === end.port);
5198
+ const side = port?.side ?? sideToward(node.box, toward);
5052
5199
  const b = node.box;
5053
5200
  const along = side === "east" || side === "west" ? "y" : "x";
5054
5201
  const start = along === "y" ? b.y : b.x;
@@ -5056,6 +5203,23 @@ function freeSide(node, end, toward, inset) {
5056
5203
  const m = Math.min(inset, len / 4);
5057
5204
  const at = side === "east" ? b.x + b.w : side === "west" ? b.x : side === "south" ? b.y + b.h : b.y;
5058
5205
  const point = pointOnSide(b, side, toward, inset);
5206
+ const range = port?.range === void 0 ? void 0 : rangeInterval(b, side, port, inset);
5207
+ if (range) {
5208
+ const facing = clampToRange(b, side, port, point, inset);
5209
+ return {
5210
+ node,
5211
+ side,
5212
+ normal: SIDE_NORMAL[side],
5213
+ along,
5214
+ at,
5215
+ lo: range[0],
5216
+ hi: range[1],
5217
+ facing: {
5218
+ point: facing,
5219
+ normal: SIDE_NORMAL[side]
5220
+ }
5221
+ };
5222
+ }
5059
5223
  return {
5060
5224
  node,
5061
5225
  side,
@@ -5215,7 +5379,7 @@ function sideEnds(input, options = {}) {
5215
5379
  // src/routers/gutter.ts
5216
5380
  /** The default cost of one bend of the gutter mode. */
5217
5381
  const GUTTER_BEND_COST = 16;
5218
- const EPS$7 = 1e-6;
5382
+ const EPS$8 = 1e-6;
5219
5383
  /** The directions of a step: east, south, west and north, with the y axis down. */
5220
5384
  const DX = [
5221
5385
  1,
@@ -5239,7 +5403,7 @@ const DY = [
5239
5403
  */
5240
5404
  function gutterRoutes(frame, nodes, edges, options = {}) {
5241
5405
  const bend = options.bendCost ?? 16;
5242
- const boxes = nodes.map((n) => n.box).filter((b) => b.w > EPS$7 && b.h > EPS$7);
5406
+ const boxes = nodes.map((n) => n.box).filter((b) => b.w > EPS$8 && b.h > EPS$8);
5243
5407
  const entries = edges.map(entriesOf);
5244
5408
  const xs = /* @__PURE__ */ new Set();
5245
5409
  const ys = /* @__PURE__ */ new Set();
@@ -5257,7 +5421,7 @@ function gutterRoutes(frame, nodes, edges, options = {}) {
5257
5421
  const cuts = [lo, lo + size];
5258
5422
  for (const b of boxes) for (const v of key === "x" ? [b.x, b.x + b.w] : [b.y, b.y + b.h]) if (v > lo && v < lo + size) cuts.push(v);
5259
5423
  cuts.sort((a, b) => a - b);
5260
- for (let i = 1; i < cuts.length; i++) if (cuts[i] - cuts[i - 1] > EPS$7) lines.add((cuts[i] + cuts[i - 1]) / 2);
5424
+ for (let i = 1; i < cuts.length; i++) if (cuts[i] - cuts[i - 1] > EPS$8) lines.add((cuts[i] + cuts[i - 1]) / 2);
5261
5425
  }
5262
5426
  edges.forEach((e, i) => {
5263
5427
  for (const { point, normal } of [{
@@ -5320,19 +5484,19 @@ function stubOnly(e, boxes) {
5320
5484
  const n = e.plug.normal;
5321
5485
  let q;
5322
5486
  if (n.x !== 0) {
5323
- if (!(p.y > b.y + EPS$7 && p.y < b.y + b.h - EPS$7)) return void 0;
5487
+ if (!(p.y > b.y + EPS$8 && p.y < b.y + b.h - EPS$8)) return void 0;
5324
5488
  q = {
5325
5489
  x: n.x > 0 ? b.x + b.w : b.x,
5326
5490
  y: p.y
5327
5491
  };
5328
- if ((q.x - p.x) * n.x > EPS$7) return void 0;
5492
+ if ((q.x - p.x) * n.x > EPS$8) return void 0;
5329
5493
  } else {
5330
- if (!(p.x > b.x + EPS$7 && p.x < b.x + b.w - EPS$7)) return void 0;
5494
+ if (!(p.x > b.x + EPS$8 && p.x < b.x + b.w - EPS$8)) return void 0;
5331
5495
  q = {
5332
5496
  x: p.x,
5333
5497
  y: n.y > 0 ? b.y + b.h : b.y
5334
5498
  };
5335
- if ((q.y - p.y) * n.y > EPS$7) return void 0;
5499
+ if ((q.y - p.y) * n.y > EPS$8) return void 0;
5336
5500
  }
5337
5501
  for (const o of boxes) if (o !== b && blocks(p, q, o)) return void 0;
5338
5502
  return [p, q];
@@ -5340,11 +5504,11 @@ function stubOnly(e, boxes) {
5340
5504
  /** This function tells if the axis-parallel segment `ab` meets the closed box `o` along a part longer than a tiny length. A segment along a side meets the box. */
5341
5505
  function blocks(a, b, o) {
5342
5506
  if (a.y === b.y) {
5343
- if (a.y < o.y - EPS$7 || a.y > o.y + o.h + EPS$7) return false;
5344
- return Math.min(Math.max(a.x, b.x), o.x + o.w) - Math.max(Math.min(a.x, b.x), o.x) > EPS$7;
5507
+ if (a.y < o.y - EPS$8 || a.y > o.y + o.h + EPS$8) return false;
5508
+ return Math.min(Math.max(a.x, b.x), o.x + o.w) - Math.max(Math.min(a.x, b.x), o.x) > EPS$8;
5345
5509
  }
5346
- if (a.x < o.x - EPS$7 || a.x > o.x + o.w + EPS$7) return false;
5347
- return Math.min(Math.max(a.y, b.y), o.y + o.h) - Math.max(Math.min(a.y, b.y), o.y) > EPS$7;
5510
+ if (a.x < o.x - EPS$8 || a.x > o.x + o.w + EPS$8) return false;
5511
+ return Math.min(Math.max(a.y, b.y), o.y + o.h) - Math.max(Math.min(a.y, b.y), o.y) > EPS$8;
5348
5512
  }
5349
5513
  /** This function gives the direction index of a unit normal: 0 east, 1 south, 2 west, 3 north. */
5350
5514
  function dirOf(n) {
@@ -5363,12 +5527,12 @@ var Grid$1 = class {
5363
5527
  this.#free = new Uint8Array(xs.length * ys.length);
5364
5528
  for (let i = 0; i < ys.length; i++) {
5365
5529
  const y = ys[i];
5366
- if (y <= frame.y + EPS$7 || y >= frame.y + frame.h - EPS$7) continue;
5530
+ if (y <= frame.y + EPS$8 || y >= frame.y + frame.h - EPS$8) continue;
5367
5531
  for (let j = 0; j < xs.length; j++) {
5368
5532
  const x = xs[j];
5369
- if (x <= frame.x + EPS$7 || x >= frame.x + frame.w - EPS$7) continue;
5533
+ if (x <= frame.x + EPS$8 || x >= frame.x + frame.w - EPS$8) continue;
5370
5534
  let free = true;
5371
- for (const b of boxes) if (x >= b.x - EPS$7 && x <= b.x + b.w + EPS$7 && y >= b.y - EPS$7 && y <= b.y + b.h + EPS$7) {
5535
+ for (const b of boxes) if (x >= b.x - EPS$8 && x <= b.x + b.w + EPS$8 && y >= b.y - EPS$8 && y <= b.y + b.h + EPS$8) {
5372
5536
  free = false;
5373
5537
  break;
5374
5538
  }
@@ -5405,11 +5569,11 @@ var Grid$1 = class {
5405
5569
  const sign = d < 2 ? 1 : -1;
5406
5570
  let k = -1;
5407
5571
  if (sign > 0) {
5408
- for (let m = 0; m < list.length; m++) if (list[m] > v + EPS$7) {
5572
+ for (let m = 0; m < list.length; m++) if (list[m] > v + EPS$8) {
5409
5573
  k = m;
5410
5574
  break;
5411
5575
  }
5412
- } else for (let m = list.length - 1; m >= 0; m--) if (list[m] < v - EPS$7) {
5576
+ } else for (let m = list.length - 1; m >= 0; m--) if (list[m] < v - EPS$8) {
5413
5577
  k = m;
5414
5578
  break;
5415
5579
  }
@@ -5608,7 +5772,7 @@ function lanes(frame, boxes, edges, entries, routes, laneGap) {
5608
5772
  const b = legs[j];
5609
5773
  if (a.route === b.route || a.horizontal !== b.horizontal) continue;
5610
5774
  if (Math.min(a.s1, b.s1) - Math.max(a.s0, b.s0) <= -laneGap) continue;
5611
- if (a.across < b.lo - EPS$7 || a.across > b.hi + EPS$7 || b.across < a.lo - EPS$7 || b.across > a.hi + EPS$7) continue;
5775
+ if (a.across < b.lo - EPS$8 || a.across > b.hi + EPS$8 || b.across < a.lo - EPS$8 || b.across > a.hi + EPS$8) continue;
5612
5776
  parent[root(i)] = root(j);
5613
5777
  }
5614
5778
  const groups = /* @__PURE__ */ new Map();
@@ -5648,9 +5812,9 @@ function free(frame, boxes, horizontal, across, s0, s1) {
5648
5812
  b.x,
5649
5813
  b.w
5650
5814
  ];
5651
- if (Math.min(s1, bs + bl) - Math.max(s0, bs) <= EPS$7) continue;
5652
- if (cs + cl <= across + EPS$7) lo = Math.max(lo, cs + cl);
5653
- if (cs >= across - EPS$7) hi = Math.min(hi, cs);
5815
+ if (Math.min(s1, bs + bl) - Math.max(s0, bs) <= EPS$8) continue;
5816
+ if (cs + cl <= across + EPS$8) lo = Math.max(lo, cs + cl);
5817
+ if (cs >= across - EPS$8) hi = Math.min(hi, cs);
5654
5818
  }
5655
5819
  return [lo, hi];
5656
5820
  }
@@ -5661,8 +5825,8 @@ function want(a, b) {
5661
5825
  if (inner(sb, sa)) return -tb;
5662
5826
  return ta === tb ? 0 : Math.sign(ta - tb);
5663
5827
  };
5664
- const left = side(a.s0, b.s0, a.t0, b.t0, (x, y) => x > y + EPS$7);
5665
- const right = side(a.s1, b.s1, a.t1, b.t1, (x, y) => x < y - EPS$7);
5828
+ const left = side(a.s0, b.s0, a.t0, b.t0, (x, y) => x > y + EPS$8);
5829
+ const right = side(a.s1, b.s1, a.t1, b.t1, (x, y) => x < y - EPS$8);
5666
5830
  if (left !== 0 && right !== 0 && left !== right) return left;
5667
5831
  return left || right;
5668
5832
  }
@@ -5680,7 +5844,7 @@ function place(group, laneGap) {
5680
5844
  const a = group[i];
5681
5845
  const b = group[j];
5682
5846
  if (a.pinned && b.pinned) {
5683
- if (Math.abs(a.across - b.across) <= EPS$7) continue;
5847
+ if (Math.abs(a.across - b.across) <= EPS$8) continue;
5684
5848
  const [x, y] = a.across < b.across ? [i, j] : [j, i];
5685
5849
  hard[x].push(y);
5686
5850
  hardIn[y]++;
@@ -5698,7 +5862,7 @@ function place(group, laneGap) {
5698
5862
  let best = -1;
5699
5863
  for (let i = 0; i < n; i++) {
5700
5864
  if (done[i] || hardIn[i] > 0) continue;
5701
- if (best < 0 || softIn[i] < softIn[best] || softIn[i] === softIn[best] && group[i].across < group[best].across - EPS$7) best = i;
5865
+ if (best < 0 || softIn[i] < softIn[best] || softIn[i] === softIn[best] && group[i].across < group[best].across - EPS$8) best = i;
5702
5866
  }
5703
5867
  done[best] = true;
5704
5868
  order.push(best);
@@ -5711,7 +5875,7 @@ function place(group, laneGap) {
5711
5875
  let to = k + 1;
5712
5876
  for (let m = k + 1; m < order.length; m++) {
5713
5877
  const b = group[order[m]];
5714
- if (b.pinned && Math.abs(b.across - a.across) <= EPS$7) order.splice(to++, 0, ...order.splice(m, 1));
5878
+ if (b.pinned && Math.abs(b.across - a.across) <= EPS$8) order.splice(to++, 0, ...order.splice(m, 1));
5715
5879
  }
5716
5880
  k = to - 1;
5717
5881
  }
@@ -5731,7 +5895,7 @@ function place(group, laneGap) {
5731
5895
  const width = (run.length - 1) * s;
5732
5896
  const mean = run.reduce((t, r) => t + group[r].across, 0) / run.length;
5733
5897
  const first = Math.min(Math.max(mean - width / 2, lo + s), hi - s - width);
5734
- run.forEach((r, m) => at[r] = room > EPS$7 && s > 0 ? first + m * s : group[r].across);
5898
+ run.forEach((r, m) => at[r] = room > EPS$8 && s > 0 ? first + m * s : group[r].across);
5735
5899
  }
5736
5900
  if (k < n) at[i] = group[i].across;
5737
5901
  start = k + 1;
@@ -5741,7 +5905,7 @@ function place(group, laneGap) {
5741
5905
  }
5742
5906
  // src/routers/frames.ts
5743
5907
  /** The tiny gap between the bounds and the band of a wall, so that a way along the bounds stays clear of the wall. */
5744
- const EPS$6 = 1e-6;
5908
+ const EPS$7 = 1e-6;
5745
5909
  /**
5746
5910
  * This function gives the inner routes of the inner edges (`splitFrames`). Each route stays in the bounds of its frame:
5747
5911
  * the box of the frame, smaller by `frameInset`. It goes around the other nodes of the frame.
@@ -5866,7 +6030,7 @@ function makeRoom$1(f, chain, children, byId, inset, options) {
5866
6030
  const margin = options.margin ?? 5;
5867
6031
  const bounds = shrink(f.box, inset);
5868
6032
  const nodes = roomNodes(f, chain, children, byId, bounds, options);
5869
- const w = shrink(bounds, -(margin / 2 + EPS$6));
6033
+ const w = shrink(bounds, -(margin / 2 + EPS$7));
5870
6034
  const t = 2 * margin + 1;
5871
6035
  const walls = [
5872
6036
  {
@@ -6225,7 +6389,7 @@ const LEVEL_LINK_DEFAULTS = Object.freeze({
6225
6389
  plugInset: 6,
6226
6390
  throughCost: 8
6227
6391
  });
6228
- const EPS$5 = 1e-6;
6392
+ const EPS$6 = 1e-6;
6229
6393
  /**
6230
6394
  * This function removes the repeated points, the points in the middle of a straight run,
6231
6395
  * and the points where the way turns back on itself.
@@ -6240,15 +6404,15 @@ function tidy$1(p) {
6240
6404
  function tidyPoints(p) {
6241
6405
  const out = [];
6242
6406
  for (const q of p) {
6243
- if (out.length > 0 && dist(q, out[out.length - 1]) <= EPS$5) continue;
6407
+ if (out.length > 0 && dist(q, out[out.length - 1]) <= EPS$6) continue;
6244
6408
  while (out.length >= 2) {
6245
6409
  const a = out[out.length - 2];
6246
6410
  const b = out[out.length - 1];
6247
6411
  const cross = (b.x - a.x) * (q.y - b.y) - (b.y - a.y) * (q.x - b.x);
6248
- if (Math.abs(cross) > EPS$5 * (1 + dist(a, b) * dist(b, q))) break;
6412
+ if (Math.abs(cross) > EPS$6 * (1 + dist(a, b) * dist(b, q))) break;
6249
6413
  out.pop();
6250
6414
  }
6251
- if (out.length > 0 && dist(q, out[out.length - 1]) <= EPS$5) continue;
6415
+ if (out.length > 0 && dist(q, out[out.length - 1]) <= EPS$6) continue;
6252
6416
  out.push(q);
6253
6417
  }
6254
6418
  return out;
@@ -6621,7 +6785,7 @@ function freeSpans(lo, hi, covered) {
6621
6785
  if (x >= hi) break;
6622
6786
  }
6623
6787
  if (x < hi) out.push([x, hi]);
6624
- return out.filter(([a, b]) => b - a > EPS$5);
6788
+ return out.filter(([a, b]) => b - a > EPS$6);
6625
6789
  }
6626
6790
  /**
6627
6791
  * This function gives the shortest way between the free plugs of the two nodes, or `undefined` when the second corner graph has none.
@@ -6753,7 +6917,7 @@ function squarePull(p, obstacles, pad, first, last, lookahead = 16) {
6753
6917
  out.push(...step.legs);
6754
6918
  const end = out[out.length - 1];
6755
6919
  const before = out[out.length - 2];
6756
- axis = Math.abs(end.y - before.y) < EPS$5 ? "x" : Math.abs(end.x - before.x) < EPS$5 ? "y" : void 0;
6920
+ axis = Math.abs(end.y - before.y) < EPS$6 ? "x" : Math.abs(end.x - before.x) < EPS$6 ? "y" : void 0;
6757
6921
  i = step.j;
6758
6922
  }
6759
6923
  return out;
@@ -6763,7 +6927,7 @@ function squarePull(p, obstacles, pad, first, last, lookahead = 16) {
6763
6927
  * Of two clear corners, it picks the corner that continues the axis `from` and arrives along the axis `to`.
6764
6928
  */
6765
6929
  function square(a, b, obstacles, pad, from, to) {
6766
- if (Math.abs(a.x - b.x) < EPS$5 || Math.abs(a.y - b.y) < EPS$5) return obstacles.clear(a, b, pad) ? [b] : void 0;
6930
+ if (Math.abs(a.x - b.x) < EPS$6 || Math.abs(a.y - b.y) < EPS$6) return obstacles.clear(a, b, pad) ? [b] : void 0;
6767
6931
  const options = [{
6768
6932
  c: {
6769
6933
  x: b.x,
@@ -9421,7 +9585,7 @@ const ADAM_SCHEMA = {
9421
9585
  /** The moment factors of Adam (Kingma and Ba 2015), as the record gives them. */
9422
9586
  const BETA1 = .9;
9423
9587
  const BETA2 = .999;
9424
- const EPS$4 = 1e-8;
9588
+ const EPS$5 = 1e-8;
9425
9589
  /** The weight of the term that holds a leg near its first place. */
9426
9590
  const HOLD = .05;
9427
9591
  /** This function gives the point of the segment `ab` nearest to `p`. */
@@ -9642,7 +9806,7 @@ function adamRoutes(input, result, options = {}) {
9642
9806
  m2[i] = BETA2 * m2[i] + .0010000000000000009 * g * g;
9643
9807
  const mh = m1[i] / (1 - b1);
9644
9808
  const vh = m2[i] / (1 - b2);
9645
- shift[i] = Math.max(-2 * c, Math.min(2 * c, shift[i] - eta * mh / (Math.sqrt(vh) + EPS$4)));
9809
+ shift[i] = Math.max(-2 * c, Math.min(2 * c, shift[i] - eta * mh / (Math.sqrt(vh) + EPS$5)));
9646
9810
  }
9647
9811
  }
9648
9812
  const moved = new Set(legs.map((l) => l.route));
@@ -10704,7 +10868,7 @@ const LANE_TRACKS = [
10704
10868
  "left-edge",
10705
10869
  "free"
10706
10870
  ];
10707
- const EPS$3 = 1e-6;
10871
+ const EPS$4 = 1e-6;
10708
10872
  const ROUNDS$1 = 8;
10709
10873
  /**
10710
10874
  * This function gives each link that runs along a gap its own lane in the gap. The lanes of a gap are `spacing` apart, around its middle line.
@@ -10729,7 +10893,7 @@ function laneGaps(items, links, options) {
10729
10893
  })));
10730
10894
  const counts = [];
10731
10895
  for (const g of boardGaps(items, options.snap)) {
10732
- if (g.width <= EPS$3) continue;
10896
+ if (g.width <= EPS$4) continue;
10733
10897
  const across = g.axis;
10734
10898
  const along = g.axis === "x" ? "y" : "x";
10735
10899
  const lo = g.at - g.width;
@@ -10740,8 +10904,8 @@ function laneGaps(items, links, options) {
10740
10904
  for (let i = 2; i <= p.length - 2; i++) {
10741
10905
  const a = p[i - 1];
10742
10906
  const b = p[i];
10743
- if (Math.abs(a[across] - b[across]) > EPS$3 || a[across] <= lo + EPS$3 || a[across] >= hi - EPS$3) continue;
10744
- if (Math.abs(a[along] - b[along]) <= EPS$3) continue;
10907
+ if (Math.abs(a[across] - b[across]) > EPS$4 || a[across] <= lo + EPS$4 || a[across] >= hi - EPS$4) continue;
10908
+ if (Math.abs(a[along] - b[along]) <= EPS$4) continue;
10745
10909
  const side = (q, r) => Math.sign(r[across] - q[across]);
10746
10910
  pieces.push({
10747
10911
  link: li,
@@ -10817,7 +10981,7 @@ function leftEdgeLanes(list, constrained) {
10817
10981
  const now = [];
10818
10982
  let end = -Infinity;
10819
10983
  for (const i of byStart) {
10820
- if (lane[i] >= 0 || wait[i] > 0 || list[i].lo < end - EPS$3) continue;
10984
+ if (lane[i] >= 0 || wait[i] > 0 || list[i].lo < end - EPS$4) continue;
10821
10985
  now.push(i);
10822
10986
  end = list[i].hi;
10823
10987
  }
@@ -10840,15 +11004,15 @@ function density(list) {
10840
11004
  }
10841
11005
  return most;
10842
11006
  }
10843
- const overlaps = (P, Q) => Math.min(P.hi, Q.hi) - Math.max(P.lo, Q.lo) > EPS$3;
11007
+ const overlaps = (P, Q) => Math.min(P.hi, Q.hi) - Math.max(P.lo, Q.lo) > EPS$4;
10844
11008
  /**
10845
11009
  * This function counts the crossings when the piece `P` is on the near side of the piece `Q`.
10846
11010
  * A turn of `P` to the far side crosses `Q` when it is inside the span of `Q`. A turn of `Q` to the near side crosses `P` in the same way.
10847
11011
  */
10848
11012
  function cost$1(P, Q) {
10849
11013
  let c = 0;
10850
- for (const [y, s] of P.turns) if (s > 0 && y > Q.lo + EPS$3 && y < Q.hi - EPS$3) c++;
10851
- for (const [y, s] of Q.turns) if (s < 0 && y > P.lo + EPS$3 && y < P.hi - EPS$3) c++;
11014
+ for (const [y, s] of P.turns) if (s > 0 && y > Q.lo + EPS$4 && y < Q.hi - EPS$4) c++;
11015
+ for (const [y, s] of Q.turns) if (s < 0 && y > P.lo + EPS$4 && y < P.hi - EPS$4) c++;
10852
11016
  return c;
10853
11017
  }
10854
11018
  /** This function gives the pieces of one gap in lane order, from the near side to the far side. */
@@ -11052,17 +11216,10 @@ function facingEnds(input) {
11052
11216
  }
11053
11217
  return out;
11054
11218
  }
11055
- // src/plugs/spread/index.ts
11219
+ // src/plugs/walls.ts
11056
11220
  /**
11057
- * The plug strategy `spread` (PLUG-SPREAD): the free plugs of each wall share the room of that wall with a minimum gap.
11058
- * The order on the wall keeps the order of the far ends, so two routes do not cross at the wall. No plug drops.
11221
+ * The walls of the plug strategies `spread`, `leaders` and `assign`: one side of one node, its room, and the order of its far ends.
11059
11222
  */
11060
- /** The defaults of the plug strategy `spread`. */
11061
- const SPREAD_DEFAULTS = Object.freeze({
11062
- plugGap: 8,
11063
- inset: 6,
11064
- rounds: 2
11065
- });
11066
11223
  /** This function gives the axis along a side. */
11067
11224
  function alongOf(side) {
11068
11225
  return side === "east" || side === "west" ? "y" : "x";
@@ -11111,6 +11268,17 @@ function keyOf$1(box, side, far) {
11111
11268
  if (out >= 0) return p;
11112
11269
  return along >= 0 ? 2 - p : -2 - p;
11113
11270
  }
11271
+ // src/plugs/spread/index.ts
11272
+ /**
11273
+ * The plug strategy `spread` (PLUG-SPREAD): the free plugs of each wall share the room of that wall with a minimum gap.
11274
+ * The order on the wall keeps the order of the far ends, so two routes do not cross at the wall. No plug drops.
11275
+ */
11276
+ /** The defaults of the plug strategy `spread`. */
11277
+ const SPREAD_DEFAULTS = Object.freeze({
11278
+ plugGap: 8,
11279
+ inset: 6,
11280
+ rounds: 2
11281
+ });
11114
11282
  /**
11115
11283
  * This function gives the sort key of a far point for a po-leader (EDA-041): its place on the axis along the wall.
11116
11284
  * A far point behind the wall gets the key of `keyOf` with a large shift, so it goes to an end of the wall.
@@ -11156,6 +11324,7 @@ function placeWall(slots, fixed, lo, hi, gap, idealOf = (s) => s.ideal) {
11156
11324
  part = Math.max(part, p);
11157
11325
  parts[part].push(s);
11158
11326
  }
11327
+ const ranged = slots.some((s) => s.range !== void 0);
11159
11328
  parts.forEach((list, p) => {
11160
11329
  if (list.length === 0) return;
11161
11330
  const a = ends[p];
@@ -11166,6 +11335,21 @@ function placeWall(slots, fixed, lo, hi, gap, idealOf = (s) => s.ideal) {
11166
11335
  const s = steps > 0 ? Math.max(0, Math.min(gap, (b - a) / steps)) : gap;
11167
11336
  const lower = a + fa * s;
11168
11337
  const upper = Math.max(lower, b - fb * s);
11338
+ if (ranged) {
11339
+ const span = list.map((x) => {
11340
+ if (!x.range) return [lower, upper];
11341
+ const lo = Math.max(lower, x.range[0]);
11342
+ const hi = Math.min(upper, x.range[1]);
11343
+ if (lo <= hi) return [lo, hi];
11344
+ const v = x.range[1] < lower ? lower : upper;
11345
+ return [v, v];
11346
+ });
11347
+ const at = placeInIntervals(list.map((x) => idealOf(x)), span.map((r) => r[0]), span.map((r) => r[1]), s);
11348
+ list.forEach((x, i) => {
11349
+ x.at = at[i];
11350
+ });
11351
+ return;
11352
+ }
11169
11353
  const at = placeInOrder(list.map((x) => idealOf(x)), lower, upper, s);
11170
11354
  list.forEach((x, i) => {
11171
11355
  x.at = at[i];
@@ -11226,12 +11410,14 @@ function spreadWith(input, options = {}, leader) {
11226
11410
  if (slots.has(key)) return key;
11227
11411
  const side = port?.side ?? sideToward(node.box, toward);
11228
11412
  const ideal = placePlug(node, end, toward).point[alongOf(side)];
11413
+ const range = rangeInterval(node.box, side, port, inset);
11229
11414
  const slot = {
11230
11415
  node,
11231
11416
  side,
11232
11417
  ideal,
11233
11418
  rank: [port ? node.ports.indexOf(port) : -1, slots.size],
11234
- at: ideal
11419
+ at: ideal,
11420
+ ...range ? { range } : {}
11235
11421
  };
11236
11422
  slots.set(key, slot);
11237
11423
  wallOf(node, side).slots.push(slot);
@@ -11280,6 +11466,467 @@ function spreadWith(input, options = {}, leader) {
11280
11466
  target: resolve(x.target)
11281
11467
  }));
11282
11468
  }
11469
+ // src/plugs/assign/index.ts
11470
+ /**
11471
+ * The plug strategy `assign` (PLUG-ASSIGN): the plugs of each wall go to the intervals of their ends, with a least gap.
11472
+ * An interval is the range of the port (PORT-RANGE), or the room of the wall. The assignment is exact for the count of the ends in their intervals.
11473
+ */
11474
+ /** The defaults of the plug strategy `assign`. */
11475
+ const ASSIGN_DEFAULTS = Object.freeze({
11476
+ plugGap: 8,
11477
+ inset: 6
11478
+ });
11479
+ /** The tolerance of the comparisons of places along a wall. */
11480
+ const EPS$3 = 1e-9;
11481
+ /** This function gives the index of the first value of the sorted list `list` that is `v` or more. */
11482
+ function lowerBound(list, v) {
11483
+ let a = 0;
11484
+ let b = list.length;
11485
+ while (a < b) {
11486
+ const mid = a + b >> 1;
11487
+ if (list[mid] < v) a = mid + 1;
11488
+ else b = mid;
11489
+ }
11490
+ return a;
11491
+ }
11492
+ /**
11493
+ * This function moves the place `p` out of the zones of the fixed plugs: the open intervals of the width `2·s` around each fixed plug.
11494
+ * The list `fixed` is sorted. The place only moves up.
11495
+ */
11496
+ function pushOut(p, fixed, s) {
11497
+ for (const f of fixed) if (p > f - s + EPS$3 && p < f + s - EPS$3) p = f + s;
11498
+ return p;
11499
+ }
11500
+ /**
11501
+ * This function gives the gap of a wall with `n` plugs: `gap`, or the largest gap that lets all the plugs go in the room.
11502
+ * The plugs keep the gap to the fixed plugs too.
11503
+ */
11504
+ function wallGap(n, fixed, lo, hi, gap) {
11505
+ if (n <= 1) return gap;
11506
+ if (fixed.length === 0) return Math.max(0, Math.min(gap, (hi - lo) / (n - 1)));
11507
+ const fits = (s) => {
11508
+ let p = lo;
11509
+ for (let c = 0; c < n; c++) {
11510
+ p = pushOut(p, fixed, s);
11511
+ if (p > hi + EPS$3) return false;
11512
+ p += s;
11513
+ }
11514
+ return true;
11515
+ };
11516
+ if (fits(gap)) return gap;
11517
+ let a = 0;
11518
+ let b = gap;
11519
+ for (let i = 0; i < 60; i++) {
11520
+ const mid = (a + b) / 2;
11521
+ if (fits(mid)) a = mid;
11522
+ else b = mid;
11523
+ }
11524
+ return Math.max(0, a - 1e-7);
11525
+ }
11526
+ /**
11527
+ * This function gives the plan of the ends of one wall in the order of their keys, with no crossing at the wall.
11528
+ * The plan has the most ends in their intervals that this order lets. The answer is exact for this order: a table of the least last place
11529
+ * for each count of ends in their intervals.
11530
+ */
11531
+ function keyOrderPlan(order, win, fixed, lo, hi, s) {
11532
+ const n = order.length;
11533
+ const nf = fixed.length;
11534
+ const at = (i, j, c) => (i * (nf + 1) + j) * (n + 1) + c;
11535
+ const best = new Float64Array((n + 1) * (nf + 1) * (n + 1)).fill(Infinity);
11536
+ const step = new Int8Array(best.length);
11537
+ best[at(0, 0, 0)] = -Infinity;
11538
+ const relax = (k, v, how) => {
11539
+ if (v < best[k]) {
11540
+ best[k] = v;
11541
+ step[k] = how;
11542
+ }
11543
+ };
11544
+ for (let i = 0; i <= n; i++) for (let j = 0; j <= nf; j++) for (let c = 0; c <= i; c++) {
11545
+ const v = best[at(i, j, c)];
11546
+ if (v === Infinity) continue;
11547
+ const next = v === -Infinity ? -Infinity : v + s;
11548
+ if (j < nf && (v === -Infinity || v + s <= fixed[j] + EPS$3)) relax(at(i, j + 1, c), fixed[j], 0);
11549
+ if (i < n) {
11550
+ const w = win[order[i]];
11551
+ if (w) {
11552
+ const p = Math.max(w[0], next);
11553
+ if (p <= w[1] + EPS$3) relax(at(i + 1, j, c + 1), p, 1);
11554
+ }
11555
+ const q = Math.max(lo, next);
11556
+ if (q <= hi + EPS$3) relax(at(i + 1, j, c), q, 2);
11557
+ }
11558
+ }
11559
+ let c = n;
11560
+ while (c >= 0 && best[at(n, nf, c)] === Infinity) c--;
11561
+ if (c < 0) return void 0;
11562
+ const items = [];
11563
+ let i = n;
11564
+ let j = nf;
11565
+ while (i > 0 || j > 0) {
11566
+ const k = at(i, j, c);
11567
+ const how = step[k];
11568
+ if (how === 0) items.push({
11569
+ end: -1,
11570
+ anchored: true,
11571
+ at: fixed[--j]
11572
+ });
11573
+ else {
11574
+ items.push({
11575
+ end: order[--i],
11576
+ anchored: how === 1,
11577
+ at: best[k]
11578
+ });
11579
+ if (how === 1) c--;
11580
+ }
11581
+ }
11582
+ return items.reverse();
11583
+ }
11584
+ /**
11585
+ * This function gives the most ends of a wall that can have their plug in their interval, in any order. It also gives one plan with that count.
11586
+ * Each end without its plug in its interval still gets a plug in the room of the wall: a free plug.
11587
+ *
11588
+ * The plan comes from two facts. First, a plan can move each plug down until it touches its interval, the room, a fixed zone or the plug before it.
11589
+ * Thus each plug is at a start place plus a whole number of gaps. Second, a swap of two plugs makes the last plug of the plan the end with the latest interval end.
11590
+ * Then the ends with an interval start up to its place are before it, and the others after it. This split gives a table over the ends and two places (the recursion `count`).
11591
+ * The free plugs have the room as interval and the latest end. Thus they split the wall into parts, and a second table chains the parts.
11592
+ */
11593
+ function bestPlan(order, win, ideals, fixed, lo, hi, s) {
11594
+ const n = order.length;
11595
+ const jobs = order.filter((e) => win[e] !== void 0).sort((a, b) => win[a][1] - win[b][1] || win[a][0] - win[b][0] || a - b);
11596
+ const r = jobs.length;
11597
+ const allowed = (t) => t >= lo - EPS$3 && t <= hi + EPS$3 && fixed.every((f) => t <= f - s + EPS$3 || t >= f + s - EPS$3);
11598
+ const starts = [
11599
+ lo,
11600
+ ...jobs.map((e) => win[e][0]),
11601
+ ...fixed.map((f) => f + s)
11602
+ ];
11603
+ const raw = [];
11604
+ for (const b of starts) for (let q = 0; q <= n; q++) {
11605
+ const t = b + q * s;
11606
+ if (t > hi + EPS$3) break;
11607
+ if (allowed(t)) raw.push(t);
11608
+ if (s === 0) break;
11609
+ }
11610
+ raw.sort((a, b) => a - b);
11611
+ const T = [];
11612
+ for (const t of raw) if (T.length === 0 || t - T[T.length - 1] > EPS$3) T.push(t);
11613
+ const m = T.length;
11614
+ const id = (k, ui, vi) => (k * (m + 2) + ui + 1) * (m + 2) + vi + 1;
11615
+ const memo = /* @__PURE__ */ new Map();
11616
+ const pick = /* @__PURE__ */ new Map();
11617
+ const count = (k, ui, vi) => {
11618
+ if (k === 0) return 0;
11619
+ const key = id(k, ui, vi);
11620
+ const got = memo.get(key);
11621
+ if (got !== void 0) return got;
11622
+ const [a, b] = win[jobs[k - 1]];
11623
+ const u = ui < 0 ? -Infinity : T[ui];
11624
+ const v = vi >= m ? Infinity : T[vi];
11625
+ let most = count(k - 1, ui, vi);
11626
+ let choice = -1;
11627
+ if (a > u + EPS$3 && a <= v + EPS$3) {
11628
+ const from = Math.max(a, ui < 0 ? lo : u + s);
11629
+ const to = Math.min(b, vi >= m ? hi : v - s);
11630
+ for (let ti = lowerBound(T, from - EPS$3); ti < m && T[ti] <= to + EPS$3; ti++) {
11631
+ const got2 = 1 + count(k - 1, ui, ti) + count(k - 1, ti, vi);
11632
+ if (got2 > most) {
11633
+ most = got2;
11634
+ choice = ti;
11635
+ }
11636
+ }
11637
+ }
11638
+ memo.set(key, most);
11639
+ if (choice >= 0) pick.set(key, choice);
11640
+ return most;
11641
+ };
11642
+ let total = r === n && count(r, -1, m) === n ? n : -1;
11643
+ let bestQ = 0;
11644
+ let bestI = -1;
11645
+ const chain = [];
11646
+ const back = [];
11647
+ for (let q = 1; q <= n; q++) {
11648
+ const cur = new Float64Array(m).fill(-Infinity);
11649
+ const from = new Int32Array(m).fill(-1);
11650
+ for (let i = 0; i < m; i++) {
11651
+ if (q === 1) {
11652
+ cur[i] = count(r, -1, i);
11653
+ continue;
11654
+ }
11655
+ const prev = chain[q - 2];
11656
+ for (let h = 0; h < i; h++) {
11657
+ if (prev[h] === -Infinity || T[i] - T[h] < s - EPS$3) continue;
11658
+ const v = prev[h] + count(r, h, i);
11659
+ if (v > cur[i]) {
11660
+ cur[i] = v;
11661
+ from[i] = h;
11662
+ }
11663
+ }
11664
+ }
11665
+ chain.push(cur);
11666
+ back.push(from);
11667
+ for (let i = 0; i < m; i++) {
11668
+ if (cur[i] === -Infinity) continue;
11669
+ const a = cur[i] + count(r, i, m);
11670
+ if (a + q >= n && a > total) {
11671
+ total = a;
11672
+ bestQ = q;
11673
+ bestI = i;
11674
+ }
11675
+ }
11676
+ }
11677
+ const placed = /* @__PURE__ */ new Map();
11678
+ const place = (k, ui, vi) => {
11679
+ if (k === 0) return;
11680
+ const t = pick.get(id(k, ui, vi));
11681
+ if (t === void 0) return place(k - 1, ui, vi);
11682
+ placed.set(jobs[k - 1], T[t]);
11683
+ place(k - 1, ui, t);
11684
+ place(k - 1, t, vi);
11685
+ };
11686
+ const free = [];
11687
+ let ui = bestI;
11688
+ for (let q = bestQ; q >= 1; q--) {
11689
+ free.push(T[ui]);
11690
+ const h = back[q - 1][ui];
11691
+ place(r, h, ui);
11692
+ ui = h;
11693
+ }
11694
+ if (bestQ === 0) place(r, -1, m);
11695
+ else place(r, bestI, m);
11696
+ free.reverse();
11697
+ const rest = order.filter((e) => !placed.has(e));
11698
+ const slots = assignInOrder(rest, free);
11699
+ const items = [];
11700
+ for (const [e, p] of placed) items.push({
11701
+ end: e,
11702
+ anchored: true,
11703
+ at: p
11704
+ });
11705
+ rest.forEach((e, i) => items.push({
11706
+ end: e,
11707
+ anchored: false,
11708
+ at: slots[i]
11709
+ }));
11710
+ for (const f of fixed) items.push({
11711
+ end: -1,
11712
+ anchored: true,
11713
+ at: f
11714
+ });
11715
+ return items.sort((a, b) => a.at - b.at || (a.end < 0 ? -1 : b.end < 0 ? 1 : order.indexOf(a.end) - order.indexOf(b.end)));
11716
+ /** This function gives each end of `list` one of the sorted places `free`, in order. The sum of the squares of the moves from the ideal places is the least. */
11717
+ function assignInOrder(list, places) {
11718
+ const a = list.length;
11719
+ const b = places.length;
11720
+ const cost = (i, j) => (places[j] - ideals[list[i]]) ** 2;
11721
+ const g = Array.from({ length: a + 1 }, () => new Float64Array(b + 1).fill(Infinity));
11722
+ for (let j = 0; j <= b; j++) g[0][j] = 0;
11723
+ for (let i = 1; i <= a; i++) for (let j = i; j <= b; j++) g[i][j] = Math.min(g[i][j - 1], g[i - 1][j - 1] + cost(i - 1, j - 1));
11724
+ const out = new Array(a);
11725
+ let j = b;
11726
+ for (let i = a; i >= 1; i--) {
11727
+ while (j > i && g[i][j - 1] === g[i][j]) j--;
11728
+ out[i - 1] = places[j - 1];
11729
+ j--;
11730
+ }
11731
+ return out;
11732
+ }
11733
+ }
11734
+ /**
11735
+ * This function gives the plan of one wall (PLUG-ASSIGN). Each end has an interval on the axis along the wall. The plugs keep at least `gap` between two of them,
11736
+ * and between a plug and a fixed plug at a place of `fixed`. When the room is too short for all the plugs, the gap shrinks evenly. The plan has three goals, in this order:
11737
+ * 1. the most ends with their plug in their interval (exact).
11738
+ * 2. no crossing at the wall: the order of the keys, when that order lets the count of goal 1.
11739
+ * 3. the least sum of the squares of the moves from the ideal places, for the order and the ends of goals 1 and 2.
11740
+ *
11741
+ * An end that cannot get a plug in its interval still gets a plug in the room. When the order of the keys loses ends, the plugs swap their places
11742
+ * to come nearer to that order, while each end keeps its interval.
11743
+ */
11744
+ function assignWall(ends, room, gap, fixed = []) {
11745
+ const n = ends.length;
11746
+ if (n === 0) return {
11747
+ at: [],
11748
+ anchored: [],
11749
+ gap
11750
+ };
11751
+ const [lo, hi] = room;
11752
+ const pins = [...fixed].sort((a, b) => a - b);
11753
+ let s = wallGap(n, pins, lo, hi, Math.max(0, gap));
11754
+ const win = ends.map((e) => {
11755
+ const a = Math.max(lo, e.lo);
11756
+ const b = Math.min(hi, e.hi);
11757
+ return a <= b + EPS$3 ? [a, Math.max(a, b)] : void 0;
11758
+ });
11759
+ const order = ends.map((_, i) => i).sort((a, b) => ends[a].key - ends[b].key || a - b);
11760
+ let items = keyOrderPlan(order, win, pins, lo, hi, s);
11761
+ if (!items) {
11762
+ s = 0;
11763
+ items = keyOrderPlan(order, win, pins, lo, hi, s);
11764
+ }
11765
+ const inKey = items.filter((x) => x.end >= 0 && x.anchored).length;
11766
+ if (inKey < win.filter((w) => w !== void 0).length && inKey < unionCap(win, pins, lo, hi, s, n)) {
11767
+ const best = bestPlan(order, win, ends.map((e) => e.ideal), pins, lo, hi, s);
11768
+ if (best.filter((x) => x.end >= 0 && x.anchored).length > inKey) items = untangle(best, ends, win);
11769
+ }
11770
+ const span = items.map((x) => x.end < 0 ? [x.at, x.at] : x.anchored ? win[x.end] : [lo, hi]);
11771
+ const placed = placeInIntervals(items.map((x) => x.end < 0 ? x.at : ends[x.end].ideal), span.map((v) => v[0]), span.map((v) => v[1]), s);
11772
+ const at = new Array(n);
11773
+ items.forEach((x, i) => {
11774
+ if (x.end >= 0) at[x.end] = placed[i];
11775
+ });
11776
+ return {
11777
+ at,
11778
+ anchored: ends.map((e, i) => at[i] >= e.lo - 1e-7 && at[i] <= e.hi + 1e-7 && win[i] !== void 0),
11779
+ gap: s
11780
+ };
11781
+ }
11782
+ /** This function gives the most plugs with the gap `s` in the union of the intervals, out of the fixed zones, and at most `n`. It is a bound of goal 1. */
11783
+ function unionCap(win, fixed, lo, hi, s, n) {
11784
+ const parts = win.filter((w) => w !== void 0).sort((a, b) => a[0] - b[0]);
11785
+ let count = 0;
11786
+ let p = -Infinity;
11787
+ for (const [a, b] of parts) {
11788
+ let v = Math.max(a, p, lo);
11789
+ for (;;) {
11790
+ v = pushOut(v, fixed, s);
11791
+ if (v > b + EPS$3 || v > hi + EPS$3 || count >= n) break;
11792
+ count++;
11793
+ p = v + s;
11794
+ v = p;
11795
+ if (s === 0) return n;
11796
+ }
11797
+ }
11798
+ return count;
11799
+ }
11800
+ /**
11801
+ * This function swaps the places of two neighbours of a plan when their keys are in the wrong order, and each end keeps its interval.
11802
+ * Thus the plan comes nearer to the order of the keys. Each swap removes one inversion, so the loop ends.
11803
+ */
11804
+ function untangle(items, ends, win) {
11805
+ const own = items.filter((x) => x.end >= 0);
11806
+ const ok = (x, p) => !x.anchored || p >= win[x.end][0] - EPS$3 && p <= win[x.end][1] + EPS$3;
11807
+ for (let changed = true, rounds = 0; changed && rounds <= own.length * own.length; rounds++) {
11808
+ changed = false;
11809
+ for (let i = 0; i + 1 < own.length; i++) {
11810
+ const x = own[i];
11811
+ const y = own[i + 1];
11812
+ if (ends[x.end].key <= ends[y.end].key) continue;
11813
+ if (!ok(x, y.at) || !ok(y, x.at)) continue;
11814
+ const t = x.at;
11815
+ x.at = y.at;
11816
+ y.at = t;
11817
+ own[i] = y;
11818
+ own[i + 1] = x;
11819
+ changed = true;
11820
+ }
11821
+ }
11822
+ return items.sort((a, b) => a.at - b.at || (a.end < 0 ? -1 : b.end < 0 ? 1 : ends[a.end].key - ends[b.end].key));
11823
+ }
11824
+ /**
11825
+ * This function gives the plugs of the plug strategy `assign` (PLUG-ASSIGN). A fixed plug stays at its point, and a plug with a side stays on that side.
11826
+ * Each free plug goes on the side that faces the other end (`sideToward`). Each end of a wall has an interval: the range of its port, or the room of the wall.
11827
+ * Then `assignWall` places the plugs of each wall, with at least `plugGap` between two of them. The ends of one port share one plug.
11828
+ *
11829
+ * The output has one entry for each edge with two known nodes, in the order of the edges.
11830
+ */
11831
+ function assignEnds(input, options = {}) {
11832
+ const { graph } = input;
11833
+ const gap = options.plugGap ?? ASSIGN_DEFAULTS.plugGap;
11834
+ const inset = options.inset ?? ASSIGN_DEFAULTS.inset;
11835
+ const byId = nodeIndex(graph);
11836
+ const slots = /* @__PURE__ */ new Map();
11837
+ const walls = /* @__PURE__ */ new Map();
11838
+ const wallOf = (node, side) => {
11839
+ const key = JSON.stringify([node.id, side]);
11840
+ let wall = walls.get(key);
11841
+ if (!wall) {
11842
+ const fixed = [];
11843
+ for (const p of node.ports ?? []) {
11844
+ const at = portOffset(node.box, p);
11845
+ if (at === void 0) continue;
11846
+ const point = {
11847
+ x: node.box.x + at.x,
11848
+ y: node.box.y + at.y
11849
+ };
11850
+ if ((p.side ?? nearestSide(node.box, point)) === side) fixed.push(point[alongOf(side)]);
11851
+ }
11852
+ walls.set(key, wall = {
11853
+ node,
11854
+ side,
11855
+ slots: [],
11856
+ fixed
11857
+ });
11858
+ }
11859
+ return wall;
11860
+ };
11861
+ const plugOf = (node, end, far) => {
11862
+ const toward = center(far.box);
11863
+ const port = end.port === void 0 ? void 0 : node.ports?.find((p) => p.id === end.port);
11864
+ if (port && portOffset(node.box, port) !== void 0) return placePlug(node, end, toward);
11865
+ const key = port ? JSON.stringify([node.id, port.id]) : JSON.stringify([node.id, slots.size]);
11866
+ if (slots.has(key)) return key;
11867
+ const side = port?.side ?? sideToward(node.box, toward);
11868
+ const ideal = placePlug(node, end, toward).point[alongOf(side)];
11869
+ const range = rangeInterval(node.box, side, port, inset);
11870
+ const slot = {
11871
+ node,
11872
+ side,
11873
+ ideal,
11874
+ at: ideal,
11875
+ ...range ? { range } : {}
11876
+ };
11877
+ slots.set(key, slot);
11878
+ wallOf(node, side).slots.push(slot);
11879
+ return key;
11880
+ };
11881
+ const ends = [];
11882
+ for (const e of graph.edges) {
11883
+ const s = byId.get(e.source.node);
11884
+ const t = byId.get(e.target.node);
11885
+ if (!s || !t) continue;
11886
+ const source = plugOf(s, e.source, t);
11887
+ const target = plugOf(t, e.target, s);
11888
+ for (const [own, far] of [[source, target], [target, source]]) {
11889
+ const slot = typeof own === "string" ? slots.get(own) : void 0;
11890
+ if (slot && slot.far === void 0) slot.far = far;
11891
+ }
11892
+ ends.push({
11893
+ edge: e.id,
11894
+ source,
11895
+ target
11896
+ });
11897
+ }
11898
+ const farPoint = (s) => {
11899
+ const far = s.far;
11900
+ if (typeof far !== "string") return far.point;
11901
+ const f = slots.get(far);
11902
+ return pointAt$1(f.node.box, f.side, f.ideal);
11903
+ };
11904
+ for (const wall of walls.values()) {
11905
+ const room = roomOf(wall.node.box, wall.side, inset);
11906
+ const plan = assignWall(wall.slots.map((s) => ({
11907
+ lo: s.range?.[0] ?? room[0],
11908
+ hi: s.range?.[1] ?? room[1],
11909
+ ideal: s.ideal,
11910
+ key: keyOf$1(wall.node.box, wall.side, farPoint(s))
11911
+ })), room, gap, wall.fixed);
11912
+ wall.slots.forEach((s, i) => {
11913
+ s.at = plan.at[i];
11914
+ });
11915
+ }
11916
+ const resolve = (p) => {
11917
+ if (typeof p !== "string") return p;
11918
+ const s = slots.get(p);
11919
+ return {
11920
+ point: pointAt$1(s.node.box, s.side, s.at),
11921
+ normal: SIDE_NORMAL[s.side]
11922
+ };
11923
+ };
11924
+ return ends.map((x) => ({
11925
+ edge: x.edge,
11926
+ source: resolve(x.source),
11927
+ target: resolve(x.target)
11928
+ }));
11929
+ }
11283
11930
  // src/plugs/leaders/index.ts
11284
11931
  /** The values of `LeaderType`. */
11285
11932
  const LEADER_TYPES = Object.freeze([
@@ -26927,6 +27574,25 @@ const SPREAD_SCHEMA = {
26927
27574
  describe: "The passes that sort each wall again by the new places of the far ends."
26928
27575
  }
26929
27576
  };
27577
+ /** The declarations of the options of the plug strategy `assign` (PLUG-ASSIGN). */
27578
+ const ASSIGN_SCHEMA = {
27579
+ plugGap: {
27580
+ type: "number",
27581
+ min: 0,
27582
+ max: 24,
27583
+ step: 1,
27584
+ default: 8,
27585
+ describe: "The least gap between two plugs of one wall. On a short wall, the gap shrinks evenly."
27586
+ },
27587
+ inset: {
27588
+ type: "number",
27589
+ min: 0,
27590
+ max: 12,
27591
+ step: 1,
27592
+ default: 6,
27593
+ describe: "A plug stays this far from the corners of its wall."
27594
+ }
27595
+ };
26930
27596
  /** The declarations of the options of the whole strategy `pipeline`. */
26931
27597
  const PIPELINE_SCHEMA = {
26932
27598
  router: {
@@ -28862,6 +29528,14 @@ const plugStrategies = Object.freeze({
28862
29528
  defaults: { ...LEADER_DEFAULTS },
28863
29529
  schema: LEADER_SCHEMA,
28864
29530
  run: (input, o) => leaderEnds(input, o)
29531
+ }),
29532
+ assign: defineStrategy({
29533
+ step: "plugs",
29534
+ name: "assign",
29535
+ describe: "PLUG-ASSIGN. The plugs of each wall go into the ranges of their ports with a minimum gap. The most ends are in their range, then the order of the far ends.",
29536
+ defaults: { ...ASSIGN_DEFAULTS },
29537
+ schema: ASSIGN_SCHEMA,
29538
+ run: (input, o) => assignEnds(input, o)
28865
29539
  })
28866
29540
  });
28867
29541
  /** The built-in level rules: the strategies of the step `levels`. The router `sections` makes its zoom levels with its option `level`. */
@@ -29008,6 +29682,42 @@ function sideOf(plug, node) {
29008
29682
  for (const [side, n] of Object.entries(SIDE_NORMAL)) if (n.x === plug.normal.x && n.y === plug.normal.y) return side;
29009
29683
  return nearestSide(node.box, plug.point);
29010
29684
  }
29685
+ /**
29686
+ * This function moves the new fixed ports of a side of a node into their ranges (PORT-RANGE). It does this when one port is out of its range.
29687
+ * The ports of the side keep their order, and two of them keep at least `gap` (`placeInIntervals`). A side with each port in its range does not change.
29688
+ */
29689
+ function keepInRanges(node, ports, gap, inset) {
29690
+ const bySide = /* @__PURE__ */ new Map();
29691
+ for (const p of ports.values()) {
29692
+ const list = bySide.get(p.side);
29693
+ if (list) list.push(p);
29694
+ else bySide.set(p.side, [p]);
29695
+ }
29696
+ const box = node.box;
29697
+ for (const [side, list] of bySide) {
29698
+ const along = alongAxis(side);
29699
+ const v = (p) => p.at[along] + (along === "y" ? box.y : box.x);
29700
+ if (!list.some((p) => {
29701
+ const r = rangeInterval(box, side, p, inset);
29702
+ return r !== void 0 && (v(p) < r[0] - 1e-6 || v(p) > r[1] + 1e-6);
29703
+ })) continue;
29704
+ const start = along === "y" ? box.y : box.x;
29705
+ const len = along === "y" ? box.h : box.w;
29706
+ const sorted = [...list].sort((a, b) => v(a) - v(b));
29707
+ const span = sorted.map((p) => rangeInterval(box, side, p, inset) ?? [start, start + len]);
29708
+ const at = placeInIntervals(sorted.map(v), span.map((r) => r[0]), span.map((r) => r[1]), gap);
29709
+ sorted.forEach((p, i) => {
29710
+ const point = {
29711
+ ...p.at,
29712
+ [along]: at[i] - start
29713
+ };
29714
+ ports.set(p.id, {
29715
+ ...p,
29716
+ at: point
29717
+ });
29718
+ });
29719
+ }
29720
+ }
29011
29721
  /** This function gives an id that no port of the node has, from `base`. */
29012
29722
  function freeId(taken, base) {
29013
29723
  let id = base;
@@ -29022,6 +29732,7 @@ function freeId(taken, base) {
29022
29732
  *
29023
29733
  * The rule sees the graph of `corners`: the plugs of `resolvePorts`, without the inner edges of the frames.
29024
29734
  * The rule is a strategy of the step `plugs`, or the name of one in the registry.
29735
+ * A new fixed port with a `range` is in its range (PORT-RANGE). When the rule gives a plug out of the range, the new plugs of that side move into their ranges.
29025
29736
  * The output is a new graph. The function does not change the input.
29026
29737
  */
29027
29738
  function fixPorts(graph, rule, options = {}) {
@@ -29090,6 +29801,16 @@ function fixPorts(graph, rule, options = {}) {
29090
29801
  target
29091
29802
  };
29092
29803
  });
29804
+ const own = strategy.defaults;
29805
+ const given = options.plugOptions ?? {};
29806
+ const num = (a, b, c) => typeof a === "number" ? a : typeof b === "number" ? b : c;
29807
+ const gap = num(given.plugGap, own.plugGap, 0);
29808
+ const inset = num(given.inset, own.inset, 6);
29809
+ const ranges = options.ranges ?? (strategy.name === "assign" ? "rule" : "keep");
29810
+ for (const [id, m] of fixed) {
29811
+ const node = byId.get(id);
29812
+ if (node && ranges === "keep") keepInRanges(node, m, gap, inset);
29813
+ }
29093
29814
  const nodes = graph.nodes.map((n) => {
29094
29815
  const m = fixed.get(n.id);
29095
29816
  const more = added.get(n.id);
@@ -31043,8 +31764,9 @@ function silhouette(graph, level) {
31043
31764
  /**
31044
31765
  * This function makes a seeded small change of a graph: one node moves by `boardUnit`, one edge goes and one new edge comes.
31045
31766
  * The moved box keeps a gap of 4 units to the other boxes. The same graph and the same seed give the same change.
31767
+ * With `edges: false`, the change only moves the node (Z11). The moved node is the node of the full change with the same seed.
31046
31768
  */
31047
- function changeGraph(graph, seed) {
31769
+ function changeGraph(graph, seed, options = {}) {
31048
31770
  const R = rng(seed * 2654435761 + 97);
31049
31771
  const n = graph.nodes.length;
31050
31772
  const u = boardUnit(graph);
@@ -31082,7 +31804,7 @@ function changeGraph(graph, seed) {
31082
31804
  let edges = [...graph.edges];
31083
31805
  let removed;
31084
31806
  let added;
31085
- if (edges.length > 1) {
31807
+ if (edges.length > 1 && options.edges !== false) {
31086
31808
  const j = Math.floor(R() * edges.length);
31087
31809
  const gone = edges[j];
31088
31810
  removed = gone.id;
@@ -31132,6 +31854,40 @@ function routeStability(graph, before, after, options = {}) {
31132
31854
  moved: s.moved
31133
31855
  };
31134
31856
  }
31857
+ /**
31858
+ * This function measures the move response of a router (Z11 of MEAS0). `change` is a change of `changeGraph` with `edges: false`.
31859
+ * It gives `undefined` when no node moved. Above `sample` routes, a seeded sample serves, and the sum scales to all the routes.
31860
+ */
31861
+ function moveResponse(graph, change, before, after, options = {}) {
31862
+ const node = change.moved;
31863
+ if (node === void 0) return void 0;
31864
+ const max = options.sample ?? 2e3;
31865
+ const prev = new Map(before.routes.map((r) => [r.edge, r.points]));
31866
+ const ends = new Map(graph.edges.map((e) => [e.id, e.source.node === node || e.target.node === node]));
31867
+ let routes = after.routes.filter((r) => prev.has(r.edge));
31868
+ const all = routes.length;
31869
+ if (routes.length > max) {
31870
+ const R = rng(options.seed ?? 1);
31871
+ routes = Array.from({ length: max }, () => routes[Math.floor(R() * routes.length)]);
31872
+ }
31873
+ const u = boardUnit(graph);
31874
+ const degree = graph.edges.filter((e) => ends.get(e.id) === true).length;
31875
+ let sum = 0;
31876
+ let far = 0;
31877
+ let spilled = 0;
31878
+ for (const r of routes) {
31879
+ const f = frechet(prev.get(r.edge), r.points);
31880
+ sum += f;
31881
+ if (ends.get(r.edge) === true) continue;
31882
+ far++;
31883
+ if (f > 1) spilled++;
31884
+ }
31885
+ const scale = routes.length > 0 ? all / routes.length : 0;
31886
+ return {
31887
+ gain: sum * scale / u / Math.max(1, degree),
31888
+ spill: far > 0 ? spilled / far : 0
31889
+ };
31890
+ }
31135
31891
  // src/bench/measures/room.ts
31136
31892
  /** Up to this number of boxes, `roomOrderFaults` examines each pair. */
31137
31893
  const EXACT_PAIRS = 2e3;
@@ -31628,7 +32384,7 @@ function routeSeparation(routes, near = 4, maxAngle = 5) {
31628
32384
  if (ink <= 0) return 0;
31629
32385
  const grid = new Grid(Math.max(16, 4 * near));
31630
32386
  legs.forEach((l, i) => grid.addSegment(i, l.a, l.b));
31631
- const sin = Math.sin(maxAngle * Math.PI / 180);
32387
+ const sin = parallelSine(maxAngle);
31632
32388
  let close = 0;
31633
32389
  legs.forEach((s, i) => {
31634
32390
  const length = dist(s.a, s.b);
@@ -31653,6 +32409,97 @@ function routeSeparation(routes, near = 4, maxAngle = 5) {
31653
32409
  });
31654
32410
  return Math.min(1, close / ink);
31655
32411
  }
32412
+ /** This function gives the sine of the largest angle between two legs that R12 and R16 count as parallel. */
32413
+ function parallelSine(maxAngle) {
32414
+ return Math.sin(maxAngle * Math.PI / 180);
32415
+ }
32416
+ /** The distance in units of the board up to which two parallel legs are neighbors for R16. It is 8 lane widths. */
32417
+ const MEASURE_BALANCE_REACH = 32;
32418
+ /**
32419
+ * This function gives the lane balance of the routes (R16 of MEAS0): the spread of the gaps between parallel routes.
32420
+ *
32421
+ * The legs are the legs of R12. For each leg `s`, its neighbor at each side is the nearest leg `t` of other edges with three properties.
32422
+ * It is within `maxAngle` degrees of parallel. Its projection overlaps `s`. Its distance at the middle of the overlap is less than `reach`.
32423
+ * Each neighbor pair gives a gap `g` with the weight `w`, the length of the overlap. A pair counts one time.
32424
+ * Two legs on one line have the gap 0.
32425
+ *
32426
+ * `cv = sqrt(Σ w (g − ḡ)² / Σ w) / ḡ`, with `ḡ = Σ w g / Σ w`. Lower is better. Routes without a neighbor pair give 0.
32427
+ * R12 counts the parallel ink that is too near. R16 counts uneven gaps, also when each gap is wide.
32428
+ */
32429
+ function routeBalance(routes, reach = 32, maxAngle = 5) {
32430
+ const legs = legsOf(routes);
32431
+ const grid = new Grid(Math.max(16, reach));
32432
+ legs.forEach((l, i) => grid.addSegment(i, l.a, l.b));
32433
+ const sin = parallelSine(maxAngle);
32434
+ const gaps = /* @__PURE__ */ new Map();
32435
+ legs.forEach((s, i) => {
32436
+ const length = dist(s.a, s.b);
32437
+ if (length <= 0) return;
32438
+ const ux = (s.b.x - s.a.x) / length;
32439
+ const uy = (s.b.y - s.a.y) / length;
32440
+ const best = /* @__PURE__ */ new Map();
32441
+ grid.querySegment(s.a, s.b, reach, (j) => {
32442
+ const t = legs[j];
32443
+ if (j === i || t.edges === s.edges) return;
32444
+ const tx = t.b.x - t.a.x;
32445
+ const ty = t.b.y - t.a.y;
32446
+ const tl2 = tx * tx + ty * ty;
32447
+ const cross = ux * ty - uy * tx;
32448
+ if (tl2 <= 0 || cross * cross > sin * sin * tl2) return;
32449
+ const p = (t.a.x - s.a.x) * ux + (t.a.y - s.a.y) * uy;
32450
+ const q = (t.b.x - s.a.x) * ux + (t.b.y - s.a.y) * uy;
32451
+ const lo = Math.max(0, Math.min(p, q));
32452
+ const hi = Math.min(length, Math.max(p, q));
32453
+ if (hi - lo <= 1e-9) return;
32454
+ const m = (lo + hi) / 2;
32455
+ const px = s.a.x + ux * m;
32456
+ const py = s.a.y + uy * m;
32457
+ const k = ((px - t.a.x) * tx + (py - t.a.y) * ty) / tl2;
32458
+ const o = (t.a.x + tx * k - px) * -uy + (t.a.y + ty * k - py) * ux;
32459
+ const g = Math.abs(o);
32460
+ if (g >= reach) return;
32461
+ const side = o < -1e-9 ? -1 : 1;
32462
+ const now = best.get(side);
32463
+ if (!now || g < now.g) best.set(side, {
32464
+ j,
32465
+ g,
32466
+ w: hi - lo
32467
+ });
32468
+ });
32469
+ for (const n of best.values()) {
32470
+ const key = i < n.j ? `${i},${n.j}` : `${n.j},${i}`;
32471
+ if (!gaps.has(key)) gaps.set(key, {
32472
+ g: n.g,
32473
+ w: n.w
32474
+ });
32475
+ }
32476
+ });
32477
+ const list = [...gaps.values()];
32478
+ const W = list.reduce((sum, x) => sum + x.w, 0);
32479
+ if (list.length === 0 || W <= 0) return {
32480
+ cv: 0,
32481
+ low: 0,
32482
+ pairs: 0
32483
+ };
32484
+ const mean = list.reduce((sum, x) => sum + x.w * x.g, 0) / W;
32485
+ const variance = list.reduce((sum, x) => sum + x.w * (x.g - mean) ** 2, 0) / W;
32486
+ const sorted = [...list].sort((x, y) => x.g - y.g);
32487
+ let acc = 0;
32488
+ let median = sorted[sorted.length - 1].g;
32489
+ for (const x of sorted) {
32490
+ acc += x.w;
32491
+ if (acc >= W / 2) {
32492
+ median = x.g;
32493
+ break;
32494
+ }
32495
+ }
32496
+ const low = list.filter((x) => x.g < median / 2).reduce((sum, x) => sum + x.w, 0) / W;
32497
+ return {
32498
+ cv: mean > 0 ? Math.sqrt(variance) / mean : 0,
32499
+ low,
32500
+ pairs: list.length
32501
+ };
32502
+ }
31656
32503
  /**
31657
32504
  * This function gives the orthogonality of the routes (R14 of MEAS0): `orth = Σ L(legs within maxAngle degrees of an axis) / Σ_e L(r_e)`.
31658
32505
  * Each route counts its own legs, also on a shared segment. The scale is 0 to 1, and higher is better for an orthogonal style.
@@ -32945,6 +33792,7 @@ function compareStep(step, graph, options = {}) {
32945
33792
  let rows;
32946
33793
  if (step === "router") {
32947
33794
  const change = graph.nodes.length <= stabilityMax ? changeGraph(graph, seed) : void 0;
33795
+ const move = graph.nodes.length <= stabilityMax ? changeGraph(graph, seed, { edges: false }) : void 0;
32948
33796
  rows = listStrategies(registry, "router").map((strategy) => {
32949
33797
  if (graph.nodes.length > (strategy.maxNodes ?? Infinity)) return {
32950
33798
  step,
@@ -32968,6 +33816,28 @@ function compareStep(step, graph, options = {}) {
32968
33816
  values["Z10.max"] = s.max;
32969
33817
  values["Z10.moved"] = s.moved;
32970
33818
  }
33819
+ if (move) {
33820
+ const z = moveResponse(graph, move, result, router.route(move.graph), { seed });
33821
+ if (z) {
33822
+ values["Z11.gain"] = z.gain;
33823
+ values["Z11.spill"] = z.spill;
33824
+ }
33825
+ }
33826
+ if (change) {
33827
+ const given = own[strategy.name] ?? {};
33828
+ const base = typeof given.seed === "number" ? given.seed : strategy.defaults?.seed;
33829
+ if (typeof base === "number") {
33830
+ const s = routeStability(graph, result, asRouter(strategy, {
33831
+ ...own[strategy.name],
33832
+ seed: base + 1
33833
+ }).route(graph), { seed });
33834
+ values["Z12.mean"] = s.mean;
33835
+ values["Z12.moved"] = s.moved;
33836
+ } else {
33837
+ values["Z12.mean"] = 0;
33838
+ values["Z12.moved"] = 0;
33839
+ }
33840
+ }
32971
33841
  return {
32972
33842
  step,
32973
33843
  strategy: strategy.name,
@@ -33675,6 +34545,24 @@ const MEASURES = Object.freeze([
33675
34545
  "higher",
33676
34546
  false
33677
34547
  ],
34548
+ [
34549
+ "R16.cv",
34550
+ "R16",
34551
+ "route.balance.cv",
34552
+ "no unit",
34553
+ "lower",
34554
+ true,
34555
+ "The spread of the gaps between parallel routes: the standard deviation of the gaps over their mean. A value of 0 is even."
34556
+ ],
34557
+ [
34558
+ "R16.low",
34559
+ "R16",
34560
+ "route.balance.low",
34561
+ "0 to 1",
34562
+ "lower",
34563
+ true,
34564
+ "The share of the parallel overlap with a gap less than half of the median gap."
34565
+ ],
33678
34566
  [
33679
34567
  "ink",
33680
34568
  "R1",
@@ -34039,6 +34927,42 @@ const MEASURES = Object.freeze([
34039
34927
  "higher",
34040
34928
  false
34041
34929
  ],
34930
+ [
34931
+ "Z11.gain",
34932
+ "Z11",
34933
+ "route.stability.move.gain",
34934
+ "no unit",
34935
+ "lower",
34936
+ false,
34937
+ "The movement of all the routes after one node moves by one unit, for each edge of that node. A value of 1 is a local response."
34938
+ ],
34939
+ [
34940
+ "Z11.spill",
34941
+ "Z11",
34942
+ "route.stability.move.spill",
34943
+ "0 to 1",
34944
+ "lower",
34945
+ false,
34946
+ "The share of the routes of other nodes that move after one node moves by one unit. A value of 0 is a local response."
34947
+ ],
34948
+ [
34949
+ "Z12.mean",
34950
+ "Z12",
34951
+ "route.stability.seed.mean",
34952
+ "units of the board",
34953
+ "lower",
34954
+ false,
34955
+ "The mean movement of the routes with the next seed of the strategy. A strategy without a seed has 0."
34956
+ ],
34957
+ [
34958
+ "Z12.moved",
34959
+ "Z12",
34960
+ "route.stability.seed.moved",
34961
+ "0 to 1",
34962
+ "lower",
34963
+ false,
34964
+ "The share of the routes that move with the next seed of the strategy. A strategy without a seed has 0."
34965
+ ],
34042
34966
  [
34043
34967
  "level-nesting",
34044
34968
  "Z6",
@@ -37143,6 +38067,9 @@ function rowValues(step, graph, row, base) {
37143
38067
  const open = openLanes(row.result);
37144
38068
  values["R12.share"] = routeSeparation(routes);
37145
38069
  values["R14.share"] = orthogonality(routes);
38070
+ const balance = routeBalance(routes);
38071
+ values["R16.cv"] = balance.cv;
38072
+ values["R16.low"] = balance.low;
37146
38073
  values["B5.crossings"] = laneCrossings(open.graph);
37147
38074
  values["B7.ratio"] = occupiedArea(routes, boardUnit(graph));
37148
38075
  const runs = runProfile(open.graph);
@@ -38589,6 +39516,6 @@ const sharedStretchStrategy = defineStrategy({
38589
39516
  name: "shared-stretch"
38590
39517
  }).route(graph)
38591
39518
  });
38592
- export { ADAM_DEFAULTS, ADAM_SCHEMA, AMBIGUITY_GUARD_DEFAULTS, ANNEALING_DEFAULTS, ANNEALING_SCHEMA, ARC_SOURCES, AREA_DEFAULTS, AREA_WIDTHS, ATTRACTION_DEFAULTS, ATTRACTION_SCHEMA, BACKTRACK_MIN, BACKTRACK_SHARE, BALANCE_DEFAULTS, BALANCE_ENDS, BIPARTITE, BIPARTITE_LAYOUTS, BOARD_PORTFOLIO_DEFAULTS, BOARD_PORTFOLIO_SETTINGS, BREAK, BREAK_EVEN, BREAK_EVEN_DEFAULTS, BRONCHIAL_DEFAULTS, BRONCHIAL_SPLITS, BUILT_IN_STRATEGIES, BoundaryWalk, CANAL_DEFAULTS, CANAL_SCHEMA, CANDIDATE_SOURCES, CAP_SWEEP, CASES, CHANNEL_REACH, CLEARANCE_CAP, COMPARE_ALL_STEPS, COMPARE_STEPS, CONFLUENT_DEFAULTS, CONSENSUS_DEFAULTS, CONSENSUS_LINKAGES, CORRIDOR_DEFAULTS, COVERAGE_FAMILIES, CROSSBAR_DEFAULTS, CUBU_DEFAULTS, CURRICULUM_DEFAULTS, CURRICULUM_ORDERS, CURRICULUM_POOL, CURVE_TYPES, CUT_DEFAULTS, CornerGraph, DEFAULT_RNG, DENSE_GROUPS, DENSITY_KERNELS, DETAIL_DEFAULTS, DIFFUSION_DEFAULTS, DISTORTION_NEIGHBOURS, DIVIDED_DEFAULTS, EDGE_DISTANCES, EDGE_PATH_DEFAULTS, EXACT_CLASSES, EXACT_EDGES_PER_NODE, EXACT_V1, EXACT_V1_BOUNDS, EXACT_V1_BOUNDS_SIZES, EXACT_V1_SEEDS, EXACT_V1_SIZES, FAN_OUT, FDEB_DEFAULTS, FIREFLY_DEFAULTS, FIXTURES, FIXTURE_LAYOUTS, FIXTURE_LOCALITY, FLOW_CLUSTER_DEFAULTS, FLOW_FIELD_DEFAULTS, FRAME_ROWS, FRECHET_DEFAULTS, FRONT_OBJECTIVES, GAP_BASELINES, GAP_BOOTSTRAP, GAP_CAPS, GAP_DEFAULTS, GAP_RULES, GAP_VIEWS, GROUP_SCOPES, GROUP_SPINES, GROWTH_DEFAULTS, GROWTH_SWEEP, GUTTER_BEND_COST, Grid, HDBSCAN_DEFAULTS, HIERARCHY_DEFAULTS, HIERARCHY_GROUPS, HIGHWAYS_ENGINE, HIGHWAYS_FAMILIES, HIGHWAY_BASES, HIGHWAY_BASE_ROUTERS, HOUGH_DEFAULTS, HUB_ALLOCATIONS, HUB_ARC_DEFAULTS, HUB_CANDIDATES, HUB_CHAIN, HYSTERESIS_DEFAULTS, INTERCHANGE_BASES, INTERCHANGE_DEFAULTS, KDEEB_DEFAULTS, KD_DEFAULTS, KENDALL_EXACT, KNEE_DEFAULTS, KNEE_RULES, LANES_DEFAULTS, LANE_ORDERS, LANE_STRETCH_DEFAULTS, LANE_STRETCH_ORDERS, LANE_TRACKS, LEADER_DEFAULTS, LEADER_TYPES, LEVEL_HIGHWAY_DEFAULTS, LINKAGES, LINK_COMMUNITY_DEFAULTS, LOCAL_MESH, MANIFOLD_DEFAULTS, MANIFOLD_SIDES, MANY_GROUPS, MEASURES, MEASURE_LANE_WIDTH, MEET_DEFAULTS, MEMBER_FORMS, MESH_DEFAULTS, MINGLE_DEFAULTS, MIN_EFFECTS, MLSEB_DEFAULTS, MLS_FITS, MODULE_RULES, MOVE_NAMES, NESTED_DEFAULTS, OD_CELL_DEFAULTS, OUTSIDE_SLACK, Obstacles, PACKED_GRID, PICK, PIPELINE, PLAN_CHANGERS, PORTFOLIO_B1, PORTFOLIO_BANDS, PORTFOLIO_BASE_MEMBER, PORTFOLIO_CAPS, PORTFOLIO_FIT, PORTFOLIO_HASHES, PORTFOLIO_KS_API, PORTFOLIO_RULES, PORTFOLIO_SETS, POWER_DEFAULTS, PREFERENCE_DEFAULTS, PREFERENCE_FEATURES, PREFERENCE_PANEL, PROPOSED_RULES, P_HUB_DEFAULTS, QUICKBUNDLES_DEFAULTS, RECORD_EPS, REROUTE_DEFAULTS, REROUTE_SCHEMA, RESISTANCE_DEFAULTS, RIDGE_DEFAULTS, ROAD_CLASS_DEFAULTS, ROOM_FAMILIES, ROUTED_STEPS, ROUTERS, SAME_HIGHWAY, SBEB_DEFAULTS, SCALE_SPACE_DEFAULTS, SCREEN_FAMILY_BY, SCREEN_RULES, SCREEN_SETS, SCREEN_STEPS, SCREEN_V1, SCREEN_V2, SCREEN_V2_HELD_OUT, SCREEN_V3, SCREEN_V3_HELD_OUT, SCREEN_WRAPS, SEARCH_DEFAULTS, SEARCH_MOVES, SECTION_DEFAULTS, SELECT_DEFAULTS, SIDE_NORMAL, SKELETON_GROUPS, SKELETON_TARGETS, SLACK_SWEEP, SLOT_DEFAULTS, SLOT_KEYS, SMALL_CLUSTERED, SOFT_KEY_DEFAULTS, SPACING_LIMIT, SPARSE_LONG, SPECTRAL_DEFAULTS, SPIRAL_DEFAULTS, SPREAD_DEFAULTS, STABLE_ID_DEFAULTS, STEINER_DEFAULTS, STEINER_GROUPS, STEINER_METHODS, STEINER_METRICS, STEPS, STRATEGIES, STREET_DEFAULTS, STREET_SOURCES, STRETCH_DEFAULTS, STRETCH_SWEEP, STUB_DEFAULTS, SUITE_COLUMNS, SWARM_SEARCHES, SoftGraph, SplitTree, TERRITORY_DEFAULTS, TERRITORY_POINTS, THIN_BARS, TIGHT_DEFAULTS, TIGHT_FIRSTS, TIGHT_SCHEMA, TIGHT_TIES, TINY_UNIFORM, TOLERANCE, TRACING_DEFAULTS, TRACING_QUANTILES, TRACING_WEIGHTS, TRACLUS_DEFAULTS, TRAFFIC_RULES, TWO_HUBS, UNEVEN_SIZES, VENATION_BACKBONES, VENATION_DEFAULTS, WEISZFELD_DEFAULTS, WIDTH_SCALES, WSPD_DEFAULTS, WayFinder, ZIPPER_DEFAULTS, activationBase, activationReport, adamRouter, adamRoutes, adamStrategy, adjustedRandIndex, agreementOf, allSections, ambiguity, ambiguityGate, ambiguityPlan, annealWays, annealingRouter, annealingStrategy, arcCurve, arcsin, areaPlan, aroundBox, asRouter, attraction, attractionRouter, attractionStrategy, backboneLinks, backtrackOf, balance, balanceOf, balancePlan, basePlan, benchBendRoom, benchCurveStrategies, benchCurves, benchHighways, benchLevelRoom, benchOne, benchRoom, benchRun, benchTracks, bendCount, bendingEnergy, bicliqueGroups, bigGroups, boardGaps, boardPortfolio, boardUnit, boardWins, bootstrapMedian, boundsFamilyName, boxDisplacement, boxExit, boxPush, boxUnit, boxesOverlap, breakEvenGate, breakEvenPlan, bronchialPlan, buildSections, bundleStretch, bundledShare, byCollection, byDataGroup, canalHubs, canalRouter, canalStrategy, canalWays, candidateArcs, candidatePlan, candidateScore, canonicalJson, capSettings, caseSeed, cellSteps, center, chainClusters, changeGraph, changedDimensions, channelOverflows, channelWidth, characteristicPoints, checkFamilies, checkLevels, checkOptions, checkParents, checkPlan, checkPort, checkProperties, checkSchema, checkSections, clampOptions, clothoidCurve, clusters1d, coAssociation, collapseLinks, commonEdges, compareAnyStep, compareMeasure, compareSpec, compareStep, compatibility, compatibilityMatrix, confluentPlan, consensusGroups, consensusLabels, consensusPlan, controlChain, cornersRouter, corpusManifest, corridorPlan, countCrossings, countOverlaps, createFamilyRegistry, createRegistry, crossbarPlan, crossesItself, crossingAngle, crossingAngles, cubicAt, cubuPlan, curriculum, curvatureVariation, curveDeviation, curveFaults, curveJoints, curveOf, curveRuns, curveSpace, curveStrategies, curvesHash, cutGroups, cutShare, declaresFamily, defineStrategy, denseGroupsBoard, densityPlan, densityRidges, depthLevels, detailLevels, detailWidth, detourSpread, diamondAngle, diffusionPlan, dimensionAt, dimensionUnit, dist, distanceDistortion, distinctPoints, divideHighway, dividedPlan, documentHash, dominates, drawnSegments, edgeBlocks, edgeEnds, edgeLengths, edgePathPlan, edgePaths, edgePlugs, edgeResistance, enumDimension, exactBoard, exactFamily, exactGenerator, exactSeed, exactStretch, exactStretches, exercised, facingEnds, familyCell, familyKind, familyOf, familySeed, farLevel, farLinks, fdebLines, fdebPlan, fft, findStrategy, finderOf, fireflies, firstLines, fitPreferences, fixPorts, fixedPortPoint, fixtureGroupOptions, fixtureParams, fixtureRngSeed, fixtureSpecOf, flowClusterPlan, flowClusters, flowDistance, flowFieldPlan, fnv1a32, follow, formatActivation, formatBendRoom, formatCapped, formatCurves, formatFaults, formatFronts, formatGap, formatHighways, formatInput, formatLevelRoom, formatLevels, formatMeasureTime, formatPaired, formatRoom, formatRules, formatScreen, formatSections, formatSummaries, formatTable, formatTracks, frameRoutes, frechet, frechetBundles, frechetPlan, freeLines, g1Defect, g2Defect, gapBaselines, gapCap, gapClosed, gapLaneCounts, gapLanes, gapNames, gapPlan, gapValues, gateStrategy, getStrategy, goesOutAndBack, graphInput, greedyBicliques, greedyCrossbars, groupEdges, groupFill, groupRun, groupSettings, groupStability, groupStrategies, groupedPlan, growthPlan, growthSettings, guardBreaks, gutterRoutes, halfPower, hasCusp, hdbscanClusters, hdbscanLabels, hdbscanPlan, hierarchyPlan, highwayChurn, highwayCurve, highwayCurves, highwayEdits, highwayIdKeep, highwayMoves, highwayRouter, highwayRouterStrategy, highwayRouters, highwayStrategies, highwaysRegistry, houghLines, houghPeaks, houghPlan, hubArcPlan, hubCandidates, hubChainBoard, hysteresisGate, hysteresisPlan, inflate, inflectionCount, inflectionCurve, inflectionSector, inkSaved, innerPoints, inputGraph, inputId, interchangeCandidates, interchangePlan, isRoutedStep, itemOverlaps, itemSizes, jaccard, jaccardIndex, jogPeaks, joinReports, jsonSafe, kMeans, kdGroups, kdeebLines, kdeebPlan, keepMembers, kendallTau, keyGroups, keyedSections, keysPlan, kneeOf, kneePlan, labelGroups, laneArea, laneCrossings, laneFidelity, laneOrder, laneRoutes, laneSeparation, laneSettings, laneStretches, lanesPlan, lanesThrough, leaderEnds, leafGroups, lengthBetween, levelCut, levelGrouping, levelHighwayPlan, levelMeasures, levelPairs, levelRatio, levelRoom, levelStrategies, levelsOf, lineDensity, linePoint, linkAgreement, linkCommunities, linkCommunityGroups, linkCommunityPlan, linkDensity, listOptions, listStrategies, madeUpPairs, makeFixture, makeRoom, manifestHash, manifoldPlan, manyGroupsBoard, matchHighways, maxStretch, mdf, meanLine, measureBoard, measureByKey, measureByName, measureHighways, measureJogs, measureLevel, measureLevelFaults, measureLinks, measureName, measureQuality, measureRouteFaults, measureStability, meetMember, meetPlan, meetingPoints, memberAt, memberStrategy, mergeAngle, meshPlan, meshWaypoints, minEffectOf, minHash, mingleBundles, minglePlan, mixedSegments, mixesCollections, mlsPoint, mlsebLines, mlsebPlan, modularity, modulesOf, mulberry32, namedObjectives, nearestCentre, nearestOnBoundary, nearestOnLine, nearestSide, nearestSkeleton, nestedGridBoard, nestedGroups, nestedPlan, nestingFaults, nestingViolations, nextRow, nodeAmbiguity, nodeIndex, nodeLinks, numberDimension, numberOf, occupiedArea, odCellPlan, odGroups, offsetLine, offsetPolyline, oneSteiner, openGaps, openLanes, optimaFile, optimumKey, optionDefaults, orderChanges, orderFragility, orderLanes, orderRows, orderStability, orderSuiteRows, orthogonality, outOfBoxes, outerGraph, outputGroups, outputHash, pHubPlan, pMedian, packedGridBoard, pairCosts, paramProblem, paretoFront, parseLines, partWithin, passSettings, pathPoints, peakCurvature, pickLevel, pickMoves, placePlug, planMoves, planOfResult, plugSpacing, plugStrategies, plugsRouter, pointAt, pointBoxDistance, pointDensity, pointGroups, pointOnSide, pointSegmentDistance, polylineLength, portOffset, portfolio, portfolioB1Spec, portfolioBand, portfolioFacts, portfolioKey, portfolioMember, portfolioMembers, portfolioName, portfolioRouter, portfolioStrategy, positionStability, powerPlan, preferences, pullApart, quadtreeGroups, quantile, quickBundles, quickBundlesPlan, randomInt, rankShares, rayToBox, recordLines, recordMoves, rectScattered, regionBounds, register, repairResult, repairStrategy, representative, rerouteRouter, rerouteStrategy, rerouteWays, resample, resistanceClusters, resistancePlan, resolvePorts, resultForm, resultHash, ridgeLines, ridgePlan, rng, roadClassPlan, roomDemand, roomLinks, roomOrderFaults, roomStrategies, roomValues, round3, roundCorners, roundedCurve, routeCrossings, routeInStableOrder, routeMeasures, routePortfolio, routeSeparation, routeStability, routeSymmetry, routerStrategies, routersOf, ruleCounts, runBenchmark, runKey, runPipeline, runProfile, runSuite, sameData, sampleCurve, samplesOf, sbebLines, sbebPlan, scaleClusters, scaleSpaceGroups, scaleSpacePlan, screen, screenInputs, screenSplit, searchPlan, searchScore, sectionsRouter, segmentBounds, segmentBoxDistance, segmentClusters, segmentDistance, segmentHitsBox, segmentHitsRect, segmentKey, segmentsCross, segmentsOf, selectBest, selectPlan, setManifest, settingAt, sha256Hex, shapeSkeleton, sharedRouter, sharedStretchStrategy, sharedStretches, sharedWaysAll, sharedWaysPlan, shuffleEdges, sideAngle, sideToward, signTestPower, silhouette, skeletonGroups, skeletonPath, slackSettings, slotJoin, slotPlan, slotsOf, smoothCurve, snapPlan, softDistance, softKeyClusters, softKeyPlan, softStep, spanningTree, spectralClusters, spectralLabels, spectralPlan, spinePart, spiralJoin, spiralPlan, spiralTree, splineCurve, splitFrames, splitPath, splitPoint, splitTargets, spreadEnds, spreadWith, stableIdGate, stableIdPlan, stableOrder, stableOrderStrategy, steinerPlan, stepRouterStrategy, stepRouters, stepSeed, straightCurve, straightRouter, strategyKey, streetEdges, streetPlan, streetPoint, streetRide, stretchGate, stretchLaneOrders, stretchPlan, stretchSettings, stubPlan, suiteFronts, sumFaults, summarize, summarizeGap, summarizeRows, swarmScore, sweepGrid, sweepOf, sweepRoom, sweepSections, sweepValues, symmetricEigen, territoryPlan, tightRouter, tightStrategy, toSvgPath, totalTurn, tracePlan, tracing, tracingLoads, tracingValue, traclusPlan, treeCost, treeDepth, trunksRouter, turns, turnsOf, unwrappedName, utf8, venationLevels, venationLinks, waySlacks, waypointWay, weberPoint, weiszfeldPlan, wholeBoard, wholeStrategies, withBends, withRepair, withStableOrder, wspdGroups, wspdPlan, zipLegs, zipperPlan };
39519
+ export { ADAM_DEFAULTS, ADAM_SCHEMA, AMBIGUITY_GUARD_DEFAULTS, ANNEALING_DEFAULTS, ANNEALING_SCHEMA, ARC_SOURCES, AREA_DEFAULTS, AREA_WIDTHS, ASSIGN_DEFAULTS, ATTRACTION_DEFAULTS, ATTRACTION_SCHEMA, BACKTRACK_MIN, BACKTRACK_SHARE, BALANCE_DEFAULTS, BALANCE_ENDS, BIPARTITE, BIPARTITE_LAYOUTS, BOARD_PORTFOLIO_DEFAULTS, BOARD_PORTFOLIO_SETTINGS, BREAK, BREAK_EVEN, BREAK_EVEN_DEFAULTS, BRONCHIAL_DEFAULTS, BRONCHIAL_SPLITS, BUILT_IN_STRATEGIES, BoundaryWalk, CANAL_DEFAULTS, CANAL_SCHEMA, CANDIDATE_SOURCES, CAP_SWEEP, CASES, CHANNEL_REACH, CLEARANCE_CAP, COMPARE_ALL_STEPS, COMPARE_STEPS, CONFLUENT_DEFAULTS, CONSENSUS_DEFAULTS, CONSENSUS_LINKAGES, CORRIDOR_DEFAULTS, COVERAGE_FAMILIES, CROSSBAR_DEFAULTS, CUBU_DEFAULTS, CURRICULUM_DEFAULTS, CURRICULUM_ORDERS, CURRICULUM_POOL, CURVE_TYPES, CUT_DEFAULTS, CornerGraph, DEFAULT_RNG, DENSE_GROUPS, DENSITY_KERNELS, DETAIL_DEFAULTS, DIFFUSION_DEFAULTS, DISTORTION_NEIGHBOURS, DIVIDED_DEFAULTS, EDGE_DISTANCES, EDGE_PATH_DEFAULTS, EXACT_CLASSES, EXACT_EDGES_PER_NODE, EXACT_V1, EXACT_V1_BOUNDS, EXACT_V1_BOUNDS_SIZES, EXACT_V1_SEEDS, EXACT_V1_SIZES, FAN_OUT, FDEB_DEFAULTS, FIREFLY_DEFAULTS, FIXTURES, FIXTURE_LAYOUTS, FIXTURE_LOCALITY, FLOW_CLUSTER_DEFAULTS, FLOW_FIELD_DEFAULTS, FRAME_ROWS, FRECHET_DEFAULTS, FRONT_OBJECTIVES, GAP_BASELINES, GAP_BOOTSTRAP, GAP_CAPS, GAP_DEFAULTS, GAP_RULES, GAP_VIEWS, GROUP_SCOPES, GROUP_SPINES, GROWTH_DEFAULTS, GROWTH_SWEEP, GUTTER_BEND_COST, Grid, HDBSCAN_DEFAULTS, HIERARCHY_DEFAULTS, HIERARCHY_GROUPS, HIGHWAYS_ENGINE, HIGHWAYS_FAMILIES, HIGHWAY_BASES, HIGHWAY_BASE_ROUTERS, HOUGH_DEFAULTS, HUB_ALLOCATIONS, HUB_ARC_DEFAULTS, HUB_CANDIDATES, HUB_CHAIN, HYSTERESIS_DEFAULTS, INTERCHANGE_BASES, INTERCHANGE_DEFAULTS, KDEEB_DEFAULTS, KD_DEFAULTS, KENDALL_EXACT, KNEE_DEFAULTS, KNEE_RULES, LANES_DEFAULTS, LANE_ORDERS, LANE_STRETCH_DEFAULTS, LANE_STRETCH_ORDERS, LANE_TRACKS, LEADER_DEFAULTS, LEADER_TYPES, LEVEL_HIGHWAY_DEFAULTS, LINKAGES, LINK_COMMUNITY_DEFAULTS, LOCAL_MESH, MANIFOLD_DEFAULTS, MANIFOLD_SIDES, MANY_GROUPS, MEASURES, MEASURE_BALANCE_REACH, MEASURE_LANE_WIDTH, MEET_DEFAULTS, MEMBER_FORMS, MESH_DEFAULTS, MINGLE_DEFAULTS, MIN_EFFECTS, MLSEB_DEFAULTS, MLS_FITS, MODULE_RULES, MOVE_NAMES, NESTED_DEFAULTS, OD_CELL_DEFAULTS, OUTSIDE_SLACK, Obstacles, PACKED_GRID, PICK, PIPELINE, PLAN_CHANGERS, PORTFOLIO_B1, PORTFOLIO_BANDS, PORTFOLIO_BASE_MEMBER, PORTFOLIO_CAPS, PORTFOLIO_FIT, PORTFOLIO_HASHES, PORTFOLIO_KS_API, PORTFOLIO_RULES, PORTFOLIO_SETS, POWER_DEFAULTS, PREFERENCE_DEFAULTS, PREFERENCE_FEATURES, PREFERENCE_PANEL, PROPOSED_RULES, P_HUB_DEFAULTS, QUICKBUNDLES_DEFAULTS, RECORD_EPS, REROUTE_DEFAULTS, REROUTE_SCHEMA, RESISTANCE_DEFAULTS, RIDGE_DEFAULTS, ROAD_CLASS_DEFAULTS, ROOM_FAMILIES, ROUTED_STEPS, ROUTERS, SAME_HIGHWAY, SBEB_DEFAULTS, SCALE_SPACE_DEFAULTS, SCREEN_FAMILY_BY, SCREEN_RULES, SCREEN_SETS, SCREEN_STEPS, SCREEN_V1, SCREEN_V2, SCREEN_V2_HELD_OUT, SCREEN_V3, SCREEN_V3_HELD_OUT, SCREEN_WRAPS, SEARCH_DEFAULTS, SEARCH_MOVES, SECTION_DEFAULTS, SELECT_DEFAULTS, SIDE_NORMAL, SKELETON_GROUPS, SKELETON_TARGETS, SLACK_SWEEP, SLOT_DEFAULTS, SLOT_KEYS, SMALL_CLUSTERED, SOFT_KEY_DEFAULTS, SPACING_LIMIT, SPARSE_LONG, SPECTRAL_DEFAULTS, SPIRAL_DEFAULTS, SPREAD_DEFAULTS, STABLE_ID_DEFAULTS, STEINER_DEFAULTS, STEINER_GROUPS, STEINER_METHODS, STEINER_METRICS, STEPS, STRATEGIES, STREET_DEFAULTS, STREET_SOURCES, STRETCH_DEFAULTS, STRETCH_SWEEP, STUB_DEFAULTS, SUITE_COLUMNS, SWARM_SEARCHES, SoftGraph, SplitTree, TERRITORY_DEFAULTS, TERRITORY_POINTS, THIN_BARS, TIGHT_DEFAULTS, TIGHT_FIRSTS, TIGHT_SCHEMA, TIGHT_TIES, TINY_UNIFORM, TOLERANCE, TRACING_DEFAULTS, TRACING_QUANTILES, TRACING_WEIGHTS, TRACLUS_DEFAULTS, TRAFFIC_RULES, TWO_HUBS, UNEVEN_SIZES, VENATION_BACKBONES, VENATION_DEFAULTS, WEISZFELD_DEFAULTS, WIDTH_SCALES, WSPD_DEFAULTS, WayFinder, ZIPPER_DEFAULTS, activationBase, activationReport, adamRouter, adamRoutes, adamStrategy, adjustedRandIndex, agreementOf, allSections, ambiguity, ambiguityGate, ambiguityPlan, annealWays, annealingRouter, annealingStrategy, arcCurve, arcsin, areaPlan, aroundBox, asRouter, assignEnds, assignWall, attraction, attractionRouter, attractionStrategy, backboneLinks, backtrackOf, balance, balanceOf, balancePlan, basePlan, benchBendRoom, benchCurveStrategies, benchCurves, benchHighways, benchLevelRoom, benchOne, benchRoom, benchRun, benchTracks, bendCount, bendingEnergy, bicliqueGroups, bigGroups, boardGaps, boardPortfolio, boardUnit, boardWins, bootstrapMedian, boundsFamilyName, boxDisplacement, boxExit, boxPush, boxUnit, boxesOverlap, breakEvenGate, breakEvenPlan, bronchialPlan, buildSections, bundleStretch, bundledShare, byCollection, byDataGroup, canalHubs, canalRouter, canalStrategy, canalWays, candidateArcs, candidatePlan, candidateScore, canonicalJson, capSettings, caseSeed, cellSteps, center, chainClusters, changeGraph, changedDimensions, channelOverflows, channelWidth, characteristicPoints, checkFamilies, checkLevels, checkOptions, checkParents, checkPlan, checkPort, checkProperties, checkSchema, checkSections, clampOptions, clothoidCurve, clusters1d, coAssociation, collapseLinks, commonEdges, compareAnyStep, compareMeasure, compareSpec, compareStep, compatibility, compatibilityMatrix, confluentPlan, consensusGroups, consensusLabels, consensusPlan, controlChain, cornersRouter, corpusManifest, corridorPlan, countCrossings, countOverlaps, createFamilyRegistry, createRegistry, crossbarPlan, crossesItself, crossingAngle, crossingAngles, cubicAt, cubuPlan, curriculum, curvatureVariation, curveDeviation, curveFaults, curveJoints, curveOf, curveRuns, curveSpace, curveStrategies, curvesHash, cutGroups, cutShare, declaresFamily, defineStrategy, denseGroupsBoard, densityPlan, densityRidges, depthLevels, detailLevels, detailWidth, detourSpread, diamondAngle, diffusionPlan, dimensionAt, dimensionUnit, dist, distanceDistortion, distinctPoints, divideHighway, dividedPlan, documentHash, dominates, drawnSegments, edgeBlocks, edgeEnds, edgeLengths, edgePathPlan, edgePaths, edgePlugs, edgeResistance, enumDimension, exactBoard, exactFamily, exactGenerator, exactSeed, exactStretch, exactStretches, exercised, facingEnds, familyCell, familyKind, familyOf, familySeed, farLevel, farLinks, fdebLines, fdebPlan, fft, findStrategy, finderOf, fireflies, firstLines, fitPreferences, fixPorts, fixedPortPoint, fixtureGroupOptions, fixtureParams, fixtureRngSeed, fixtureSpecOf, flowClusterPlan, flowClusters, flowDistance, flowFieldPlan, fnv1a32, follow, formatActivation, formatBendRoom, formatCapped, formatCurves, formatFaults, formatFronts, formatGap, formatHighways, formatInput, formatLevelRoom, formatLevels, formatMeasureTime, formatPaired, formatRoom, formatRules, formatScreen, formatSections, formatSummaries, formatTable, formatTracks, frameRoutes, frechet, frechetBundles, frechetPlan, freeLines, g1Defect, g2Defect, gapBaselines, gapCap, gapClosed, gapLaneCounts, gapLanes, gapNames, gapPlan, gapValues, gateStrategy, getStrategy, goesOutAndBack, graphInput, greedyBicliques, greedyCrossbars, groupEdges, groupFill, groupRun, groupSettings, groupStability, groupStrategies, groupedPlan, growthPlan, growthSettings, guardBreaks, gutterRoutes, halfPower, hasCusp, hdbscanClusters, hdbscanLabels, hdbscanPlan, hierarchyPlan, highwayChurn, highwayCurve, highwayCurves, highwayEdits, highwayIdKeep, highwayMoves, highwayRouter, highwayRouterStrategy, highwayRouters, highwayStrategies, highwaysRegistry, houghLines, houghPeaks, houghPlan, hubArcPlan, hubCandidates, hubChainBoard, hysteresisGate, hysteresisPlan, inflate, inflectionCount, inflectionCurve, inflectionSector, inkSaved, innerPoints, inputGraph, inputId, interchangeCandidates, interchangePlan, isRoutedStep, itemOverlaps, itemSizes, jaccard, jaccardIndex, jogPeaks, joinReports, jsonSafe, kMeans, kdGroups, kdeebLines, kdeebPlan, keepMembers, kendallTau, keyGroups, keyedSections, keysPlan, kneeOf, kneePlan, labelGroups, laneArea, laneCrossings, laneFidelity, laneOrder, laneRoutes, laneSeparation, laneSettings, laneStretches, lanesPlan, lanesThrough, leaderEnds, leafGroups, lengthBetween, levelCut, levelGrouping, levelHighwayPlan, levelMeasures, levelPairs, levelRatio, levelRoom, levelStrategies, levelsOf, lineDensity, linePoint, linkAgreement, linkCommunities, linkCommunityGroups, linkCommunityPlan, linkDensity, listOptions, listStrategies, madeUpPairs, makeFixture, makeRoom, manifestHash, manifoldPlan, manyGroupsBoard, matchHighways, maxStretch, mdf, meanLine, measureBoard, measureByKey, measureByName, measureHighways, measureJogs, measureLevel, measureLevelFaults, measureLinks, measureName, measureQuality, measureRouteFaults, measureStability, meetMember, meetPlan, meetingPoints, memberAt, memberStrategy, mergeAngle, meshPlan, meshWaypoints, minEffectOf, minHash, mingleBundles, minglePlan, mixedSegments, mixesCollections, mlsPoint, mlsebLines, mlsebPlan, modularity, modulesOf, moveResponse, mulberry32, namedObjectives, nearestCentre, nearestOnBoundary, nearestOnLine, nearestSide, nearestSkeleton, nestedGridBoard, nestedGroups, nestedPlan, nestingFaults, nestingViolations, nextRow, nodeAmbiguity, nodeIndex, nodeLinks, numberDimension, numberOf, occupiedArea, odCellPlan, odGroups, offsetLine, offsetPolyline, oneSteiner, openGaps, openLanes, optimaFile, optimumKey, optionDefaults, orderChanges, orderFragility, orderLanes, orderRows, orderStability, orderSuiteRows, orthogonality, outOfBoxes, outerGraph, outputGroups, outputHash, pHubPlan, pMedian, packedGridBoard, pairCosts, paramProblem, paretoFront, parseLines, partWithin, passSettings, pathPoints, peakCurvature, pickLevel, pickMoves, placePlug, planMoves, planOfResult, plugSpacing, plugStrategies, plugsRouter, pointAt, pointBoxDistance, pointDensity, pointGroups, pointOnSide, pointSegmentDistance, polylineLength, portOffset, portfolio, portfolioB1Spec, portfolioBand, portfolioFacts, portfolioKey, portfolioMember, portfolioMembers, portfolioName, portfolioRouter, portfolioStrategy, positionStability, powerPlan, preferences, pullApart, quadtreeGroups, quantile, quickBundles, quickBundlesPlan, randomInt, rankShares, rayToBox, recordLines, recordMoves, rectScattered, regionBounds, register, repairResult, repairStrategy, representative, rerouteRouter, rerouteStrategy, rerouteWays, resample, resistanceClusters, resistancePlan, resolvePorts, resultForm, resultHash, ridgeLines, ridgePlan, rng, roadClassPlan, roomDemand, roomLinks, roomOrderFaults, roomStrategies, roomValues, round3, roundCorners, roundedCurve, routeBalance, routeCrossings, routeInStableOrder, routeMeasures, routePortfolio, routeSeparation, routeStability, routeSymmetry, routerStrategies, routersOf, ruleCounts, runBenchmark, runKey, runPipeline, runProfile, runSuite, sameData, sampleCurve, samplesOf, sbebLines, sbebPlan, scaleClusters, scaleSpaceGroups, scaleSpacePlan, screen, screenInputs, screenSplit, searchPlan, searchScore, sectionsRouter, segmentBounds, segmentBoxDistance, segmentClusters, segmentDistance, segmentHitsBox, segmentHitsRect, segmentKey, segmentsCross, segmentsOf, selectBest, selectPlan, setManifest, settingAt, sha256Hex, shapeSkeleton, sharedRouter, sharedStretchStrategy, sharedStretches, sharedWaysAll, sharedWaysPlan, shuffleEdges, sideAngle, sideToward, signTestPower, silhouette, skeletonGroups, skeletonPath, slackSettings, slotJoin, slotPlan, slotsOf, smoothCurve, snapPlan, softDistance, softKeyClusters, softKeyPlan, softStep, spanningTree, spectralClusters, spectralLabels, spectralPlan, spinePart, spiralJoin, spiralPlan, spiralTree, splineCurve, splitFrames, splitPath, splitPoint, splitTargets, spreadEnds, spreadWith, stableIdGate, stableIdPlan, stableOrder, stableOrderStrategy, steinerPlan, stepRouterStrategy, stepRouters, stepSeed, straightCurve, straightRouter, strategyKey, streetEdges, streetPlan, streetPoint, streetRide, stretchGate, stretchLaneOrders, stretchPlan, stretchSettings, stubPlan, suiteFronts, sumFaults, summarize, summarizeGap, summarizeRows, swarmScore, sweepGrid, sweepOf, sweepRoom, sweepSections, sweepValues, symmetricEigen, territoryPlan, tightRouter, tightStrategy, toSvgPath, totalTurn, tracePlan, tracing, tracingLoads, tracingValue, traclusPlan, treeCost, treeDepth, trunksRouter, turns, turnsOf, unwrappedName, utf8, venationLevels, venationLinks, waySlacks, waypointWay, weberPoint, weiszfeldPlan, wholeBoard, wholeStrategies, withBends, withRepair, withStableOrder, wspdGroups, wspdPlan, zipLegs, zipperPlan };
38593
39520
 
38594
39521
  //# sourceMappingURL=index.js.map