@mark1russell7/highways 0.4.1 → 0.5.0
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/README.md +43 -1
- package/build/index.d.ts +1377 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8202 -1515
- package/build/index.js.map +1 -1
- package/package.json +3 -1
- package/src/bench/exact/index.ts +197 -0
- package/src/bench/measures/highways.ts +93 -0
- package/src/bench/measures/index.ts +2 -0
- package/src/bench/measures/lanes.ts +304 -0
- package/src/bench/measures/levels.ts +86 -2
- package/src/bench/screen/forms.ts +11 -5
- package/src/bench/screen/measures.ts +23 -0
- package/src/bench/screen/screen.ts +42 -0
- package/src/channels.ts +56 -0
- package/src/highways/base.ts +126 -0
- package/src/highways/diffusion/index.ts +103 -0
- package/src/highways/edge-paths/index.ts +155 -0
- package/src/highways/fixtures.test-util.ts +71 -0
- package/src/highways/flow-clusters/index.ts +143 -0
- package/src/highways/flow-field/index.ts +49 -0
- package/src/highways/gates/ambiguity/index.ts +110 -0
- package/src/highways/gates/breakeven/index.ts +62 -0
- package/src/highways/gates/hysteresis/index.ts +48 -0
- package/src/highways/gates/ids/index.ts +59 -0
- package/src/highways/gates/index.ts +6 -0
- package/src/highways/gates/model.ts +129 -0
- package/src/highways/gates/stretch/index.ts +39 -0
- package/src/highways/grouped.ts +123 -0
- package/src/highways/growth/index.ts +148 -0
- package/src/highways/hdbscan/index.ts +250 -0
- package/src/highways/hough/index.ts +182 -0
- package/src/highways/hub-arcs/index.ts +181 -0
- package/src/highways/index.ts +30 -0
- package/src/highways/lanes/index.ts +189 -0
- package/src/highways/level-links/index.ts +114 -0
- package/src/highways/meet/index.ts +84 -0
- package/src/highways/mingle/index.ts +171 -0
- package/src/highways/model.ts +5 -0
- package/src/highways/nested/index.ts +143 -0
- package/src/highways/od-cells/index.ts +50 -0
- package/src/highways/quickbundles/index.ts +117 -0
- package/src/highways/ridges/index.ts +328 -0
- package/src/highways/road-classes/index.ts +74 -0
- package/src/highways/router.ts +1 -1
- package/src/highways/select/index.ts +162 -0
- package/src/highways/snap.ts +110 -0
- package/src/highways/soft-keys/index.ts +114 -0
- package/src/highways/spiral/index.ts +205 -0
- package/src/highways/stretches.ts +141 -0
- package/src/highways/stubs/index.ts +117 -0
- package/src/highways/traclus/index.ts +296 -0
- package/src/highways/weiszfeld/index.ts +105 -0
- package/src/highways/wspd/index.ts +130 -0
- package/src/highways/zipper/index.ts +168 -0
- package/src/index.ts +1 -0
- package/src/routers/shared.ts +56 -3
- package/src/strategies/builtins.ts +303 -7
- package/src/strategies/schemas.ts +300 -0
- package/src/visibility.ts +5 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { boardUnit, segmentKey } from "../../bench/measures/routes.ts";
|
|
2
|
+
import { dist, type Point } from "../../geom.ts";
|
|
3
|
+
import { Grid, segmentBounds } from "../../grid.ts";
|
|
4
|
+
import { WayFinder } from "../../ways.ts";
|
|
5
|
+
import { basePlan, orderAlongRoutes, planRoutes, routeIndex, tidyPlan, type HighwayBase } from "../base.ts";
|
|
6
|
+
import { memberAt, type Highway, type HighwayInput, type HighwayPlan } from "../model.ts";
|
|
7
|
+
|
|
8
|
+
/** The options of the highway strategy `zipper` (NAT-022 with WILD-037 of the notes). */
|
|
9
|
+
export interface ZipperOptions {
|
|
10
|
+
/** The plan whose routes the zipper joins. The default is `shared-ways`. */
|
|
11
|
+
readonly from: HighwayBase;
|
|
12
|
+
/** Two legs zip when they are this distance apart or nearer, in units of the board (the median short side of the boxes). The default is 1. */
|
|
13
|
+
readonly near: number;
|
|
14
|
+
/** Two legs zip when the angle between them is this value or less, in degrees. The default is 15. */
|
|
15
|
+
readonly maxAngle: number;
|
|
16
|
+
/** The adhesion over the tension: two legs zip when `adhesion * overlap` is more than the length that the zip adds. The default is 0.3. */
|
|
17
|
+
readonly adhesion: number;
|
|
18
|
+
/** Two legs zip when they overlap along this length or more, in units of the board. The default is 1. */
|
|
19
|
+
readonly minOverlap: number;
|
|
20
|
+
/** The number of passes. Each pass routes the plan and zips the legs again. The default is 1. */
|
|
21
|
+
readonly passes: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The default options of the strategy `zipper`. */
|
|
25
|
+
export const ZIPPER_DEFAULTS: Readonly<ZipperOptions> = Object.freeze({ from: "shared-ways", near: 1, maxAngle: 15, adhesion: 0.3, minOverlap: 1, passes: 1 });
|
|
26
|
+
|
|
27
|
+
/** One leg of a route that no other route shares. */
|
|
28
|
+
interface Leg {
|
|
29
|
+
readonly edge: string;
|
|
30
|
+
readonly a: Point;
|
|
31
|
+
readonly b: Point;
|
|
32
|
+
readonly len: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A pair of legs that can zip, with the line of the zip. */
|
|
36
|
+
export interface Zip {
|
|
37
|
+
readonly gain: number;
|
|
38
|
+
/** The first point of the zipped line. */
|
|
39
|
+
readonly from: Point;
|
|
40
|
+
/** The last point of the zipped line. */
|
|
41
|
+
readonly to: Point;
|
|
42
|
+
/** This value is `true` when the leg goes from `from` to `to`, for each of the two legs. */
|
|
43
|
+
readonly forward: readonly [boolean, boolean];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The limits of `zipLegs`, in the units of the board. */
|
|
47
|
+
export interface ZipLimits {
|
|
48
|
+
readonly near: number;
|
|
49
|
+
readonly maxAngle: number;
|
|
50
|
+
readonly adhesion: number;
|
|
51
|
+
readonly minOverlap: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* This function tells if two legs zip (NAT-022), and gives the zipped line.
|
|
56
|
+
* The zipped line is the mean of the two legs, with the weight of their lengths, along the stretch where they overlap.
|
|
57
|
+
* The legs zip when the angle between them is `maxAngle` or less, and they are `near` or nearer along the overlap.
|
|
58
|
+
* The overlap must be `minOverlap` or more.
|
|
59
|
+
* The zip adds two jogs to each leg, so it adds the length `2 (d_a + d_b)`, with the distances of the legs to the zipped line.
|
|
60
|
+
* The legs zip only when `adhesion * overlap` is more than that length.
|
|
61
|
+
*/
|
|
62
|
+
export function zipLegs(a0: Point, a1: Point, b0: Point, b1: Point, o: ZipLimits): Zip | undefined {
|
|
63
|
+
const la = dist(a0, a1);
|
|
64
|
+
const lb = dist(b0, b1);
|
|
65
|
+
if (la <= 1e-9 || lb <= 1e-9) return undefined;
|
|
66
|
+
const ua = { x: (a1.x - a0.x) / la, y: (a1.y - a0.y) / la };
|
|
67
|
+
const ub = { x: (b1.x - b0.x) / lb, y: (b1.y - b0.y) / lb };
|
|
68
|
+
const dot = ua.x * ub.x + ua.y * ub.y;
|
|
69
|
+
if (Math.abs(dot) < Math.cos((o.maxAngle * Math.PI) / 180) - 1e-12) return undefined;
|
|
70
|
+
const sign = dot >= 0 ? 1 : -1;
|
|
71
|
+
const dx = ua.x * la + sign * ub.x * lb;
|
|
72
|
+
const dy = ua.y * la + sign * ub.y * lb;
|
|
73
|
+
const dl = Math.hypot(dx, dy);
|
|
74
|
+
if (dl <= 1e-9) return undefined;
|
|
75
|
+
const u = { x: dx / dl, y: dy / dl };
|
|
76
|
+
const o0 = { x: ((a0.x + a1.x) * la + (b0.x + b1.x) * lb) / (2 * (la + lb)), y: ((a0.y + a1.y) * la + (b0.y + b1.y) * lb) / (2 * (la + lb)) };
|
|
77
|
+
const along = (p: Point): number => (p.x - o0.x) * u.x + (p.y - o0.y) * u.y;
|
|
78
|
+
const across = (p: Point): number => (p.x - o0.x) * u.y - (p.y - o0.y) * u.x;
|
|
79
|
+
const ta = [along(a0), along(a1)] as const;
|
|
80
|
+
const tb = [along(b0), along(b1)] as const;
|
|
81
|
+
const lo = Math.max(Math.min(...ta), Math.min(...tb));
|
|
82
|
+
const hi = Math.min(Math.max(...ta), Math.max(...tb));
|
|
83
|
+
const overlap = hi - lo;
|
|
84
|
+
if (overlap < o.minOverlap || overlap <= 1e-9) return undefined;
|
|
85
|
+
// The offset of a leg from the zipped line at the place t along it.
|
|
86
|
+
const offset = (p0: Point, p1: Point, t0: number, t1: number, t: number): number => {
|
|
87
|
+
const s = Math.abs(t1 - t0) <= 1e-12 ? 0 : (t - t0) / (t1 - t0);
|
|
88
|
+
return across({ x: p0.x + (p1.x - p0.x) * s, y: p0.y + (p1.y - p0.y) * s });
|
|
89
|
+
};
|
|
90
|
+
let jog = 0;
|
|
91
|
+
for (const t of [lo, hi]) {
|
|
92
|
+
const da = offset(a0, a1, ta[0], ta[1], t);
|
|
93
|
+
const db = offset(b0, b1, tb[0], tb[1], t);
|
|
94
|
+
if (Math.abs(da - db) > o.near) return undefined;
|
|
95
|
+
jog += Math.abs(da) + Math.abs(db);
|
|
96
|
+
}
|
|
97
|
+
const gain = o.adhesion * overlap - jog;
|
|
98
|
+
if (!(gain > 0)) return undefined;
|
|
99
|
+
const from = { x: o0.x + u.x * lo, y: o0.y + u.y * lo };
|
|
100
|
+
const to = { x: o0.x + u.x * hi, y: o0.y + u.y * hi };
|
|
101
|
+
return { gain, from, to, forward: [ua.x * u.x + ua.y * u.y > 0, ub.x * u.x + ub.y * u.y > 0] };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* This function gives the highways of the plan `from`, with the legs that run near and nearly parallel zipped together (NAT-022, WILD-037).
|
|
106
|
+
* A pass routes the plan with `follow` and takes each leg that no other route shares. A grid of the legs finds the near pairs.
|
|
107
|
+
* The pairs that zip (`zipLegs`) go from the largest gain to the smallest, and each leg zips one time in a pass at most.
|
|
108
|
+
* A zipped line that goes into a box, made larger by 1, does not zip.
|
|
109
|
+
* Each zip is a new highway of two members. The `seq` of each member follows the place of its merge point along the route of the edge.
|
|
110
|
+
*/
|
|
111
|
+
export function zipperPlan(input: HighwayInput, o: Readonly<ZipperOptions>): HighwayPlan {
|
|
112
|
+
const graph = input.graph;
|
|
113
|
+
const unit = boardUnit(graph);
|
|
114
|
+
const limits: ZipLimits = { near: o.near * unit, maxAngle: o.maxAngle, adhesion: o.adhesion, minOverlap: Math.max(1e-6, o.minOverlap * unit) };
|
|
115
|
+
const obstacles = new WayFinder(graph.nodes).obstacles;
|
|
116
|
+
let plan = basePlan(input, o.from);
|
|
117
|
+
for (let pass = 0; pass < Math.max(1, Math.round(o.passes)); pass++) {
|
|
118
|
+
const routes = planRoutes(graph, plan).routes;
|
|
119
|
+
const users = new Map<string, number>();
|
|
120
|
+
for (const r of routes) {
|
|
121
|
+
const own = new Set<string>();
|
|
122
|
+
for (let i = 1; i < r.points.length; i++) {
|
|
123
|
+
const k = segmentKey(r.points[i - 1]!, r.points[i]!);
|
|
124
|
+
if (k !== undefined) own.add(k);
|
|
125
|
+
}
|
|
126
|
+
for (const k of own) users.set(k, (users.get(k) ?? 0) + 1);
|
|
127
|
+
}
|
|
128
|
+
const legs: Leg[] = [];
|
|
129
|
+
for (const r of routes)
|
|
130
|
+
for (let i = 1; i < r.points.length; i++) {
|
|
131
|
+
const a = r.points[i - 1]!;
|
|
132
|
+
const b = r.points[i]!;
|
|
133
|
+
const k = segmentKey(a, b);
|
|
134
|
+
const len = dist(a, b);
|
|
135
|
+
if (k === undefined || (users.get(k) ?? 0) > 1 || len < limits.minOverlap) continue;
|
|
136
|
+
legs.push({ edge: r.edge, a, b, len });
|
|
137
|
+
}
|
|
138
|
+
const grid = new Grid<number>(Math.max(32, 4 * limits.near));
|
|
139
|
+
legs.forEach((l, i) => {
|
|
140
|
+
const bx = segmentBounds(l.a, l.b);
|
|
141
|
+
grid.add(i, { x: bx.x - limits.near, y: bx.y - limits.near, w: bx.w + 2 * limits.near, h: bx.h + 2 * limits.near });
|
|
142
|
+
});
|
|
143
|
+
const found: { i: number; j: number; zip: Zip }[] = [];
|
|
144
|
+
legs.forEach((l, i) => {
|
|
145
|
+
grid.query(segmentBounds(l.a, l.b), (j) => {
|
|
146
|
+
if (j <= i || legs[j]!.edge === l.edge) return;
|
|
147
|
+
const m = legs[j]!;
|
|
148
|
+
const zip = zipLegs(l.a, l.b, m.a, m.b, limits);
|
|
149
|
+
if (zip) found.push({ i, j, zip });
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
found.sort((p, q) => q.zip.gain - p.zip.gain || p.i - q.i || p.j - q.j);
|
|
153
|
+
const used = new Set<number>();
|
|
154
|
+
const added: Highway[] = [];
|
|
155
|
+
for (const { i, j, zip } of found) {
|
|
156
|
+
if (used.has(i) || used.has(j)) continue;
|
|
157
|
+
if (!obstacles.clear(zip.from, zip.to, 1)) continue;
|
|
158
|
+
used.add(i);
|
|
159
|
+
used.add(j);
|
|
160
|
+
const spine = [zip.from, zip.to];
|
|
161
|
+
const members = [legs[i]!, legs[j]!].map((l, k) => (zip.forward[k] ? memberAt(spine, l.edge, 0, 1) : memberAt(spine, l.edge, 1, 0)));
|
|
162
|
+
added.push({ id: "", key: `zip${pass}`, spine, lanes: members.map((m) => m.edge), members });
|
|
163
|
+
}
|
|
164
|
+
if (added.length === 0) break;
|
|
165
|
+
plan = tidyPlan(orderAlongRoutes({ highways: [...plan.highways, ...added] }, routeIndex(routes)));
|
|
166
|
+
}
|
|
167
|
+
return tidyPlan(plan);
|
|
168
|
+
}
|
package/src/index.ts
CHANGED
package/src/routers/shared.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { center, dist, type Point } from "../geom.ts";
|
|
2
2
|
import { nodeIndex, type Edge, type Graph, type GroupRule, type Result, type Route, type Router, type SharedRun } from "../model.ts";
|
|
3
|
+
import { channelWidth } from "../channels.ts";
|
|
3
4
|
import { placePlug, type Plug } from "../plugs.ts";
|
|
4
5
|
import { buildSections, type Section } from "../sections.ts";
|
|
5
6
|
import { orderLanes, type RunOrder } from "../segments.ts";
|
|
@@ -44,6 +45,20 @@ export interface SharedOptions extends WayOptions {
|
|
|
44
45
|
* With `snell`, the route meets the run at the angle `acos(d)`, with `d` the cost share of the run for the route (NAT-035).
|
|
45
46
|
*/
|
|
46
47
|
readonly merge?: ShareMerge;
|
|
48
|
+
/**
|
|
49
|
+
* The pull of the busy links of the pass before (HWY-005, Winding Roads). With a value above 0 and two passes or more, each later pass routes each edge again from no ways.
|
|
50
|
+
* A link then costs `len / (1 + reuse * u)^reuseExponent`, with `u` the edges of the key on it in the pass before. The default is 0: the later passes are rip-up passes.
|
|
51
|
+
*/
|
|
52
|
+
readonly reuse?: number;
|
|
53
|
+
/** The exponent of the pull of `reuse`. The default is 1. */
|
|
54
|
+
readonly reuseExponent?: number;
|
|
55
|
+
/**
|
|
56
|
+
* The cost of a lane that does not fit in its channel (HWY-016). One more edge on a link with `f` edges of the key costs `capacity * max(0, (f + 1) * laneWidth - width)` more.
|
|
57
|
+
* `width` is the free space across the link (`channelWidth`). The default is 0: the router does not read the channels.
|
|
58
|
+
*/
|
|
59
|
+
readonly capacity?: number;
|
|
60
|
+
/** The width of one lane, for `capacity`. The default is 4. */
|
|
61
|
+
readonly laneWidth?: number;
|
|
47
62
|
/** The benchmark skips a fixture with more nodes. */
|
|
48
63
|
readonly maxNodes?: number;
|
|
49
64
|
}
|
|
@@ -94,6 +109,10 @@ export function sharedRouter(options: SharedOptions): Router {
|
|
|
94
109
|
// The cost share of a link for one more edge, when `f` edges of the key use it already. The share falls when `f` grows.
|
|
95
110
|
const share = (f: number): number => (f <= 0 ? 1 : flow ? (f + 1) ** alpha - f ** alpha : discount);
|
|
96
111
|
const passes = Math.max(1, Math.floor(options.passes ?? 1));
|
|
112
|
+
const reuse = Math.max(0, options.reuse ?? 0);
|
|
113
|
+
const reuseExponent = Math.max(0, options.reuseExponent ?? 1);
|
|
114
|
+
const capacity = Math.max(0, options.capacity ?? 0);
|
|
115
|
+
const laneWidth = Math.max(0, options.laneWidth ?? 4);
|
|
97
116
|
const keyOf = (e: Edge): string => {
|
|
98
117
|
if (!options.rule) return "*";
|
|
99
118
|
return options.rule(e) ?? `\u0000${e.id}`;
|
|
@@ -131,14 +150,34 @@ export function sharedRouter(options: SharedOptions): Router {
|
|
|
131
150
|
const order = orderWays(all, options.order ?? "span", graph);
|
|
132
151
|
// The largest number of edges on one link of each key. The cost share of that number is the floor of the search.
|
|
133
152
|
const top = new Map<string, number>();
|
|
134
|
-
|
|
153
|
+
// The width of the channel of each link, for the option `capacity`. Each link gets its width at its first use.
|
|
154
|
+
const widths = new Map<number, number>();
|
|
155
|
+
const widthOf = (i: number, j: number): number => {
|
|
156
|
+
const k = link(i, j);
|
|
157
|
+
let wd = widths.get(k);
|
|
158
|
+
if (wd === undefined) widths.set(k, (wd = channelWidth(ways.obstacles, corners.points[i]!, corners.points[j]!)));
|
|
159
|
+
return wd;
|
|
160
|
+
};
|
|
161
|
+
/** This function routes one way. `before` holds the edges of the key on each link in the pass before, for the option `reuse`. */
|
|
162
|
+
const route = (w: Way, before?: ReadonlyMap<number, number>): void => {
|
|
135
163
|
let used = discounted.get(w.key);
|
|
136
164
|
if (!used) discounted.set(w.key, (used = new Map()));
|
|
137
165
|
const mine = used;
|
|
138
|
-
|
|
139
|
-
|
|
166
|
+
let floor = flow ? share(top.get(w.key) ?? 0) : discount;
|
|
167
|
+
let cost = flow
|
|
140
168
|
? (i: number, j: number, d: number): number => d * share(mine.get(link(i, j))?.length ?? 0)
|
|
141
169
|
: (i: number, j: number, d: number): number => (mine.has(link(i, j)) ? d * discount : d);
|
|
170
|
+
if (capacity > 0) {
|
|
171
|
+
const plain = cost;
|
|
172
|
+
cost = (i, j, d) => plain(i, j, d) + capacity * Math.max(0, ((mine.get(link(i, j))?.length ?? 0) + 1) * laneWidth - widthOf(i, j));
|
|
173
|
+
}
|
|
174
|
+
if (before !== undefined) {
|
|
175
|
+
const plain = cost;
|
|
176
|
+
let most = 0;
|
|
177
|
+
for (const u of before.values()) most = Math.max(most, u);
|
|
178
|
+
cost = (i, j, d) => plain(i, j, d) / (1 + reuse * (before.get(link(i, j)) ?? 0)) ** reuseExponent;
|
|
179
|
+
floor /= (1 + reuse * most) ** reuseExponent;
|
|
180
|
+
}
|
|
142
181
|
const way = corners.path(ways.out(w.ps), ways.out(w.pt), cost, floor);
|
|
143
182
|
if (!way) return;
|
|
144
183
|
w.found = true;
|
|
@@ -157,6 +196,20 @@ export function sharedRouter(options: SharedOptions): Router {
|
|
|
157
196
|
for (const w of order) route(w);
|
|
158
197
|
// Each later pass takes each edge off its links and finds its way again, with the ways of the other edges fixed.
|
|
159
198
|
for (let pass = 1; pass < passes; pass++) {
|
|
199
|
+
if (reuse > 0) {
|
|
200
|
+
// Winding Roads (HWY-005): each edge finds its way again from no ways, and the busy links of the pass before pull it.
|
|
201
|
+
const before = new Map<string, Map<number, number>>();
|
|
202
|
+
for (const [key, links] of discounted) before.set(key, new Map([...links].map(([k, list]) => [k, list.length])));
|
|
203
|
+
discounted.clear();
|
|
204
|
+
top.clear();
|
|
205
|
+
for (const w of order) {
|
|
206
|
+
w.found = false;
|
|
207
|
+
w.seq = [];
|
|
208
|
+
w.ids = [];
|
|
209
|
+
}
|
|
210
|
+
for (const w of order) route(w, before.get(w.key) ?? new Map());
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
160
213
|
for (const w of order) {
|
|
161
214
|
if (!w.found) continue;
|
|
162
215
|
const mine = discounted.get(w.key)!;
|
|
@@ -12,9 +12,40 @@ import { KD_DEFAULTS, kdGroups, type KdOptions } from "../groups/kd/index.ts";
|
|
|
12
12
|
import { byDataGroup, keyGroups, type KeyOptions } from "../groups/keys/index.ts";
|
|
13
13
|
import { quadtreeGroups } from "../groups/quadtree/index.ts";
|
|
14
14
|
import { CORRIDOR_DEFAULTS, corridorPlan, type CorridorOptions } from "../highways/corridors/index.ts";
|
|
15
|
+
import { ambiguityGate, type AmbiguityGuardOptions } from "../highways/gates/ambiguity/index.ts";
|
|
16
|
+
import { breakEvenGate, type BreakEvenOptions } from "../highways/gates/breakeven/index.ts";
|
|
17
|
+
import { hysteresisGate, type HysteresisOptions } from "../highways/gates/hysteresis/index.ts";
|
|
18
|
+
import { stableIdGate, type StableIdOptions } from "../highways/gates/ids/index.ts";
|
|
19
|
+
import { gateStrategy } from "../highways/gates/model.ts";
|
|
20
|
+
import { stretchGate, type StretchOptions } from "../highways/gates/stretch/index.ts";
|
|
21
|
+
import { DIFFUSION_DEFAULTS, diffusionPlan, type DiffusionOptions } from "../highways/diffusion/index.ts";
|
|
22
|
+
import { FLOW_CLUSTER_DEFAULTS, flowClusterPlan, type FlowClusterOptions } from "../highways/flow-clusters/index.ts";
|
|
23
|
+
import { EDGE_PATH_DEFAULTS, edgePathPlan, type EdgePathOptions } from "../highways/edge-paths/index.ts";
|
|
24
|
+
import { HDBSCAN_DEFAULTS, hdbscanPlan, type HdbscanOptions } from "../highways/hdbscan/index.ts";
|
|
25
|
+
import { HOUGH_DEFAULTS, houghPlan, type HoughOptions } from "../highways/hough/index.ts";
|
|
26
|
+
import { HUB_ARC_DEFAULTS, hubArcPlan, type HubArcOptions } from "../highways/hub-arcs/index.ts";
|
|
27
|
+
import { RIDGE_DEFAULTS, ridgePlan, type RidgeOptions } from "../highways/ridges/index.ts";
|
|
28
|
+
import { SOFT_KEY_DEFAULTS, softKeyPlan, type SoftKeyOptions } from "../highways/soft-keys/index.ts";
|
|
29
|
+
import { FLOW_FIELD_DEFAULTS, flowFieldPlan, type FlowFieldOptions } from "../highways/flow-field/index.ts";
|
|
15
30
|
import { keysPlan, type KeysOptions } from "../highways/keys/index.ts";
|
|
31
|
+
import { LEVEL_HIGHWAY_DEFAULTS, levelHighwayPlan, type LevelHighwayOptions } from "../highways/level-links/index.ts";
|
|
32
|
+
import { MEET_DEFAULTS, meetPlan, type MeetOptions } from "../highways/meet/index.ts";
|
|
33
|
+
import { MINGLE_DEFAULTS, minglePlan, type MingleOptions } from "../highways/mingle/index.ts";
|
|
16
34
|
import type { HighwayInput } from "../highways/model.ts";
|
|
35
|
+
import { OD_CELL_DEFAULTS, odCellPlan, type OdCellOptions } from "../highways/od-cells/index.ts";
|
|
36
|
+
import { QUICKBUNDLES_DEFAULTS, quickBundlesPlan, type QuickBundlesOptions } from "../highways/quickbundles/index.ts";
|
|
37
|
+
import { NESTED_DEFAULTS, nestedPlan, type NestedOptions } from "../highways/nested/index.ts";
|
|
38
|
+
import { LANES_DEFAULTS, lanesPlan, type LanesOptions } from "../highways/lanes/index.ts";
|
|
39
|
+
import { GROWTH_DEFAULTS, growthPlan, type GrowthOptions } from "../highways/growth/index.ts";
|
|
40
|
+
import { ROAD_CLASS_DEFAULTS, roadClassPlan, type RoadClassOptions } from "../highways/road-classes/index.ts";
|
|
41
|
+
import { SELECT_DEFAULTS, selectPlan, type SelectOptions } from "../highways/select/index.ts";
|
|
17
42
|
import { sharedWaysPlan, type SharedWaysOptions } from "../highways/shared-ways/index.ts";
|
|
43
|
+
import { SPIRAL_DEFAULTS, spiralPlan, type SpiralOptions } from "../highways/spiral/index.ts";
|
|
44
|
+
import { STUB_DEFAULTS, stubPlan, type StubOptions } from "../highways/stubs/index.ts";
|
|
45
|
+
import { TRACLUS_DEFAULTS, traclusPlan, type TraclusOptions } from "../highways/traclus/index.ts";
|
|
46
|
+
import { WEISZFELD_DEFAULTS, weiszfeldPlan, type WeiszfeldOptions } from "../highways/weiszfeld/index.ts";
|
|
47
|
+
import { WSPD_DEFAULTS, wspdPlan, type WspdOptions } from "../highways/wspd/index.ts";
|
|
48
|
+
import { ZIPPER_DEFAULTS, zipperPlan, type ZipperOptions } from "../highways/zipper/index.ts";
|
|
18
49
|
import type { Graph, Result, Router } from "../model.ts";
|
|
19
50
|
import { roomDemand, type RoomMethod, type RoomOptions } from "../room/index.ts";
|
|
20
51
|
import { levelRoom, type LevelRoomOptions } from "../room/levels/index.ts";
|
|
@@ -27,7 +58,7 @@ import { byCollection, trunksRouter, type TrunksOptions } from "../routers/trunk
|
|
|
27
58
|
import { buildSections, SECTION_DEFAULTS, type SectionOptions } from "../sections.ts";
|
|
28
59
|
import { createRegistry, defineStrategy, type AnyStrategy, type CurveInput, type LaneBoard, type Registry, type Strategy } from "./model.ts";
|
|
29
60
|
import type { OptionSchema } from "./options.ts";
|
|
30
|
-
import { CLOTHOID_SCHEMA, CORNERS_SCHEMA, CORRIDOR_SCHEMA, CUT_SCHEMA, HIGHWAY_SCHEMA, KD_SCHEMA, KEY_SCHEMA, LEVEL_ROOM_SCHEMA, RADIUS_SCHEMA, ROOM_SCHEMA, SECTION_SCHEMA, SECTIONS_SCHEMA, SHARED_SCHEMA, SMOOTH_SCHEMA, TENSION_SCHEMA, TRUNKS_SCHEMA } from "./schemas.ts";
|
|
61
|
+
import { CLOTHOID_SCHEMA, CORNERS_SCHEMA, CORRIDOR_SCHEMA, CUT_SCHEMA, DIFFUSION_SCHEMA, EDGE_PATH_SCHEMA, FLOW_CLUSTER_SCHEMA, FLOW_FIELD_SCHEMA, GROWTH_SCHEMA, HDBSCAN_SCHEMA, HIGHWAY_SCHEMA, HOUGH_SCHEMA, HUB_ARC_SCHEMA, KD_SCHEMA, KEY_SCHEMA, LANES_SCHEMA, LEVEL_HIGHWAY_SCHEMA, LEVEL_ROOM_SCHEMA, MEET_SCHEMA, MINGLE_SCHEMA, NESTED_SCHEMA, OD_CELL_SCHEMA, QUICKBUNDLES_SCHEMA, RADIUS_SCHEMA, RIDGE_SCHEMA, ROAD_CLASS_SCHEMA, ROOM_SCHEMA, SECTION_SCHEMA, SELECT_SCHEMA, SECTIONS_SCHEMA, SHARED_SCHEMA, SMOOTH_SCHEMA, SOFT_KEY_SCHEMA, SPIRAL_SCHEMA, STUB_SCHEMA, TENSION_SCHEMA, TRACLUS_SCHEMA, TRUNKS_SCHEMA, WEISZFELD_SCHEMA, WSPD_SCHEMA, ZIPPER_SCHEMA } from "./schemas.ts";
|
|
31
62
|
|
|
32
63
|
/** The options of a router strategy: the options of its router, without the name and the limit of the benchmark. */
|
|
33
64
|
export type RouterPreset<O> = Omit<O, "name" | "maxNodes">;
|
|
@@ -340,14 +371,57 @@ export const groupStrategies: {
|
|
|
340
371
|
}),
|
|
341
372
|
});
|
|
342
373
|
|
|
374
|
+
/** The highway strategy `corridors` (HWY-031): the base of the built-in gates. */
|
|
375
|
+
const corridors: Strategy<"highway", CorridorOptions> = defineStrategy({
|
|
376
|
+
step: "highway",
|
|
377
|
+
name: "corridors",
|
|
378
|
+
describe: "Corridor signatures. Edges whose first ways share long links make a cluster, found with MinHash. The longest common run of each cluster is its highway.",
|
|
379
|
+
defaults: { ...CORRIDOR_DEFAULTS } as CorridorOptions,
|
|
380
|
+
schema: CORRIDOR_SCHEMA,
|
|
381
|
+
maxNodes: 10_000,
|
|
382
|
+
run: (input: HighwayInput, o: Readonly<CorridorOptions>) => corridorPlan(input, o),
|
|
383
|
+
});
|
|
384
|
+
|
|
343
385
|
/**
|
|
344
386
|
* The built-in highway strategies: the decisions of the routers `trunks` and `shared` as plans, and corridor signatures (HWY-031).
|
|
387
|
+
* The strategies `corridors+<gate>` put a gate of BUILD-W2A after `corridors`: `breakeven` (HWY-047), `stretch` (HWY-048), `ambiguity` (HWY-049),
|
|
388
|
+
* `hysteresis` (HWY-046) and `ids` (HWY-054). `gateStrategy` puts a gate after any other plan.
|
|
345
389
|
* `follow` routes the edges along a plan, and `highwayRouter` gives a router that does both.
|
|
346
390
|
*/
|
|
347
391
|
export const highwayStrategies: {
|
|
348
392
|
readonly keys: Strategy<"highway", KeysOptions>;
|
|
349
393
|
readonly "shared-ways": Strategy<"highway", SharedWaysOptions>;
|
|
350
394
|
readonly corridors: Strategy<"highway", CorridorOptions>;
|
|
395
|
+
readonly meet: Strategy<"highway", MeetOptions>;
|
|
396
|
+
readonly weiszfeld: Strategy<"highway", WeiszfeldOptions>;
|
|
397
|
+
readonly stubs: Strategy<"highway", StubOptions>;
|
|
398
|
+
readonly zipper: Strategy<"highway", ZipperOptions>;
|
|
399
|
+
readonly "level-links": Strategy<"highway", LevelHighwayOptions>;
|
|
400
|
+
readonly quickbundles: Strategy<"highway", QuickBundlesOptions>;
|
|
401
|
+
readonly "road-classes": Strategy<"highway", RoadClassOptions>;
|
|
402
|
+
readonly "flow-field": Strategy<"highway", FlowFieldOptions>;
|
|
403
|
+
readonly wspd: Strategy<"highway", WspdOptions>;
|
|
404
|
+
readonly "od-cells": Strategy<"highway", OdCellOptions>;
|
|
405
|
+
readonly select: Strategy<"highway", SelectOptions>;
|
|
406
|
+
readonly mingle: Strategy<"highway", MingleOptions>;
|
|
407
|
+
readonly spiral: Strategy<"highway", SpiralOptions>;
|
|
408
|
+
readonly "flow-clusters": Strategy<"highway", FlowClusterOptions>;
|
|
409
|
+
readonly traclus: Strategy<"highway", TraclusOptions>;
|
|
410
|
+
readonly diffusion: Strategy<"highway", DiffusionOptions>;
|
|
411
|
+
readonly nested: Strategy<"highway", NestedOptions>;
|
|
412
|
+
readonly lanes: Strategy<"highway", LanesOptions>;
|
|
413
|
+
readonly growth: Strategy<"highway", GrowthOptions>;
|
|
414
|
+
readonly "edge-paths": Strategy<"highway", EdgePathOptions>;
|
|
415
|
+
readonly "soft-keys": Strategy<"highway", SoftKeyOptions>;
|
|
416
|
+
readonly hough: Strategy<"highway", HoughOptions>;
|
|
417
|
+
readonly ridges: Strategy<"highway", RidgeOptions>;
|
|
418
|
+
readonly hdbscan: Strategy<"highway", HdbscanOptions>;
|
|
419
|
+
readonly "hub-arcs": Strategy<"highway", HubArcOptions>;
|
|
420
|
+
readonly "corridors+breakeven": Strategy<"highway", CorridorOptions & BreakEvenOptions>;
|
|
421
|
+
readonly "corridors+stretch": Strategy<"highway", CorridorOptions & StretchOptions>;
|
|
422
|
+
readonly "corridors+ambiguity": Strategy<"highway", CorridorOptions & AmbiguityGuardOptions>;
|
|
423
|
+
readonly "corridors+hysteresis": Strategy<"highway", CorridorOptions & HysteresisOptions>;
|
|
424
|
+
readonly "corridors+ids": Strategy<"highway", CorridorOptions & StableIdOptions>;
|
|
351
425
|
} = Object.freeze({
|
|
352
426
|
keys: defineStrategy({
|
|
353
427
|
step: "highway",
|
|
@@ -367,15 +441,237 @@ export const highwayStrategies: {
|
|
|
367
441
|
maxNodes: 10_000,
|
|
368
442
|
run: (input: HighwayInput, o: Readonly<SharedWaysOptions>) => sharedWaysPlan(input, o),
|
|
369
443
|
}),
|
|
370
|
-
corridors
|
|
444
|
+
corridors,
|
|
445
|
+
meet: defineStrategy({
|
|
446
|
+
step: "highway",
|
|
447
|
+
name: "meet",
|
|
448
|
+
describe: "Meet points (HWY-050). Each member of the plan `from` joins its spine where its first route meets the spine, and leaves where they part.",
|
|
449
|
+
defaults: { ...MEET_DEFAULTS } as MeetOptions,
|
|
450
|
+
schema: MEET_SCHEMA,
|
|
451
|
+
maxNodes: 10_000,
|
|
452
|
+
run: (input: HighwayInput, o: Readonly<MeetOptions>) => meetPlan(input, o),
|
|
453
|
+
}),
|
|
454
|
+
weiszfeld: defineStrategy({
|
|
455
|
+
step: "highway",
|
|
456
|
+
name: "weiszfeld",
|
|
457
|
+
describe: "Weiszfeld meeting points (HWY-043). Each highway of the plan `from` gets the merge and split points with the least ink, with a weighted trunk.",
|
|
458
|
+
defaults: { ...WEISZFELD_DEFAULTS } as WeiszfeldOptions,
|
|
459
|
+
schema: WEISZFELD_SCHEMA,
|
|
460
|
+
maxNodes: 10_000,
|
|
461
|
+
run: (input: HighwayInput, o: Readonly<WeiszfeldOptions>) => weiszfeldPlan(input, o),
|
|
462
|
+
}),
|
|
463
|
+
stubs: defineStrategy({
|
|
464
|
+
step: "highway",
|
|
465
|
+
name: "stubs",
|
|
466
|
+
describe: "Stubs (HWY-015, WILD-005). The ends on one side of a box that go in near directions share one plug and one short stub.",
|
|
467
|
+
defaults: { ...STUB_DEFAULTS } as StubOptions,
|
|
468
|
+
schema: STUB_SCHEMA,
|
|
469
|
+
maxNodes: 10_000,
|
|
470
|
+
run: (input: HighwayInput, o: Readonly<StubOptions>) => stubPlan(input, o),
|
|
471
|
+
}),
|
|
472
|
+
zipper: defineStrategy({
|
|
473
|
+
step: "highway",
|
|
474
|
+
name: "zipper",
|
|
475
|
+
describe: "The axon zipper (NAT-022, WILD-037). Two near, nearly parallel legs zip into one line when the shared length is more than the jogs.",
|
|
476
|
+
defaults: { ...ZIPPER_DEFAULTS } as ZipperOptions,
|
|
477
|
+
schema: ZIPPER_SCHEMA,
|
|
478
|
+
maxNodes: 10_000,
|
|
479
|
+
run: (input: HighwayInput, o: Readonly<ZipperOptions>) => zipperPlan(input, o),
|
|
480
|
+
}),
|
|
481
|
+
"level-links": defineStrategy({
|
|
482
|
+
step: "highway",
|
|
483
|
+
name: "level-links",
|
|
484
|
+
describe: "Level links as highways (HWY-056, WILD-034). The edges between two items of a zoom level ride the link of the level.",
|
|
485
|
+
defaults: { ...LEVEL_HIGHWAY_DEFAULTS } as LevelHighwayOptions,
|
|
486
|
+
schema: LEVEL_HIGHWAY_SCHEMA,
|
|
487
|
+
maxNodes: 10_000,
|
|
488
|
+
run: (input: HighwayInput, o: Readonly<LevelHighwayOptions>) => levelHighwayPlan(input, o),
|
|
489
|
+
}),
|
|
490
|
+
quickbundles: defineStrategy({
|
|
491
|
+
step: "highway",
|
|
492
|
+
name: "quickbundles",
|
|
493
|
+
describe: "QuickBundles (HWY-026). One pass puts each edge in the nearest cluster of resampled lines. Each cluster gets highways.",
|
|
494
|
+
defaults: { ...QUICKBUNDLES_DEFAULTS } as QuickBundlesOptions,
|
|
495
|
+
schema: QUICKBUNDLES_SCHEMA,
|
|
496
|
+
maxNodes: 10_000,
|
|
497
|
+
run: (input: HighwayInput, o: Readonly<QuickBundlesOptions>) => quickBundlesPlan(input, o),
|
|
498
|
+
}),
|
|
499
|
+
"road-classes": defineStrategy({
|
|
500
|
+
step: "highway",
|
|
501
|
+
name: "road-classes",
|
|
502
|
+
describe: "Road classes (HWY-034, EDA-050). The links that most ways use become main roads, and a second pass routes the edges onto them.",
|
|
503
|
+
defaults: { ...ROAD_CLASS_DEFAULTS } as RoadClassOptions,
|
|
504
|
+
schema: ROAD_CLASS_SCHEMA,
|
|
505
|
+
maxNodes: 10_000,
|
|
506
|
+
run: (input: HighwayInput, o: Readonly<RoadClassOptions>) => roadClassPlan(input, o),
|
|
507
|
+
}),
|
|
508
|
+
"flow-field": defineStrategy({
|
|
509
|
+
step: "highway",
|
|
510
|
+
name: "flow-field",
|
|
511
|
+
describe: "A flow field for each target (EDA-046, HWY-037). The edges into one side of a box merge like streams on shortest ways to one root.",
|
|
512
|
+
defaults: { ...FLOW_FIELD_DEFAULTS } as FlowFieldOptions,
|
|
513
|
+
schema: FLOW_FIELD_SCHEMA,
|
|
514
|
+
maxNodes: 10_000,
|
|
515
|
+
run: (input: HighwayInput, o: Readonly<FlowFieldOptions>) => flowFieldPlan(input, o),
|
|
516
|
+
}),
|
|
517
|
+
wspd: defineStrategy({
|
|
518
|
+
step: "highway",
|
|
519
|
+
name: "wspd",
|
|
520
|
+
describe: "Well-separated pairs (HWY-018). The edges between the two sets of one pair share one highway.",
|
|
521
|
+
defaults: { ...WSPD_DEFAULTS } as WspdOptions,
|
|
522
|
+
schema: WSPD_SCHEMA,
|
|
523
|
+
maxNodes: 10_000,
|
|
524
|
+
run: (input: HighwayInput, o: Readonly<WspdOptions>) => wspdPlan(input, o),
|
|
525
|
+
}),
|
|
526
|
+
"od-cells": defineStrategy({
|
|
527
|
+
step: "highway",
|
|
528
|
+
name: "od-cells",
|
|
529
|
+
describe: "Origin and destination cells (EDA-033, WILD-032). The edges between two cells of a zoom level share one trunk.",
|
|
530
|
+
defaults: { ...OD_CELL_DEFAULTS } as OdCellOptions,
|
|
531
|
+
schema: OD_CELL_SCHEMA,
|
|
532
|
+
maxNodes: 10_000,
|
|
533
|
+
run: (input: HighwayInput, o: Readonly<OdCellOptions>) => odCellPlan(input, o),
|
|
534
|
+
}),
|
|
535
|
+
select: defineStrategy({
|
|
536
|
+
step: "highway",
|
|
537
|
+
name: "select",
|
|
538
|
+
describe: "Candidate generation and selection (HWY-045). Some strategies propose highways, and a greedy pass with swaps keeps the best set.",
|
|
539
|
+
defaults: { ...SELECT_DEFAULTS } as SelectOptions,
|
|
540
|
+
schema: SELECT_SCHEMA,
|
|
541
|
+
maxNodes: 10_000,
|
|
542
|
+
run: (input: HighwayInput, o: Readonly<SelectOptions>) => selectPlan(input, o),
|
|
543
|
+
}),
|
|
544
|
+
mingle: defineStrategy({
|
|
545
|
+
step: "highway",
|
|
546
|
+
name: "mingle",
|
|
547
|
+
describe: "MINGLE (HWY-003). Edges are points in 4D. Near bundles merge level by level while the merge saves ink, and each bundle gets one trunk.",
|
|
548
|
+
defaults: { ...MINGLE_DEFAULTS } as MingleOptions,
|
|
549
|
+
schema: MINGLE_SCHEMA,
|
|
550
|
+
maxNodes: 10_000,
|
|
551
|
+
run: (input: HighwayInput, o: Readonly<MingleOptions>) => minglePlan(input, o),
|
|
552
|
+
}),
|
|
553
|
+
spiral: defineStrategy({
|
|
554
|
+
step: "highway",
|
|
555
|
+
name: "spiral",
|
|
556
|
+
describe: "Spiral trees (HWY-017, EDA-035). The edges of a node with many edges branch on arcs of spirals, and each branch with two edges is a highway.",
|
|
557
|
+
defaults: { ...SPIRAL_DEFAULTS } as SpiralOptions,
|
|
558
|
+
schema: SPIRAL_SCHEMA,
|
|
559
|
+
maxNodes: 10_000,
|
|
560
|
+
run: (input: HighwayInput, o: Readonly<SpiralOptions>) => spiralPlan(input, o),
|
|
561
|
+
}),
|
|
562
|
+
"flow-clusters": defineStrategy({
|
|
563
|
+
step: "highway",
|
|
564
|
+
name: "flow-clusters",
|
|
565
|
+
describe: "Flow clusters (HWY-023). A hierarchical clustering puts the edges with near sources and near targets together, and each cluster gets highways.",
|
|
566
|
+
defaults: { ...FLOW_CLUSTER_DEFAULTS } as FlowClusterOptions,
|
|
567
|
+
schema: FLOW_CLUSTER_SCHEMA,
|
|
568
|
+
maxNodes: 10_000,
|
|
569
|
+
run: (input: HighwayInput, o: Readonly<FlowClusterOptions>) => flowClusterPlan(input, o),
|
|
570
|
+
}),
|
|
571
|
+
traclus: defineStrategy({
|
|
572
|
+
step: "highway",
|
|
573
|
+
name: "traclus",
|
|
574
|
+
describe: "TRACLUS (HWY-025). The first routes break into segments, and the dense clusters of near segments become highways.",
|
|
575
|
+
defaults: { ...TRACLUS_DEFAULTS } as TraclusOptions,
|
|
576
|
+
schema: TRACLUS_SCHEMA,
|
|
577
|
+
maxNodes: 10_000,
|
|
578
|
+
run: (input: HighwayInput, o: Readonly<TraclusOptions>) => traclusPlan(input, o),
|
|
579
|
+
}),
|
|
580
|
+
diffusion: defineStrategy({
|
|
581
|
+
step: "highway",
|
|
582
|
+
name: "diffusion",
|
|
583
|
+
describe: "Route-aware bundling (HWY-020). The density of the first ways diffuses on the corner graph, and dense links cost less in a second pass.",
|
|
584
|
+
defaults: { ...DIFFUSION_DEFAULTS } as DiffusionOptions,
|
|
585
|
+
schema: DIFFUSION_SCHEMA,
|
|
586
|
+
maxNodes: 10_000,
|
|
587
|
+
run: (input: HighwayInput, o: Readonly<DiffusionOptions>) => diffusionPlan(input, o),
|
|
588
|
+
}),
|
|
589
|
+
nested: defineStrategy({
|
|
590
|
+
step: "highway",
|
|
591
|
+
name: "nested",
|
|
592
|
+
describe: "Highways for each zoom level that nest (HWY-052). Each level puts the edges of each cluster of the level above in finer clusters.",
|
|
593
|
+
defaults: { ...NESTED_DEFAULTS } as NestedOptions,
|
|
594
|
+
schema: NESTED_SCHEMA,
|
|
595
|
+
maxNodes: 10_000,
|
|
596
|
+
run: (input: HighwayInput, o: Readonly<NestedOptions>) => nestedPlan(input, o),
|
|
597
|
+
}),
|
|
598
|
+
lanes: defineStrategy({
|
|
599
|
+
step: "highway",
|
|
600
|
+
name: "lanes",
|
|
601
|
+
describe: "Width, lanes and capacity (HWY-051). A highway that does not fit in its channels splits into two highways by lane order.",
|
|
602
|
+
defaults: { ...LANES_DEFAULTS } as LanesOptions,
|
|
603
|
+
schema: LANES_SCHEMA,
|
|
604
|
+
maxNodes: 10_000,
|
|
605
|
+
run: (input: HighwayInput, o: Readonly<LanesOptions>) => lanesPlan(input, o),
|
|
606
|
+
}),
|
|
607
|
+
growth: defineStrategy({
|
|
608
|
+
step: "highway",
|
|
609
|
+
name: "growth",
|
|
610
|
+
describe: "Local growth (HWY-055). Each edge, the longest first, joins the highway with the largest saving, or starts a highway.",
|
|
611
|
+
defaults: { ...GROWTH_DEFAULTS } as GrowthOptions,
|
|
612
|
+
schema: GROWTH_SCHEMA,
|
|
613
|
+
maxNodes: 10_000,
|
|
614
|
+
run: (input: HighwayInput, o: Readonly<GrowthOptions>) => growthPlan(input, o),
|
|
615
|
+
}),
|
|
616
|
+
"edge-paths": defineStrategy({
|
|
617
|
+
step: "highway",
|
|
618
|
+
name: "edge-paths",
|
|
619
|
+
describe: "Edge-path bundling (HWY-012). An edge with a short path of other edges follows their ways, and the shared stretches become highways.",
|
|
620
|
+
defaults: { ...EDGE_PATH_DEFAULTS } as EdgePathOptions,
|
|
621
|
+
schema: EDGE_PATH_SCHEMA,
|
|
622
|
+
maxNodes: 10_000,
|
|
623
|
+
run: (input: HighwayInput, o: Readonly<EdgePathOptions>) => edgePathPlan(input, o),
|
|
624
|
+
}),
|
|
625
|
+
"soft-keys": defineStrategy({
|
|
626
|
+
step: "highway",
|
|
627
|
+
name: "soft-keys",
|
|
628
|
+
describe: "Soft keys (HWY-019). The key of an edge adds a distance to the flow distance, and a hierarchical clustering makes the highways.",
|
|
629
|
+
defaults: { ...SOFT_KEY_DEFAULTS } as SoftKeyOptions,
|
|
630
|
+
schema: SOFT_KEY_SCHEMA,
|
|
631
|
+
maxNodes: 10_000,
|
|
632
|
+
run: (input: HighwayInput, o: Readonly<SoftKeyOptions>) => softKeyPlan(input, o),
|
|
633
|
+
}),
|
|
634
|
+
hough: defineStrategy({
|
|
635
|
+
step: "highway",
|
|
636
|
+
name: "hough",
|
|
637
|
+
describe: "Hough votes (HWY-024). The straight lines of the edges vote for corridors, and each peak of the votes becomes a highway.",
|
|
638
|
+
defaults: { ...HOUGH_DEFAULTS } as HoughOptions,
|
|
639
|
+
schema: HOUGH_SCHEMA,
|
|
640
|
+
maxNodes: 10_000,
|
|
641
|
+
run: (input: HighwayInput, o: Readonly<HoughOptions>) => houghPlan(input, o),
|
|
642
|
+
}),
|
|
643
|
+
ridges: defineStrategy({
|
|
644
|
+
step: "highway",
|
|
645
|
+
name: "ridges",
|
|
646
|
+
describe: "Ridges of the line density (HWY-032). The ridges of the blurred density of the edges become highways, and near edges snap to them.",
|
|
647
|
+
defaults: { ...RIDGE_DEFAULTS } as RidgeOptions,
|
|
648
|
+
schema: RIDGE_SCHEMA,
|
|
649
|
+
maxNodes: 10_000,
|
|
650
|
+
run: (input: HighwayInput, o: Readonly<RidgeOptions>) => ridgePlan(input, o),
|
|
651
|
+
}),
|
|
652
|
+
hdbscan: defineStrategy({
|
|
653
|
+
step: "highway",
|
|
654
|
+
name: "hdbscan",
|
|
655
|
+
describe: "HDBSCAN on edges (HWY-028). The density clusters of the edges that live over a long range become highways.",
|
|
656
|
+
defaults: { ...HDBSCAN_DEFAULTS } as HdbscanOptions,
|
|
657
|
+
schema: HDBSCAN_SCHEMA,
|
|
658
|
+
maxNodes: 10_000,
|
|
659
|
+
run: (input: HighwayInput, o: Readonly<HdbscanOptions>) => hdbscanPlan(input, o),
|
|
660
|
+
}),
|
|
661
|
+
"hub-arcs": defineStrategy({
|
|
371
662
|
step: "highway",
|
|
372
|
-
name: "
|
|
373
|
-
describe: "
|
|
374
|
-
defaults: { ...
|
|
375
|
-
schema:
|
|
663
|
+
name: "hub-arcs",
|
|
664
|
+
describe: "Hub arc location (HWY-042). A greedy pass with swaps chooses at most q arcs from the candidates, and each edge rides its best arc.",
|
|
665
|
+
defaults: { ...HUB_ARC_DEFAULTS } as HubArcOptions,
|
|
666
|
+
schema: HUB_ARC_SCHEMA,
|
|
376
667
|
maxNodes: 10_000,
|
|
377
|
-
run: (input: HighwayInput, o: Readonly<
|
|
668
|
+
run: (input: HighwayInput, o: Readonly<HubArcOptions>) => hubArcPlan(input, o),
|
|
378
669
|
}),
|
|
670
|
+
"corridors+breakeven": gateStrategy(corridors, breakEvenGate),
|
|
671
|
+
"corridors+stretch": gateStrategy(corridors, stretchGate),
|
|
672
|
+
"corridors+ambiguity": gateStrategy(corridors, ambiguityGate),
|
|
673
|
+
"corridors+hysteresis": gateStrategy(corridors, hysteresisGate),
|
|
674
|
+
"corridors+ids": gateStrategy(corridors, stableIdGate),
|
|
379
675
|
});
|
|
380
676
|
|
|
381
677
|
/** The built-in strategies of each step, in order. A consumer adds its own: `createRegistry([...BUILT_IN_STRATEGIES, mine])`. */
|