@draftably/editor 0.4.0 → 0.6.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.
Files changed (82) hide show
  1. package/dist/assets/engine.worker-iJs-dOtM.js +8071 -0
  2. package/dist/element.js +27 -16
  3. package/dist/index.js +12154 -11312
  4. package/dist/types/src/components/scene/EdgeView.d.ts +2 -0
  5. package/dist/types/src/components/scene/Lifelines.d.ts +4 -3
  6. package/dist/types/src/components/session-context.d.ts +9 -2
  7. package/dist/types/src/dsl/compile.d.ts +2 -0
  8. package/dist/types/src/dsl/sequence-layout.d.ts +87 -0
  9. package/dist/types/src/dsl/source-edits.d.ts +2 -0
  10. package/dist/types/src/dsl/syntax.d.ts +8 -2
  11. package/dist/types/src/dsl/translate.d.ts +1 -1
  12. package/dist/types/src/editor/internal.d.ts +9 -2
  13. package/dist/types/src/editor/mount.d.ts +13 -6
  14. package/dist/types/src/engines/explorer-engine.d.ts +63 -0
  15. package/dist/types/src/engines/layout/arrange.d.ts +36 -0
  16. package/dist/types/src/engines/layout/compute.d.ts +7 -0
  17. package/dist/types/src/engines/layout/forces.d.ts +8 -0
  18. package/dist/types/src/engines/layout/grid.d.ts +10 -0
  19. package/dist/types/src/engines/layout/intent.d.ts +14 -0
  20. package/dist/types/src/engines/layout/layered.d.ts +5 -0
  21. package/dist/types/src/engines/layout/objective.d.ts +6 -0
  22. package/dist/types/src/engines/layout/packing.d.ts +7 -0
  23. package/dist/types/src/engines/layout/report.d.ts +7 -0
  24. package/dist/types/src/engines/layout/scene.d.ts +53 -0
  25. package/dist/types/src/engines/layout/seed.d.ts +3 -0
  26. package/dist/types/src/engines/layout/separation.d.ts +17 -0
  27. package/dist/types/src/engines/layout/settings.d.ts +6 -0
  28. package/dist/types/src/engines/layout/topology.d.ts +18 -0
  29. package/dist/types/src/engines/layout/trees.d.ts +8 -0
  30. package/dist/types/src/engines/layout/types.d.ts +133 -0
  31. package/dist/types/src/engines/layout/util.d.ts +19 -0
  32. package/dist/types/src/engines/layout-engine.d.ts +15 -0
  33. package/dist/types/src/engines/routing/compute.d.ts +6 -0
  34. package/dist/types/src/engines/routing/conflicts.d.ts +36 -0
  35. package/dist/types/src/engines/routing/context.d.ts +37 -0
  36. package/dist/types/src/engines/routing/cost.d.ts +13 -0
  37. package/dist/types/src/engines/routing/doglegs.d.ts +16 -0
  38. package/dist/types/src/engines/routing/find.d.ts +6 -0
  39. package/dist/types/src/engines/routing/geometry.d.ts +24 -0
  40. package/dist/types/src/engines/routing/heap.d.ts +20 -0
  41. package/dist/types/src/engines/routing/improve.d.ts +7 -0
  42. package/dist/types/src/engines/routing/nudge.d.ts +7 -0
  43. package/dist/types/src/engines/routing/paths.d.ts +8 -0
  44. package/dist/types/src/engines/routing/planned.d.ts +7 -0
  45. package/dist/types/src/engines/routing/ports.d.ts +10 -0
  46. package/dist/types/src/engines/routing/repair.d.ts +9 -0
  47. package/dist/types/src/engines/routing/score.d.ts +12 -0
  48. package/dist/types/src/engines/routing/search.d.ts +16 -0
  49. package/dist/types/src/engines/routing/settings.d.ts +4 -0
  50. package/dist/types/src/engines/routing/sidepairs.d.ts +20 -0
  51. package/dist/types/src/engines/routing/tidy-ports.d.ts +13 -0
  52. package/dist/types/src/engines/routing/tidy.d.ts +10 -0
  53. package/dist/types/src/engines/routing/tuning.d.ts +9 -0
  54. package/dist/types/src/engines/routing/types.d.ts +157 -0
  55. package/dist/types/src/engines/routing-engine.d.ts +20 -0
  56. package/dist/types/src/engines/sculpt-engine.d.ts +87 -0
  57. package/dist/types/src/lib/engine-tasks.d.ts +4 -4
  58. package/dist/types/src/model/appearance.d.ts +18 -2
  59. package/dist/types/src/model/edits.d.ts +19 -0
  60. package/dist/types/src/model/labels.d.ts +23 -0
  61. package/dist/types/src/model/markers.d.ts +7 -0
  62. package/dist/types/src/model/note.d.ts +11 -2
  63. package/dist/types/src/model/routes.d.ts +5 -3
  64. package/dist/types/src/model/status.d.ts +18 -0
  65. package/dist/types/src/model/types.d.ts +20 -1
  66. package/dist/types/src/runtime/color-scheme.d.ts +15 -0
  67. package/dist/types/src/runtime/host.d.ts +34 -2
  68. package/dist/types/src/runtime/page-shared.d.ts +1 -1
  69. package/dist/types/src/runtime/schemes.d.ts +2 -0
  70. package/dist/types/src/runtime/svg-renderer.d.ts +9 -4
  71. package/dist/types/src/runtime/types.d.ts +7 -0
  72. package/dist/types/src/state/active-session.d.ts +9 -2
  73. package/dist/types/src/state/canvas-commands.d.ts +37 -0
  74. package/dist/types/src/state/session/actions.d.ts +2 -0
  75. package/dist/types/src/state/session/emphasis.d.ts +3 -0
  76. package/dist/types/src/state/session/file-session.d.ts +1 -1
  77. package/dist/types/src/state/session/index.d.ts +14 -2
  78. package/dist/types/src/state/session/layout.d.ts +1 -1
  79. package/dist/types/src/state/session/scene.d.ts +11 -9
  80. package/dist/types/src/state/session/sculpt.d.ts +1 -1
  81. package/package.json +1 -1
  82. package/dist/assets/engine.worker-C6o2x40z.js +0 -7609
@@ -0,0 +1,133 @@
1
+ import type { Diagram, Direction, IntentPlacement, LayoutAlgorithmId, LayoutIntent, LayoutSettings, NodeGeometry, NodeLayout, Point, Rect } from '../../model/types.ts';
2
+ export type LayoutSettingKey = 'boundaryPull' | 'hierarchySweeps' | 'groupGap' | 'rankGap' | 'edgeLength' | 'repulsion' | 'flowStrength' | 'alignment' | 'compactness' | 'gridStrength' | 'ordering' | 'fold' | 'rootId';
3
+ export interface LayoutAlgorithm {
4
+ id: LayoutAlgorithmId;
5
+ name: string;
6
+ family: string;
7
+ short: string;
8
+ detail: string;
9
+ controls: LayoutSettingKey[];
10
+ }
11
+ export interface LayoutMetrics {
12
+ overlaps: number;
13
+ overlapArea: number;
14
+ width: number;
15
+ height: number;
16
+ aspect: number;
17
+ area: number;
18
+ linkLength: number;
19
+ }
20
+ export type LayoutScope = 'all' | 'selection' | 'neighbourhood' | 'container';
21
+ export interface LayoutOptions extends Partial<LayoutSettings> {
22
+ scope?: LayoutScope;
23
+ containerId?: string;
24
+ selected?: string[];
25
+ fixedIds?: string[];
26
+ viewport?: {
27
+ w: number;
28
+ h: number;
29
+ };
30
+ warmStart?: boolean;
31
+ settle?: boolean;
32
+ maxIterations?: number;
33
+ trace?: boolean;
34
+ rebuild?: boolean;
35
+ /** Travel per tick below which a settling solve counts the tick as quiet; 0.08 when unset. */
36
+ tolerance?: number;
37
+ /** False restores the v7 order within ranks: a rebuild never keeps the authored order. */
38
+ orderPrior?: boolean;
39
+ }
40
+ export type Termination = 'settled' | 'objective plateau' | 'iteration limit' | 'bounded pass';
41
+ /** Why one solve of one container's children stopped. */
42
+ export interface SolverStop {
43
+ parentId: string | null;
44
+ iterations: number;
45
+ reason: Exclude<Termination, 'bounded pass'>;
46
+ }
47
+ export interface LayoutReport {
48
+ intent: Array<{
49
+ role: string;
50
+ id: string;
51
+ placement: IntentPlacement;
52
+ error: number;
53
+ satisfied: boolean;
54
+ pinned: boolean;
55
+ }>;
56
+ backwardEdges: number;
57
+ directedEdges: number;
58
+ feedbackEdges: number;
59
+ algorithm: LayoutAlgorithmId;
60
+ name: string;
61
+ mode: 'pass' | 'rebuild' | 'journey';
62
+ scope: LayoutScope;
63
+ moved: number;
64
+ maxDisplacement: number;
65
+ rmsDisplacement: number;
66
+ movementLimit: number | null;
67
+ iterations: number;
68
+ batches: number;
69
+ before: LayoutMetrics;
70
+ after: LayoutMetrics;
71
+ warnings: string[];
72
+ milliseconds: number;
73
+ deterministic: boolean;
74
+ termination: Termination;
75
+ solverStops: SolverStop[];
76
+ /** Added by the app: where the computation ran. */
77
+ thread?: string;
78
+ /** Added by the app when a timeline step is kept. */
79
+ selectedStep?: number;
80
+ }
81
+ export interface TraceFrame {
82
+ nodes: NodeGeometry;
83
+ iteration: number;
84
+ phase: string;
85
+ metrics: LayoutMetrics;
86
+ }
87
+ export interface LayoutResult {
88
+ document: Diagram;
89
+ report: LayoutReport;
90
+ trace?: TraceFrame[];
91
+ }
92
+ /**
93
+ * Settings inside a solve. `radial` is an old name that `settings` maps to `tree`, so no solve
94
+ * runs with it; the radial target and its branches stay as the original engine had them.
95
+ */
96
+ export interface Settings extends Omit<LayoutSettings, 'algorithm'> {
97
+ algorithm: LayoutAlgorithmId | 'radial';
98
+ }
99
+ /** A box a solve places: a shape or a whole group, with its top-left corner and size. */
100
+ export type Item = Rect & {
101
+ id: string;
102
+ fixed?: boolean;
103
+ };
104
+ /** A link between two items of one solve. */
105
+ export interface Link {
106
+ from: string;
107
+ to: string;
108
+ directed?: boolean;
109
+ }
110
+ export interface Topology<E extends Link = Link> {
111
+ intent: LayoutIntent;
112
+ /** Item IDs, sorted. */
113
+ ids: string[];
114
+ index: Map<string, number>;
115
+ /** Directed neighbours out of, into, and (with undirected links) beside each item. */
116
+ out: Map<string, string[]>;
117
+ inc: Map<string, string[]>;
118
+ adj: Map<string, string[]>;
119
+ rank: Map<string, number>;
120
+ /** `from + '\0' + to` of each directed link that runs against the ranks. */
121
+ feedback: Set<string>;
122
+ edges: E[];
123
+ forward: E[];
124
+ components: string[][];
125
+ /** Source order of each item, when a rebuild keeps the authored order within ranks. */
126
+ declared?: Map<string, number>;
127
+ /** Which side of a centred start each item sits on: 1, -1, or 0 for the start. */
128
+ branchSides?: Map<string, number>;
129
+ }
130
+ /** Centre points by item ID. */
131
+ export type Centres = Map<string, Point>;
132
+ export type Axis = 'x' | 'y';
133
+ export type { Diagram, Direction, LayoutIntent, NodeLayout, Point, Rect };
@@ -0,0 +1,19 @@
1
+ import type { Point, Rect } from './types.ts';
2
+ export declare const copy: <T>(x: T) => T;
3
+ export declare const clamp: (x: number, lo: number, hi: number) => number;
4
+ export declare const cmp: (a: string, b: string) => 0 | 1 | -1;
5
+ export declare const magnitude: (x: number, y: number) => number;
6
+ export declare const wait: () => Promise<unknown>;
7
+ export declare function bounds(items: Rect[]): Rect;
8
+ export declare const midpoint: (r: Rect) => Point;
9
+ /** How far two boxes, kept `gap` apart, reach into each other; null when they are clear. */
10
+ export interface Penetration {
11
+ x: number;
12
+ y: number;
13
+ dx: number;
14
+ dy: number;
15
+ area: number;
16
+ }
17
+ export declare function penetration(a: Rect, b: Rect, gap?: number): Penetration | null;
18
+ /** A fixed number in [0, 1) for an ID: FNV-1a. It stands in for a random start. */
19
+ export declare function hash(id: string): number;
@@ -0,0 +1,15 @@
1
+ import { compute } from './layout/compute.ts';
2
+ import { normalizeIntent } from './layout/intent.ts';
3
+ import { settings } from './layout/settings.ts';
4
+ import { topology } from './layout/topology.ts';
5
+ export declare const LayoutLab: {
6
+ algorithms: import("./layout-engine.ts").LayoutAlgorithm[];
7
+ defaults: import("../model/types.ts").LayoutSettings;
8
+ settings: typeof settings;
9
+ compute: typeof compute;
10
+ normalizeIntent: typeof normalizeIntent;
11
+ placements: Record<import("../model/types.ts").IntentPlacement, [number, number]>;
12
+ topology: typeof topology;
13
+ };
14
+ export type { LayoutAlgorithm, LayoutMetrics, LayoutOptions, LayoutReport, LayoutResult, LayoutScope, LayoutSettingKey, SolverStop, Termination, TraceFrame, } from './layout/types.ts';
15
+ export type { Point } from '../model/types.ts';
@@ -0,0 +1,6 @@
1
+ import type { RouteInput, RoutingOptions, RoutingResult } from './types.ts';
2
+ /**
3
+ * Routes every edge between boxes that stay where they are: each edge in turn (locked ones and
4
+ * short ones first), then the improvement, repair, nudge and tidy passes unless `quick`.
5
+ */
6
+ export declare function compute(input: RouteInput, rawOptions?: RoutingOptions): RoutingResult;
@@ -0,0 +1,36 @@
1
+ import type { Point, RoutingSettings } from './types.ts';
2
+ /** A routed line, as the pair checks read it: only its points. */
3
+ export type Line = {
4
+ points: Point[];
5
+ };
6
+ export interface Span {
7
+ /** Segment count. */
8
+ n: number;
9
+ h: Uint8Array;
10
+ lo: Float64Array;
11
+ hi: Float64Array;
12
+ at: Float64Array;
13
+ x0: number;
14
+ y0: number;
15
+ x1: number;
16
+ y1: number;
17
+ }
18
+ export declare function spanOf(ps: Point[]): Span;
19
+ /** Flattens `used` for the checks below. False when it already was: the caller that got true calls `unflatten`. */
20
+ export declare function flatten(used: Line[]): boolean;
21
+ export declare const unflatten: (used: Line[]) => boolean;
22
+ export declare function parallelNear(a: Point, b: Point, c: Point, d: Point, gap: number): {
23
+ overlap: number;
24
+ separation: number;
25
+ deficit: number;
26
+ } | null;
27
+ export declare function parallelConflicts(points: Point[], used: Line[], gap: number): {
28
+ lanePairs: number;
29
+ laneDeficit: number;
30
+ minGap: number | null;
31
+ };
32
+ export declare function siblingDeficit(points: Point[], used: Line[], min: number, gap: number): number;
33
+ export declare function conflicts(points: Point[], used: Line[], bound?: number, s?: RoutingSettings): {
34
+ crossings: number;
35
+ shared: number;
36
+ };
@@ -0,0 +1,37 @@
1
+ import type { Box, NearBox, Obstacle, Point, Route, RouteEdge, RouteGroupInput, RouteInput, RouteMetrics, RoutingOptions, Side } from './types.ts';
2
+ /** The drawing as the passes change it. */
3
+ export interface RouteState {
4
+ routes: Route[];
5
+ /** Edges whose planned route was taken as it is: no pass moves them. */
6
+ fixedPlans: Set<string>;
7
+ }
8
+ export declare function gate(obs: Obstacle[], start: Point, fs: Side, end: Point, ts: Side): Obstacle[];
9
+ /**
10
+ * One routing run's view of the scene: the boxes and groups, the edges in routing order, the
11
+ * obstacles each edge sees, and the measures the passes compare drawings with.
12
+ */
13
+ export declare function createContext(input: RouteInput, rawOptions: RoutingOptions): {
14
+ rawOptions: RoutingOptions;
15
+ facingFans: boolean;
16
+ titleBands: boolean;
17
+ began: number;
18
+ s: import("./types.ts").RoutingSettings;
19
+ rects: Map<string, Box>;
20
+ groups: Map<string, RouteGroupInput>;
21
+ near: NearBox[];
22
+ ancestors: (id: string) => string[];
23
+ previous: Map<string, import("./types.ts").PriorRoute>;
24
+ edges: RouteEdge[];
25
+ state: RouteState;
26
+ opened: WeakSet<Route>;
27
+ syncOpen: () => void;
28
+ beyond: (id: string, other: string, side: Side) => boolean;
29
+ crowdedSide: (id: string, side: Side) => boolean;
30
+ flowOf: (e: RouteEdge) => "right" | "down" | undefined;
31
+ scores: WeakMap<Route, number>;
32
+ obsFor: (e: RouteEdge, open?: boolean) => Obstacle[];
33
+ metrics: (rs: Route[]) => RouteMetrics;
34
+ total: (rs: Route[]) => number;
35
+ far: (r: Route) => boolean;
36
+ };
37
+ export type Context = ReturnType<typeof createContext>;
@@ -0,0 +1,13 @@
1
+ import { type Line } from './conflicts.ts';
2
+ import type { NearBox, Point, RoutingSettings } from './types.ts';
3
+ export declare const STANDOFF_WEIGHT = 0.5, BORDER_WEIGHT = 2;
4
+ export declare function standoffCost(ps: Point[], near: NearBox[], bound?: number): number;
5
+ export declare function standoffSegment(a: Point, b: Point, near: NearBox[]): number;
6
+ /** Adds the grid lines a standoff band out from each box to `xs` and `ys`. */
7
+ export declare function standoffLines(near: NearBox[], xs: number[], ys: number[]): void;
8
+ export declare function ownCost(ps: Point[], s: RoutingSettings): number;
9
+ export declare function cost(ps: Point[], used: Line[], s: RoutingSettings, near?: NearBox[]): number;
10
+ export declare function costOf(ps: Point[], s: RoutingSettings, standoff: number, c: {
11
+ crossings: number;
12
+ shared: number;
13
+ }, laneDeficit: number): number;
@@ -0,0 +1,16 @@
1
+ import { type Line } from './conflicts.ts';
2
+ import type { NearBox, Obstacle, Point, RoutingSettings } from './types.ts';
3
+ export interface Lanes {
4
+ xs: number[];
5
+ ys: number[];
6
+ }
7
+ export declare function laneLines(used: Line[], s: RoutingSettings): Lanes;
8
+ export declare function doglegs(a: Point, b: Point, obs: Obstacle[], s: RoutingSettings, lanes: Lanes, near: NearBox[], room: number, visit: (path: Point[], k: number, horizontal?: boolean) => void): void;
9
+ /**
10
+ * A lower bound on the crossings `conflicts` finds between a dogleg (2 to 4 points, before
11
+ * `compress`) and `used`: segments that cross well inside both. Two such crossings are never one
12
+ * point of one route, so each is a crossing `conflicts` counts. Runs along a fixed line use the
13
+ * level or upright segments that span it; a dogleg's middle run, which moves with its lane, uses
14
+ * the segments inside its fixed span.
15
+ */
16
+ export declare function crossingFloor(used: Line[]): (a: Point, b: Point) => (raw: Point[], horizontal?: boolean) => number;
@@ -0,0 +1,6 @@
1
+ import type { Context } from './context.ts';
2
+ import type { Obstacle, PriorRoute, Route, RouteEdge } from './types.ts';
3
+ /** The sides an edge had before, to keep them (`portStability`) or, with `quick`, to reuse them. */
4
+ type Prior = PriorRoute | null | undefined;
5
+ export declare function find(ctx: Context, e: RouteEdge, used: Route[], old?: Prior, wall?: Obstacle[]): Route;
6
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { Obstacle, Point, Rect, Side } from './types.ts';
2
+ export declare const SIDES: Side[], EPS = 0.0001;
3
+ export declare const round: (n: number) => number, clamp: (n: number, a: number, b: number) => number;
4
+ export declare const clone: <T>(v: T) => T, cmp: (a: string, b: string) => 0 | 1 | -1;
5
+ export declare const center: (r: Rect) => Point;
6
+ export declare const same: (a: Point, b: Point) => boolean;
7
+ /** Distance along the axes: every line is level or upright. */
8
+ export declare const distance: (a: Point, b: Point) => number;
9
+ /** The unit vector out of each side. */
10
+ export declare const vector: Record<Side, Point>;
11
+ export declare function inflate<R extends Rect>(r: R, p: number): R;
12
+ /** Whether the level or upright segment a→b enters an obstacle. A slanted segment always does. */
13
+ export declare function blocked(a: Point, b: Point, obs: Obstacle[]): boolean;
14
+ /** A path with rounded points, without repeats or points inside a straight run. */
15
+ export declare function compress(ps: Point[]): Point[];
16
+ export declare function segments(ps: Point[]): [Point, Point][];
17
+ export declare function below(a: number[], v: number): number;
18
+ export declare function atMost(a: number[], v: number): number;
19
+ export declare function lengthOf(ps: Point[]): number;
20
+ export declare function bendsOf(start: Point, ps: Point[], end: Point): number;
21
+ /** Length of a route's axis-aligned segments that lies inside rectangle `r`. */
22
+ export declare function insideLength(ps: Point[], r: Rect): number;
23
+ /** The length of a path, added segment by segment. */
24
+ export declare const pathLength: (ps: Point[]) => number;
@@ -0,0 +1,20 @@
1
+ /** A binary min-heap on `f`, in typed arrays. `pop` leaves the entry in `key` and `g`. */
2
+ export declare class Heap {
3
+ keys: Int32Array;
4
+ gs: Float64Array;
5
+ fs: Float64Array;
6
+ length: number;
7
+ key: number;
8
+ g: number;
9
+ constructor();
10
+ push(key: number, g: number, f: number): void;
11
+ pop(): void;
12
+ }
13
+ /** A binary min-heap of objects ordered by `before`. */
14
+ export declare class Queue<T> {
15
+ items: T[];
16
+ before: (a: T, b: T) => boolean;
17
+ constructor(before: (a: T, b: T) => boolean);
18
+ push(v: T): void;
19
+ pop(): T;
20
+ }
@@ -0,0 +1,7 @@
1
+ import type { Context } from './context.ts';
2
+ /**
3
+ * Routes again each line that is blocked, crosses, shares a run, runs too close to another or
4
+ * uses a far side, worst first, and keeps a new route when the whole drawing costs less. Returns
5
+ * how many it kept.
6
+ */
7
+ export declare function improve(ctx: Context): number;
@@ -0,0 +1,7 @@
1
+ import type { Context } from './context.ts';
2
+ /**
3
+ * Slide complete internal orthogonal segments into nearby free channels.
4
+ * Endpoints, sides, node coordinates and authored waypoint routes stay fixed.
5
+ * Returns how many routes it moved.
6
+ */
7
+ export declare function nudge(ctx: Context): number;
@@ -0,0 +1,8 @@
1
+ import { type Context } from './context.ts';
2
+ import type { Obstacle, Point, Route, RouteEdge, Side } from './types.ts';
3
+ /**
4
+ * Whether a path leaves and enters along its sides' normals, stays level or upright, clears the
5
+ * obstacles (its own end boxes aside, on the first and last segment) and never doubles back.
6
+ */
7
+ export declare function validPath(points: Point[], e: RouteEdge, fs: Side, ts: Side, obs: Obstacle[]): boolean;
8
+ export declare function simplify(ctx: Context, points: Point[], e: RouteEdge, fs: Side, ts: Side, obs: Obstacle[], used: Route[]): Point[];
@@ -0,0 +1,7 @@
1
+ import type { Context } from './context.ts';
2
+ import type { Route, RouteEdge } from './types.ts';
3
+ /**
4
+ * A line's planned route (`plan`, from a `flow` scope's ranks) is its route when it leaves and
5
+ * enters by facing sides and clears every box.
6
+ */
7
+ export declare function planned(ctx: Context, e: RouteEdge): Route | null;
@@ -0,0 +1,10 @@
1
+ import type { End, Point, Rect, Route, RoutingSettings, ShapeKind, Side } from './types.ts';
2
+ /** The fields of a route that hold the side and the offset of one end. */
3
+ export declare const sideKey: (end: End) => `${End}Side`;
4
+ export declare const offsetKey: (end: End) => `${End}Offset`;
5
+ /** The point on `side` of a shape, `offset` along it from the middle, on the shape's outline. */
6
+ export declare function port(rect: Rect & {
7
+ shape?: ShapeKind;
8
+ }, side: Side, offset?: number): Point;
9
+ export declare const TIGHT_PITCH = 12;
10
+ export declare function slot(id: string, side: Side, r: Rect, used: Route[], s: RoutingSettings, other: Rect, tight?: boolean, spread?: boolean): number;
@@ -0,0 +1,9 @@
1
+ import type { Context } from './context.ts';
2
+ /**
3
+ * Edges are routed one at a time, so a crossing priced when the later edge was routed is never
4
+ * repaired: moving either line alone costs more than the crossing. Try each crossing pair
5
+ * together: route one with the other's line as a wall, then the other round it, and keep the
6
+ * pair when the drawing as a whole costs less (NEXT.md item 9, R6).
7
+ * Returns how many pairs it rerouted.
8
+ */
9
+ export declare function repair(ctx: Context): number;
@@ -0,0 +1,12 @@
1
+ import type { Context } from './context.ts';
2
+ import type { Candidate, Point, PriorRoute, Route, RouteEdge, Side } from './types.ts';
3
+ /**
4
+ * The score of a candidate route of edge `e` against the routed lines `used`: length, bends,
5
+ * standoff, crossings and lane conflicts, plus what its sides cost (far sides, conventions, fans,
6
+ * the layout's direction, a change from `old`). `from` and `to` are the sides the edge may use.
7
+ */
8
+ export declare function createScorer(ctx: Context, e: RouteEdge, used: Route[], old: PriorRoute | null | undefined, from: Side[], to: Side[]): {
9
+ axis: (c: Candidate) => number;
10
+ score: (points: Point[], c: Candidate, bound?: number, floor?: boolean) => number;
11
+ sideFloor: (c: Candidate) => number;
12
+ };
@@ -0,0 +1,16 @@
1
+ import type { NearBox, Obstacle, Point, Route, RoutingSettings } from './types.ts';
2
+ /** Search window margins round the two boxes, tried in turn; null is the whole grid. */
3
+ export declare const WINDOWS: (number | null)[];
4
+ /** A search window: only grid lines inside it are used. */
5
+ export interface Window {
6
+ x0: number;
7
+ y0: number;
8
+ x1: number;
9
+ y1: number;
10
+ }
11
+ /**
12
+ * A* over the grid of lines through the ends, the obstacles' sides, the standoff bands and a lane
13
+ * beside every routed line. Returns the cheapest path from `start` to `end`, or null when none
14
+ * is found within `limit` visits.
15
+ */
16
+ export declare function search(start: Point, end: Point, obs: Obstacle[], used: Route[], s: RoutingSettings, near?: NearBox[], limit?: number, win?: Window | null): Point[] | null;
@@ -0,0 +1,4 @@
1
+ import type { RoutingSettings } from './types.ts';
2
+ export declare const defaults: RoutingSettings;
3
+ /** Checked settings: defaults under `raw`, with every number held to its range. */
4
+ export declare function settings(raw?: Partial<RoutingSettings>): RoutingSettings;
@@ -0,0 +1,20 @@
1
+ import type { Context } from './context.ts';
2
+ import type { Candidate, Obstacle, PriorRoute, Route, RouteEdge, Side } from './types.ts';
3
+ /** One pair of sides an edge may use, with its ports and stubs, and how to look for its paths. */
4
+ export interface SidePair {
5
+ c: Candidate;
6
+ /** Stacked neighbours closer than two stubs: the stubs meet halfway and no grid search runs. */
7
+ tight: boolean;
8
+ /** The obstacles its doglegs must clear. */
9
+ walls: Obstacle[];
10
+ /** The two ports face each other on one clear line. */
11
+ straight: boolean;
12
+ /** False for an edge with waypoints: only the grid search routes it. */
13
+ paths: boolean;
14
+ }
15
+ /**
16
+ * Port choice: for each allowed pair of sides, the port offsets the fans already on those sides
17
+ * leave free, the stubs out of them, and the obstacles gated to them. Pairs whose stubs are
18
+ * blocked, or whose diamond vertex is taken while another is free, are left out.
19
+ */
20
+ export declare function sidePairs(ctx: Context, e: RouteEdge, used: Route[], edgeObs: Obstacle[], from: Side[], to: Side[], axis: (c: Candidate) => number, old: PriorRoute | null | undefined): SidePair[];
@@ -0,0 +1,13 @@
1
+ import type { Context } from './context.ts';
2
+ import type { Box, End, Point, Route } from './types.ts';
3
+ /**
4
+ * Tries a tidy move: new points for some routes, by index, with their new offsets. It stands
5
+ * only when every moved route stays valid and nothing gets worse.
6
+ */
7
+ export type Attempt = (changes: Map<number, Point[]>, extra?: Record<number, Partial<Route>>, throughWaypoints?: boolean, slack?: number) => boolean;
8
+ /** Whether a shape's sides are straight, so a port may slide along them. */
9
+ export declare const flat: (r: Box) => boolean;
10
+ export declare const ends: End[];
11
+ /** The point `k` steps in from one end of a path. */
12
+ export declare const tip: (ps: Point[], end: End, k?: number) => Point;
13
+ export declare function centrePorts(ctx: Context, attempt: Attempt): void;
@@ -0,0 +1,10 @@
1
+ import type { Context } from './context.ts';
2
+ /**
3
+ * Tidy: make routes look drafted on purpose. Ports on a side sit symmetrically
4
+ * about its centre, edges fanning from one side turn on one line, and parallel
5
+ * runs in a corridor keep one pitch. A move stands only when every moved route
6
+ * stays valid and nothing gets worse: no crossing, shared run, lane conflict,
7
+ * short step or standoff cost is added.
8
+ * Returns how many moves stood.
9
+ */
10
+ export declare function tidy(ctx: Context): number;
@@ -0,0 +1,9 @@
1
+ import type { Flow, Side } from './types.ts';
2
+ export declare const AXIS_PENALTY = 100, TRAILING: Record<Flow, Side>, LEADING: Record<Flow, Side>, BACK_PENALTY = 300, BACK_OUT: Record<Flow, Side[]>, BACK_IN: Record<Flow, Side[]>;
3
+ export declare const HOOK_PENALTY = 80;
4
+ export declare const ARRIVE_BELOW = 40, ARRIVE_RIGHT = 12, LEAVE_BACK = 12, DETOUR_WEIGHT = 0.5, LINGER_WEIGHT = 0.3, BOUND_SLACK = 1;
5
+ export declare const FAN_PITCH = 10;
6
+ export declare const DETOUR_RATIO = 1.75, DETOUR_SOFT = 1.2, BAND_COST = 120;
7
+ export declare const PORT_SLACK = 4;
8
+ export declare const FAR_PENALTY = 100, OWN_STANDOFF = 1;
9
+ export declare const MAX_REPAIRS = 4, FAN_GROUP = 4, REPAIR_EDGES = 32;
@@ -0,0 +1,157 @@
1
+ import type { EdgeLayout, Point, Rect, RoutingSettings, ShapeKind, Side } from '../../model/types.ts';
2
+ export interface RouteNodeInput extends Rect {
3
+ id: string;
4
+ parentId?: string;
5
+ shape: ShapeKind;
6
+ /** The stub for lines that end on an open group's frame: the group's rectangle. */
7
+ frame?: boolean;
8
+ }
9
+ export interface RouteGroupInput extends Rect {
10
+ id: string;
11
+ parentId?: string;
12
+ collapsed: boolean;
13
+ /** Width of the header's icon and title from the group's left edge; the rest of the band is open. */
14
+ titleWidth?: number;
15
+ /** Height of the title band the router keeps clear; 28 when unset. */
16
+ titleHeight?: number;
17
+ }
18
+ export interface RouteEdgeInput {
19
+ id: string;
20
+ from: string;
21
+ to: string;
22
+ directed?: boolean;
23
+ /** The edge's text. A labelled edge keeps the full port gap in a fan. */
24
+ label?: string;
25
+ cfg: EdgeLayout;
26
+ }
27
+ export interface RouteInput {
28
+ nodes: RouteNodeInput[];
29
+ groups: RouteGroupInput[];
30
+ edges: RouteEdgeInput[];
31
+ }
32
+ export interface Route {
33
+ id: string;
34
+ from: string;
35
+ to: string;
36
+ fromSide: Side;
37
+ toSide: Side;
38
+ fromOffset: number;
39
+ toOffset: number;
40
+ points: Point[];
41
+ blocked: boolean;
42
+ reason?: string;
43
+ }
44
+ export interface PriorRoute {
45
+ id: string;
46
+ fromSide: Side;
47
+ toSide: Side;
48
+ }
49
+ /** What a set of routes costs: the counts the passes compare and the report shows. */
50
+ export interface RouteMetrics {
51
+ crossings: number;
52
+ shared: number;
53
+ lanePairs: number;
54
+ laneDeficit: number;
55
+ minimumConflictingGap: number | null;
56
+ requestedEdgeGap: number;
57
+ blocked: number;
58
+ bends: number;
59
+ length: number;
60
+ }
61
+ export interface RoutingReport extends RouteMetrics {
62
+ initial: RouteMetrics;
63
+ accepted: number;
64
+ /** Crossing pairs the repair pass rerouted. Absent with `quick` or `facing: false`. */
65
+ repaired?: number;
66
+ nudged: number;
67
+ tidied?: number;
68
+ edges: number;
69
+ milliseconds: number;
70
+ passes: number;
71
+ }
72
+ export interface RoutingResult {
73
+ routes: Route[];
74
+ report: RoutingReport;
75
+ }
76
+ export interface RoutingOptions extends Partial<RoutingSettings> {
77
+ previous?: PriorRoute[];
78
+ quick?: boolean;
79
+ /** A reading direction for every edge without its own `cfg.flow`; forward edges run along it. */
80
+ direction?: 'right' | 'down';
81
+ /**
82
+ * Keep fans on the sides facing their other ends, off far sides, and a full standoff away
83
+ * from their own end boxes. On unless false.
84
+ */
85
+ facing?: boolean;
86
+ /**
87
+ * Keep lines out of a group's title band, except a line straight into or out of a member's
88
+ * top. On unless false.
89
+ */
90
+ titleBands?: boolean;
91
+ /** Search each route near its two boxes first, and the whole grid only when that fails. On unless false. */
92
+ window?: boolean;
93
+ /** Even out ports, fans and corridors after routing. On unless false. */
94
+ tidy?: boolean;
95
+ /**
96
+ * In the tidy pass, straighten steps under 12px by sliding a run (and its port, or through a
97
+ * fold lane's waypoints) onto the next. On unless false.
98
+ */
99
+ jogs?: boolean;
100
+ }
101
+ export type Axis = 'x' | 'y';
102
+ /** Which end of an edge: its source or its target. */
103
+ export type End = 'from' | 'to';
104
+ export type Flow = 'right' | 'down';
105
+ /** A shape the router ends lines on: a node, or a collapsed group drawn as a box. */
106
+ export type Box = Rect & {
107
+ id: string;
108
+ parentId?: string;
109
+ shape?: ShapeKind;
110
+ frame?: boolean;
111
+ };
112
+ /**
113
+ * A rectangle no line may enter. `across` lets an upright line cross it but none run along it;
114
+ * with `gates`, only at those x coordinates.
115
+ */
116
+ export type Obstacle = Rect & {
117
+ id?: string;
118
+ across?: boolean;
119
+ gates?: number[];
120
+ };
121
+ /** A box or group border that lines should keep `band` away from, at a cost instead of a wall. */
122
+ export type NearBox = Rect & {
123
+ id: string;
124
+ band: number;
125
+ /** A group border: it counts from both sides. */
126
+ border?: boolean;
127
+ weight?: number;
128
+ };
129
+ /** A side and offset the repair pass fixes at one end of an edge. */
130
+ export interface Pin {
131
+ side: Side;
132
+ offset: number;
133
+ }
134
+ /** An edge as routed: the input edge with a config, and the repair pass's pins. */
135
+ export type RouteEdge = RouteEdgeInput & {
136
+ pin?: Partial<Record<End, Pin>>;
137
+ };
138
+ /** One choice of sides and ports for an edge, with its stubs and the obstacles it sees. */
139
+ export interface Candidate {
140
+ /** Source and target sides. */
141
+ fs: Side;
142
+ ts: Side;
143
+ /** Port offsets along those sides. */
144
+ fo: number;
145
+ toff: number;
146
+ start: Point;
147
+ end: Point;
148
+ /** Where the stubs out of each port end. */
149
+ as: Point;
150
+ bs: Point;
151
+ obs: Obstacle[];
152
+ /** A lower bound on the route's score, to order the grid searches. */
153
+ lower: number;
154
+ /** The fan-out cost of the side pair, worked out once. */
155
+ fanout?: number;
156
+ }
157
+ export type { EdgeLayout, Point, Rect, RoutingSettings, ShapeKind, Side };