@formicoidea/labre-framework-cynefin 0.41.0 → 0.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +384 -0
- package/dist/commands.js +5 -1
- package/dist/cynefin/consts.d.ts +19 -0
- package/dist/cynefin/consts.js +12 -0
- package/dist/cynefin/crop.d.ts +6 -0
- package/dist/cynefin/crop.js +45 -0
- package/dist/cynefin/element-renderer.js +12 -8
- package/dist/cynefin/element-view.d.ts +41 -4
- package/dist/cynefin/element-view.js +59 -5
- package/dist/cynefin/labels.d.ts +25 -0
- package/dist/cynefin/labels.js +40 -0
- package/dist/estuarine/consts.d.ts +112 -21
- package/dist/estuarine/consts.js +101 -29
- package/dist/estuarine/element-renderer.d.ts +7 -0
- package/dist/estuarine/element-renderer.js +56 -24
- package/dist/estuarine/element-view.d.ts +10 -2
- package/dist/estuarine/element-view.js +17 -2
- package/dist/estuarine/labels.d.ts +40 -0
- package/dist/estuarine/labels.js +50 -0
- package/dist/presets.d.ts +13 -2
- package/dist/presets.js +13 -2
- package/dist/templates/index.js +17 -10
- package/dist/toolbar/palette.d.ts +31 -0
- package/dist/toolbar/palette.js +105 -0
- package/dist/toolbar/senior-button.d.ts +2 -0
- package/dist/toolbar/senior-button.js +6 -2
- package/dist/translations.js +9 -0
- package/dist/utils.d.ts +6 -8
- package/dist/utils.js +6 -7
- package/dist/view.js +7 -0
- package/package.json +2 -2
|
@@ -1,23 +1,77 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrameworkBackgroundView, hitTestBackgroundLabel, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
3
|
+
import { GfxViewInteractionExtension } from '@formicoidea/labre-core/std/gfx';
|
|
4
|
+
import { cynefinCroppedXYWH } from './crop.js';
|
|
5
|
+
import { cynefinLabelHits } from './labels.js';
|
|
2
6
|
/**
|
|
3
7
|
* View for the Cynefin background. Registering it ensures `gfx.view.get(model)`
|
|
4
8
|
* returns a view (required so move / select interactions work).
|
|
9
|
+
*
|
|
10
|
+
* Extends {@link FrameworkBackgroundView} rather than
|
|
11
|
+
* `DeclaredBackgroundView`: this diagram is a figurative reproduction of the
|
|
12
|
+
* official SVG and has no `FrameworkBackgroundDef` behind it, so it says where
|
|
13
|
+
* its labels are in its own reference space. The gesture itself — where a
|
|
14
|
+
* double-click is allowed to land, the in-place `<input>`, the commit that
|
|
15
|
+
* refuses to write an untouched value — is the shared one (issue #355).
|
|
5
16
|
*/
|
|
6
|
-
export class CynefinView extends
|
|
17
|
+
export class CynefinView extends FrameworkBackgroundView {
|
|
7
18
|
static { this.type = 'cynefin'; }
|
|
19
|
+
labelAt(lx, ly, w, h) {
|
|
20
|
+
const translate = (wording) => translateKey(this.gfx.std, ...wording);
|
|
21
|
+
const hit = hitTestBackgroundLabel(cynefinLabelHits(this.model, w, h, translate), lx, ly);
|
|
22
|
+
return hit ? { prop: hit.prop, text: hit.text } : null;
|
|
23
|
+
}
|
|
8
24
|
}
|
|
9
25
|
/**
|
|
10
|
-
* Resize gating
|
|
11
|
-
*
|
|
26
|
+
* Resize gating and, when the handle is let go, the crop.
|
|
27
|
+
*
|
|
28
|
+
* ## Gating
|
|
29
|
+
*
|
|
30
|
+
* The resize handles are hidden unless `model.resizeEnabled` is true (toggled
|
|
31
|
+
* from the toolbar). Moving / selecting stays available.
|
|
32
|
+
*
|
|
33
|
+
* ## The crop
|
|
34
|
+
*
|
|
35
|
+
* The diagram is a fixed drawing fitted uniformly into the element, so dragged
|
|
36
|
+
* off its proportion the element letterboxes: 400 model units of nothing either
|
|
37
|
+
* side at 1600 × 600. `onResizeEnd` brings the frame back onto the picture (see
|
|
38
|
+
* `./crop.ts`) — the drawing is neither deformed nor truncated, the element
|
|
39
|
+
* simply stops claiming room it does not paint.
|
|
40
|
+
*
|
|
41
|
+
* ## Why here, and why one undo step
|
|
42
|
+
*
|
|
43
|
+
* A resize STASHES `xywh` on the way in, writes the model on every move, and
|
|
44
|
+
* commits the stash in `onResizeEnd`, inside the manager's own
|
|
45
|
+
* `store.transact` (`std/gfx/interactivity/manager.ts`). Writing the cropped
|
|
46
|
+
* box BEFORE calling `default` therefore changes what is committed rather than
|
|
47
|
+
* adding a second write: the whole gesture stays one Yjs write, and one undo
|
|
48
|
+
* step. `captureSync` would do the opposite here — it would close the step and
|
|
49
|
+
* make the crop a second one.
|
|
50
|
+
*
|
|
51
|
+
* This seam is also what keeps the crop a GESTURE. A cascade watching `xywh`
|
|
52
|
+
* would fire on a peer's resize too and re-crop, on every screen, a frame
|
|
53
|
+
* nobody on that screen touched; and it would fire on a document being loaded.
|
|
54
|
+
* The readonly guard is belt and braces — `handleElementResize` already refuses
|
|
55
|
+
* to start in a readonly store — but it is the invariant, so it is stated.
|
|
12
56
|
*/
|
|
13
57
|
export const CynefinInteraction = GfxViewInteractionExtension(CynefinView.type, {
|
|
14
|
-
handleResize({ model }) {
|
|
58
|
+
handleResize({ std, model }) {
|
|
15
59
|
return {
|
|
16
60
|
beforeResize({ set }) {
|
|
17
61
|
if (!model.resizeEnabled) {
|
|
18
62
|
set({ allowedHandlers: [] });
|
|
19
63
|
}
|
|
20
64
|
},
|
|
65
|
+
onResizeEnd(context) {
|
|
66
|
+
if (!std.store.readonly) {
|
|
67
|
+
const cropped = cynefinCroppedXYWH(model.xywh);
|
|
68
|
+
// No write at all when the frame is already on the drawing: an
|
|
69
|
+
// undo entry for a gesture that changed nothing is a bug.
|
|
70
|
+
if (cropped)
|
|
71
|
+
model.xywh = cropped;
|
|
72
|
+
}
|
|
73
|
+
context.default(context);
|
|
74
|
+
},
|
|
21
75
|
};
|
|
22
76
|
},
|
|
23
77
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { BackgroundLabelHit } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { CynefinElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import type { DomainBlock } from './consts.js';
|
|
5
|
+
/** Resolves a wording against the host's catalogue, or falls back to English. */
|
|
6
|
+
export type CynefinWordingResolver = (wording: ChromeWording) => string;
|
|
7
|
+
/**
|
|
8
|
+
* What a domain heading SAYS: the user's own word, else the vocabulary.
|
|
9
|
+
*
|
|
10
|
+
* The very precedence `backgroundLabelText` applies to a declared background —
|
|
11
|
+
* user text wins over the catalogue, the catalogue over the shipped English —
|
|
12
|
+
* spelled out here because this diagram has no declaration to apply it for it.
|
|
13
|
+
* An empty string is a real answer (the user erased the word), which is why the
|
|
14
|
+
* test is against `undefined` and not against falsiness.
|
|
15
|
+
*/
|
|
16
|
+
export declare function cynefinHeadingText(model: CynefinElementModel, block: DomainBlock, translate: CynefinWordingResolver): string;
|
|
17
|
+
/**
|
|
18
|
+
* Clickable boxes of the four domain headings, in ELEMENT-LOCAL coordinates.
|
|
19
|
+
*
|
|
20
|
+
* Built from the same reference positions and the same uniform letterbox fit
|
|
21
|
+
* the renderer draws them with, so the target tracks the word at any size, and
|
|
22
|
+
* empty while the headings are hidden — a label that is not painted may not be
|
|
23
|
+
* aimed at either.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cynefinLabelHits(model: CynefinElementModel, w: number, h: number, translate: CynefinWordingResolver): BackgroundLabelHit[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { backgroundTextHitBox } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { refScale } from '../utils.js';
|
|
3
|
+
import { DOMAINS, HEADING_SIZE, REF_H, REF_W } from './consts.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a domain heading SAYS: the user's own word, else the vocabulary.
|
|
6
|
+
*
|
|
7
|
+
* The very precedence `backgroundLabelText` applies to a declared background —
|
|
8
|
+
* user text wins over the catalogue, the catalogue over the shipped English —
|
|
9
|
+
* spelled out here because this diagram has no declaration to apply it for it.
|
|
10
|
+
* An empty string is a real answer (the user erased the word), which is why the
|
|
11
|
+
* test is against `undefined` and not against falsiness.
|
|
12
|
+
*/
|
|
13
|
+
export function cynefinHeadingText(model, block, translate) {
|
|
14
|
+
const own = model[block.prop];
|
|
15
|
+
return own === undefined || own === null
|
|
16
|
+
? translate(block.heading)
|
|
17
|
+
: String(own);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clickable boxes of the four domain headings, in ELEMENT-LOCAL coordinates.
|
|
21
|
+
*
|
|
22
|
+
* Built from the same reference positions and the same uniform letterbox fit
|
|
23
|
+
* the renderer draws them with, so the target tracks the word at any size, and
|
|
24
|
+
* empty while the headings are hidden — a label that is not painted may not be
|
|
25
|
+
* aimed at either.
|
|
26
|
+
*/
|
|
27
|
+
export function cynefinLabelHits(model, w, h, translate) {
|
|
28
|
+
if (!model.showTitles)
|
|
29
|
+
return [];
|
|
30
|
+
const { s, ox, oy } = refScale(w, h, REF_W, REF_H);
|
|
31
|
+
return DOMAINS.map(block => {
|
|
32
|
+
const text = cynefinHeadingText(model, block, translate);
|
|
33
|
+
return {
|
|
34
|
+
id: block.prop,
|
|
35
|
+
prop: block.prop,
|
|
36
|
+
text,
|
|
37
|
+
...backgroundTextHitBox(text, HEADING_SIZE * s, ox + block.x * s, oy + block.hy * s),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
2
2
|
/**
|
|
3
3
|
* Visual constants for the Estuarine framework map, reproduced from the official
|
|
4
|
-
* SVG (viewBox 0 0 690 801). All geometry is authored in that fixed
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* gets a longer time axis and a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* (
|
|
4
|
+
* SVG (viewBox 0 0 690 801). All geometry is authored in that fixed space; the
|
|
5
|
+
* renderer reads every coordinate below as a RATIO of the CROPPED window onto
|
|
6
|
+
* it ({@link REF_X} … {@link REF_H}) and maps it onto the element's real width
|
|
7
|
+
* and height independently, so a stretched map gets a longer time axis and a
|
|
8
|
+
* taller energy axis rather than the same drawing letterboxed (see
|
|
9
|
+
* `EstuarineFit` in `./element-renderer.ts`). The e axis is vertical &
|
|
10
|
+
* double-headed (energy), the t axis horizontal & single-headed (time only
|
|
11
|
+
* flows one way).
|
|
11
12
|
*
|
|
12
13
|
* The three curve legends below (`LABELS`) carry a `wording`, resolved by
|
|
13
14
|
* `element-renderer.ts` through the `CanvasRenderer` it is handed at paint
|
|
@@ -15,8 +16,40 @@ import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
|
15
16
|
* italic `e` / `t` axis LETTERS (`AXIS_LABELS`) are notation, not words, and
|
|
16
17
|
* are deliberately NOT keyed (PO decision).
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The CROPPED window onto the authored 690 × 801 SVG space — the rectangle the
|
|
21
|
+
* element's bounds are mapped onto.
|
|
22
|
+
*
|
|
23
|
+
* The SVG's own viewBox is loose around the drawing: the t axis stops at
|
|
24
|
+
* `x = 616 / 690` and the e axis at `y = 763 / 801`, so a board born on the raw
|
|
25
|
+
* viewBox carried dead space on every side (measured on the painted pixels at
|
|
26
|
+
* birth size: 16.6 left, 12.6 top, 35.5 right, 20.4 bottom, in model units).
|
|
27
|
+
* The board's own border is therefore nowhere near its drawing, which is both
|
|
28
|
+
* ugly and awkward — a background is grabbed BY that border
|
|
29
|
+
* (`framework-background/hit-test.ts`).
|
|
30
|
+
*
|
|
31
|
+
* So the reference box is the ink's bounding box grown by {@link REF_MARGIN},
|
|
32
|
+
* not the viewBox. `REF_X` / `REF_Y` is where that window starts in authored
|
|
33
|
+
* coordinates; every authored number in this file keeps its ORIGINAL value and
|
|
34
|
+
* the renderer subtracts the origin (`ax` / `ay`), so nothing about the drawing
|
|
35
|
+
* moves relative to anything else — only the frame around it tightens.
|
|
36
|
+
*
|
|
37
|
+
* `MAP_SCALE` (see `../presets.ts`) multiplies these, so a new board is born
|
|
38
|
+
* smaller while its drawing keeps exactly the size it had.
|
|
39
|
+
*/
|
|
40
|
+
export declare const REF_X = 8;
|
|
41
|
+
export declare const REF_Y = 4.5;
|
|
42
|
+
export declare const REF_W = 659;
|
|
43
|
+
export declare const REF_H = 786.5;
|
|
44
|
+
/**
|
|
45
|
+
* Space left between the ink and the edge of the board, in reference units.
|
|
46
|
+
*
|
|
47
|
+
* Small but not zero: a stroke is centred on its path, a glyph is measured
|
|
48
|
+
* generously, and a board whose ink touched its own border would look clipped.
|
|
49
|
+
* ~6 units is a little under one axis width (8) — enough to read as a margin,
|
|
50
|
+
* little enough that the border is visibly AROUND the drawing.
|
|
51
|
+
*/
|
|
52
|
+
export declare const REF_MARGIN = 6;
|
|
20
53
|
export declare const COLORS: {
|
|
21
54
|
readonly axis: "#941253";
|
|
22
55
|
/** Italic e / t axis letters. */
|
|
@@ -42,12 +75,38 @@ export declare const T_AXIS: {
|
|
|
42
75
|
readonly x2: 616;
|
|
43
76
|
};
|
|
44
77
|
export declare const AXIS_WIDTH = 8;
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
/**
|
|
79
|
+
* A filled arrowhead, welded to the END OF ITS AXIS.
|
|
80
|
+
*
|
|
81
|
+
* Declared the way the shared primitive declares one (`drawAxis`,
|
|
82
|
+
* `blocks/surface/src/framework-background/renderer.ts`): the head has ONE
|
|
83
|
+
* proportional anchor — {@link at}, the point where its axis stops, which
|
|
84
|
+
* travels with the stretch — and every other number below is an offset from
|
|
85
|
+
* that anchor in FIXED units, painted at the isotropic `strokeScale`.
|
|
86
|
+
*
|
|
87
|
+
* That split is the whole point. The vertices used to be three absolute
|
|
88
|
+
* authored points, so a stretch moved the tip by `sx` while the renderer
|
|
89
|
+
* rebuilt the base from the tip at the isotropic factor: as soon as
|
|
90
|
+
* `sx ≠ strokeScale` the triangle tore away from the line it belongs to
|
|
91
|
+
* (a 29-unit gap at 1600 × 400, the head drowning in the stroke at 400 × 1600).
|
|
92
|
+
* Anchoring the head to the axis end instead keeps it soldered at every ratio,
|
|
93
|
+
* and reproduces the authored SVG exactly when `sx === sy` — which is what the
|
|
94
|
+
* numbers below were read off.
|
|
95
|
+
*/
|
|
96
|
+
export interface EstuarineArrowhead {
|
|
97
|
+
/** Authored end of the axis this head is welded to — the ratio anchor. */
|
|
98
|
+
at: readonly [number, number];
|
|
99
|
+
/** Unit vector pointing OUT of the axis, towards the tip. */
|
|
100
|
+
dir: readonly [number, number];
|
|
101
|
+
/** How far past the axis end the tip sits, in fixed units. */
|
|
102
|
+
tip: number;
|
|
103
|
+
/** How far SHORT of the axis end the base sits: the weld's overlap. */
|
|
104
|
+
overlap: number;
|
|
105
|
+
/** Half the base's width, in fixed units. */
|
|
106
|
+
halfWidth: number;
|
|
107
|
+
}
|
|
108
|
+
/** The three heads: both ends of the e axis, the right end of the t axis. */
|
|
109
|
+
export declare const ARROWHEADS: readonly EstuarineArrowhead[];
|
|
51
110
|
/** Liminal: green boundary rising gently then dipping at the right end. */
|
|
52
111
|
export declare const LIMINAL_PATH = "M 63 193 C 67 192, 78 189, 85 188 C 92 187, 100 186, 107 185 C 114 184, 122 183, 129 183 C 136 183, 144 183, 151 183 C 158 183, 166 183, 173 183 C 180 183, 188 184, 195 185 C 202 186, 210 188, 217 189 C 224 190, 232 192, 239 194 C 246 196, 254 198, 261 201 C 268 204, 276 207, 283 210 C 290 213, 298 217, 305 220 C 312 223, 320 226, 327 230 C 334 234, 342 238, 349 242 C 356 246, 364 250, 371 255 C 378 260, 386 264, 393 269 C 400 274, 408 278, 415 283 C 422 288, 430 292, 437 297 C 444 302, 451 306, 458 310 C 465 314, 473 319, 480 323 C 487 327, 495 332, 502 335 C 509 338, 517 341, 524 343 C 531 345, 539 348, 546 349 C 553 350, 561 350, 568 350 C 575 350, 583 348, 590 346 C 597 344, 605 340, 612 336 C 619 332, 626 325, 633 319 C 640 313, 648 302, 651 298 C 654 294, 654 295, 654 294";
|
|
53
112
|
export declare const LIMINAL_WIDTH = 4.5;
|
|
@@ -59,10 +118,22 @@ export declare const VOLATILE_PATH = "M 58 446 C 61 447, 70 451, 76 454 C 82 457
|
|
|
59
118
|
export declare const VOLATILE_WIDTH = 5;
|
|
60
119
|
/** Every {@link LABELS} wording, for `translations.ts`'s manifest. */
|
|
61
120
|
export declare const ESTUARINE_CANVAS_WORDINGS: readonly ChromeWording[];
|
|
62
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Uppercase legends: anchored centre, alphabetic baseline, with letter-spacing.
|
|
123
|
+
*
|
|
124
|
+
* `prop` names the model key a double-click renames the legend into (issue
|
|
125
|
+
* #355), and `visibleProp` the toggle that shows it — the legend is painted
|
|
126
|
+
* with its curve and hidden with it, so it is aimable exactly when it is drawn.
|
|
127
|
+
*
|
|
128
|
+
* The three legends and nothing else: the italic `e` / `t` axis letters are
|
|
129
|
+
* notation, not words (PO decision, and the reason they carry no i18n key
|
|
130
|
+
* either), so they are not renamable.
|
|
131
|
+
*/
|
|
63
132
|
export declare const LABELS: {
|
|
64
133
|
readonly counterfactual: {
|
|
65
134
|
readonly wording: ChromeWording;
|
|
135
|
+
readonly prop: "counterfactualLabel";
|
|
136
|
+
readonly visibleProp: "showCounterfactual";
|
|
66
137
|
readonly x: 422;
|
|
67
138
|
readonly y: 25;
|
|
68
139
|
readonly size: 20;
|
|
@@ -70,6 +141,8 @@ export declare const LABELS: {
|
|
|
70
141
|
};
|
|
71
142
|
readonly liminal: {
|
|
72
143
|
readonly wording: ChromeWording;
|
|
144
|
+
readonly prop: "liminalLabel";
|
|
145
|
+
readonly visibleProp: "showLiminal";
|
|
73
146
|
readonly x: 316;
|
|
74
147
|
readonly y: 192;
|
|
75
148
|
readonly size: 18;
|
|
@@ -77,23 +150,41 @@ export declare const LABELS: {
|
|
|
77
150
|
};
|
|
78
151
|
readonly volatile: {
|
|
79
152
|
readonly wording: ChromeWording;
|
|
153
|
+
readonly prop: "volatileLabel";
|
|
154
|
+
readonly visibleProp: "showVolatile";
|
|
80
155
|
readonly x: 219;
|
|
81
156
|
readonly y: 783;
|
|
82
157
|
readonly size: 20;
|
|
83
158
|
readonly color: "#e63322";
|
|
84
159
|
};
|
|
85
160
|
};
|
|
86
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* Italic Georgia axis letters (left-anchored, alphabetic baseline).
|
|
163
|
+
*
|
|
164
|
+
* Each letter NAMES an axis, so it is declared against that axis the way a
|
|
165
|
+
* declared background declares a word (`backgroundPoint`,
|
|
166
|
+
* `blocks/surface/src/framework-background/def.ts`): a proportional anchor
|
|
167
|
+
* (`at`, the end of the axis it names) plus a FIXED `dx`/`dy` gap, painted at
|
|
168
|
+
* the isotropic `strokeScale` like the glyph itself.
|
|
169
|
+
*
|
|
170
|
+
* They used to be absolute authored positions projected by `sx`/`sy` while the
|
|
171
|
+
* glyph was typed isotropically, so the gap between letter and axis grew or
|
|
172
|
+
* collapsed with the ratio — the `t` climbing onto its own axis at 1600 × 400,
|
|
173
|
+
* falling far below it at 400 × 1600. `at + d·strokeScale` is exactly the
|
|
174
|
+
* authored position when `sx === sy`, and a constant gap everywhere else.
|
|
175
|
+
*/
|
|
87
176
|
export declare const AXIS_LABELS: {
|
|
88
177
|
readonly e: {
|
|
89
178
|
readonly text: "e";
|
|
90
|
-
readonly
|
|
91
|
-
readonly
|
|
179
|
+
readonly at: readonly [43.5, 97];
|
|
180
|
+
readonly dx: -29.5;
|
|
181
|
+
readonly dy: 41;
|
|
92
182
|
};
|
|
93
183
|
readonly t: {
|
|
94
184
|
readonly text: "t";
|
|
95
|
-
readonly
|
|
96
|
-
readonly
|
|
185
|
+
readonly at: readonly [616, 649];
|
|
186
|
+
readonly dx: -36;
|
|
187
|
+
readonly dy: 36;
|
|
97
188
|
};
|
|
98
189
|
readonly size: 34;
|
|
99
190
|
};
|
package/dist/estuarine/consts.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Visual constants for the Estuarine framework map, reproduced from the official
|
|
3
|
-
* SVG (viewBox 0 0 690 801). All geometry is authored in that fixed
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* gets a longer time axis and a
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
3
|
+
* SVG (viewBox 0 0 690 801). All geometry is authored in that fixed space; the
|
|
4
|
+
* renderer reads every coordinate below as a RATIO of the CROPPED window onto
|
|
5
|
+
* it ({@link REF_X} … {@link REF_H}) and maps it onto the element's real width
|
|
6
|
+
* and height independently, so a stretched map gets a longer time axis and a
|
|
7
|
+
* taller energy axis rather than the same drawing letterboxed (see
|
|
8
|
+
* `EstuarineFit` in `./element-renderer.ts`). The e axis is vertical &
|
|
9
|
+
* double-headed (energy), the t axis horizontal & single-headed (time only
|
|
10
|
+
* flows one way).
|
|
10
11
|
*
|
|
11
12
|
* The three curve legends below (`LABELS`) carry a `wording`, resolved by
|
|
12
13
|
* `element-renderer.ts` through the `CanvasRenderer` it is handed at paint
|
|
@@ -14,8 +15,40 @@
|
|
|
14
15
|
* italic `e` / `t` axis LETTERS (`AXIS_LABELS`) are notation, not words, and
|
|
15
16
|
* are deliberately NOT keyed (PO decision).
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The CROPPED window onto the authored 690 × 801 SVG space — the rectangle the
|
|
20
|
+
* element's bounds are mapped onto.
|
|
21
|
+
*
|
|
22
|
+
* The SVG's own viewBox is loose around the drawing: the t axis stops at
|
|
23
|
+
* `x = 616 / 690` and the e axis at `y = 763 / 801`, so a board born on the raw
|
|
24
|
+
* viewBox carried dead space on every side (measured on the painted pixels at
|
|
25
|
+
* birth size: 16.6 left, 12.6 top, 35.5 right, 20.4 bottom, in model units).
|
|
26
|
+
* The board's own border is therefore nowhere near its drawing, which is both
|
|
27
|
+
* ugly and awkward — a background is grabbed BY that border
|
|
28
|
+
* (`framework-background/hit-test.ts`).
|
|
29
|
+
*
|
|
30
|
+
* So the reference box is the ink's bounding box grown by {@link REF_MARGIN},
|
|
31
|
+
* not the viewBox. `REF_X` / `REF_Y` is where that window starts in authored
|
|
32
|
+
* coordinates; every authored number in this file keeps its ORIGINAL value and
|
|
33
|
+
* the renderer subtracts the origin (`ax` / `ay`), so nothing about the drawing
|
|
34
|
+
* moves relative to anything else — only the frame around it tightens.
|
|
35
|
+
*
|
|
36
|
+
* `MAP_SCALE` (see `../presets.ts`) multiplies these, so a new board is born
|
|
37
|
+
* smaller while its drawing keeps exactly the size it had.
|
|
38
|
+
*/
|
|
39
|
+
export const REF_X = 8;
|
|
40
|
+
export const REF_Y = 4.5;
|
|
41
|
+
export const REF_W = 659;
|
|
42
|
+
export const REF_H = 786.5;
|
|
43
|
+
/**
|
|
44
|
+
* Space left between the ink and the edge of the board, in reference units.
|
|
45
|
+
*
|
|
46
|
+
* Small but not zero: a stroke is centred on its path, a glyph is measured
|
|
47
|
+
* generously, and a board whose ink touched its own border would look clipped.
|
|
48
|
+
* ~6 units is a little under one axis width (8) — enough to read as a margin,
|
|
49
|
+
* little enough that the border is visibly AROUND the drawing.
|
|
50
|
+
*/
|
|
51
|
+
export const REF_MARGIN = 6;
|
|
19
52
|
export const COLORS = {
|
|
20
53
|
axis: '#941253',
|
|
21
54
|
/** Italic e / t axis letters. */
|
|
@@ -33,23 +66,32 @@ export const E_AXIS = { x: 43.5, y1: 97, y2: 763 };
|
|
|
33
66
|
/** t axis (horizontal, single-headed → right): y, left x, right x. */
|
|
34
67
|
export const T_AXIS = { y: 649, x1: 28, x2: 616 };
|
|
35
68
|
export const AXIS_WIDTH = 8;
|
|
36
|
-
/**
|
|
69
|
+
/** The three heads: both ends of the e axis, the right end of the t axis. */
|
|
37
70
|
export const ARROWHEADS = [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
[
|
|
41
|
-
[
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
// e — top
|
|
72
|
+
{
|
|
73
|
+
at: [E_AXIS.x, E_AXIS.y1],
|
|
74
|
+
dir: [0, -1],
|
|
75
|
+
tip: 25,
|
|
76
|
+
overlap: 3,
|
|
77
|
+
halfWidth: 13.5,
|
|
78
|
+
},
|
|
79
|
+
// e — bottom
|
|
80
|
+
{
|
|
81
|
+
at: [E_AXIS.x, E_AXIS.y2],
|
|
82
|
+
dir: [0, 1],
|
|
83
|
+
tip: 22,
|
|
84
|
+
overlap: 5,
|
|
85
|
+
halfWidth: 13.5,
|
|
86
|
+
},
|
|
87
|
+
// t — right
|
|
88
|
+
{
|
|
89
|
+
at: [T_AXIS.x2, T_AXIS.y],
|
|
90
|
+
dir: [1, 0],
|
|
91
|
+
tip: 27,
|
|
92
|
+
overlap: 3,
|
|
93
|
+
halfWidth: 13,
|
|
94
|
+
},
|
|
53
95
|
];
|
|
54
96
|
/** Liminal: green boundary rising gently then dipping at the right end. */
|
|
55
97
|
export const LIMINAL_PATH = 'M 63 193 C 67 192, 78 189, 85 188 C 92 187, 100 186, 107 185 C 114 184, 122 183, 129 183 C 136 183, 144 183, 151 183 C 158 183, 166 183, 173 183 C 180 183, 188 184, 195 185 C 202 186, 210 188, 217 189 C 224 190, 232 192, 239 194 C 246 196, 254 198, 261 201 C 268 204, 276 207, 283 210 C 290 213, 298 217, 305 220 C 312 223, 320 226, 327 230 C 334 234, 342 238, 349 242 C 356 246, 364 250, 371 255 C 378 260, 386 264, 393 269 C 400 274, 408 278, 415 283 C 422 288, 430 292, 437 297 C 444 302, 451 306, 458 310 C 465 314, 473 319, 480 323 C 487 327, 495 332, 502 335 C 509 338, 517 341, 524 343 C 531 345, 539 348, 546 349 C 553 350, 561 350, 568 350 C 575 350, 583 348, 590 346 C 597 344, 605 340, 612 336 C 619 332, 626 325, 633 319 C 640 313, 648 302, 651 298 C 654 294, 654 295, 654 294';
|
|
@@ -78,10 +120,22 @@ export const ESTUARINE_CANVAS_WORDINGS = [
|
|
|
78
120
|
LABEL_LIMINAL,
|
|
79
121
|
LABEL_VOLATILE,
|
|
80
122
|
];
|
|
81
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* Uppercase legends: anchored centre, alphabetic baseline, with letter-spacing.
|
|
125
|
+
*
|
|
126
|
+
* `prop` names the model key a double-click renames the legend into (issue
|
|
127
|
+
* #355), and `visibleProp` the toggle that shows it — the legend is painted
|
|
128
|
+
* with its curve and hidden with it, so it is aimable exactly when it is drawn.
|
|
129
|
+
*
|
|
130
|
+
* The three legends and nothing else: the italic `e` / `t` axis letters are
|
|
131
|
+
* notation, not words (PO decision, and the reason they carry no i18n key
|
|
132
|
+
* either), so they are not renamable.
|
|
133
|
+
*/
|
|
82
134
|
export const LABELS = {
|
|
83
135
|
counterfactual: {
|
|
84
136
|
wording: LABEL_COUNTERFACTUAL,
|
|
137
|
+
prop: 'counterfactualLabel',
|
|
138
|
+
visibleProp: 'showCounterfactual',
|
|
85
139
|
x: 422,
|
|
86
140
|
y: 25,
|
|
87
141
|
size: 20,
|
|
@@ -89,6 +143,8 @@ export const LABELS = {
|
|
|
89
143
|
},
|
|
90
144
|
liminal: {
|
|
91
145
|
wording: LABEL_LIMINAL,
|
|
146
|
+
prop: 'liminalLabel',
|
|
147
|
+
visibleProp: 'showLiminal',
|
|
92
148
|
x: 316,
|
|
93
149
|
y: 192,
|
|
94
150
|
size: 18,
|
|
@@ -96,16 +152,32 @@ export const LABELS = {
|
|
|
96
152
|
},
|
|
97
153
|
volatile: {
|
|
98
154
|
wording: LABEL_VOLATILE,
|
|
155
|
+
prop: 'volatileLabel',
|
|
156
|
+
visibleProp: 'showVolatile',
|
|
99
157
|
x: 219,
|
|
100
158
|
y: 783,
|
|
101
159
|
size: 20,
|
|
102
160
|
color: COLORS.volatile,
|
|
103
161
|
},
|
|
104
162
|
};
|
|
105
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* Italic Georgia axis letters (left-anchored, alphabetic baseline).
|
|
165
|
+
*
|
|
166
|
+
* Each letter NAMES an axis, so it is declared against that axis the way a
|
|
167
|
+
* declared background declares a word (`backgroundPoint`,
|
|
168
|
+
* `blocks/surface/src/framework-background/def.ts`): a proportional anchor
|
|
169
|
+
* (`at`, the end of the axis it names) plus a FIXED `dx`/`dy` gap, painted at
|
|
170
|
+
* the isotropic `strokeScale` like the glyph itself.
|
|
171
|
+
*
|
|
172
|
+
* They used to be absolute authored positions projected by `sx`/`sy` while the
|
|
173
|
+
* glyph was typed isotropically, so the gap between letter and axis grew or
|
|
174
|
+
* collapsed with the ratio — the `t` climbing onto its own axis at 1600 × 400,
|
|
175
|
+
* falling far below it at 400 × 1600. `at + d·strokeScale` is exactly the
|
|
176
|
+
* authored position when `sx === sy`, and a constant gap everywhere else.
|
|
177
|
+
*/
|
|
106
178
|
export const AXIS_LABELS = {
|
|
107
|
-
e: { text: 'e', x:
|
|
108
|
-
t: { text: 't',
|
|
179
|
+
e: { text: 'e', at: [E_AXIS.x, E_AXIS.y1], dx: -29.5, dy: 41 },
|
|
180
|
+
t: { text: 't', at: [T_AXIS.x2, T_AXIS.y], dx: -36, dy: 36 },
|
|
109
181
|
size: 34,
|
|
110
182
|
};
|
|
111
183
|
export const LABEL_LETTER_SPACING = 4;
|
|
@@ -109,6 +109,13 @@ export declare function applyEstuarineTransform(ctx: CanvasRenderingContext2D, w
|
|
|
109
109
|
* arrowheads and every word. Their POSITION follows the stretch — an axis
|
|
110
110
|
* ends where the map now ends — while their SHAPE does not, because a
|
|
111
111
|
* stretched arrowhead or a squashed letter is a defect, never a feature.
|
|
112
|
+
*
|
|
113
|
+
* One rule keeps that split honest, and it is the shared primitive's own
|
|
114
|
+
* (`drawAxis` / `backgroundPoint`): **an anchor is a RATIO of the frame, and
|
|
115
|
+
* every offset from an anchor is in FIXED units.** A shape must never take
|
|
116
|
+
* its anchor from one and its offset from the other — that mix is exactly
|
|
117
|
+
* what tore the arrowheads off their axes and slid the `e` / `t` letters
|
|
118
|
+
* around on a stretched map.
|
|
112
119
|
* - **Stretched reference space**, entered by {@link applyEstuarineTransform}:
|
|
113
120
|
* the three curves, which are boundaries across the plane and must cover
|
|
114
121
|
* whatever plane the user has made.
|