@graview/layout 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 (47) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +20 -0
  3. package/dist/band.d.ts +103 -0
  4. package/dist/band.d.ts.map +1 -0
  5. package/dist/band.js +344 -0
  6. package/dist/band.js.map +1 -0
  7. package/dist/city.d.ts +161 -0
  8. package/dist/city.d.ts.map +1 -0
  9. package/dist/city.js +319 -0
  10. package/dist/city.js.map +1 -0
  11. package/dist/ids.d.ts +79 -0
  12. package/dist/ids.d.ts.map +1 -0
  13. package/dist/ids.js +115 -0
  14. package/dist/ids.js.map +1 -0
  15. package/dist/index.d.ts +19 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +10 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/interpolate.d.ts +27 -0
  20. package/dist/interpolate.d.ts.map +1 -0
  21. package/dist/interpolate.js +217 -0
  22. package/dist/interpolate.js.map +1 -0
  23. package/dist/label-fit.d.ts +115 -0
  24. package/dist/label-fit.d.ts.map +1 -0
  25. package/dist/label-fit.js +162 -0
  26. package/dist/label-fit.js.map +1 -0
  27. package/dist/layout.d.ts +99 -0
  28. package/dist/layout.d.ts.map +1 -0
  29. package/dist/layout.js +1391 -0
  30. package/dist/layout.js.map +1 -0
  31. package/dist/rank.d.ts +35 -0
  32. package/dist/rank.d.ts.map +1 -0
  33. package/dist/rank.js +84 -0
  34. package/dist/rank.js.map +1 -0
  35. package/dist/related.d.ts +42 -0
  36. package/dist/related.d.ts.map +1 -0
  37. package/dist/related.js +207 -0
  38. package/dist/related.js.map +1 -0
  39. package/dist/types.d.ts +404 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +27 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/view-state.d.ts +189 -0
  44. package/dist/view-state.d.ts.map +1 -0
  45. package/dist/view-state.js +350 -0
  46. package/dist/view-state.js.map +1 -0
  47. package/package.json +54 -0
package/dist/city.d.ts ADDED
@@ -0,0 +1,161 @@
1
+ import { type AnySchema, type CityMap, type Plot } from "@graview/core";
2
+ import type { CityFrame, Layout } from "./types.js";
3
+ /**
4
+ * THE CITY AT ALTITUDE: districts on the lattice, one uniform scale.
5
+ *
6
+ * The map is the declaration's (`cityMap`): a kind's plot never moves
7
+ * because of what happened today or how wide the window is. What this
8
+ * file decides is only how the lattice meets the canvas — ONE scale and
9
+ * ONE translate, so the city keeps its shape at every width, rather than
10
+ * the per-axis stretch a ring needed to fill a wide canvas — and how big
11
+ * each district card is drawn: nearer rows larger, as the ring drew the
12
+ * near side of the ellipse larger, with the reader's unit honoured and
13
+ * given back only as far as the cards need to keep off each other.
14
+ */
15
+ export interface CityCard {
16
+ readonly id: string;
17
+ readonly kind: string;
18
+ /** Members standing today, for the plot's side. */
19
+ readonly count: number;
20
+ /** Extra height this card asks for, opened in place. */
21
+ readonly opened: number;
22
+ /**
23
+ * The least of that the listing gives up to: a few rows it keeps however
24
+ * crowded the city is. Shrunk to nothing, an opened district of four kinds
25
+ * listed one and counted three — opening it answered nothing.
26
+ */
27
+ readonly openedMin?: number;
28
+ /**
29
+ * Extra height for a drive-in's marquee — the showings drawn as buttons
30
+ * under the nameplate. Not given back like the listing: a marquee
31
+ * clipped is a button nobody can press.
32
+ */
33
+ readonly marquee?: number;
34
+ }
35
+ export interface PlacedCard {
36
+ readonly id: string;
37
+ readonly x: number;
38
+ readonly y: number;
39
+ readonly width: number;
40
+ readonly height: number;
41
+ readonly depth: number;
42
+ readonly plot: Plot;
43
+ }
44
+ /** Two drawn boxes sharing ground, with a little air required between them. */
45
+ export declare function collides(a: {
46
+ x: number;
47
+ y: number;
48
+ width: number;
49
+ height: number;
50
+ }, b: {
51
+ x: number;
52
+ y: number;
53
+ width: number;
54
+ height: number;
55
+ }, air?: number): boolean;
56
+ export declare function placeCity(cards: readonly CityCard[], schema: AnySchema, base: {
57
+ readonly width: number;
58
+ readonly height: number;
59
+ }, canvas: {
60
+ readonly width: number;
61
+ readonly height: number;
62
+ }, inset?: {
63
+ readonly left?: number;
64
+ readonly right?: number;
65
+ }, options?: {
66
+ readonly order?: readonly string[];
67
+ readonly plots?: Readonly<Record<string, {
68
+ col: number;
69
+ row: number;
70
+ }>>;
71
+ readonly scale?: number;
72
+ /** The height a card is never drawn under, whatever the canvas — the layout's own floor. */
73
+ readonly minHeight?: number;
74
+ /** A factor on the fitted cell: the camera brought closer, the city allowed past the window. */
75
+ readonly zoom?: number;
76
+ /** The pan baked into the cards, recorded on the frame so the ground can tween with them. */
77
+ readonly pan?: {
78
+ readonly x: number;
79
+ readonly y: number;
80
+ };
81
+ /**
82
+ * Ground already taken — the picture standing in the middle from
83
+ * altitude. No district is laid under it: the city slides aside, and
84
+ * grows, until every district has ground of its own.
85
+ */
86
+ readonly avoid?: readonly {
87
+ readonly x: number;
88
+ readonly y: number;
89
+ readonly width: number;
90
+ readonly height: number;
91
+ }[];
92
+ }): {
93
+ readonly placed: readonly PlacedCard[];
94
+ readonly frame: CityFrame;
95
+ readonly map: CityMap;
96
+ };
97
+ /**
98
+ * HOW FAR THE CAMERA MAY GO. A little way over a picture that fits — losing
99
+ * the scene off the edge of its own window is not panning, it is dropping
100
+ * it — and as far as the city reaches when the city is bigger than the
101
+ * window: every district is reachable, none is lost. Symmetric about the
102
+ * centre so the same limit serves both directions, and read by the scene
103
+ * and by the tests alike, so "reachable" means one thing.
104
+ */
105
+ export declare function cameraLimit(result: {
106
+ readonly width: number;
107
+ readonly height: number;
108
+ readonly city?: CityFrame;
109
+ }): {
110
+ readonly x: number;
111
+ readonly y: number;
112
+ };
113
+ /** A district the window does not reach, the edge it lies past, and the pan that brings it in. */
114
+ export interface PastTheEdge {
115
+ /** The district's card: `kind:<kind>`. */
116
+ readonly id: string;
117
+ readonly kind: string;
118
+ readonly side: "left" | "right" | "top" | "bottom";
119
+ /** Where along that edge it lies, clamped to the canvas: y for left and right, x for top and bottom. */
120
+ readonly along: number;
121
+ /** The move that brings it a quarter of the way in from that edge. */
122
+ readonly by: {
123
+ readonly x: number;
124
+ readonly y: number;
125
+ };
126
+ }
127
+ /**
128
+ * THE EDGE SAYS WHAT IS PAST IT. A city wider than the window is reached
129
+ * by panning, and a district has a corner of its own that no phone is
130
+ * allowed to move — so on a 390-pixel phone the conference's Topics and
131
+ * Staff stood wholly off the canvas, with nothing on it saying they were
132
+ * there or which way to go. Every district whose middle is past the edge
133
+ * is named here with the side it lies past, so the scene can put a sign on
134
+ * that edge, and with the pan that brings it into the window.
135
+ */
136
+ export declare function districtsPastTheEdge(result: Pick<Layout, "nodes" | "width" | "height" | "city">): readonly PastTheEdge[];
137
+ /**
138
+ * THE PAN THAT KEEPS THE POINT UNDER THE POINTER STILL when the city is
139
+ * zoomed. The city is placed about the canvas's centre, so scaling its cell
140
+ * by `ratio` moves every point away from (or toward) the centre; the pan
141
+ * moves the opposite way by the pointer's share of that, and what was under
142
+ * the finger stays under it. Whole offset in, pan out: the camera's own
143
+ * flight is part of where the city is, and only the pan is the person's.
144
+ */
145
+ export declare function panForZoom(pan: {
146
+ readonly x: number;
147
+ readonly y: number;
148
+ }, camera: {
149
+ readonly x: number;
150
+ readonly y: number;
151
+ }, pointer: {
152
+ readonly x: number;
153
+ readonly y: number;
154
+ }, centre: {
155
+ readonly x: number;
156
+ readonly y: number;
157
+ }, ratio: number): {
158
+ readonly x: number;
159
+ readonly y: number;
160
+ };
161
+ //# sourceMappingURL=city.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"city.d.ts","sourceRoot":"","sources":["../src/city.ts"],"names":[],"mappings":"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"}
package/dist/city.js ADDED
@@ -0,0 +1,319 @@
1
+ import { cityExtent, cityMap, toIso } from "@graview/core";
2
+ /** Two drawn boxes sharing ground, with a little air required between them. */
3
+ export function collides(a, b, air = 6) {
4
+ return (a.x < b.x + b.width + air &&
5
+ b.x < a.x + a.width + air &&
6
+ a.y < b.y + b.height + air &&
7
+ b.y < a.y + a.height + air);
8
+ }
9
+ /** The centre of a plot's diamond, in lattice pixels at `cell`. */
10
+ const centreOf = (plot, cell) => toIso(plot.col + plot.side / 2, plot.row + plot.side / 2, cell);
11
+ export function placeCity(cards, schema, base, canvas, inset = {}, options = {}) {
12
+ const counts = {};
13
+ for (const card of cards)
14
+ counts[card.kind] = card.count;
15
+ const map = cityMap(schema, {
16
+ ...(options.order ? { order: options.order } : {}),
17
+ counts,
18
+ ...(options.plots ? { plots: options.plots } : {}),
19
+ });
20
+ const left = inset.left ?? 0;
21
+ const span = canvas.width - left - (inset.right ?? 0);
22
+ const cx = left + span / 2;
23
+ const cy = canvas.height * 0.53;
24
+ /*
25
+ * ONE SCALE. The map's bounding diamond, at one pixel per cell, is
26
+ * fitted into the room the canvas has once a card's own size is taken
27
+ * off each edge — a card is centred on its plot and must not leave the
28
+ * canvas. The cell is then the same on both axes, which is the whole
29
+ * point: a city that was squashed to fit a wide window and stretched to
30
+ * fit a tall one had no shape of its own.
31
+ */
32
+ const extent = cityExtent(map);
33
+ const corners = [
34
+ toIso(extent.minCol, extent.minRow, 1),
35
+ toIso(extent.maxCol, extent.minRow, 1),
36
+ toIso(extent.minCol, extent.maxRow, 1),
37
+ toIso(extent.maxCol, extent.maxRow, 1),
38
+ ];
39
+ const minX = Math.min(...corners.map((c) => c.x));
40
+ const maxX = Math.max(...corners.map((c) => c.x));
41
+ const minY = Math.min(...corners.map((c) => c.y));
42
+ const maxY = Math.max(...corners.map((c) => c.y));
43
+ const spread = { x: Math.max(1, maxX - minX), y: Math.max(0.5, maxY - minY) };
44
+ const roomX = Math.max(40, span - base.width * 1.1);
45
+ const roomY = Math.max(24, canvas.height * 0.62 - base.height * 1.1);
46
+ const fitted = Math.max(18, Math.min(160, roomX / spread.x, roomY / spread.y));
47
+ const scale = options.scale ?? 1;
48
+ const minHeight = options.minHeight ?? 0;
49
+ const avoid = options.avoid ?? [];
50
+ /** Where the whole city is slid to stand beside the rails rather than under them (see below). */
51
+ let nudge = 0;
52
+ const at = (cell, asked, openedShare = 1, shift = { x: 0, y: 0 }) => {
53
+ const originX = cx - ((minX + maxX) / 2) * cell + shift.x + nudge;
54
+ const originY = cy - ((minY + maxY) / 2) * cell + shift.y;
55
+ const size = { width: base.width * asked, height: base.height * asked };
56
+ const rows = cards.map((card) => centreOf(map.get(card.kind), cell).y);
57
+ const lowest = Math.max(...rows);
58
+ const highest = Math.min(...rows);
59
+ return cards.map((card) => {
60
+ const plot = map.get(card.kind);
61
+ const centre = centreOf(plot, cell);
62
+ /*
63
+ * NEARNESS FROM THE ROW. Lower on the ground is nearer the viewer,
64
+ * exactly as the bottom of the ring was: 1 there, 0 at the top, and
65
+ * the depth number everything else reads (`1 − near·0.65`) is
66
+ * unchanged so the plane styles and the altitude opacity are too.
67
+ */
68
+ const near = lowest === highest ? 0.5 : (centre.y - highest) / (lowest - highest);
69
+ const grow = 0.85 + near * 0.45;
70
+ const width = size.width * grow;
71
+ const base = Math.max(minHeight, size.height * grow);
72
+ const height = base + (card.marquee ?? 0) + (card.opened > 0 ? Math.max(card.openedMin ?? 0, card.opened * openedShare) : 0);
73
+ /*
74
+ * NOT HELD INSIDE THE CANVAS. The ring pushed its near card back in
75
+ * from the edge; a city has a shape, and a district pushed off its
76
+ * corner to fit a phone is a district on the wrong corner. A city
77
+ * wider than the window is reached by panning, which the camera
78
+ * bounds to the map's extent for exactly this reason.
79
+ */
80
+ /*
81
+ * The NAMEPLATE is centred on the plot; what hangs under it — a
82
+ * marquee, an opened listing — grows DOWNWARD, so the top of the card
83
+ * stays where the plot's far half is, and a screen standing at the
84
+ * far edge is never covered by its own district's growth.
85
+ */
86
+ return {
87
+ id: card.id,
88
+ x: originX + centre.x - width / 2,
89
+ y: originY + centre.y - base / 2,
90
+ width,
91
+ height,
92
+ depth: 1 - near * 0.65,
93
+ plot,
94
+ };
95
+ });
96
+ };
97
+ const crowded = (placed) => placed.some((one, index) => placed.some((other, index2) => index2 !== index && collides(one, other))) ||
98
+ placed.some((one) => avoid.some((box) => collides(one, box)));
99
+ /*
100
+ * The reader's unit is given back first, down to the size a card has
101
+ * always been; and when the cards still stand in each other at that
102
+ * size, the CITY grows — the cell widens until every district has its
103
+ * own ground — rather than the cards shrinking below what their names
104
+ * need. A district's legibility is not the window's to take.
105
+ */
106
+ /*
107
+ * THE CITY SLIDES ASIDE for the picture in the middle, when there is one.
108
+ * A district on the map's own centre stands exactly where the live view
109
+ * stands, and no amount of growing moves it — scaling about the centre
110
+ * keeps the centre where it is. So the map is tried in place first, and
111
+ * then shifted: by the width of what it must clear, right, left, down,
112
+ * up, and the diagonals — the first shift with every district on ground
113
+ * of its own is the one kept. No picture in the middle means no shift.
114
+ */
115
+ const shifts = (cell) => {
116
+ if (avoid.length === 0)
117
+ return [{ x: 0, y: 0 }];
118
+ const dx = Math.max(...avoid.map((box) => box.width)) / 2 + cell;
119
+ const dy = Math.max(...avoid.map((box) => box.height)) / 2 + cell / 2;
120
+ const out = [{ x: 0, y: 0 }];
121
+ for (const share of [0.5, 0.75, 1, 1.25]) {
122
+ const sx = dx * share;
123
+ const sy = dy * share;
124
+ out.push({ x: sx, y: 0 }, { x: -sx, y: 0 }, { x: 0, y: sy }, { x: 0, y: -sy }, { x: sx, y: sy }, { x: -sx, y: sy }, { x: sx, y: -sy }, { x: -sx, y: -sy });
125
+ }
126
+ return out;
127
+ };
128
+ /** How much of the city is off the canvas: the shift that shows the most is the one kept. */
129
+ const right = canvas.width - (inset.right ?? 0);
130
+ const outside = (placed) => placed.reduce((sum, card) => {
131
+ const visibleW = Math.max(0, Math.min(card.x + card.width, canvas.width) - Math.max(card.x, 0));
132
+ const visibleH = Math.max(0, Math.min(card.y + card.height, canvas.height) - Math.max(card.y, 0));
133
+ return sum + card.width * card.height - visibleW * visibleH;
134
+ }, 0);
135
+ const settle = (cell, asked, openedShare) => {
136
+ let best;
137
+ let fallback;
138
+ for (const shift of shifts(cell)) {
139
+ const placed = at(cell, asked, openedShare, shift);
140
+ if (crowded(placed)) {
141
+ fallback ??= { placed, shift };
142
+ continue;
143
+ }
144
+ const off = outside(placed);
145
+ if (!best || off < best.off - 0.5)
146
+ best = { placed, shift, off };
147
+ }
148
+ return best ?? fallback;
149
+ };
150
+ let cell = fitted;
151
+ let asked = scale;
152
+ let openedShare = 1;
153
+ let settled = settle(cell, asked, openedShare);
154
+ while (crowded(settled.placed) && asked > 1.001) {
155
+ asked = Math.max(1, asked - 0.05);
156
+ settled = settle(cell, asked, openedShare);
157
+ }
158
+ /*
159
+ * An opened district's listing is the luxury and the city is the picture:
160
+ * in a short canvas the listing gives up its room before the city has to
161
+ * grow past the window, exactly as the ring's listing gave up its room
162
+ * before the ring gave up being a ring.
163
+ */
164
+ while (crowded(settled.placed) && openedShare > 0.3) {
165
+ openedShare = Math.max(0.3, openedShare - 0.1);
166
+ settled = settle(cell, asked, openedShare);
167
+ }
168
+ /*
169
+ * A SMALLER CITY BEFORE A CITY OFF THE EDGE. With a picture standing in
170
+ * the middle, the map fitted to the room may not stand beside it and
171
+ * stay on the canvas; a tighter lattice usually can, and the districts
172
+ * keep their size — only the streets between them shorten. So the cell
173
+ * is tried smaller, down to where the districts would touch, and the
174
+ * largest cell whose every district is on the canvas is the one kept.
175
+ * Only when no cell will do does the city grow past the window.
176
+ */
177
+ if (crowded(settled.placed) || outside(settled.placed) > 0) {
178
+ let best = crowded(settled.placed) ? undefined : { cell, settled, off: outside(settled.placed) };
179
+ for (let share = 0.9; share >= 0.3; share -= 0.1) {
180
+ const tighter = fitted * share;
181
+ const trial = settle(tighter, asked, openedShare);
182
+ if (crowded(trial.placed))
183
+ continue;
184
+ const off = outside(trial.placed);
185
+ if (!best || off < best.off - 0.5)
186
+ best = { cell: tighter, settled: trial, off };
187
+ if (off === 0)
188
+ break;
189
+ }
190
+ if (best) {
191
+ cell = best.cell;
192
+ settled = best.settled;
193
+ }
194
+ }
195
+ for (let grown = 0; crowded(settled.placed) && grown < 80; grown++) {
196
+ cell *= 1.06;
197
+ settled = settle(cell, asked, openedShare);
198
+ }
199
+ /*
200
+ * BESIDE THE RAILS, NOT UNDER THEM. The map is centred on its lattice's
201
+ * bounding diamond, and the districts are not: a card is centred on its
202
+ * plot and the plots do not fill the diamond, so a city grown just wide
203
+ * enough for its names stood twenty-five pixels left of the room it
204
+ * fitted — and the leftmost district under the inspector. When the
205
+ * districts fit between the rails, the city slides the least distance
206
+ * that puts every one of them there; when they do not, the camera pans
207
+ * to them, as before.
208
+ */
209
+ // With a picture standing in the middle the city has already stepped aside for it, and that placement stands.
210
+ if (avoid.length === 0) {
211
+ const lowX = Math.min(...settled.placed.map((card) => card.x));
212
+ const highX = Math.max(...settled.placed.map((card) => card.x + card.width));
213
+ const slide = highX - lowX > right - left ? 0 : lowX < left ? left - lowX : highX > right ? right - highX : 0;
214
+ if (slide !== 0) {
215
+ nudge = slide;
216
+ const slid = at(cell, asked, openedShare, settled.shift);
217
+ if (crowded(slid))
218
+ nudge = 0;
219
+ else
220
+ settled = { placed: slid, shift: settled.shift };
221
+ }
222
+ }
223
+ /*
224
+ * FLYING CLOSER. The fit above is the whole map in the window; a zoom is
225
+ * the camera brought in afterwards, so the map keeps its shape and only
226
+ * the cell grows — the city may run past the window, and the camera
227
+ * pans to reach it.
228
+ */
229
+ if (options.zoom && options.zoom !== 1) {
230
+ cell *= options.zoom;
231
+ nudge *= options.zoom;
232
+ settled = settle(cell, asked, openedShare);
233
+ }
234
+ const placed = settled.placed;
235
+ const originX = cx - ((minX + maxX) / 2) * cell + settled.shift.x + nudge;
236
+ const originY = cy - ((minY + maxY) / 2) * cell + settled.shift.y;
237
+ const frame = {
238
+ cell,
239
+ originX,
240
+ originY,
241
+ pan: options.pan ?? { x: 0, y: 0 },
242
+ extent: {
243
+ x: originX + minX * cell,
244
+ y: originY + minY * cell,
245
+ width: spread.x * cell,
246
+ height: spread.y * cell,
247
+ },
248
+ openedShare,
249
+ };
250
+ return { placed, frame, map };
251
+ }
252
+ /**
253
+ * HOW FAR THE CAMERA MAY GO. A little way over a picture that fits — losing
254
+ * the scene off the edge of its own window is not panning, it is dropping
255
+ * it — and as far as the city reaches when the city is bigger than the
256
+ * window: every district is reachable, none is lost. Symmetric about the
257
+ * centre so the same limit serves both directions, and read by the scene
258
+ * and by the tests alike, so "reachable" means one thing.
259
+ */
260
+ export function cameraLimit(result) {
261
+ const slack = { x: result.width * 0.45, y: result.height * 0.45 };
262
+ if (!result.city)
263
+ return slack;
264
+ const { extent } = result.city;
265
+ const margin = 24;
266
+ return {
267
+ x: Math.max(slack.x, extent.x + extent.width - result.width + margin, -extent.x + margin),
268
+ y: Math.max(slack.y, extent.y + extent.height - result.height + margin, -extent.y + margin),
269
+ };
270
+ }
271
+ /** How far in from an edge a district brought into the window stands, at the least: room for its nameplate's height. */
272
+ const EDGE_CLEAR = 48;
273
+ /**
274
+ * THE EDGE SAYS WHAT IS PAST IT. A city wider than the window is reached
275
+ * by panning, and a district has a corner of its own that no phone is
276
+ * allowed to move — so on a 390-pixel phone the conference's Topics and
277
+ * Staff stood wholly off the canvas, with nothing on it saying they were
278
+ * there or which way to go. Every district whose middle is past the edge
279
+ * is named here with the side it lies past, so the scene can put a sign on
280
+ * that edge, and with the pan that brings it into the window.
281
+ */
282
+ export function districtsPastTheEdge(result) {
283
+ if (!result.city)
284
+ return [];
285
+ const { width, height } = result;
286
+ const out = [];
287
+ for (const node of result.nodes) {
288
+ if (!node.id.startsWith("kind:") || Math.round(node.plane) !== 2)
289
+ continue;
290
+ const cx = node.x + node.width / 2;
291
+ const cy = node.y + node.height / 2;
292
+ const side = cx < 0 ? "left" : cx > width ? "right" : cy < 0 ? "top" : cy > height ? "bottom" : null;
293
+ if (!side)
294
+ continue;
295
+ const across = side === "left" || side === "right";
296
+ const along = across ? Math.max(0, Math.min(height, cy)) : Math.max(0, Math.min(width, cx));
297
+ // A quarter of the way in from the edge it was past; along that edge, only as far as keeps it off the other two.
298
+ const inward = (at, span) => (at < span / 4 ? span / 4 - at : at > (span * 3) / 4 ? (span * 3) / 4 - at : 0);
299
+ const clear = (at, span) => (at < EDGE_CLEAR ? EDGE_CLEAR - at : at > span - EDGE_CLEAR ? span - EDGE_CLEAR - at : 0);
300
+ const by = across ? { x: inward(cx, width), y: clear(cy, height) } : { x: clear(cx, width), y: inward(cy, height) };
301
+ out.push({ id: node.id, kind: node.id.slice("kind:".length), side, along, by });
302
+ }
303
+ return out;
304
+ }
305
+ /**
306
+ * THE PAN THAT KEEPS THE POINT UNDER THE POINTER STILL when the city is
307
+ * zoomed. The city is placed about the canvas's centre, so scaling its cell
308
+ * by `ratio` moves every point away from (or toward) the centre; the pan
309
+ * moves the opposite way by the pointer's share of that, and what was under
310
+ * the finger stays under it. Whole offset in, pan out: the camera's own
311
+ * flight is part of where the city is, and only the pan is the person's.
312
+ */
313
+ export function panForZoom(pan, camera, pointer, centre, ratio) {
314
+ return {
315
+ x: pan.x + (pointer.x - pan.x - camera.x - centre.x) * (1 - ratio),
316
+ y: pan.y + (pointer.y - pan.y - camera.y - centre.y) * (1 - ratio),
317
+ };
318
+ }
319
+ //# sourceMappingURL=city.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"city.js","sourceRoot":"","sources":["../src/city.ts"],"names":[],"mappings":"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"}
package/dist/ids.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { type ViewState } from "./view-state.js";
2
+ export declare const AGGREGATE_PREFIX = "aggregate:";
3
+ /**
4
+ * The kinds plane's cards have ids of their own.
5
+ *
6
+ * They cannot share `aggregate:<kind>` with a focusable group, because an app
7
+ * whose primary view IS one kind — the coaching example's formation is
8
+ * `aggregate:position` — would then place the same id twice: once as the
9
+ * focus and once as its own card in the strip. The strip is a MAP of kinds,
10
+ * not a set of groups you focus, so it gets its own namespace.
11
+ */
12
+ export declare const KIND_PREFIX = "kind:";
13
+ export declare function kindCardId(kind: string): string;
14
+ export declare function kindOfCard(id: string): string | null;
15
+ /**
16
+ * The card that stands for the districts the row could not hold.
17
+ *
18
+ * Its own id rather than a kind's, because it is not a kind: it is the row
19
+ * saying what it had to leave out, and naming them.
20
+ */
21
+ export declare const BEYOND_CARD = "kinds:beyond";
22
+ /** The kinds an id stands for, whichever namespace it is in. */
23
+ export declare function kindsOf(id: string): string[];
24
+ /**
25
+ * The id of a group standing in for one or more kinds.
26
+ *
27
+ * Several kinds because a group is a view of a SET of kinds, not a synonym
28
+ * for one: the household example's week is its blocks and its runs together, and no node
29
+ * kind called "week" exists or should. Kinds are sorted so the same group is
30
+ * always the same id, which keeps it stable in a URL.
31
+ */
32
+ export declare function aggregateId(...kinds: readonly string[]): string;
33
+ export declare function isAggregateId(id: string): boolean;
34
+ /** The kinds a group id names, or an empty list if it is not a group id. */
35
+ export declare function kindsOfAggregate(id: string): string[];
36
+ /**
37
+ * Going deeper into a card, as view state.
38
+ *
39
+ * A record ZOOMS: the same scene with the record grown to most of it, and
40
+ * the same gesture on the zoomed record zooms back out. A KIND CARD stands
41
+ * for a group, so deeper means the group: on the ground it zooms into the
42
+ * group as a place (`aggregate:<kind>`), and from altitude it opens the
43
+ * district in place — the exploded view — and the same gesture closes it.
44
+ *
45
+ * UNLESS THE KIND HAS A PICTURE OF ITS OWN. A district explodes into a ring
46
+ * of chips because a bag of names is the best a generic card can do with its
47
+ * members. A kind with a lens registered over it has something better, and
48
+ * the card already says so with its ◆ — so "deeper" there means that
49
+ * picture, and the district stays shut. Exploding it replaced a designed
50
+ * view with the fallback it exists to improve on.
51
+ *
52
+ * The card's own id (`kind:<kind>`) is never made the focus. A focus
53
+ * resolves to a node or to a group's kinds, and a kind card is neither: a
54
+ * focus on one laid out an empty scene with the card's name in the URL,
55
+ * which is what double-clicking a group looked like before this existed.
56
+ */
57
+ export declare function withJackIn(state: ViewState, id: string, options?: {
58
+ readonly ownPicture?: boolean;
59
+ /**
60
+ * Words to carry into a district: the search that lit it. Descending
61
+ * into a lit district opens it already narrowed — the arrangement's own
62
+ * `in.q` — so the row shows why, and clearing the row clears only that.
63
+ */
64
+ readonly carry?: string;
65
+ }): ViewState;
66
+ /**
67
+ * Ranks by a STABLE key — the node id — and never by a mutable count.
68
+ *
69
+ * This is the single rule that protects spatial memory. Ordering people by
70
+ * "how many runs they have" would reshuffle the whole plane the moment
71
+ * anything is reassigned, and the picture you remember would stop being the
72
+ * picture you get.
73
+ */
74
+ export declare function byStableKey(a: {
75
+ id: string;
76
+ }, b: {
77
+ id: string;
78
+ }): number;
79
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAShH,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpD;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAE1C,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAG5C;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE/D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAIrD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE;IACP,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACpB,GACL,SAAS,CAuBX;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAExE"}