@mark1russell7/highways 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark Russell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,625 @@
1
+ /** A point in the plane. */
2
+ export interface Point {
3
+ readonly x: number;
4
+ readonly y: number;
5
+ }
6
+ /** A box with its top-left corner at (`x`, `y`). */
7
+ export interface Box {
8
+ readonly x: number;
9
+ readonly y: number;
10
+ readonly w: number;
11
+ readonly h: number;
12
+ }
13
+ /** This function gives the distance between two points. */
14
+ export declare function dist(a: Point, b: Point): number;
15
+ /** This function gives the center of a box. */
16
+ export declare function center(b: Box): Point;
17
+ /** This function gives a box that is larger by `m` on each side. A negative `m` makes the box smaller. */
18
+ export declare function inflate(b: Box, m: number): Box;
19
+ /** This function gives the sum of the lengths of the segments of a polyline. */
20
+ export declare function polylineLength(p: readonly Point[]): number;
21
+ /**
22
+ * This function tells if the segments `ab` and `cd` cross at one point inside both of them.
23
+ * Segments that only touch at an end, or that are collinear, do not cross.
24
+ */
25
+ export declare function segmentsCross(a: Point, b: Point, c: Point, d: Point): boolean;
26
+ /**
27
+ * This function tells if the segment `ab` goes through the inside of the box.
28
+ * The function clips the segment to the box (Liang-Barsky) and asks for a part longer than `eps`.
29
+ */
30
+ export declare function segmentHitsBox(a: Point, b: Point, box: Box, eps?: number): boolean;
31
+ /** This function tells if the segment `ab` goes through the inside of the box from (`x0`, `y0`) to (`x1`, `y1`). */
32
+ export declare function segmentHitsRect(a: Point, b: Point, x0: number, y0: number, x1: number, y1: number, eps?: number): boolean;
33
+ /** This function gives the distance from a point to a box. The distance is 0 when the point is in the box. */
34
+ export declare function pointBoxDistance(p: Point, b: Box): number;
35
+ /** This function gives the distance from a point to the segment `ab`. */
36
+ export declare function pointSegmentDistance(p: Point, a: Point, b: Point): number;
37
+ /** This function gives the distance from the segment `ab` to a box. The distance is 0 when they touch. */
38
+ export declare function segmentBoxDistance(a: Point, b: Point, box: Box): number;
39
+ /**
40
+ * This function gives the turn at each inner vertex of a polyline, in radians, from -π to π.
41
+ * A positive turn is counterclockwise in a frame with the y axis up. The function skips segments shorter than `eps`.
42
+ */
43
+ export declare function turns(p: readonly Point[], eps?: number): number[];
44
+ /** This function counts the vertices of a polyline where the direction changes by more than `minTurn` radians. */
45
+ export declare function bendCount(p: readonly Point[], minTurn?: number): number;
46
+ /**
47
+ * This function counts the inflections of a polyline: the changes of the sign of the turn.
48
+ * A turn smaller than `minTurn` radians does not count. Thus a straight part between two turns keeps their signs.
49
+ */
50
+ export declare function inflectionCount(p: readonly Point[], minTurn?: number): number;
51
+ /**
52
+ * This function gives the discrete Fréchet distance between two polylines (Eiter and Mannila, 1994).
53
+ * The cost is O(|p|·|q|) in time and O(|q|) in memory.
54
+ */
55
+ export declare function frechet(p: readonly Point[], q: readonly Point[]): number;
56
+ /**
57
+ * A uniform grid of buckets over the plane. A box item goes into each bucket that the box touches.
58
+ * A segment item goes only into the buckets along the segment, so a long diagonal segment stays cheap.
59
+ * A query gives each item in the buckets that it touches, one time each.
60
+ */
61
+ export declare class Grid<T> {
62
+ #private;
63
+ /** This constructor makes an empty grid with square buckets of side `cell`. */
64
+ constructor(cell: number);
65
+ /** This method adds an item with its bounding box. */
66
+ add(item: T, b: Box): void;
67
+ /** This method adds the segment `ab` as an item. */
68
+ addSegment(item: T, a: Point, b: Point): void;
69
+ /** This method gives each item whose buckets touch the box to `visit`, one time each. */
70
+ query(b: Box, visit: (item: T) => void): void;
71
+ /** This method gives each item whose buckets come within `margin` of the segment `ab` to `visit`, one time each. */
72
+ querySegment(a: Point, b: Point, margin: number, visit: (item: T) => void): void;
73
+ }
74
+ /** This function gives the bounding box of the segment `ab`. */
75
+ export declare function segmentBounds(a: Point, b: Point): Box;
76
+ /** A side of a node box. */
77
+ export type Side = "north" | "east" | "south" | "west";
78
+ /**
79
+ * A plug of a node: a place where edges go into the node.
80
+ * A plug with `at` is fixed. The router does not move it. A plug without `at` is free: the router places it.
81
+ */
82
+ export interface Port {
83
+ readonly id: string;
84
+ /** This point is the fixed place of the plug, relative to the top-left corner of the node box. */
85
+ readonly at?: Point;
86
+ /** This side is where a free plug goes. Without it, the router picks the side. */
87
+ readonly side?: Side;
88
+ }
89
+ /** A node: a box that routes must not go through. */
90
+ export interface Node {
91
+ readonly id: string;
92
+ readonly box: Box;
93
+ readonly ports?: readonly Port[];
94
+ }
95
+ /** One end of an edge: a node, and optionally one plug of that node. */
96
+ export interface End {
97
+ readonly node: string;
98
+ readonly port?: string;
99
+ }
100
+ /** An edge between two nodes. `data` is free for the grouping rules. */
101
+ export interface Edge {
102
+ readonly id: string;
103
+ readonly source: End;
104
+ readonly target: End;
105
+ readonly data?: Readonly<Record<string, unknown>>;
106
+ }
107
+ /** The input of a router. */
108
+ export interface Graph {
109
+ readonly nodes: readonly Node[];
110
+ readonly edges: readonly Edge[];
111
+ }
112
+ /** The route of one edge: a polyline from the source plug to the target plug, in absolute coordinates. */
113
+ export interface Route {
114
+ readonly edge: string;
115
+ readonly points: readonly Point[];
116
+ }
117
+ /**
118
+ * One zoom level of a result. Each shown item is a node or a group of nodes.
119
+ * Each link joins two shown items and carries the count of the edges that it stands for.
120
+ */
121
+ export interface Level {
122
+ readonly depth: number;
123
+ readonly items: readonly LevelItem[];
124
+ readonly links: readonly LevelLink[];
125
+ }
126
+ /** A shown item of a level: one node, or a group that stands for its nodes. */
127
+ export interface LevelItem {
128
+ readonly id: string;
129
+ readonly box: Box;
130
+ readonly nodes: readonly string[];
131
+ }
132
+ /** A link of a level: the edges between two shown items, drawn as one path. */
133
+ export interface LevelLink {
134
+ readonly source: string;
135
+ readonly target: string;
136
+ readonly count: number;
137
+ readonly points: readonly Point[];
138
+ }
139
+ /** The output of a router. `levels` is present when the router makes zoom levels. */
140
+ export interface Result {
141
+ readonly routes: readonly Route[];
142
+ readonly levels?: readonly Level[];
143
+ readonly bundles?: readonly Bundle[];
144
+ /** The stretches of way that several routes share, if the router gives them. */
145
+ readonly runs?: readonly SharedRun[];
146
+ }
147
+ /**
148
+ * A bundle: edges from one node that share one plug and one trunk. Then the edges fan out to their targets.
149
+ * A renderer draws a bundle as one fat edge with a count, or as one lane for each edge.
150
+ */
151
+ export interface Bundle {
152
+ readonly id: string;
153
+ /** The key that the grouping rule gave each edge of the bundle. */
154
+ readonly key: string;
155
+ /** The node where the edges share their plug. */
156
+ readonly node: string;
157
+ /** The edges in lane order. Lane 0 turns most to the left of the trunk, in a frame with the y axis down. */
158
+ readonly edges: readonly string[];
159
+ /** The shared start of each route of the bundle, from the plug to the split point. */
160
+ readonly trunk: readonly Point[];
161
+ }
162
+ /**
163
+ * A stretch of way that several routes share, with the same points. Each route of `edges` has `points` in it,
164
+ * in this order or in the reverse order. A renderer draws a run as one fat line, or as one lane for each edge.
165
+ */
166
+ export interface SharedRun {
167
+ readonly id: string;
168
+ /** The key of the grouping rule of the edges. */
169
+ readonly key: string;
170
+ readonly points: readonly Point[];
171
+ /** The edges in lane order. Lane 0 is on the left of the run, from its first point to its last. The y axis points down. */
172
+ readonly edges: readonly string[];
173
+ }
174
+ /** A grouping rule gives each edge a key. The edges from one plug with one key make a bundle. An edge without a key stays alone. */
175
+ export type GroupRule = (edge: Edge) => string | undefined;
176
+ /** A router: a strategy with a name. The same input must give the same result. */
177
+ export interface Router {
178
+ readonly name: string;
179
+ /** The benchmark skips a fixture with more nodes than this. Without it, the router takes each fixture. */
180
+ readonly maxNodes?: number;
181
+ route(graph: Graph): Result;
182
+ }
183
+ /** This function gives a map from the id of each node to the node. */
184
+ export declare function nodeIndex(g: Graph): Map<string, Node>;
185
+ /** This function gives the absolute place of a fixed plug, or `undefined` for a free plug or no plug. */
186
+ export declare function fixedPortPoint(node: Node, end: End): Point | undefined;
187
+ /** This function gives a seeded random source with values in [0, 1) (mulberry32). */
188
+ export declare function rng(seed: number): () => number;
189
+ /**
190
+ * The ways a fixture puts its nodes and picks its edges. In `collections`, each cluster is one collection,
191
+ * and some nodes link to many nodes of one other collection (a fan-out).
192
+ */
193
+ export type FixtureKind = "uniform" | "clustered" | "grid" | "hubs" | "collections";
194
+ /** The parameters of a generated graph. */
195
+ export interface FixtureSpec {
196
+ readonly name: string;
197
+ readonly kind: FixtureKind;
198
+ readonly nodes: number;
199
+ /** The number of edges is about `nodes × edgesPerNode`. */
200
+ readonly edgesPerNode: number;
201
+ readonly seed: number;
202
+ /** This share of the edges, from 0 to 1, joins a node near the source. */
203
+ readonly locality?: number;
204
+ /** This share of the nodes, from 0 to 1, has one fixed plug that its outgoing edges use. */
205
+ readonly fixedPorts?: number;
206
+ }
207
+ /** The fixtures of the benchmark, from 10 to 100 000 nodes. */
208
+ export declare const FIXTURES: readonly FixtureSpec[];
209
+ /**
210
+ * This function makes the graph of a fixture. The same spec always gives the same graph.
211
+ * Node boxes do not overlap, and a gap of 12 units stays between them.
212
+ */
213
+ export declare function makeFixture(spec: FixtureSpec): Graph;
214
+ /** A plug: the point where a route leaves or enters a node, and the outward normal of its side. */
215
+ export interface Plug {
216
+ readonly point: Point;
217
+ readonly normal: Point;
218
+ }
219
+ /** The outward normal of each side, in a frame with the y axis down. */
220
+ export declare const SIDE_NORMAL: Readonly<Record<Side, Point>>;
221
+ /** This function gives the side of the box that is nearest to the point. */
222
+ export declare function nearestSide(box: Box, p: Point): Side;
223
+ /**
224
+ * This function gives the side that faces `toward`: the side of the larger component of the direction from the center.
225
+ * Thus the angle between the normal of the side and that direction is 45° or less.
226
+ */
227
+ export declare function sideToward(box: Box, toward: Point): Side;
228
+ /**
229
+ * This function gives the point on a side of the box that is nearest to the line from the center to `toward`.
230
+ * The point stays away from the corners by `inset`, or by a quarter of the side when the side is short.
231
+ */
232
+ export declare function pointOnSide(box: Box, side: Side, toward: Point, inset?: number): Point;
233
+ /**
234
+ * 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.
235
+ * The router places a free plug on the side that faces `toward`.
236
+ */
237
+ export declare function placePlug(node: Node, end: End, toward: Point): Plug;
238
+ /** The options of the inflection rule. */
239
+ export interface InflectionOptions {
240
+ /** The half width of the axis and diagonal bands, in radians. The default is 7°. */
241
+ readonly tol?: number;
242
+ /** The number of segments of the curve. The default is 24. */
243
+ readonly samples?: number;
244
+ /** The largest angle between the tangent and the chord, in radians. The default is 1.35 (approximately 77°). */
245
+ readonly maxAngle?: number;
246
+ }
247
+ /**
248
+ * This function gives the number of inflections that the rule asks for a chord at the angle `angle` (radians).
249
+ * It gives 0 near an axis, 1 near a diagonal and 2 between them. The bands are `tol` wide on each side.
250
+ */
251
+ export declare function inflectionSector(angle: number, tol?: number): 0 | 1 | 2;
252
+ /**
253
+ * This function draws the curve of the inflection rule from the plug `p0` to `target`.
254
+ * The curve leaves `p0` along `normal`. In the frame of the chord, its side offset f has f(0) = f(L) = 0 and f'(0) = tan θ.
255
+ * Here θ is the angle from the chord to the normal. A polynomial of degree 2, 3 or 4 gives 0, 1 or 2 inflections,
256
+ * at 1/2, or at 1/3 and 2/3, of the chord.
257
+ */
258
+ export declare function inflectionCurve(p0: Point, normal: Point, target: Point, options?: InflectionOptions): Point[];
259
+ /**
260
+ * This function rounds each inner vertex of a polyline with a quadratic Bézier curve.
261
+ * The curve starts and ends at most `radius` from the vertex, and at most half of each segment. The ends of the polyline stay.
262
+ */
263
+ export declare function roundCorners(p: readonly Point[], radius: number, steps?: number): Point[];
264
+ /** The nodes as obstacles, with a grid for fast tests. */
265
+ export declare class Obstacles {
266
+ #private;
267
+ /** This constructor indexes the boxes of the nodes. */
268
+ constructor(nodes: readonly Node[], cell: number);
269
+ /** This method tells if the segment `ab` stays out of each box, made larger by `pad`. The nodes in `skip` do not count. */
270
+ clear(a: Point, b: Point, pad: number, skip?: ReadonlySet<Node>): boolean;
271
+ /** This method tells if the polyline stays out of each box, made larger by `pad`. The nodes in `skip` do not count. */
272
+ clearPath(p: readonly Point[], pad: number, skip?: ReadonlySet<Node>): boolean;
273
+ /** This method tells if the point is inside a box, made larger by `pad`. The node `skip` does not count. */
274
+ inside(p: Point, pad: number, skip?: Node): boolean;
275
+ }
276
+ /** The options of the corner graph. */
277
+ export interface CornerGraphOptions {
278
+ /** The distance from a box to its corners. */
279
+ readonly margin: number;
280
+ /** The number of visible neighbours of each corner. */
281
+ readonly k: number;
282
+ /**
283
+ * The spacing of a lattice of free points over the plane, in units of the mean node size. The default is 3, and 0 turns it off.
284
+ * The lattice links groups of nodes across empty space. Without it, the nearest corners of a group are all in that group.
285
+ */
286
+ readonly lattice?: number;
287
+ }
288
+ /**
289
+ * A sparse visibility graph. Its vertices are the corners of the boxes, made larger by `margin`.
290
+ * Each corner links to its `k` nearest corners that it can see. A lattice of free points links the groups of nodes.
291
+ * A path goes from corner to corner around the boxes.
292
+ * The obstacle test uses half of the margin, so a path keeps a clearance of at least half of the margin.
293
+ */
294
+ export declare class CornerGraph {
295
+ #private;
296
+ readonly points: Point[];
297
+ /** This constructor makes the graph of the nodes. */
298
+ constructor(nodes: readonly Node[], obstacles: Obstacles, options: CornerGraphOptions);
299
+ /** This method gives the number of links of the graph. */
300
+ get linkCount(): number;
301
+ /** This method gives the index of the corner nearest to `p`, seen or not, or -1 when the graph has no corner near it. */
302
+ nearest(p: Point): number;
303
+ /** This method gives at most `k` corners that `p` can see, nearest first. The corner `self` does not count. */
304
+ nearestVisible(p: Point, k: number, self?: number): number[];
305
+ /**
306
+ * This method gives a shortest path from `from` to `to` in the graph (A*), with the two points as its ends.
307
+ * It gives `undefined` when the graph links no corner near `from` to a corner near `to`.
308
+ * The function `cost` can change the cost of the link from corner `i` to corner `j`, of length `d`.
309
+ * It must give at least `floor` × `d`, so that the estimate of A* stays below the true cost.
310
+ */
311
+ path(from: Point, to: Point, cost?: (i: number, j: number, d: number) => number, floor?: number): Point[] | undefined;
312
+ /** This method removes the vertices that a later vertex makes needless: from each vertex, it goes to the farthest vertex in view. */
313
+ pull(p: readonly Point[], lookahead?: number): Point[];
314
+ }
315
+ /** The options of a way finder. */
316
+ export interface WayOptions {
317
+ /** The distance from a box to the corners of the graph. The default is 5. */
318
+ readonly margin?: number;
319
+ /** The number of visible neighbours of each corner. The default is 8. */
320
+ readonly k?: number;
321
+ /** With `smooth`, a way in clear view uses the inflection rule, and the other ways get round corners. */
322
+ readonly smooth?: boolean;
323
+ }
324
+ /**
325
+ * The ways around the nodes of one graph. A way leaves a plug along its normal.
326
+ * It goes a shortest way (A*) through the corner graph, without the needless corners. It enters the other plug along its normal.
327
+ */
328
+ export declare class WayFinder {
329
+ #private;
330
+ readonly obstacles: Obstacles;
331
+ readonly corners: CornerGraph;
332
+ readonly margin: number;
333
+ /** This constructor makes the obstacles and the corner graph of the nodes. */
334
+ constructor(nodes: readonly Node[], options?: WayOptions);
335
+ /** This method gives the point at `margin` from the plug, along its normal. */
336
+ out(p: Plug): Point;
337
+ /** This method gives a way from plug to plug. Without a way in the graph, it gives the straight line. */
338
+ between(ps: Plug, pt: Plug, ends: ReadonlySet<Node>): Point[];
339
+ /** This method gives a way from a plug to a free point, or `undefined` when the graph has none. */
340
+ fromPlug(ps: Plug, to: Point): Point[] | undefined;
341
+ /** This method gives a way from a free point to a plug, or `undefined` when the graph has none. */
342
+ toPlug(from: Point, pt: Plug): Point[] | undefined;
343
+ }
344
+ /** A piece of drawn way. A shared segment carries several edges, in lane order. A private segment carries one edge. */
345
+ export interface Segment {
346
+ readonly id: string;
347
+ readonly points: readonly Point[];
348
+ /** The edges in lane order. Lane 0 is on the left of the segment, from its first point to its last. The y axis points down. */
349
+ readonly edges: readonly string[];
350
+ /** The number of edges. A renderer shows it on a fat edge. */
351
+ readonly count: number;
352
+ }
353
+ /** One step of a path: a segment, the way through it, and the lane of the edge in it. */
354
+ export interface SegmentRef {
355
+ readonly segment: string;
356
+ /** This value is true when the edge goes from the last point of the segment to its first point. */
357
+ readonly reverse: boolean;
358
+ readonly lane: number;
359
+ }
360
+ /** The route of one edge as a list of segments. */
361
+ export interface Path {
362
+ readonly edge: string;
363
+ readonly segments: readonly SegmentRef[];
364
+ }
365
+ /** The routes of a result as shared geometry. A renderer can draw each segment one time, with a count. */
366
+ export interface SegmentGraph {
367
+ readonly segments: readonly Segment[];
368
+ readonly paths: readonly Path[];
369
+ }
370
+ /**
371
+ * This function breaks each route into segments. The runs and the trunks of the bundles become shared segments.
372
+ * The other parts of each route become private segments. The segments of a path, in order, give the points of its route again.
373
+ */
374
+ export declare function segmentsOf(result: Result): SegmentGraph;
375
+ /** This function gives the points of a path, with the segments in the order and the way of the path. */
376
+ export declare function pathPoints(graph: SegmentGraph, path: Path): Point[];
377
+ /**
378
+ * This function moves each point of a polyline by `d` to the left, in a frame with the y axis down.
379
+ * At an inner point, it moves along the bisector, at most 2 × `d` far.
380
+ */
381
+ export declare function offsetPolyline(p: readonly Point[], d: number): Point[];
382
+ /** The options of `pullApart`. */
383
+ export interface PullApartOptions {
384
+ /** The distance between two lanes. */
385
+ readonly spacing: number;
386
+ /** The largest width of a segment, from its outer lane on the left to its outer lane on the right. */
387
+ readonly maxWidth: number;
388
+ }
389
+ /**
390
+ * This function pulls the lanes of each shared segment apart, and gives a route for each edge.
391
+ * The lanes keep the lane order, so they do not cross in a segment. The two ends of each route stay at their plugs.
392
+ */
393
+ export declare function pullApart(graph: SegmentGraph, options: PullApartOptions): Route[];
394
+ /**
395
+ * This function gives the lane order of the edges on a shared way `run` (metro-line order). Two routes go on together
396
+ * past the last point of the run until they part. The route that turns more to the left there gets the lane more to the left.
397
+ * Two routes can part only before the first point. Then the route that comes from more to the left gets the lane more to the left.
398
+ * A route that does not hold the run goes last.
399
+ */
400
+ export declare function orderLanes(run: readonly Point[], routes: readonly Route[]): string[];
401
+ /** A section of the plane: a cell of a quadtree, with the nodes whose centers are in it. */
402
+ export interface Section {
403
+ /** The path from the root: "s", then one digit from 0 to 3 for each step down. */
404
+ readonly id: string;
405
+ readonly depth: number;
406
+ readonly cell: Box;
407
+ /** The indices of the nodes, in the order of the graph. */
408
+ readonly nodes: readonly number[];
409
+ readonly children: readonly Section[];
410
+ }
411
+ /** The options of the tree of sections. */
412
+ export interface SectionOptions {
413
+ /** A section with more nodes splits. The default is 8. */
414
+ readonly cap?: number;
415
+ /** The tree stops at this depth. The default is 16. */
416
+ readonly maxDepth?: number;
417
+ }
418
+ /**
419
+ * This function makes the tree of sections of the nodes. A cell with more than `cap` nodes splits in four.
420
+ * When the nodes of a cell are all in one quarter, the cell becomes that quarter and does not make a level.
421
+ */
422
+ export declare function buildSections(nodes: readonly Node[], options?: SectionOptions): Section;
423
+ /** This function gives the sections of the tree, parents before children. */
424
+ export declare function allSections(root: Section): Section[];
425
+ /** This function gives the depth of the deepest section. */
426
+ export declare function treeDepth(root: Section): number;
427
+ /** This function gives the point where the ray from the center of the box to `toward` leaves the box. */
428
+ export declare function boxExit(box: Box, toward: Point): Point;
429
+ /**
430
+ * The control of the benchmark: each edge is one straight line between its plugs.
431
+ * A free plug goes where the line between the two centers leaves the box.
432
+ */
433
+ export declare const straightRouter: Router;
434
+ /**
435
+ * Plugs and the inflection rule, with no obstacles. Each edge leaves its source plug at the tangent of the side.
436
+ * It goes to the target plug on the curve of the rule. This router measures the rule alone.
437
+ */
438
+ export declare const plugsRouter: Router;
439
+ /** The options of the corner router. */
440
+ interface CornersOptions extends WayOptions {
441
+ /** The name in the benchmark. */
442
+ readonly name: string;
443
+ /** The benchmark skips a fixture with more nodes. */
444
+ readonly maxNodes?: number;
445
+ }
446
+ /**
447
+ * This function makes a router that goes around the nodes.
448
+ * Each edge leaves its plug along the normal of the side. It goes a shortest way (A*) through a sparse visibility graph
449
+ * of the box corners, without the needless corners. It enters the target plug along its normal.
450
+ */
451
+ export declare function cornersRouter(options: CornersOptions): Router;
452
+ /** The options of the router with shared ways. */
453
+ interface SharedOptions extends WayOptions {
454
+ /** The name in the benchmark. */
455
+ readonly name: string;
456
+ /** The edges with one key share their ways. Without a rule, all edges share. An edge without a key shares with no edge. */
457
+ readonly rule?: GroupRule;
458
+ /** The cost of a link that an edge with the same key uses already, as a share of its length. The default is 0.5. */
459
+ readonly discount?: number;
460
+ /** The benchmark skips a fixture with more nodes. */
461
+ readonly maxNodes?: number;
462
+ }
463
+ /**
464
+ * This function makes a router where edges share their ways (after Pupyrev, Nachmanson, Bereg and Holroyd, "Edge routing with ordered bundles").
465
+ * The edges go one by one through the corner graph. A link that an edge with the same key uses already costs less.
466
+ * Thus the edges of a key come together on common ways. Each stretch that several edges share has the same points in each route.
467
+ * The result gives each stretch as a run, with the order of its lanes.
468
+ */
469
+ export declare function sharedRouter(options: SharedOptions): Router;
470
+ /** The options of the router with sections. */
471
+ interface SectionsOptions extends WayOptions, SectionOptions {
472
+ /** The name in the benchmark. */
473
+ readonly name: string;
474
+ /**
475
+ * The number of hubs on each side of the lowest common section that a way passes. A smaller number gives shorter ways
476
+ * and fewer shared ways. Without it, a way passes each hub from its node up.
477
+ */
478
+ readonly hubs?: number;
479
+ /** A way through the hubs can be at most this many times as long as the line between its plugs. Else the edge goes directly. The default is 2. */
480
+ readonly stretch?: number;
481
+ /** The benchmark skips a fixture with more nodes. */
482
+ readonly maxNodes?: number;
483
+ }
484
+ /**
485
+ * This function makes a router with a tree of sections. Each section has a hub: the free corner nearest to the center of its nodes.
486
+ * An edge goes from its node up the hubs of its sections to the lowest common section, across, and down to the other node.
487
+ * Each way from hub to hub is found one time, so all the edges on it share its points.
488
+ * The edges in one section of the lowest level go straight around the nodes. The result gives one zoom level for each depth of the tree.
489
+ */
490
+ export declare function sectionsRouter(options: SectionsOptions): Router;
491
+ /** The options of the trunk router. */
492
+ interface TrunksOptions extends WayOptions {
493
+ /** The name in the benchmark. */
494
+ readonly name: string;
495
+ /** The grouping rule. The edges from one plug with one key share a trunk. */
496
+ readonly rule: GroupRule;
497
+ /** The split point is at this share of the way to the center of the targets. The default is 0.5. */
498
+ readonly split?: number;
499
+ /** A group with fewer edges stays unbundled. The default is 2. */
500
+ readonly minSize?: number;
501
+ /** The benchmark skips a fixture with more nodes. */
502
+ readonly maxNodes?: number;
503
+ }
504
+ /** The rule that groups the edges by the collection of their target, in `data.collection`. */
505
+ export declare const byCollection: GroupRule;
506
+ /**
507
+ * This function groups the edges by their source plug and their key. Each group keeps the order of the edges.
508
+ * A group with fewer than `minSize` edges does not appear.
509
+ */
510
+ export declare function groupEdges(edges: readonly Edge[], rule: GroupRule, minSize?: number): Map<string, Edge[]>;
511
+ /**
512
+ * This function gives the lane order of the branches that leave the split point `from`, with the trunk along `dir`.
513
+ * Lane 0 turns most to the left, in a frame with the y axis down. Thus two lanes do not cross at the split.
514
+ */
515
+ export declare function laneOrder(dir: Point, from: Point, branches: readonly {
516
+ readonly edge: string;
517
+ readonly next: Point;
518
+ }[]): string[];
519
+ /**
520
+ * This function gives the split point of a bundle, on the line from `start` to the center of the targets.
521
+ * The point is at `split` of the way, but at most half of the way to the nearest target. It moves back toward `start` out of the boxes.
522
+ */
523
+ export declare function splitPoint(start: Point, targets: readonly Point[], split: number, free: (p: Point) => boolean, step: number): Point;
524
+ /**
525
+ * This function makes a router that bundles the edges by a rule. The edges of a group share one plug and one trunk
526
+ * to a split point. From there, each edge goes its own way. The router gives the bundles with their lane order.
527
+ * The edges outside the groups go as in the corner router.
528
+ */
529
+ export declare function trunksRouter(options: TrunksOptions): Router;
530
+ /** The routers that the benchmark races. */
531
+ export declare const ROUTERS: readonly Router[];
532
+ /** A broken hard rule. A correct router has none. */
533
+ export interface Violation {
534
+ readonly rule: "complete" | "ends-at-plugs" | "fixed-plugs" | "no-pass-through" | "counts-sum" | "bundle-trunk" | "run-shared";
535
+ readonly edge?: string;
536
+ readonly detail: string;
537
+ }
538
+ /** The tolerance of the hard rules, in units of the plane. */
539
+ export declare const TOLERANCE = .5;
540
+ /**
541
+ * This function checks the hard rules of a result. Each edge has one route, and each route starts and ends at its nodes.
542
+ * Fixed plugs stay in place. No route goes through a node that is not one of its ends. The counts of each level add up.
543
+ *
544
+ * Each route of a bundle starts with its trunk, and an edge is in one bundle at most.
545
+ * Each run has two edges or more, and the route of each edge holds the points of the run in a row.
546
+ * The function gives at most `limit` violations.
547
+ */
548
+ export declare function checkProperties(graph: Graph, result: Result, limit?: number): Violation[];
549
+ /** The quality of a result. Lower is better for each value except `clearance`. */
550
+ export interface Quality {
551
+ /** The length of all drawn lines. A segment that several routes share counts one time. */
552
+ readonly ink: number;
553
+ /** The number of points where two drawn segments cross. */
554
+ readonly crossings: number;
555
+ /** This value is true when `crossings` is an estimate from a sample of the segments. */
556
+ readonly crossingsEstimated: boolean;
557
+ /** The mean number of bends of a route. */
558
+ readonly bends: number;
559
+ /** The mean number of inflections of a route. */
560
+ readonly inflections: number;
561
+ /** The mean distance from a route to the nearest node that is not one of its ends, up to `CLEARANCE_CAP`. */
562
+ readonly clearance: number;
563
+ /** The share of the routes that come closer than 4 units to a node that is not one of their ends. */
564
+ readonly tight: number;
565
+ /** The mean ratio of the length of a route to the distance between its ends. */
566
+ readonly detour: number;
567
+ /** The mean angle, in degrees, between the first or last segment of a route and the normal of the side of its node. */
568
+ readonly plugAngle: number;
569
+ }
570
+ /** The clearance stops at this distance. */
571
+ export declare const CLEARANCE_CAP = 40;
572
+ /** This function measures the quality of a result. */
573
+ export declare function measureQuality(graph: Graph, result: Result): Quality;
574
+ /**
575
+ * This function gives an angle in degrees: the angle between the segment from `at` to `next` and the outward normal.
576
+ * The normal is the normal of the side of the box nearest to `at`. A route that starts inside the box (an interior route) gives no angle.
577
+ */
578
+ export declare function sideAngle(box: Box, at: Point, next: Point): number | undefined;
579
+ /**
580
+ * This function counts the crossings of a set of segments with a grid of buckets.
581
+ * Above 20 000 segments, it counts the crossings of a seeded sample of 2 000 segments and scales the count.
582
+ */
583
+ export declare function countCrossings(segs: readonly (readonly [Point, Point])[]): [number, boolean];
584
+ /** The change between two results of the same graph: the Fréchet distance between the two routes of each edge. */
585
+ export interface Stability {
586
+ readonly mean: number;
587
+ readonly max: number;
588
+ /** The share of the routes that moved more than 1 unit. */
589
+ readonly moved: number;
590
+ }
591
+ /** This function measures how much the routes changed between two results. */
592
+ export declare function measureStability(before: Result, after: Result): Stability;
593
+ /** One row of the benchmark: one router on one fixture. */
594
+ export interface BenchRow {
595
+ readonly router: string;
596
+ readonly fixture: string;
597
+ readonly nodes: number;
598
+ readonly edges: number;
599
+ /** The median time of one run, in milliseconds. */
600
+ readonly ms: number;
601
+ /** This value is false when two runs on the same input gave different results. */
602
+ readonly deterministic: boolean;
603
+ readonly violations: number;
604
+ /** The first violations, for the report. */
605
+ readonly sample: readonly Violation[];
606
+ readonly quality: Quality;
607
+ }
608
+ /** The options of a benchmark run. */
609
+ export interface BenchOptions {
610
+ /** The number of timed routes of each fixture by each router. The default is 3. */
611
+ readonly repeat?: number;
612
+ }
613
+ /** This function routes each fixture with each router, checks the hard rules and measures the quality. */
614
+ export declare function runBenchmark(routers: readonly Router[], fixtures: readonly FixtureSpec[], options?: BenchOptions): BenchRow[];
615
+ /**
616
+ * This function tells if two values hold the same data, as JSON sees it. It does not make a string,
617
+ * because the result of a large graph can be longer than the longest string.
618
+ */
619
+ export declare function sameData(a: unknown, b: unknown): boolean;
620
+ /** This function routes one graph with one router and measures the result. */
621
+ export declare function benchOne(router: Router, fixture: string, graph: Graph, repeat?: number): BenchRow;
622
+ /** This function writes the rows as a Markdown table. */
623
+ export declare function formatTable(rows: readonly BenchRow[]): string;
624
+ export type { CornersOptions, SectionsOptions, SharedOptions, TrunksOptions };
625
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/geom.ts","../src/grid.ts","../src/model.ts","../src/fixtures.ts","../src/plugs.ts","../src/curves.ts","../src/visibility.ts","../src/ways.ts","../src/segments.ts","../src/sections.ts","../src/routers/straight.ts","../src/routers/plugs.ts","../src/routers/corners.ts","../src/routers/shared.ts","../src/routers/sections.ts","../src/routers/trunks.ts","../src/routers/index.ts","../src/bench/properties.ts","../src/bench/quality.ts","../src/bench/run.ts"],"mappings":"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"}