@realflow/core 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.
Files changed (57) hide show
  1. package/dist/algorithms.d.ts +25 -0
  2. package/dist/algorithms.d.ts.map +1 -0
  3. package/dist/algorithms.js +158 -0
  4. package/dist/algorithms.js.map +1 -0
  5. package/dist/collab.d.ts +105 -0
  6. package/dist/collab.d.ts.map +1 -0
  7. package/dist/collab.js +182 -0
  8. package/dist/collab.js.map +1 -0
  9. package/dist/geometry.d.ts +33 -0
  10. package/dist/geometry.d.ts.map +1 -0
  11. package/dist/geometry.js +113 -0
  12. package/dist/geometry.js.map +1 -0
  13. package/dist/guides.d.ts +14 -0
  14. package/dist/guides.d.ts.map +1 -0
  15. package/dist/guides.js +52 -0
  16. package/dist/guides.js.map +1 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +14 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/layout-worker.d.ts +53 -0
  22. package/dist/layout-worker.d.ts.map +1 -0
  23. package/dist/layout-worker.js +65 -0
  24. package/dist/layout-worker.js.map +1 -0
  25. package/dist/layout.d.ts +97 -0
  26. package/dist/layout.d.ts.map +1 -0
  27. package/dist/layout.js +644 -0
  28. package/dist/layout.js.map +1 -0
  29. package/dist/ops.d.ts +142 -0
  30. package/dist/ops.d.ts.map +1 -0
  31. package/dist/ops.js +351 -0
  32. package/dist/ops.js.map +1 -0
  33. package/dist/paths.d.ts +41 -0
  34. package/dist/paths.d.ts.map +1 -0
  35. package/dist/paths.js +179 -0
  36. package/dist/paths.js.map +1 -0
  37. package/dist/routing.d.ts +36 -0
  38. package/dist/routing.d.ts.map +1 -0
  39. package/dist/routing.js +185 -0
  40. package/dist/routing.js.map +1 -0
  41. package/dist/spatial.d.ts +43 -0
  42. package/dist/spatial.d.ts.map +1 -0
  43. package/dist/spatial.js +175 -0
  44. package/dist/spatial.js.map +1 -0
  45. package/dist/store.d.ts +274 -0
  46. package/dist/store.d.ts.map +1 -0
  47. package/dist/store.js +1610 -0
  48. package/dist/store.js.map +1 -0
  49. package/dist/types.d.ts +207 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +2 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/utils.d.ts +6 -0
  54. package/dist/utils.d.ts.map +1 -0
  55. package/dist/utils.js +25 -0
  56. package/dist/utils.js.map +1 -0
  57. package/package.json +40 -0
@@ -0,0 +1,25 @@
1
+ import type { Edge, Node } from './types.js';
2
+ import type { FlowStore } from './store.js';
3
+ /** Nodes with an edge pointing into the given node. */
4
+ export declare const getIncomers: (store: FlowStore, id: string) => Node[];
5
+ /** Nodes the given node points to. */
6
+ export declare const getOutgoers: (store: FlowStore, id: string) => Node[];
7
+ /** Every edge touching any of the given nodes. */
8
+ export declare const getConnectedEdges: (store: FlowStore, ids: string[]) => Edge[];
9
+ /**
10
+ * Kahn topological sort. Returns node ids in dependency order, or null if
11
+ * the graph contains a directed cycle.
12
+ */
13
+ export declare const topologicalSort: (store: FlowStore) => string[] | null;
14
+ export declare const hasCycle: (store: FlowStore) => boolean;
15
+ /** Weakly connected components as arrays of node ids. */
16
+ export declare const connectedComponents: (store: FlowStore) => string[][];
17
+ /** BFS shortest path (by hop count). Directed by default. */
18
+ export declare const shortestPath: (store: FlowStore, from: string, to: string, { directed }?: {
19
+ directed?: boolean;
20
+ }) => string[] | null;
21
+ /** All transitive ancestors (upstream nodes). */
22
+ export declare const getAncestors: (store: FlowStore, id: string) => Set<string>;
23
+ /** All transitive descendants (downstream nodes). */
24
+ export declare const getDescendants: (store: FlowStore, id: string) => Set<string>;
25
+ //# sourceMappingURL=algorithms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../src/algorithms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,uDAAuD;AACvD,eAAO,MAAM,WAAW,GAAI,OAAO,SAAS,EAAE,IAAI,MAAM,KAAG,IAAI,EAW9D,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,WAAW,GAAI,OAAO,SAAS,EAAE,IAAI,MAAM,KAAG,IAAI,EAW9D,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,iBAAiB,GAAI,OAAO,SAAS,EAAE,KAAK,MAAM,EAAE,KAAG,IAAI,EAYvE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,KAAG,MAAM,EAAE,GAAG,IAoB7D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,KAAG,OAA0C,CAAC;AAEvF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,GAAI,OAAO,SAAS,KAAG,MAAM,EAAE,EAsB9D,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,YAAY,GACvB,OAAO,SAAS,EAChB,MAAM,MAAM,EACZ,IAAI,MAAM,EACV,eAAqB;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,KAC/C,MAAM,EAAE,GAAG,IAyBb,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,YAAY,GAAI,OAAO,SAAS,EAAE,IAAI,MAAM,KAAG,GAAG,CAAC,MAAM,CAarE,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,EAAE,IAAI,MAAM,KAAG,GAAG,CAAC,MAAM,CAavE,CAAC"}
@@ -0,0 +1,158 @@
1
+ /** Nodes with an edge pointing into the given node. */
2
+ export const getIncomers = (store, id) => {
3
+ const out = [];
4
+ const seen = new Set();
5
+ for (const e of store.edgesOf(id)) {
6
+ if (e.target === id && !seen.has(e.source)) {
7
+ seen.add(e.source);
8
+ const n = store.getNode(e.source);
9
+ if (n)
10
+ out.push(n);
11
+ }
12
+ }
13
+ return out;
14
+ };
15
+ /** Nodes the given node points to. */
16
+ export const getOutgoers = (store, id) => {
17
+ const out = [];
18
+ const seen = new Set();
19
+ for (const e of store.edgesOf(id)) {
20
+ if (e.source === id && !seen.has(e.target)) {
21
+ seen.add(e.target);
22
+ const n = store.getNode(e.target);
23
+ if (n)
24
+ out.push(n);
25
+ }
26
+ }
27
+ return out;
28
+ };
29
+ /** Every edge touching any of the given nodes. */
30
+ export const getConnectedEdges = (store, ids) => {
31
+ const seen = new Set();
32
+ const out = [];
33
+ for (const id of ids) {
34
+ for (const e of store.edgesOf(id)) {
35
+ if (!seen.has(e.id)) {
36
+ seen.add(e.id);
37
+ out.push(e);
38
+ }
39
+ }
40
+ }
41
+ return out;
42
+ };
43
+ /**
44
+ * Kahn topological sort. Returns node ids in dependency order, or null if
45
+ * the graph contains a directed cycle.
46
+ */
47
+ export const topologicalSort = (store) => {
48
+ const indegree = new Map();
49
+ for (const n of store.getNodes())
50
+ indegree.set(n.id, 0);
51
+ for (const e of store.getEdges()) {
52
+ indegree.set(e.target, (indegree.get(e.target) ?? 0) + 1);
53
+ }
54
+ const queue = [];
55
+ for (const [id, deg] of indegree)
56
+ if (deg === 0)
57
+ queue.push(id);
58
+ const order = [];
59
+ while (queue.length > 0) {
60
+ const id = queue.shift();
61
+ order.push(id);
62
+ for (const e of store.edgesOf(id)) {
63
+ if (e.source !== id)
64
+ continue;
65
+ const d = (indegree.get(e.target) ?? 0) - 1;
66
+ indegree.set(e.target, d);
67
+ if (d === 0)
68
+ queue.push(e.target);
69
+ }
70
+ }
71
+ return order.length === store.nodes.size ? order : null;
72
+ };
73
+ export const hasCycle = (store) => topologicalSort(store) === null;
74
+ /** Weakly connected components as arrays of node ids. */
75
+ export const connectedComponents = (store) => {
76
+ const seen = new Set();
77
+ const components = [];
78
+ for (const start of store.nodes.keys()) {
79
+ if (seen.has(start))
80
+ continue;
81
+ const comp = [];
82
+ const stack = [start];
83
+ seen.add(start);
84
+ while (stack.length > 0) {
85
+ const id = stack.pop();
86
+ comp.push(id);
87
+ for (const e of store.edgesOf(id)) {
88
+ const other = e.source === id ? e.target : e.source;
89
+ if (!seen.has(other) && store.nodes.has(other)) {
90
+ seen.add(other);
91
+ stack.push(other);
92
+ }
93
+ }
94
+ }
95
+ components.push(comp);
96
+ }
97
+ return components;
98
+ };
99
+ /** BFS shortest path (by hop count). Directed by default. */
100
+ export const shortestPath = (store, from, to, { directed = true } = {}) => {
101
+ if (from === to)
102
+ return [from];
103
+ const prev = new Map();
104
+ const queue = [from];
105
+ const seen = new Set([from]);
106
+ while (queue.length > 0) {
107
+ const id = queue.shift();
108
+ for (const e of store.edgesOf(id)) {
109
+ const next = e.source === id ? e.target : directed ? null : e.source;
110
+ if (!next || seen.has(next))
111
+ continue;
112
+ seen.add(next);
113
+ prev.set(next, id);
114
+ if (next === to) {
115
+ const path = [to];
116
+ let cur = to;
117
+ while (cur !== from) {
118
+ cur = prev.get(cur);
119
+ path.unshift(cur);
120
+ }
121
+ return path;
122
+ }
123
+ queue.push(next);
124
+ }
125
+ }
126
+ return null;
127
+ };
128
+ /** All transitive ancestors (upstream nodes). */
129
+ export const getAncestors = (store, id) => {
130
+ const out = new Set();
131
+ const stack = [id];
132
+ while (stack.length > 0) {
133
+ const cur = stack.pop();
134
+ for (const e of store.edgesOf(cur)) {
135
+ if (e.target === cur && !out.has(e.source)) {
136
+ out.add(e.source);
137
+ stack.push(e.source);
138
+ }
139
+ }
140
+ }
141
+ return out;
142
+ };
143
+ /** All transitive descendants (downstream nodes). */
144
+ export const getDescendants = (store, id) => {
145
+ const out = new Set();
146
+ const stack = [id];
147
+ while (stack.length > 0) {
148
+ const cur = stack.pop();
149
+ for (const e of store.edgesOf(cur)) {
150
+ if (e.source === cur && !out.has(e.target)) {
151
+ out.add(e.target);
152
+ stack.push(e.target);
153
+ }
154
+ }
155
+ }
156
+ return out;
157
+ };
158
+ //# sourceMappingURL=algorithms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"algorithms.js","sourceRoot":"","sources":["../src/algorithms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import type { Edge, Node, XY } from './types.js';
2
+ import type { FlowStore } from './store.js';
3
+ /**
4
+ * Transport-agnostic real-time collaboration.
5
+ *
6
+ * ReFlow's core stays zero-dependency: `Collab` captures local graph changes
7
+ * as element-level patches and applies remote ones, converging via
8
+ * last-write-wins per element. Wire it to ANY transport — a WebSocket,
9
+ * BroadcastChannel, WebRTC, or a Yjs `Y.Map` (see docs/collaboration.md).
10
+ * Remote edits bypass the local undo stack and never echo back.
11
+ */
12
+ export interface GraphPatch {
13
+ /** Peer that produced this patch; used to drop echoes and break ties. */
14
+ origin: string;
15
+ nodes?: {
16
+ upsert?: Node[];
17
+ remove?: string[];
18
+ };
19
+ edges?: {
20
+ upsert?: Edge[];
21
+ remove?: string[];
22
+ };
23
+ /**
24
+ * Lamport version per element id (node or edge). Enables deterministic
25
+ * last-write-wins convergence regardless of delivery order — higher wins,
26
+ * ties broken by `origin`.
27
+ */
28
+ versions?: Record<string, number>;
29
+ }
30
+ export interface CollabOptions {
31
+ /** Unique id for this peer/tab. */
32
+ peerId: string;
33
+ /** Send a patch to every other peer. */
34
+ broadcast: (patch: GraphPatch) => void;
35
+ }
36
+ /**
37
+ * Bridges a FlowStore to a broadcast transport. Local commits are diffed into
38
+ * a patch and broadcast; incoming patches are applied without echo.
39
+ */
40
+ export declare class Collab {
41
+ private store;
42
+ private peerId;
43
+ private broadcast;
44
+ private prevNodes;
45
+ private prevEdges;
46
+ private unsub;
47
+ private clock;
48
+ /** Winning version per element id (nodes and edges share the id space). */
49
+ private ver;
50
+ constructor(store: FlowStore, options: CollabOptions);
51
+ private onLocalCommit;
52
+ /**
53
+ * Apply a patch from another peer. Uses per-element Lamport versions for
54
+ * deterministic last-write-wins: an element is applied only if the incoming
55
+ * version beats what we've already accepted, so peers converge regardless
56
+ * of message order. Falls back to accept-all when a patch carries no
57
+ * versions (e.g. a fullState onboarding patch).
58
+ */
59
+ receive(patch: GraphPatch): void;
60
+ /** Full-state patch for onboarding a newly-joined peer. */
61
+ fullState(): GraphPatch;
62
+ destroy(): void;
63
+ }
64
+ export interface PeerState {
65
+ id: string;
66
+ name?: string;
67
+ color?: string;
68
+ /** Cursor position in flow coordinates. */
69
+ cursor?: XY | null;
70
+ /** Selected node/edge ids. */
71
+ selection?: string[];
72
+ /** Last-seen timestamp (ms), for stale-peer eviction. */
73
+ lastSeen?: number;
74
+ }
75
+ export interface PresenceOptions {
76
+ peerId: string;
77
+ broadcast: (state: PeerState) => void;
78
+ /** Called whenever the set of remote peers changes. */
79
+ onChange?: (peers: PeerState[]) => void;
80
+ /** Drop peers not seen for this long (ms). Default 15000. */
81
+ timeout?: number;
82
+ }
83
+ /**
84
+ * Lightweight presence: broadcast your cursor/selection/identity and track
85
+ * remote peers. Transport-agnostic, same as `Collab`.
86
+ */
87
+ export declare class Presence {
88
+ private peerId;
89
+ private broadcast;
90
+ private onChange?;
91
+ private timeout;
92
+ private local;
93
+ private peers;
94
+ constructor(options: PresenceOptions);
95
+ /** Update and broadcast our local presence (merged with previous). */
96
+ update(state: Partial<Omit<PeerState, 'id'>>, now?: number): void;
97
+ /** Apply a presence update received from another peer. */
98
+ receive(state: PeerState, now?: number): void;
99
+ /** Drop peers not seen within the timeout window. */
100
+ prune(now?: number): void;
101
+ remotePeers(): PeerState[];
102
+ /** Remove a peer immediately (e.g. on disconnect). */
103
+ remove(peerId: string): void;
104
+ }
105
+ //# sourceMappingURL=collab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collab.d.ts","sourceRoot":"","sources":["../src/collab.ts"],"names":[],"mappings":"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"}
package/dist/collab.js ADDED
@@ -0,0 +1,182 @@
1
+ const newer = (incoming, stored) => {
2
+ if (!stored)
3
+ return true;
4
+ if (incoming.c !== stored.c)
5
+ return incoming.c > stored.c;
6
+ return incoming.p > stored.p;
7
+ };
8
+ const snapshotMap = (items) => new Map(items.map((i) => [i.id, i]));
9
+ /**
10
+ * Bridges a FlowStore to a broadcast transport. Local commits are diffed into
11
+ * a patch and broadcast; incoming patches are applied without echo.
12
+ */
13
+ export class Collab {
14
+ store;
15
+ peerId;
16
+ broadcast;
17
+ prevNodes;
18
+ prevEdges;
19
+ unsub;
20
+ clock = 0;
21
+ /** Winning version per element id (nodes and edges share the id space). */
22
+ ver = new Map();
23
+ constructor(store, options) {
24
+ this.store = store;
25
+ this.peerId = options.peerId;
26
+ this.broadcast = options.broadcast;
27
+ this.prevNodes = snapshotMap(store.getNodes());
28
+ this.prevEdges = snapshotMap(store.getEdges());
29
+ this.unsub = store.subscribe('commit', () => this.onLocalCommit());
30
+ }
31
+ onLocalCommit() {
32
+ const nodes = this.store.getNodes();
33
+ const edges = this.store.getEdges();
34
+ const patch = { origin: this.peerId };
35
+ const versions = {};
36
+ const stamp = (id) => {
37
+ this.clock++;
38
+ this.ver.set(id, { c: this.clock, p: this.peerId });
39
+ versions[id] = this.clock;
40
+ };
41
+ const upNodes = [];
42
+ const rmNodes = [];
43
+ const nextNodeIds = new Set();
44
+ for (const n of nodes) {
45
+ nextNodeIds.add(n.id);
46
+ if (this.prevNodes.get(n.id) !== n) {
47
+ upNodes.push(n);
48
+ stamp(n.id);
49
+ }
50
+ }
51
+ for (const id of this.prevNodes.keys())
52
+ if (!nextNodeIds.has(id)) {
53
+ rmNodes.push(id);
54
+ stamp(id);
55
+ }
56
+ const upEdges = [];
57
+ const rmEdges = [];
58
+ const nextEdgeIds = new Set();
59
+ for (const e of edges) {
60
+ nextEdgeIds.add(e.id);
61
+ if (this.prevEdges.get(e.id) !== e) {
62
+ upEdges.push(e);
63
+ stamp(e.id);
64
+ }
65
+ }
66
+ for (const id of this.prevEdges.keys())
67
+ if (!nextEdgeIds.has(id)) {
68
+ rmEdges.push(id);
69
+ stamp(id);
70
+ }
71
+ this.prevNodes = snapshotMap(nodes);
72
+ this.prevEdges = snapshotMap(edges);
73
+ if (upNodes.length || rmNodes.length)
74
+ patch.nodes = { upsert: upNodes, remove: rmNodes };
75
+ if (upEdges.length || rmEdges.length)
76
+ patch.edges = { upsert: upEdges, remove: rmEdges };
77
+ if (patch.nodes || patch.edges) {
78
+ patch.versions = versions;
79
+ this.broadcast(patch);
80
+ }
81
+ }
82
+ /**
83
+ * Apply a patch from another peer. Uses per-element Lamport versions for
84
+ * deterministic last-write-wins: an element is applied only if the incoming
85
+ * version beats what we've already accepted, so peers converge regardless
86
+ * of message order. Falls back to accept-all when a patch carries no
87
+ * versions (e.g. a fullState onboarding patch).
88
+ */
89
+ receive(patch) {
90
+ if (patch.origin === this.peerId)
91
+ return;
92
+ const versions = patch.versions;
93
+ // Advance our Lamport clock past everything we've seen.
94
+ if (versions)
95
+ for (const v of Object.values(versions))
96
+ this.clock = Math.max(this.clock, v);
97
+ const accept = (id) => {
98
+ if (!versions)
99
+ return true;
100
+ const incoming = { c: versions[id] ?? 0, p: patch.origin };
101
+ if (!newer(incoming, this.ver.get(id)))
102
+ return false;
103
+ this.ver.set(id, incoming);
104
+ return true;
105
+ };
106
+ const nodesUpsert = (patch.nodes?.upsert ?? []).filter((n) => accept(n.id));
107
+ const nodesRemove = (patch.nodes?.remove ?? []).filter((id) => accept(id));
108
+ const edgesUpsert = (patch.edges?.upsert ?? []).filter((e) => accept(e.id));
109
+ const edgesRemove = (patch.edges?.remove ?? []).filter((id) => accept(id));
110
+ this.store.applyRemotePatch({
111
+ nodes: { upsert: nodesUpsert, remove: nodesRemove },
112
+ edges: { upsert: edgesUpsert, remove: edgesRemove },
113
+ });
114
+ // Keep the diff baseline in sync so remote changes aren't re-broadcast.
115
+ this.prevNodes = snapshotMap(this.store.getNodes());
116
+ this.prevEdges = snapshotMap(this.store.getEdges());
117
+ }
118
+ /** Full-state patch for onboarding a newly-joined peer. */
119
+ fullState() {
120
+ return {
121
+ origin: this.peerId,
122
+ nodes: { upsert: this.store.getNodes() },
123
+ edges: { upsert: this.store.getEdges() },
124
+ };
125
+ }
126
+ destroy() {
127
+ this.unsub();
128
+ }
129
+ }
130
+ /**
131
+ * Lightweight presence: broadcast your cursor/selection/identity and track
132
+ * remote peers. Transport-agnostic, same as `Collab`.
133
+ */
134
+ export class Presence {
135
+ peerId;
136
+ broadcast;
137
+ onChange;
138
+ timeout;
139
+ local;
140
+ peers = new Map();
141
+ constructor(options) {
142
+ this.peerId = options.peerId;
143
+ this.broadcast = options.broadcast;
144
+ this.onChange = options.onChange;
145
+ this.timeout = options.timeout ?? 15000;
146
+ this.local = { id: options.peerId };
147
+ }
148
+ /** Update and broadcast our local presence (merged with previous). */
149
+ update(state, now = Date.now()) {
150
+ this.local = { ...this.local, ...state, id: this.peerId, lastSeen: now };
151
+ this.broadcast(this.local);
152
+ }
153
+ /** Apply a presence update received from another peer. */
154
+ receive(state, now = Date.now()) {
155
+ if (state.id === this.peerId)
156
+ return;
157
+ this.peers.set(state.id, { ...state, lastSeen: state.lastSeen ?? now });
158
+ this.prune(now);
159
+ this.onChange?.(this.remotePeers());
160
+ }
161
+ /** Drop peers not seen within the timeout window. */
162
+ prune(now = Date.now()) {
163
+ let changed = false;
164
+ for (const [id, p] of this.peers) {
165
+ if (now - (p.lastSeen ?? 0) > this.timeout) {
166
+ this.peers.delete(id);
167
+ changed = true;
168
+ }
169
+ }
170
+ if (changed)
171
+ this.onChange?.(this.remotePeers());
172
+ }
173
+ remotePeers() {
174
+ return [...this.peers.values()];
175
+ }
176
+ /** Remove a peer immediately (e.g. on disconnect). */
177
+ remove(peerId) {
178
+ if (this.peers.delete(peerId))
179
+ this.onChange?.(this.remotePeers());
180
+ }
181
+ }
182
+ //# sourceMappingURL=collab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collab.js","sourceRoot":"","sources":["../src/collab.ts"],"names":[],"mappings":"AA+BA,MAAM,KAAK,GAAG,CAAC,QAAa,EAAE,MAAuB,EAAW,EAAE;IAChE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,QAAQ,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;AAC/B,CAAC,CAAC;AASF,MAAM,WAAW,GAAG,CAA2B,KAAU,EAAkB,EAAE,CAC3E,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEvC;;;GAGG;AACH,MAAM,OAAO,MAAM;IACT,KAAK,CAAY;IACjB,MAAM,CAAS;IACf,SAAS,CAA8B;IACvC,SAAS,CAAoB;IAC7B,SAAS,CAAoB;IAC7B,KAAK,CAAa;IAClB,KAAK,GAAG,CAAC,CAAC;IAClB,2EAA2E;IACnE,GAAG,GAAG,IAAI,GAAG,EAAe,CAAC;IAErC,YAAY,KAAgB,EAAE,OAAsB;QAClD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACrE,CAAC;IAEO,aAAa;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,KAAK,GAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAClD,MAAM,QAAQ,GAA2B,EAAE,CAAC;QAE5C,MAAM,KAAK,GAAG,CAAC,EAAU,EAAQ,EAAE;YACjC,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACpD,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,CAAC,CAAC;QAEF,MAAM,OAAO,GAAW,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtB,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACd,CAAC;QACH,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YACpC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjB,KAAK,CAAC,EAAE,CAAC,CAAC;YACZ,CAAC;QAEH,MAAM,OAAO,GAAW,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtB,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAChB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACd,CAAC;QACH,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YACpC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACjB,KAAK,CAAC,EAAE,CAAC,CAAC;YACZ,CAAC;QAEH,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAEpC,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;YAAE,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACzF,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;YAAE,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACzF,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC1B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,KAAiB;QACvB,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO;QACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,wDAAwD;QACxD,IAAI,QAAQ;YAAE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;gBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAE5F,MAAM,MAAM,GAAG,CAAC,EAAU,EAAW,EAAE;YACrC,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAC3B,MAAM,QAAQ,GAAQ,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;YAChE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEF,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3E,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;YAC1B,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE;YACnD,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE;SACpD,CAAC,CAAC;QACH,wEAAwE;QACxE,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,2DAA2D;IAC3D,SAAS;QACP,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE;YACxC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE;SACzC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;CACF;AAyBD;;;GAGG;AACH,MAAM,OAAO,QAAQ;IACX,MAAM,CAAS;IACf,SAAS,CAA6B;IACtC,QAAQ,CAAgC;IACxC,OAAO,CAAS;IAChB,KAAK,CAAY;IACjB,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE7C,YAAY,OAAwB;QAClC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,sEAAsE;IACtE,MAAM,CAAC,KAAqC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QAC5D,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACzE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,0DAA0D;IAC1D,OAAO,CAAC,KAAgB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACxC,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO;QACrC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,GAAG,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,qDAAqD;IACrD,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACtB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,IAAI,OAAO;YAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,WAAW;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,sDAAsD;IACtD,MAAM,CAAC,MAAc;QACnB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACrE,CAAC;CACF"}
@@ -0,0 +1,33 @@
1
+ import type { Rect, XY, Viewport, Side } from './types.js';
2
+ export declare const clamp: (v: number, lo: number, hi: number) => number;
3
+ export declare const rectCenter: (r: Rect) => XY;
4
+ export declare const rectContains: (r: Rect, p: XY) => boolean;
5
+ export declare const rectContainsRect: (outer: Rect, inner: Rect) => boolean;
6
+ export declare const rectsIntersect: (a: Rect, b: Rect) => boolean;
7
+ export declare const rectUnion: (a: Rect | null, b: Rect) => Rect;
8
+ export declare const expandRect: (r: Rect, m: number) => Rect;
9
+ export declare const rectFromPoints: (a: XY, b: XY) => Rect;
10
+ /** Convert a screen point to flow coordinates. */
11
+ export declare const screenToFlow: (p: XY, v: Viewport) => XY;
12
+ /** Convert a flow point to screen coordinates. */
13
+ export declare const flowToScreen: (p: XY, v: Viewport) => XY;
14
+ /** The flow-space rectangle visible in a screen of the given size. */
15
+ export declare const visibleRect: (v: Viewport, width: number, height: number) => Rect;
16
+ /**
17
+ * Viewport that fits `bounds` inside a `width`x`height` screen with padding
18
+ * (fraction of the screen, default 0.1).
19
+ */
20
+ export declare const fitRect: (bounds: Rect, width: number, height: number, padding?: number, minZoom?: number, maxZoom?: number) => Viewport;
21
+ /**
22
+ * Zoom the viewport by `factor` while keeping the screen point `pivot`
23
+ * stationary (the classic zoom-at-cursor).
24
+ */
25
+ export declare const zoomAt: (v: Viewport, factor: number, pivot: XY, minZoom: number, maxZoom: number) => Viewport;
26
+ /** Outward unit vector for a node side. */
27
+ export declare const sideDir: (s: Side) => XY;
28
+ /** Default anchor point for a side of a rect (border midpoint). */
29
+ export declare const sideAnchor: (r: Rect, s: Side) => XY;
30
+ /** Pick the node side whose anchor is closest to pointing at `toward`. */
31
+ export declare const closestSide: (r: Rect, toward: XY) => Side;
32
+ export declare const snapToGrid: (p: XY, grid: number) => XY;
33
+ //# sourceMappingURL=geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAExD,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,EAAE,IAAI,MAAM,EAAE,IAAI,MAAM,KAAG,MAC3B,CAAC;AAEhC,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,KAAG,EAAuD,CAAC;AAE7F,eAAO,MAAM,YAAY,GAAI,GAAG,IAAI,EAAE,GAAG,EAAE,KAAG,OAC6B,CAAC;AAE5E,eAAO,MAAM,gBAAgB,GAAI,OAAO,IAAI,EAAE,OAAO,IAAI,KAAG,OAIV,CAAC;AAEnD,eAAO,MAAM,cAAc,GAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAG,OAC8C,CAAC;AAEjG,eAAO,MAAM,SAAS,GAAI,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAG,IAUnD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,EAAE,GAAG,MAAM,KAAG,IAK9C,CAAC;AAEH,eAAO,MAAM,cAAc,GAAI,GAAG,EAAE,EAAE,GAAG,EAAE,KAAG,IAK5C,CAAC;AAEH,kDAAkD;AAClD,eAAO,MAAM,YAAY,GAAI,GAAG,EAAE,EAAE,GAAG,QAAQ,KAAG,EAGhD,CAAC;AAEH,kDAAkD;AAClD,eAAO,MAAM,YAAY,GAAI,GAAG,EAAE,EAAE,GAAG,QAAQ,KAAG,EAGhD,CAAC;AAEH,sEAAsE;AACtE,eAAO,MAAM,WAAW,GAAI,GAAG,QAAQ,EAAE,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,IAKvE,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,OAAO,GAClB,QAAQ,IAAI,EACZ,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,gBAAa,EACb,gBAAa,EACb,gBAAW,KACV,QAcF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,GACjB,GAAG,QAAQ,EACX,QAAQ,MAAM,EACd,OAAO,EAAE,EACT,SAAS,MAAM,EACf,SAAS,MAAM,KACd,QAQF,CAAC;AAEF,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAI,GAAG,IAAI,KAAG,EAWjC,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,EAAE,GAAG,IAAI,KAAG,EAW7C,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,WAAW,GAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,KAAG,IAMjD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,GAAG,EAAE,EAAE,MAAM,MAAM,KAAG,EAG1C,CAAC"}
@@ -0,0 +1,113 @@
1
+ export const clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
2
+ export const rectCenter = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
3
+ export const rectContains = (r, p) => p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y && p.y <= r.y + r.height;
4
+ export const rectContainsRect = (outer, inner) => inner.x >= outer.x &&
5
+ inner.y >= outer.y &&
6
+ inner.x + inner.width <= outer.x + outer.width &&
7
+ inner.y + inner.height <= outer.y + outer.height;
8
+ export const rectsIntersect = (a, b) => a.x <= b.x + b.width && b.x <= a.x + a.width && a.y <= b.y + b.height && b.y <= a.y + a.height;
9
+ export const rectUnion = (a, b) => {
10
+ if (!a)
11
+ return { ...b };
12
+ const x = Math.min(a.x, b.x);
13
+ const y = Math.min(a.y, b.y);
14
+ return {
15
+ x,
16
+ y,
17
+ width: Math.max(a.x + a.width, b.x + b.width) - x,
18
+ height: Math.max(a.y + a.height, b.y + b.height) - y,
19
+ };
20
+ };
21
+ export const expandRect = (r, m) => ({
22
+ x: r.x - m,
23
+ y: r.y - m,
24
+ width: r.width + 2 * m,
25
+ height: r.height + 2 * m,
26
+ });
27
+ export const rectFromPoints = (a, b) => ({
28
+ x: Math.min(a.x, b.x),
29
+ y: Math.min(a.y, b.y),
30
+ width: Math.abs(a.x - b.x),
31
+ height: Math.abs(a.y - b.y),
32
+ });
33
+ /** Convert a screen point to flow coordinates. */
34
+ export const screenToFlow = (p, v) => ({
35
+ x: (p.x - v.x) / v.zoom,
36
+ y: (p.y - v.y) / v.zoom,
37
+ });
38
+ /** Convert a flow point to screen coordinates. */
39
+ export const flowToScreen = (p, v) => ({
40
+ x: p.x * v.zoom + v.x,
41
+ y: p.y * v.zoom + v.y,
42
+ });
43
+ /** The flow-space rectangle visible in a screen of the given size. */
44
+ export const visibleRect = (v, width, height) => ({
45
+ x: -v.x / v.zoom,
46
+ y: -v.y / v.zoom,
47
+ width: width / v.zoom,
48
+ height: height / v.zoom,
49
+ });
50
+ /**
51
+ * Viewport that fits `bounds` inside a `width`x`height` screen with padding
52
+ * (fraction of the screen, default 0.1).
53
+ */
54
+ export const fitRect = (bounds, width, height, padding = 0.1, minZoom = 0.1, maxZoom = 2) => {
55
+ if (bounds.width <= 0 || bounds.height <= 0 || width <= 0 || height <= 0) {
56
+ return { x: 0, y: 0, zoom: 1 };
57
+ }
58
+ const zoom = clamp(Math.min((width * (1 - 2 * padding)) / bounds.width, (height * (1 - 2 * padding)) / bounds.height), minZoom, maxZoom);
59
+ const c = rectCenter(bounds);
60
+ return { x: width / 2 - c.x * zoom, y: height / 2 - c.y * zoom, zoom };
61
+ };
62
+ /**
63
+ * Zoom the viewport by `factor` while keeping the screen point `pivot`
64
+ * stationary (the classic zoom-at-cursor).
65
+ */
66
+ export const zoomAt = (v, factor, pivot, minZoom, maxZoom) => {
67
+ const zoom = clamp(v.zoom * factor, minZoom, maxZoom);
68
+ const k = zoom / v.zoom;
69
+ return {
70
+ x: pivot.x - (pivot.x - v.x) * k,
71
+ y: pivot.y - (pivot.y - v.y) * k,
72
+ zoom,
73
+ };
74
+ };
75
+ /** Outward unit vector for a node side. */
76
+ export const sideDir = (s) => {
77
+ switch (s) {
78
+ case 'left':
79
+ return { x: -1, y: 0 };
80
+ case 'right':
81
+ return { x: 1, y: 0 };
82
+ case 'top':
83
+ return { x: 0, y: -1 };
84
+ default:
85
+ return { x: 0, y: 1 };
86
+ }
87
+ };
88
+ /** Default anchor point for a side of a rect (border midpoint). */
89
+ export const sideAnchor = (r, s) => {
90
+ switch (s) {
91
+ case 'left':
92
+ return { x: r.x, y: r.y + r.height / 2 };
93
+ case 'right':
94
+ return { x: r.x + r.width, y: r.y + r.height / 2 };
95
+ case 'top':
96
+ return { x: r.x + r.width / 2, y: r.y };
97
+ default:
98
+ return { x: r.x + r.width / 2, y: r.y + r.height };
99
+ }
100
+ };
101
+ /** Pick the node side whose anchor is closest to pointing at `toward`. */
102
+ export const closestSide = (r, toward) => {
103
+ const c = rectCenter(r);
104
+ const dx = toward.x - c.x;
105
+ const dy = toward.y - c.y;
106
+ if (Math.abs(dx) > Math.abs(dy))
107
+ return dx > 0 ? 'right' : 'left';
108
+ return dy > 0 ? 'bottom' : 'top';
109
+ };
110
+ export const snapToGrid = (p, grid) => grid > 0
111
+ ? { x: Math.round(p.x / grid) * grid, y: Math.round(p.y / grid) * grid }
112
+ : p;
113
+ //# sourceMappingURL=geometry.js.map