@mark1russell7/highways 0.1.5 → 0.2.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 CHANGED
@@ -122,12 +122,65 @@ On the packed board, the links are short only because they go through the boxes.
122
122
 
123
123
  The links of a gap without lanes share one line. Thus the crossings on that line do not count. With a lane for each link, the crossings show, and the order `crossings` removes 20% of them. The spacing changes the crossings less than 5%. The mean detour stays near 1.32 for each knob, because a link with legs parallel to the axes goes around the boxes.
124
124
 
125
+ ## Options of the zoom levels
126
+
127
+ The router `sections` makes its hubs and its zoom levels from a tree of groups. Each rule and each limit of that tree is an option. The defaults give the output of version 0.1.5.
128
+
129
+ | Option | Default | Where | What it sets |
130
+ |---|---|---|---|
131
+ | `group` | the quadtree | `sectionsRouter` | The grouping rule: a strategy of the step `group`. `groupOptions` gives its options. |
132
+ | `levels` | each depth | `sectionsRouter` | The depths of the tree that get a level with groups. The level of single nodes always comes last. |
133
+ | `stretch` | 2 | `sectionsRouter` | The hub limit. A way through the hubs is at most this many times the straight line. `Infinity` keeps each such way. |
134
+ | `stretchPad` | 4 | `sectionsRouter` | A way through the hubs can be this many margins longer than the hub limit. |
135
+ | `hubSlack` | 80 | `sectionsRouter` | A way skips a hub that is this many units farther from the target than the way was before. |
136
+ | `hubs` | `Infinity` | `sectionsRouter` | The number of hubs on each side of the lowest common section that a way passes. |
137
+ | `cap` | 8 | `buildSections` | The section limit. A section with more nodes splits in four. |
138
+ | `maxDepth` | 16 | `buildSections` | The depth limit of the tree. |
139
+ | `collapse` | 32 | `buildSections` | A cell shrinks to one quarter at most this many times without a split. |
140
+ | `tightMargin` | 1 | `levelLinks` | The margin of the second corner graph of the level links. |
141
+ | `plugInset` | 6 | `levelLinks` | A plug of a level link stays this far from the corners of its side. |
142
+ | `routesAt`, `itemAt` | 22, 120 | `pickLevel` | The far-view thresholds, in pixels on the screen. `PICK` holds them. |
143
+
144
+ `SECTIONS_DEFAULTS`, `SECTION_DEFAULTS`, `LEVEL_LINK_DEFAULTS` and `PICK` give the default numbers. `itemSizes(levels)` gives the sizes that `pickLevel(sizes, scale, options)` reads.
145
+
146
+ The built-in grouping rules are in `groupStrategies`. The rule `quadtree` splits the plane, as `buildSections` does. The rule `keys` makes one group for each key of the nodes. Its default key is the string `data.group` of a node, and a node without a key is a group alone. With the option `split`, a large group splits into a quadtree.
147
+
148
+ A consumer rule groups the nodes by its own field. Then the far view shows one item for each group:
149
+
150
+ ```ts
151
+ import { defineStrategy, keyedSections, sectionsRouter } from "@mark1russell7/highways";
152
+
153
+ const byTeam = defineStrategy({
154
+ step: "group",
155
+ name: "team",
156
+ describe: "One group for each team.",
157
+ defaults: {},
158
+ run: (graph) => keyedSections(graph.nodes, { key: (node) => String(node.data?.["team"]) }),
159
+ });
160
+ const result = sectionsRouter({ name: "teams", group: byTeam }).route(graph);
161
+ // result.levels[1] has one item for each team. The id of an item is "s:" and the team.
162
+ ```
163
+
164
+ The router examines the tree of a consumer rule with `checkSections`. Each node must be in exactly one leaf. The children of a section split its nodes, and a child is one level deeper than its parent.
165
+
166
+ The benchmark sweeps the hub limit, `hubSlack`, `cap` and the grouping rules on the case boards. Each table starts with the defaults. On `case-hub-chain`:
167
+
168
+ | `stretch` | Worst detour | Routes through hubs | Shared runs | Out and back |
169
+ |---|---:|---:|---:|---:|
170
+ | 1.25 | 1.19 | 14 | 4 | 0 |
171
+ | 1.5 | 1.39 | 20 | 8 | 0 |
172
+ | 2 (default) | 1.78 | 24 | 10 | 0 |
173
+ | 3 | 2.05 | 25 | 11 | 0 |
174
+
175
+ A smaller hub limit gives shorter routes and fewer shared runs. No route goes through a box or out and back at any value. On `case-many-groups`, the rule `keys` has one key for each group of cards. It gives 32 items at the far view, and no level link goes out and back.
176
+
125
177
  ## Strategies
126
178
 
127
179
  The steps of the pipeline that vary have strategies. Each step has one interface, so a consumer chooses a strategy by its name or adds its own.
128
180
 
129
181
  | Step | Input | Output | Built-in strategies |
130
182
  |---|---|---|---|
183
+ | `group` | `Graph` | `Section`: the tree of groups of the router `sections` | `quadtree`, `keys` |
131
184
  | `router` | `Graph` | `Result` | `straight`, `plugs`, `corners`, `corners-smooth`, `trunks`, `shared`, `shared-all`, `sections`, `sections-tree` |
132
185
  | `curve` | `CurveInput`: the points, the normals of the plugs and the free space | `CurveSegment[]` | `straight`, `rounded`, `spline`, `arc` |
133
186
  | `room` | `RoomInput`: the boxes and the links of a board | `Gap[]` | `routes`, `lines` |
@@ -139,7 +192,7 @@ A strategy has a `step`, a `name`, a `describe` text, its `defaults` and the met
139
192
  - `register(registry, ...strategies)` gives a new registry. It does not change the registry before it.
140
193
  - `listStrategies(registry, step)` gives the strategies of a step, in order. `findStrategy` and `getStrategy` give one strategy by its name.
141
194
  - `asRouter(strategy, options?)` gives a `Router`, and `routersOf(registry)` gives the routers of a registry.
142
- - `STRATEGIES` is the registry of `BUILT_IN_STRATEGIES`. The records `routerStrategies`, `curveStrategies` and `roomStrategies` give each built-in strategy with its typed options.
195
+ - `STRATEGIES` is the registry of `BUILT_IN_STRATEGIES`. The records `groupStrategies`, `routerStrategies`, `curveStrategies` and `roomStrategies` give each built-in strategy with its typed options.
143
196
 
144
197
  A registry is data. No import changes it. The benchmark and the site take their lists from `STRATEGIES`, so a new built-in strategy gets its rows and its button with no other edit. A consumer makes its own registry:
145
198
 
@@ -167,7 +220,7 @@ const rows = runBenchmark(routersOf(registry), FIXTURES.slice(0, 1)); // The row
167
220
 
168
221
  The built-in strategies give the same output as their functions. `curveStrategies.rounded.run({ points, space }, { radius: 8 })` gives the segments of `curveOf(points, { type: "rounded", space, radius: 8 })`. `benchCurveStrategies` and the fourth argument of `benchRoom` take a list of strategies.
169
222
 
170
- The plugs and the zoom levels have one strategy each, so they have no step yet.
223
+ The plugs have one strategy, so they have no step yet. The step `group` gives the grouping rule of the zoom levels.
171
224
 
172
225
  ## License
173
226
 
package/build/index.d.ts CHANGED
@@ -96,6 +96,8 @@ export interface Node {
96
96
  readonly id: string;
97
97
  readonly box: Box;
98
98
  readonly ports?: readonly Port[];
99
+ /** This data is free for the grouping rules of the nodes. The rule `keys` reads the string `data.group` by default. */
100
+ readonly data?: Readonly<Record<string, unknown>>;
99
101
  }
100
102
  /** One end of an edge: a node, and optionally one plug of that node. */
101
103
  export interface End {
@@ -652,7 +654,11 @@ export interface SectionOptions {
652
654
  readonly cap?: number;
653
655
  /** The tree stops at this depth. The default is 16. */
654
656
  readonly maxDepth?: number;
657
+ /** A cell shrinks to one quarter at most this many times without a split. Then it stops. The default is 32. */
658
+ readonly collapse?: number;
655
659
  }
660
+ /** The default values of `SectionOptions`. */
661
+ export declare const SECTION_DEFAULTS: Readonly<Required<SectionOptions>>;
656
662
  /**
657
663
  * This function makes the tree of sections of the nodes. A cell with more than `cap` nodes splits in four.
658
664
  * When the nodes of a cell are all in one quarter, the cell becomes that quarter and does not make a level.
@@ -662,6 +668,55 @@ export declare function buildSections(nodes: readonly Node[], options?: SectionO
662
668
  export declare function allSections(root: Section): Section[];
663
669
  /** This function gives the depth of the deepest section. */
664
670
  export declare function treeDepth(root: Section): number;
671
+ /**
672
+ * This function examines a tree of sections from a grouping rule. It throws an error when the tree is not valid for `count` nodes.
673
+ * In a valid tree, each node is in exactly one leaf. The children of a section split its nodes, and a child is one level deeper than its parent.
674
+ */
675
+ export declare function checkSections(root: Section, count: number): void;
676
+ /**
677
+ * This function gives the tree of sections of the grouping rule `quadtree`: the cells of a quadtree of the plane (`buildSections`).
678
+ * It is the default grouping rule of the router `sections`.
679
+ */
680
+ export declare function quadtreeGroups(graph: Graph, options?: SectionOptions): Section;
681
+ /** A key rule gives the key of the group of a node. A node without a key is a group alone. */
682
+ type NodeKey = (node: Node, index: number) => string | undefined;
683
+ /** The options of the grouping rule `keys`. */
684
+ interface KeyOptions {
685
+ /** This function gives the key of the group of each node. The default is `byDataGroup`. */
686
+ readonly key?: NodeKey;
687
+ /**
688
+ * With this value, a group with more nodes than its `cap` splits into the cells of a quadtree, as `buildSections` does.
689
+ * Without it, each group is one leaf. The default is no split.
690
+ */
691
+ readonly split?: SectionOptions;
692
+ }
693
+ /** This key rule gives the string `data.group` of the node, or `undefined` when the node has no such string. */
694
+ export declare const byDataGroup: NodeKey;
695
+ /**
696
+ * This function gives the tree of sections of the grouping rule `keys`. The root holds each node. Each child of the root holds the nodes of one key,
697
+ * in the order of their first node. The id of a group is `s:` and its key. A node without a key is a group alone, with the id `s#` and its index.
698
+ * Thus the far view of the router `sections` shows one item for each group.
699
+ */
700
+ export declare function keyedSections(nodes: readonly Node[], options?: KeyOptions): Section;
701
+ /** This function gives the tree of sections of the grouping rule `keys` for a graph. */
702
+ export declare function keyGroups(graph: Graph, options?: KeyOptions): Section;
703
+ /** This function gives the median width of the shown items of each level, in units of the plane. */
704
+ export declare function itemSizes(levels: readonly Level[]): number[];
705
+ /** The far-view thresholds of `pickLevel`, in pixels on the screen. */
706
+ export interface PickOptions {
707
+ /** When a node is at least this wide on the screen, the view shows each route. */
708
+ readonly routesAt: number;
709
+ /** A level is fine enough when its median item is at least this wide on the screen. */
710
+ readonly itemAt: number;
711
+ }
712
+ /** The default thresholds: a node of 22 pixels shows its routes, and a group of 120 pixels shows its parts. */
713
+ export declare const PICK: PickOptions;
714
+ /**
715
+ * This function picks the level that a camera shows at a scale, in pixels for each unit of the plane. The sizes come from `itemSizes`.
716
+ * The value `sizes.length` stands for the routes of each edge. The last level holds the nodes alone.
717
+ * Each threshold that `options` does not give gets its value from `PICK`.
718
+ */
719
+ export declare function pickLevel(sizes: readonly number[], scale: number, options?: Partial<PickOptions>): number;
665
720
  /** This function gives the point where the ray from the center of the box to `toward` leaves the box. */
666
721
  export declare function boxExit(box: Box, toward: Point): Point;
667
722
  /**
@@ -705,73 +760,16 @@ interface SharedOptions extends WayOptions {
705
760
  * The result gives each stretch as a run, with the order of its lanes.
706
761
  */
707
762
  export declare function sharedRouter(options: SharedOptions): Router;
708
- /** The options of the router with sections. */
709
- interface SectionsOptions extends WayOptions, SectionOptions {
710
- /** The name in the benchmark. */
711
- readonly name: string;
763
+ /** The options of the links of a level: the options of the ways, and two limits of the level links. */
764
+ interface LevelLinkOptions extends WayOptions {
712
765
  /**
713
- * The number of hubs on each side of the lowest common section that a way passes. A smaller number gives shorter ways
714
- * and fewer shared ways. Without it, a way passes each hub from its node up.
766
+ * The margin of the second corner graph. A gap between two boxes wider than approximately 1.5 times this margin lets a link through.
767
+ * The second graph uses the smaller of this value and `margin`. The default is 1.
715
768
  */
716
- readonly hubs?: number;
717
- /** 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. */
718
- readonly stretch?: number;
719
- /** The benchmark skips a fixture with more nodes. */
720
- readonly maxNodes?: number;
769
+ readonly tightMargin?: number;
770
+ /** A plug stays at least this far from the corners of its side. The level links use the smaller of this value and `margin`. The default is 6. */
771
+ readonly plugInset?: number;
721
772
  }
722
- /**
723
- * 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.
724
- * An edge goes from its node up the hubs of its sections to the lowest common section, across, and down to the other node.
725
- * Each way from hub to hub is found one time, so all the edges on it share its points.
726
- * The edges in one section of the lowest level go straight around the nodes.
727
- *
728
- * An edge skips a hub that takes it away from its target. A way that still goes out and back gives way to the direct way.
729
- *
730
- * The result gives one zoom level for each depth of the tree.
731
- * A link of a level goes from the boundary of one item to the boundary of the other, around the other items.
732
- */
733
- export declare function sectionsRouter(options: SectionsOptions): Router;
734
- /** The options of the trunk router. */
735
- interface TrunksOptions extends WayOptions {
736
- /** The name in the benchmark. */
737
- readonly name: string;
738
- /** The grouping rule. The edges from one plug with one key share a trunk. */
739
- readonly rule: GroupRule;
740
- /** The split point is at this share of the way to the center of the targets. The default is 0.5. */
741
- readonly split?: number;
742
- /** A group with fewer edges stays unbundled. The default is 2. */
743
- readonly minSize?: number;
744
- /** The benchmark skips a fixture with more nodes. */
745
- readonly maxNodes?: number;
746
- }
747
- /** The rule that groups the edges by the collection of their target, in `data.collection`. */
748
- export declare const byCollection: GroupRule;
749
- /**
750
- * This function groups the edges by their source plug and their key. Each group keeps the order of the edges.
751
- * A group with fewer than `minSize` edges does not appear.
752
- */
753
- export declare function groupEdges(edges: readonly Edge[], rule: GroupRule, minSize?: number): Map<string, Edge[]>;
754
- /**
755
- * This function gives the lane order of the branches that leave the split point `from`, with the trunk along `dir`.
756
- * Lane 0 turns most to the left, in a frame with the y axis down. Thus two lanes do not cross at the split.
757
- */
758
- export declare function laneOrder(dir: Point, from: Point, branches: readonly {
759
- readonly edge: string;
760
- readonly next: Point;
761
- }[]): string[];
762
- /**
763
- * This function gives the split point of a bundle, on the line from `start` to the center of the targets.
764
- * 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.
765
- */
766
- export declare function splitPoint(start: Point, targets: readonly Point[], split: number, free: (p: Point) => boolean, step: number): Point;
767
- /**
768
- * This function makes a router that bundles the edges by a rule. The edges of a group share one plug and one trunk
769
- * to a split point. From there, each edge goes its own way. The router gives the bundles with their lane order.
770
- * The edges outside the groups go as in the corner router.
771
- */
772
- export declare function trunksRouter(options: TrunksOptions): Router;
773
- /** The built-in routers. The registry `STRATEGIES` holds the same routers as strategies, and the benchmark races the routers of the registry. */
774
- export declare const ROUTERS: readonly Router[];
775
773
  /** An item with a box: a node of a graph, an item of a level or a box of a host layout. */
776
774
  export interface Boxed {
777
775
  readonly id: string;
@@ -862,6 +860,207 @@ export declare function roomDemand(items: readonly Boxed[], links: readonly Room
862
860
  export declare function makeRoom<T extends Boxed>(items: readonly T[], links: readonly RoomLink[], options?: RoomOptions): Room<T>;
863
861
  /** This function gives the links between the nodes of a graph, with the count of the edges of each pair. A loop does not count. */
864
862
  export declare function roomLinks(graph: Graph): RoomLink[];
863
+ /** The input of a curve strategy: one polyline and the context of its route. */
864
+ export interface CurveInput {
865
+ /** These points are the polyline: a route of a router, a lane of `laneRoutes` or a link of a zoom level. */
866
+ readonly points: readonly Point[];
867
+ /** The curve leaves the first point along this outward normal of its plug, if the strategy reads it. */
868
+ readonly startNormal?: Point;
869
+ /** The curve comes into the last point against this outward normal of its plug, if the strategy reads it. */
870
+ readonly endNormal?: Point;
871
+ /** The boxes that the curve must not go through, from `curveSpace`. */
872
+ readonly space?: CurveSpace;
873
+ }
874
+ /** The input of a room strategy: the boxes of a board and the links between them. */
875
+ export interface RoomInput {
876
+ readonly items: readonly Boxed[];
877
+ readonly links: readonly RoomLink[];
878
+ }
879
+ /**
880
+ * The steps that have strategies, with the fixed input and output type of each step.
881
+ *
882
+ * - `group`: a graph in, the tree of sections out. The router `sections` makes its hubs and its zoom levels from this tree.
883
+ * - `router`: a graph in, the routes (and the `levels`, `bundles` and `runs`) out.
884
+ * - `curve`: a polyline in, the segments of a curve out.
885
+ * - `room`: the boxes and links of a board in, the gaps of the board with their need out.
886
+ */
887
+ export interface StepTypes {
888
+ readonly group: {
889
+ readonly input: Graph;
890
+ readonly output: Section;
891
+ };
892
+ readonly router: {
893
+ readonly input: Graph;
894
+ readonly output: Result;
895
+ };
896
+ readonly curve: {
897
+ readonly input: CurveInput;
898
+ readonly output: CurveSegment[];
899
+ };
900
+ readonly room: {
901
+ readonly input: RoomInput;
902
+ readonly output: Gap[];
903
+ };
904
+ }
905
+ /** The name of a step that has strategies. */
906
+ export type Step = keyof StepTypes;
907
+ /** The input type of a step. */
908
+ export type StepInput<S extends Step> = StepTypes[S]["input"];
909
+ /** The output type of a step. */
910
+ export type StepOutput<S extends Step> = StepTypes[S]["output"];
911
+ /** The steps that have strategies, in the order of the pipeline. */
912
+ export declare const STEPS: readonly Step[];
913
+ /**
914
+ * A strategy: one way to do one step, with a name and a description.
915
+ * The same input and the same options must give the same output.
916
+ */
917
+ export interface Strategy<S extends Step = Step, O extends object = object> {
918
+ /** The step of the strategy. It fixes the input and the output type. */
919
+ readonly step: S;
920
+ /** The name of the strategy. Each name is unique in its step of a registry. */
921
+ readonly name: string;
922
+ /** One or two sentences that tell what the strategy does. The site and the documents show them. */
923
+ readonly describe: string;
924
+ /** The values of the options when the caller gives no value. */
925
+ readonly defaults: Readonly<O>;
926
+ /** The benchmark skips an input with more nodes than this. Without it, the strategy takes each input. */
927
+ readonly maxNodes?: number;
928
+ /** This method does the step. Each option that `options` does not give gets its value from `defaults`. */
929
+ run(input: StepInput<S>, options?: Partial<O>): StepOutput<S>;
930
+ }
931
+ /** A strategy of any step. A registry holds a list of these. */
932
+ export type AnyStrategy = { [S in Step]: Strategy<S>; }[Step];
933
+ /** The definition of a strategy, for `defineStrategy`. */
934
+ export interface StrategyDefinition<S extends Step, O extends object> {
935
+ readonly step: S;
936
+ readonly name: string;
937
+ readonly describe: string;
938
+ /** These values are the options when the caller gives no value. Give `{}` for a strategy without options. */
939
+ readonly defaults: O;
940
+ readonly maxNodes?: number;
941
+ /** This function does the step. It gets each option, from the caller or from `defaults`. */
942
+ run(input: StepInput<S>, options: Readonly<O>): StepOutput<S>;
943
+ }
944
+ /**
945
+ * This function makes a strategy from its definition. The strategy fills the missing options from `defaults`.
946
+ * When the caller gives no option, the definition gets the `defaults` object itself.
947
+ */
948
+ export declare function defineStrategy<S extends Step, O extends object>(definition: StrategyDefinition<S, O>): Strategy<S, O>;
949
+ /**
950
+ * A registry: the strategies of each step, in order. A registry is data. No import changes it.
951
+ * A consumer makes its own registry with `createRegistry`, and adds a strategy with `register`.
952
+ * The list `group` is optional in the type, so a registry from version 0.1.5 stays a registry. `createRegistry` always gives it.
953
+ */
954
+ export type Registry = { readonly [S in Exclude<Step, "group">]: readonly Strategy<S>[]; } & {
955
+ readonly group?: readonly Strategy<"group">[];
956
+ };
957
+ /** This function makes a registry from a list of strategies. It keeps their order. Two strategies with one step and one name are an error. */
958
+ export declare function createRegistry(strategies: readonly AnyStrategy[]): Registry;
959
+ /** This function gives a new registry with the strategies of `registry` and then `strategies`. It does not change `registry`. */
960
+ export declare function register(registry: Registry, ...strategies: readonly AnyStrategy[]): Registry;
961
+ /** This function gives the strategies of one step of a registry, in order. */
962
+ export declare function listStrategies<S extends Step>(registry: Registry, step: S): readonly Strategy<S>[];
963
+ /** This function gives the strategy of one step with a name, or `undefined` when the registry has no such strategy. */
964
+ export declare function findStrategy<S extends Step>(registry: Registry, step: S, name: string): Strategy<S> | undefined;
965
+ /** This function gives the strategy of one step with a name. When the registry has no such strategy, the function throws an error. */
966
+ export declare function getStrategy<S extends Step>(registry: Registry, step: S, name: string): Strategy<S>;
967
+ /** This function gives a `Router` that runs a router strategy with the options. The router keeps the name and the `maxNodes` of the strategy. */
968
+ export declare function asRouter<O extends object>(strategy: Strategy<"router", O>, options?: Partial<O>): Router;
969
+ /** This function gives the router strategies of a registry as `Router` objects, in order. The benchmark races them. */
970
+ export declare function routersOf(registry: Registry): Router[];
971
+ /**
972
+ * The options of the router with sections. Each option has a default, and the defaults give the output of version 0.1.5.
973
+ * The options `cap`, `maxDepth` and `collapse` are the limits of the default grouping rule, the quadtree of `buildSections`.
974
+ * The options `tightMargin` and `plugInset` are the limits of the level links (`levelLinks`).
975
+ */
976
+ interface SectionsOptions extends LevelLinkOptions, SectionOptions {
977
+ /** The name in the benchmark. */
978
+ readonly name: string;
979
+ /**
980
+ * The number of hubs on each side of the lowest common section that a way passes. A smaller number gives shorter ways
981
+ * and fewer shared ways. Without it, a way passes each hub from its node up.
982
+ */
983
+ readonly hubs?: number;
984
+ /**
985
+ * The hub limit. A way through the hubs can be at most this many times as long as the line between its plugs, plus `stretchPad` margins.
986
+ * Else the edge goes directly. The default is 2. The value `Infinity` keeps each way through the hubs.
987
+ */
988
+ readonly stretch?: number;
989
+ /** A way through the hubs can be this many margins longer than `stretch` times the line between its plugs. The default is 4. */
990
+ readonly stretchPad?: number;
991
+ /**
992
+ * A way skips a hub that is more than this many units farther from the target than the nearest approach of the way before it.
993
+ * The default is 80, two times `BACKTRACK_MIN`. The value `Infinity` keeps each hub.
994
+ */
995
+ readonly hubSlack?: number;
996
+ /**
997
+ * The grouping rule: a strategy of the step `group` that makes the tree of sections. The hubs and the zoom levels come from this tree.
998
+ * Without it, the router uses the quadtree of `buildSections` with the options `cap`, `maxDepth` and `collapse`.
999
+ */
1000
+ readonly group?: Strategy<"group">;
1001
+ /** The options of the strategy `group`. The strategy gets its `defaults` for each option that this object does not give. */
1002
+ readonly groupOptions?: object;
1003
+ /**
1004
+ * The depths of the tree that get a zoom level with groups. The last level, with each node alone, always comes after them.
1005
+ * The default is each depth, from 0 to the depth of the deepest section.
1006
+ */
1007
+ readonly levels?: readonly number[];
1008
+ /** The benchmark skips a fixture with more nodes. */
1009
+ readonly maxNodes?: number;
1010
+ }
1011
+ /**
1012
+ * 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.
1013
+ * An edge goes from its node up the hubs of its sections to the lowest common section, across, and down to the other node.
1014
+ * Each way from hub to hub is found one time, so all the edges on it share its points.
1015
+ * The edges in one section of the lowest level go straight around the nodes.
1016
+ *
1017
+ * An edge skips a hub that takes it away from its target. A way that still goes out and back gives way to the direct way.
1018
+ *
1019
+ * The result gives one zoom level for each depth of the tree.
1020
+ * A link of a level goes from the boundary of one item to the boundary of the other, around the other items.
1021
+ */
1022
+ export declare function sectionsRouter(options: SectionsOptions): Router;
1023
+ /** The options of the trunk router. */
1024
+ interface TrunksOptions extends WayOptions {
1025
+ /** The name in the benchmark. */
1026
+ readonly name: string;
1027
+ /** The grouping rule. The edges from one plug with one key share a trunk. */
1028
+ readonly rule: GroupRule;
1029
+ /** The split point is at this share of the way to the center of the targets. The default is 0.5. */
1030
+ readonly split?: number;
1031
+ /** A group with fewer edges stays unbundled. The default is 2. */
1032
+ readonly minSize?: number;
1033
+ /** The benchmark skips a fixture with more nodes. */
1034
+ readonly maxNodes?: number;
1035
+ }
1036
+ /** The rule that groups the edges by the collection of their target, in `data.collection`. */
1037
+ export declare const byCollection: GroupRule;
1038
+ /**
1039
+ * This function groups the edges by their source plug and their key. Each group keeps the order of the edges.
1040
+ * A group with fewer than `minSize` edges does not appear.
1041
+ */
1042
+ export declare function groupEdges(edges: readonly Edge[], rule: GroupRule, minSize?: number): Map<string, Edge[]>;
1043
+ /**
1044
+ * This function gives the lane order of the branches that leave the split point `from`, with the trunk along `dir`.
1045
+ * Lane 0 turns most to the left, in a frame with the y axis down. Thus two lanes do not cross at the split.
1046
+ */
1047
+ export declare function laneOrder(dir: Point, from: Point, branches: readonly {
1048
+ readonly edge: string;
1049
+ readonly next: Point;
1050
+ }[]): string[];
1051
+ /**
1052
+ * This function gives the split point of a bundle, on the line from `start` to the center of the targets.
1053
+ * 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.
1054
+ */
1055
+ export declare function splitPoint(start: Point, targets: readonly Point[], split: number, free: (p: Point) => boolean, step: number): Point;
1056
+ /**
1057
+ * This function makes a router that bundles the edges by a rule. The edges of a group share one plug and one trunk
1058
+ * to a split point. From there, each edge goes its own way. The router gives the bundles with their lane order.
1059
+ * The edges outside the groups go as in the corner router.
1060
+ */
1061
+ export declare function trunksRouter(options: TrunksOptions): Router;
1062
+ /** The built-in routers. The registry `STRATEGIES` holds the same routers as strategies, and the benchmark races the routers of the registry. */
1063
+ export declare const ROUTERS: readonly Router[];
865
1064
  /**
866
1065
  * The lane order of the pieces of the links in one gap.
867
1066
  *
@@ -1066,106 +1265,6 @@ export declare function formatTable(rows: readonly BenchRow[]): string;
1066
1265
  * A router without such levels gets a dash in the columns of the levels.
1067
1266
  */
1068
1267
  export declare function formatFaults(rows: readonly BenchRow[]): string;
1069
- /** The input of a curve strategy: one polyline and the context of its route. */
1070
- export interface CurveInput {
1071
- /** These points are the polyline: a route of a router, a lane of `laneRoutes` or a link of a zoom level. */
1072
- readonly points: readonly Point[];
1073
- /** The curve leaves the first point along this outward normal of its plug, if the strategy reads it. */
1074
- readonly startNormal?: Point;
1075
- /** The curve comes into the last point against this outward normal of its plug, if the strategy reads it. */
1076
- readonly endNormal?: Point;
1077
- /** The boxes that the curve must not go through, from `curveSpace`. */
1078
- readonly space?: CurveSpace;
1079
- }
1080
- /** The input of a room strategy: the boxes of a board and the links between them. */
1081
- export interface RoomInput {
1082
- readonly items: readonly Boxed[];
1083
- readonly links: readonly RoomLink[];
1084
- }
1085
- /**
1086
- * The steps that have strategies, with the fixed input and output type of each step.
1087
- *
1088
- * - `router`: a graph in, the routes (and the `levels`, `bundles` and `runs`) out.
1089
- * - `curve`: a polyline in, the segments of a curve out.
1090
- * - `room`: the boxes and links of a board in, the gaps of the board with their need out.
1091
- */
1092
- export interface StepTypes {
1093
- readonly router: {
1094
- readonly input: Graph;
1095
- readonly output: Result;
1096
- };
1097
- readonly curve: {
1098
- readonly input: CurveInput;
1099
- readonly output: CurveSegment[];
1100
- };
1101
- readonly room: {
1102
- readonly input: RoomInput;
1103
- readonly output: Gap[];
1104
- };
1105
- }
1106
- /** The name of a step that has strategies. */
1107
- export type Step = keyof StepTypes;
1108
- /** The input type of a step. */
1109
- export type StepInput<S extends Step> = StepTypes[S]["input"];
1110
- /** The output type of a step. */
1111
- export type StepOutput<S extends Step> = StepTypes[S]["output"];
1112
- /** The steps that have strategies, in the order of the pipeline. */
1113
- export declare const STEPS: readonly Step[];
1114
- /**
1115
- * A strategy: one way to do one step, with a name and a description.
1116
- * The same input and the same options must give the same output.
1117
- */
1118
- export interface Strategy<S extends Step = Step, O extends object = object> {
1119
- /** The step of the strategy. It fixes the input and the output type. */
1120
- readonly step: S;
1121
- /** The name of the strategy. Each name is unique in its step of a registry. */
1122
- readonly name: string;
1123
- /** One or two sentences that tell what the strategy does. The site and the documents show them. */
1124
- readonly describe: string;
1125
- /** The values of the options when the caller gives no value. */
1126
- readonly defaults: Readonly<O>;
1127
- /** The benchmark skips an input with more nodes than this. Without it, the strategy takes each input. */
1128
- readonly maxNodes?: number;
1129
- /** This method does the step. Each option that `options` does not give gets its value from `defaults`. */
1130
- run(input: StepInput<S>, options?: Partial<O>): StepOutput<S>;
1131
- }
1132
- /** A strategy of any step. A registry holds a list of these. */
1133
- export type AnyStrategy = { [S in Step]: Strategy<S>; }[Step];
1134
- /** The definition of a strategy, for `defineStrategy`. */
1135
- export interface StrategyDefinition<S extends Step, O extends object> {
1136
- readonly step: S;
1137
- readonly name: string;
1138
- readonly describe: string;
1139
- /** These values are the options when the caller gives no value. Give `{}` for a strategy without options. */
1140
- readonly defaults: O;
1141
- readonly maxNodes?: number;
1142
- /** This function does the step. It gets each option, from the caller or from `defaults`. */
1143
- run(input: StepInput<S>, options: Readonly<O>): StepOutput<S>;
1144
- }
1145
- /**
1146
- * This function makes a strategy from its definition. The strategy fills the missing options from `defaults`.
1147
- * When the caller gives no option, the definition gets the `defaults` object itself.
1148
- */
1149
- export declare function defineStrategy<S extends Step, O extends object>(definition: StrategyDefinition<S, O>): Strategy<S, O>;
1150
- /**
1151
- * A registry: the strategies of each step, in order. A registry is data. No import changes it.
1152
- * A consumer makes its own registry with `createRegistry`, and adds a strategy with `register`.
1153
- */
1154
- export type Registry = { readonly [S in Step]: readonly Strategy<S>[]; };
1155
- /** This function makes a registry from a list of strategies. It keeps their order. Two strategies with one step and one name are an error. */
1156
- export declare function createRegistry(strategies: readonly AnyStrategy[]): Registry;
1157
- /** This function gives a new registry with the strategies of `registry` and then `strategies`. It does not change `registry`. */
1158
- export declare function register(registry: Registry, ...strategies: readonly AnyStrategy[]): Registry;
1159
- /** This function gives the strategies of one step of a registry, in order. */
1160
- export declare function listStrategies<S extends Step>(registry: Registry, step: S): readonly Strategy<S>[];
1161
- /** This function gives the strategy of one step with a name, or `undefined` when the registry has no such strategy. */
1162
- export declare function findStrategy<S extends Step>(registry: Registry, step: S, name: string): Strategy<S> | undefined;
1163
- /** This function gives the strategy of one step with a name. When the registry has no such strategy, the function throws an error. */
1164
- export declare function getStrategy<S extends Step>(registry: Registry, step: S, name: string): Strategy<S>;
1165
- /** This function gives a `Router` that runs a router strategy with the options. The router keeps the name and the `maxNodes` of the strategy. */
1166
- export declare function asRouter<O extends object>(strategy: Strategy<"router", O>, options?: Partial<O>): Router;
1167
- /** This function gives the router strategies of a registry as `Router` objects, in order. The benchmark races them. */
1168
- export declare function routersOf(registry: Registry): Router[];
1169
1268
  /**
1170
1269
  * One row of the benchmark of the curves: one curve type on the routes of one router on one fixture.
1171
1270
  * The type `T` of the name of the curve is `CurveType` for `benchCurves`, and `string` for `benchCurveStrategies`.
@@ -1268,6 +1367,59 @@ export declare function laneSettings(spacings?: readonly number[], orders?: read
1268
1367
  export declare function passSettings(gutters?: readonly number[], passes?: readonly number[]): RoomSetting[];
1269
1368
  /** This function writes the rows of `benchRoom` as a Markdown table. */
1270
1369
  export declare function formatRoom(rows: readonly RoomBenchRow[]): string;
1370
+ /** The options of one setting of the router `sections`, without the name. */
1371
+ export type SectionsSettingOptions = Omit<SectionsOptions, "name" | "maxNodes">;
1372
+ /** One setting of a sweep of the router `sections`. */
1373
+ export interface SectionsSetting {
1374
+ /** The name of the setting in the column `setting`. */
1375
+ readonly name: string;
1376
+ readonly options: SectionsSettingOptions;
1377
+ }
1378
+ /** One row of a sweep of the router `sections`: one setting on one board, and the measures of its routes and of its zoom levels. */
1379
+ export interface SectionsBenchRow {
1380
+ readonly fixture: string;
1381
+ readonly setting: string;
1382
+ /** The faults of the routes of the edges (`measureRouteFaults`). */
1383
+ readonly faults: LinkFaults;
1384
+ /** The sum of the faults of the levels with groups (`measureLevelFaults` and `sumFaults`). */
1385
+ readonly levelFaults: LinkFaults;
1386
+ /** The sum of the overlaps of the levels with groups. */
1387
+ readonly overlaps: number;
1388
+ /** The number of routes that go through hubs: the routes that are not the same as the direct route of their edge. */
1389
+ readonly hubRoutes: number;
1390
+ /** The number of shared stretches of way of the result (`runs`). */
1391
+ readonly runs: number;
1392
+ /** The number of zoom levels, with the last level of single nodes. */
1393
+ readonly levels: number;
1394
+ /** The number of items of the far view: the first level with more than one item. */
1395
+ readonly farItems: number;
1396
+ /** The time of one route, in milliseconds. */
1397
+ readonly ms: number;
1398
+ }
1399
+ /**
1400
+ * This function measures the router `sections` on one board with each setting. The first row is the fair baseline: the defaults, with no option.
1401
+ * The direct routes come from the hub limit 0, so `hubRoutes` counts the routes that use the hubs.
1402
+ */
1403
+ export declare function sweepSections(fixture: string, graph: Graph, settings: readonly SectionsSetting[]): SectionsBenchRow[];
1404
+ /** The values of the hub limit (`stretch`) of the sweep of CUSTOM. The default is 2. */
1405
+ export declare const STRETCH_SWEEP: readonly number[];
1406
+ /** This function gives the settings of the sweep of the hub limit: one setting for each value of `stretch`. */
1407
+ export declare function stretchSettings(values?: readonly number[]): SectionsSetting[];
1408
+ /** The values of `hubSlack` of the sweep of CUSTOM. The default is 80. */
1409
+ export declare const SLACK_SWEEP: readonly number[];
1410
+ /** This function gives the settings of the sweep of the slack of the hubs: one setting for each value of `hubSlack`. */
1411
+ export declare function slackSettings(values?: readonly number[]): SectionsSetting[];
1412
+ /** The values of the section limit (`cap`) of the sweep of CUSTOM. The default is 8. */
1413
+ export declare const CAP_SWEEP: readonly number[];
1414
+ /** This function gives the settings of the sweep of the section limit: one setting for each value of `cap`. */
1415
+ export declare function capSettings(values?: readonly number[]): SectionsSetting[];
1416
+ /**
1417
+ * This function gives one setting for each grouping rule: the router `sections` with the strategy as its option `group`.
1418
+ * The map `options` gives the options of a strategy by its name.
1419
+ */
1420
+ export declare function groupSettings(strategies?: readonly Strategy<"group">[], options?: Readonly<Record<string, object>>): SectionsSetting[];
1421
+ /** This function writes the rows of `sweepSections` as a Markdown table. */
1422
+ export declare function formatSections(rows: readonly SectionsBenchRow[]): string;
1271
1423
  /** The options of a router strategy: the options of its router, without the name and the limit of the benchmark. */
1272
1424
  export type RouterPreset<O> = Omit<O, "name" | "maxNodes">;
1273
1425
  /** The built-in router strategies, in the order of the benchmark. They route as the routers of `ROUTERS`. */
@@ -1306,9 +1458,14 @@ export declare const roomStrategies: {
1306
1458
  readonly routes: Strategy<"room", RoomPreset>;
1307
1459
  readonly lines: Strategy<"room", RoomPreset>;
1308
1460
  };
1461
+ /** The built-in grouping rules: the strategies of the step `group`. The router `sections` makes its hubs and its zoom levels from their tree. */
1462
+ export declare const groupStrategies: {
1463
+ readonly quadtree: Strategy<"group", Required<SectionOptions>>;
1464
+ readonly keys: Strategy<"group", KeyOptions>;
1465
+ };
1309
1466
  /** The built-in strategies of each step, in order. A consumer adds its own: `createRegistry([...BUILT_IN_STRATEGIES, mine])`. */
1310
1467
  export declare const BUILT_IN_STRATEGIES: readonly AnyStrategy[];
1311
1468
  /** The registry of the built-in strategies. The benchmark and the site take their lists from it. */
1312
1469
  export declare const STRATEGIES: Registry;
1313
- export type { ArcOptions, CornersOptions, CurveSpace, RoundedOptions, SectionsOptions, SharedOptions, SplineOptions, TrunksOptions };
1470
+ export type { ArcOptions, CornersOptions, CurveSpace, KeyOptions, NodeKey, RoundedOptions, SectionsOptions, SharedOptions, SplineOptions, TrunksOptions };
1314
1471
  //# sourceMappingURL=index.d.ts.map