bireactive 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/dist/automerge/doc-cell.d.ts +20 -0
- package/dist/automerge/doc-cell.js +80 -0
- package/dist/automerge/index.d.ts +3 -0
- package/dist/automerge/index.js +12 -0
- package/dist/automerge/reconcile.d.ts +5 -0
- package/dist/automerge/reconcile.js +63 -0
- package/dist/core/_counts.d.ts +48 -0
- package/dist/core/_counts.js +51 -0
- package/dist/core/cell.d.ts +148 -112
- package/dist/core/cell.js +945 -768
- package/dist/core/debug.d.ts +25 -0
- package/dist/core/debug.js +121 -0
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +9 -2
- package/dist/core/index.js +8 -1
- package/dist/core/lenses/aggregates.d.ts +42 -52
- package/dist/core/lenses/aggregates.js +225 -116
- package/dist/core/lenses/geometry.d.ts +22 -4
- package/dist/core/lenses/geometry.js +59 -27
- package/dist/core/lenses/index.d.ts +6 -6
- package/dist/core/lenses/index.js +6 -6
- package/dist/core/lenses/memory.js +4 -17
- package/dist/core/lenses/numerical.d.ts +100 -0
- package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
- package/dist/core/lenses/point-cloud.d.ts +67 -0
- package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +226 -84
- package/dist/core/lenses/snap.d.ts +18 -0
- package/dist/core/lenses/snap.js +138 -0
- package/dist/core/lenses/text.d.ts +40 -0
- package/dist/core/lenses/text.js +202 -0
- package/dist/core/lifecycle.js +3 -6
- package/dist/core/linalg.js +5 -11
- package/dist/core/optic.d.ts +13 -0
- package/dist/core/optic.js +39 -0
- package/dist/core/optics.d.ts +10 -0
- package/dist/core/optics.js +26 -0
- package/dist/core/store.d.ts +9 -0
- package/dist/core/store.js +77 -0
- package/dist/core/traits.d.ts +4 -7
- package/dist/core/traits.js +8 -12
- package/dist/core/values/anchor.js +0 -4
- package/dist/core/values/arr.d.ts +110 -0
- package/dist/core/values/arr.js +336 -0
- package/dist/core/values/audio.d.ts +8 -9
- package/dist/core/values/audio.js +11 -28
- package/dist/core/values/bool.d.ts +11 -11
- package/dist/core/values/bool.js +12 -22
- package/dist/core/values/box.d.ts +15 -20
- package/dist/core/values/box.js +20 -33
- package/dist/core/values/canvas.d.ts +18 -25
- package/dist/core/values/canvas.js +32 -66
- package/dist/core/values/color.d.ts +5 -7
- package/dist/core/values/color.js +5 -11
- package/dist/core/values/field.d.ts +6 -7
- package/dist/core/values/field.js +10 -35
- package/dist/core/values/flags.d.ts +1 -2
- package/dist/core/values/flags.js +1 -17
- package/dist/core/values/gpu.d.ts +6 -10
- package/dist/core/values/gpu.js +8 -22
- package/dist/core/values/matrix.d.ts +2 -4
- package/dist/core/values/matrix.js +2 -12
- package/dist/core/values/num.d.ts +19 -28
- package/dist/core/values/num.js +23 -41
- package/dist/core/values/pose.d.ts +2 -4
- package/dist/core/values/pose.js +3 -12
- package/dist/core/values/range.d.ts +18 -26
- package/dist/core/values/range.js +22 -39
- package/dist/core/values/reg/ambiguity.d.ts +8 -0
- package/dist/core/values/reg/ambiguity.js +131 -0
- package/dist/core/values/reg/engine.d.ts +91 -0
- package/dist/core/values/reg/engine.js +373 -0
- package/dist/core/values/reg/nfa.d.ts +42 -0
- package/dist/core/values/reg/nfa.js +391 -0
- package/dist/core/values/reg/regex.d.ts +7 -0
- package/dist/core/values/reg/regex.js +318 -0
- package/dist/core/values/reg/types.d.ts +60 -0
- package/dist/core/values/reg/types.js +3 -0
- package/dist/core/values/reg.d.ts +250 -0
- package/dist/core/values/reg.js +649 -0
- package/dist/core/values/str.d.ts +16 -60
- package/dist/core/values/str.js +133 -315
- package/dist/core/values/template.js +1 -24
- package/dist/core/values/transform.d.ts +3 -5
- package/dist/core/values/transform.js +3 -12
- package/dist/core/values/tri.d.ts +9 -10
- package/dist/core/values/tri.js +9 -15
- package/dist/core/values/vec.d.ts +9 -24
- package/dist/core/values/vec.js +9 -64
- package/dist/formats/lens.js +6 -9
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +5 -0
- package/dist/jsx-runtime.d.ts +54 -0
- package/dist/jsx-runtime.js +219 -0
- package/dist/schema/lens.js +5 -5
- package/dist/shapes/drag-behaviors.d.ts +56 -0
- package/dist/shapes/drag-behaviors.js +102 -0
- package/dist/shapes/drag-spec.d.ts +52 -0
- package/dist/shapes/drag-spec.js +112 -0
- package/dist/shapes/index.d.ts +3 -1
- package/dist/shapes/index.js +3 -1
- package/dist/shapes/interaction.d.ts +2 -3
- package/dist/shapes/interaction.js +77 -56
- package/dist/shapes/label.js +6 -0
- package/dist/shapes/layout.d.ts +47 -1
- package/dist/shapes/layout.js +59 -1
- package/package.json +22 -1
- package/dist/coll.d.ts +0 -74
- package/dist/coll.js +0 -210
- package/dist/core/lenses/closed-form-policies.d.ts +0 -57
- package/dist/core/lenses/decompositions.d.ts +0 -14
- package/dist/core/lenses/decompositions.js +0 -224
- package/dist/core/lenses/domain-aggregates.d.ts +0 -42
- package/dist/core/lenses/domain-aggregates.js +0 -245
- package/dist/core/lenses/typed-factor.d.ts +0 -40
package/dist/shapes/layout.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Spatial composition primitives.
|
|
2
|
-
import { Box, boxExpand, reader, transformBox } from "../core/index.js";
|
|
2
|
+
import { Box, boxExpand, derive, reader, transformBox, } from "../core/index.js";
|
|
3
3
|
/** Lay out `shapes` in a row/column. First stays put; the rest bind
|
|
4
4
|
* their `translate` reactively to sit `gap` past the previous.
|
|
5
5
|
* Reflows on size or anchor change. */
|
|
@@ -72,3 +72,61 @@ export function split(source, axis, parts, opts = {}) {
|
|
|
72
72
|
export function grid(source, rows, cols, opts = {}) {
|
|
73
73
|
return split(source, "y", rows, opts).map(row => split(row, "x", cols, opts));
|
|
74
74
|
}
|
|
75
|
+
/** Intrinsic ("hug-contents") layout of a tree as nested vertical stacks:
|
|
76
|
+
* each container's size is the bottom-up sum of its children, each child is
|
|
77
|
+
* placed top-down from its container. Unlike `row`/`col` (which fit items
|
|
78
|
+
* into a fixed container), the containers grow to fit. Pure function of the
|
|
79
|
+
* inputs, so feeding it a *previewed* tree yields a previewed layout. */
|
|
80
|
+
export function treeStack(opts) {
|
|
81
|
+
const header = opts.header ?? 0;
|
|
82
|
+
const pad = opts.pad ?? 0;
|
|
83
|
+
const gap = opts.gap ?? 0;
|
|
84
|
+
const minW = opts.minWidth ?? 0;
|
|
85
|
+
const emptyH = opts.emptyHeight ?? header + 2 * pad;
|
|
86
|
+
const measure = (id) => {
|
|
87
|
+
if (!opts.container(id))
|
|
88
|
+
return opts.leaf(id);
|
|
89
|
+
const ks = opts.kids(id);
|
|
90
|
+
if (ks.length === 0)
|
|
91
|
+
return { w: minW, h: emptyH };
|
|
92
|
+
let maxw = 0;
|
|
93
|
+
let h = header + pad;
|
|
94
|
+
for (const c of ks) {
|
|
95
|
+
const m = measure(c);
|
|
96
|
+
maxw = Math.max(maxw, m.w);
|
|
97
|
+
h += m.h + gap;
|
|
98
|
+
}
|
|
99
|
+
return { w: Math.max(minW, maxw + 2 * pad), h: h + pad - gap };
|
|
100
|
+
};
|
|
101
|
+
const place = (id, x, y, out) => {
|
|
102
|
+
const m = measure(id);
|
|
103
|
+
out.set(id, { x, y, w: m.w, h: m.h });
|
|
104
|
+
if (opts.container(id)) {
|
|
105
|
+
let cy = y + header + pad;
|
|
106
|
+
for (const c of opts.kids(id)) {
|
|
107
|
+
place(c, x + pad, cy, out);
|
|
108
|
+
cy += measure(c).h + gap;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const boxes = derive(() => {
|
|
113
|
+
const out = new Map();
|
|
114
|
+
for (const r of opts.roots()) {
|
|
115
|
+
const o = opts.origin(r);
|
|
116
|
+
place(r, o.x, o.y, out);
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
});
|
|
120
|
+
const cache = new Map();
|
|
121
|
+
return {
|
|
122
|
+
boxes,
|
|
123
|
+
box(id) {
|
|
124
|
+
let b = cache.get(id);
|
|
125
|
+
if (!b) {
|
|
126
|
+
b = Box.derive(() => boxes.value.get(id) ?? { x: 0, y: 0, w: 0, h: 0 });
|
|
127
|
+
cache.set(id, b);
|
|
128
|
+
}
|
|
129
|
+
return b;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bireactive",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Bi-directional reactive programming.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
13
|
},
|
|
14
|
+
"./automerge": {
|
|
15
|
+
"types": "./dist/automerge/index.d.ts",
|
|
16
|
+
"import": "./dist/automerge/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./jsx-runtime": {
|
|
19
|
+
"types": "./dist/jsx-runtime.d.ts",
|
|
20
|
+
"import": "./dist/jsx-runtime.js"
|
|
21
|
+
},
|
|
22
|
+
"./jsx-dev-runtime": {
|
|
23
|
+
"types": "./dist/jsx-dev-runtime.d.ts",
|
|
24
|
+
"import": "./dist/jsx-dev-runtime.js"
|
|
25
|
+
},
|
|
14
26
|
"./package.json": "./package.json"
|
|
15
27
|
},
|
|
16
28
|
"sideEffects": false,
|
|
@@ -22,6 +34,9 @@
|
|
|
22
34
|
"scripts": {
|
|
23
35
|
"dev": "vite --host",
|
|
24
36
|
"site": "vite build && typedoc",
|
|
37
|
+
"docs": "typedoc",
|
|
38
|
+
"docs:dev": "typedoc --watch",
|
|
39
|
+
"docs:preview": "python3 -m http.server 5556 --directory dist-web/api",
|
|
25
40
|
"preview": "vite preview",
|
|
26
41
|
"prebuild": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
27
42
|
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json --resolve-full-paths",
|
|
@@ -38,6 +53,9 @@
|
|
|
38
53
|
"postversion": "npm publish && git push --follow-tags"
|
|
39
54
|
},
|
|
40
55
|
"dependencies": {
|
|
56
|
+
"@automerge/automerge-repo": "^2.6.0-subduction.34",
|
|
57
|
+
"@automerge/automerge-repo-network-broadcastchannel": "^2.6.0-subduction.34",
|
|
58
|
+
"@automerge/automerge-repo-storage-indexeddb": "^2.6.0-subduction.34",
|
|
41
59
|
"prism-esm": "^1.29.0-fix.6",
|
|
42
60
|
"temml": "^0.13.3"
|
|
43
61
|
},
|
|
@@ -45,6 +63,7 @@
|
|
|
45
63
|
"@biomejs/biome": "2.4.15",
|
|
46
64
|
"@preact/signals-core": "^1.14.2",
|
|
47
65
|
"@types/node": "^22.0.0",
|
|
66
|
+
"@typhonjs-typedoc/typedoc-theme-dmt": "^0.4.0",
|
|
48
67
|
"alien-signals": "^3.2.1",
|
|
49
68
|
"fast-check": "^4.8.0",
|
|
50
69
|
"gray-matter": "^4.0.3",
|
|
@@ -57,6 +76,8 @@
|
|
|
57
76
|
"typescript": "^6.0.3",
|
|
58
77
|
"vite": "^7.3.3",
|
|
59
78
|
"vite-node": "^5.3.0",
|
|
79
|
+
"vite-plugin-top-level-await": "^1.6.0",
|
|
80
|
+
"vite-plugin-wasm": "^3.6.0",
|
|
60
81
|
"vitest": "^4.1.7"
|
|
61
82
|
},
|
|
62
83
|
"keywords": [
|
package/dist/coll.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { type Cell, type Read, type Writable } from "./core/index.js";
|
|
2
|
-
/** Accessor for an element's writable field cell. Forward reads `.value`;
|
|
3
|
-
* the backward pass writes it. */
|
|
4
|
-
export type Accessor<E, V> = (e: E) => Writable<Cell<V>>;
|
|
5
|
-
/** A forward test over an element's fields, optionally assertable —
|
|
6
|
-
* `assert(e)` makes the test pass by writing fields. */
|
|
7
|
-
export interface FieldPred<E> {
|
|
8
|
-
(e: E): boolean;
|
|
9
|
-
assert?: (e: E) => void;
|
|
10
|
-
}
|
|
11
|
-
export interface Group<K, E> {
|
|
12
|
-
key: K;
|
|
13
|
-
items: readonly E[];
|
|
14
|
-
}
|
|
15
|
-
export interface GroupOpts<E, K> {
|
|
16
|
-
/** Fixed key order; seeds empty buckets and pins column order. */
|
|
17
|
-
order?: readonly K[];
|
|
18
|
-
/** Order field within each group; enables `move(e, key, index)`. */
|
|
19
|
-
sort?: Accessor<E, number>;
|
|
20
|
-
}
|
|
21
|
-
/** `field === value`, assertable by writing the field. */
|
|
22
|
-
export declare function is<E, V>(field: Accessor<E, V>, value: V): FieldPred<E>;
|
|
23
|
-
/** Conjunction; asserts every clause. */
|
|
24
|
-
export declare function allPass<E>(...preds: readonly FieldPred<E>[]): FieldPred<E>;
|
|
25
|
-
/** Read-only projection with chainable structural lenses. */
|
|
26
|
-
export declare class View<E> {
|
|
27
|
-
protected readonly list: Read<readonly E[]>;
|
|
28
|
-
readonly key: (e: E) => unknown;
|
|
29
|
-
protected readonly parent: View<E> | null;
|
|
30
|
-
protected constructor(list: Read<readonly E[]>, key: (e: E) => unknown, parent: View<E> | null);
|
|
31
|
-
/** Current members; tracked when read in an effect/derive. */
|
|
32
|
-
get items(): readonly E[];
|
|
33
|
-
/** The source collection at the head of the chain. */
|
|
34
|
-
get root(): Coll<E>;
|
|
35
|
-
/** Make `e` appear in this view: satisfy own constraint, recursively up. */
|
|
36
|
-
assertContains(e: E): void;
|
|
37
|
-
protected assertSelf(_e: E): void;
|
|
38
|
-
filter(pred: FieldPred<E>): View<E>;
|
|
39
|
-
sortBy(field: Accessor<E, number>): SortView<E>;
|
|
40
|
-
groupBy<K>(field: Accessor<E, K>, opts?: GroupOpts<E, K>): GroupView<K, E>;
|
|
41
|
-
map<F>(f: (e: E) => F): Read<readonly F[]>;
|
|
42
|
-
/** Remove `e` from the source. */
|
|
43
|
-
remove(e: E): void;
|
|
44
|
-
}
|
|
45
|
-
/** A writable source collection. */
|
|
46
|
-
export declare class Coll<E> extends View<E> {
|
|
47
|
-
#private;
|
|
48
|
-
constructor(items: readonly E[], key: (e: E) => unknown);
|
|
49
|
-
assertContains(e: E): void;
|
|
50
|
-
insert(e: E, at?: number): void;
|
|
51
|
-
removeFromSource(e: E): void;
|
|
52
|
-
}
|
|
53
|
-
/** Sorted view. `move` writes the order field between the drop neighbours. */
|
|
54
|
-
export declare class SortView<E> extends View<E> {
|
|
55
|
-
#private;
|
|
56
|
-
constructor(parent: View<E>, field: Accessor<E, number>);
|
|
57
|
-
move(e: E, to: number): void;
|
|
58
|
-
}
|
|
59
|
-
/** Grouped view. `move`/`insert` write the group field (and, with a `sort`
|
|
60
|
-
* field, the order field). Backward composition runs the parent chain. */
|
|
61
|
-
export declare class GroupView<K, E> {
|
|
62
|
-
#private;
|
|
63
|
-
readonly groups: Read<readonly Group<K, E>[]>;
|
|
64
|
-
constructor(parent: View<E>, field: Accessor<E, K>, opts?: GroupOpts<E, K>);
|
|
65
|
-
get value(): readonly Group<K, E>[];
|
|
66
|
-
map<F>(f: (g: Group<K, E>) => F): Read<readonly F[]>;
|
|
67
|
-
/** Place `e` in group `toKey` at `index`. Inserts it into the source if
|
|
68
|
-
* it isn't there yet, asserts every upstream filter, then writes the
|
|
69
|
-
* group key and order field — all in one batch. */
|
|
70
|
-
move(e: E, toKey: K, index?: number): void;
|
|
71
|
-
insert(e: E, toKey: K, index?: number): void;
|
|
72
|
-
remove(e: E): void;
|
|
73
|
-
}
|
|
74
|
-
export declare function coll<E>(items: readonly E[], key: (e: E) => unknown): Coll<E>;
|
package/dist/coll.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
// coll.ts — a keyed, ordered, *bidirectional* collection.
|
|
2
|
-
//
|
|
3
|
-
// The structural sibling of `tree.ts`. A `Coll<E>` holds stable element
|
|
4
|
-
// handles (records of cells), and each projection — `filter` / `sortBy` /
|
|
5
|
-
// `groupBy` — is a WRITABLE structural lens. You edit the *view* and the
|
|
6
|
-
// backward pass writes the elements' own fields:
|
|
7
|
-
//
|
|
8
|
-
// sortBy(rank) → move(e, i) writes `rank` (position is a field)
|
|
9
|
-
// groupBy(key) → move(e, k, i) writes `key` (membership is a field)
|
|
10
|
-
// filter(pred) → insert/assert writes whatever pred demands
|
|
11
|
-
//
|
|
12
|
-
// Because position / group / visibility are the elements' own writable
|
|
13
|
-
// cells, these lenses are COMPLEMENT-FREE — the "complement" lives in the
|
|
14
|
-
// field cells, not the lens (contrast `Seq<T>`, where order is imposed and
|
|
15
|
-
// must be remembered). One `move`/`insert` composes the backward passes up
|
|
16
|
-
// the chain in a single `batch`: drop into a filtered + grouped + sorted
|
|
17
|
-
// view and the filter's predicate, the group key, and the rank all get set
|
|
18
|
-
// at once.
|
|
19
|
-
//
|
|
20
|
-
// Two change classes, mirroring the engine's two levels:
|
|
21
|
-
// • value change — a field cell changes; forward derivations re-flow.
|
|
22
|
-
// • structural edit — insert/remove/move; a discrete batched transition.
|
|
23
|
-
// No lens edges are built at runtime, so acyclicity holds as ever.
|
|
24
|
-
import { batch, cell, derive } from "./core/index.js";
|
|
25
|
-
/** `field === value`, assertable by writing the field. */
|
|
26
|
-
export function is(field, value) {
|
|
27
|
-
const p = ((e) => field(e).value === value);
|
|
28
|
-
p.assert = (e) => {
|
|
29
|
-
field(e).value = value;
|
|
30
|
-
};
|
|
31
|
-
return p;
|
|
32
|
-
}
|
|
33
|
-
/** Conjunction; asserts every clause. */
|
|
34
|
-
export function allPass(...preds) {
|
|
35
|
-
const p = ((e) => preds.every(q => q(e)));
|
|
36
|
-
p.assert = (e) => {
|
|
37
|
-
for (const q of preds)
|
|
38
|
-
q.assert?.(e);
|
|
39
|
-
};
|
|
40
|
-
return p;
|
|
41
|
-
}
|
|
42
|
-
/** Read-only projection with chainable structural lenses. */
|
|
43
|
-
export class View {
|
|
44
|
-
list;
|
|
45
|
-
key;
|
|
46
|
-
parent;
|
|
47
|
-
constructor(list, key, parent) {
|
|
48
|
-
this.list = list;
|
|
49
|
-
this.key = key;
|
|
50
|
-
this.parent = parent;
|
|
51
|
-
}
|
|
52
|
-
/** Current members; tracked when read in an effect/derive. */
|
|
53
|
-
get items() {
|
|
54
|
-
return this.list.value;
|
|
55
|
-
}
|
|
56
|
-
/** The source collection at the head of the chain. */
|
|
57
|
-
get root() {
|
|
58
|
-
let v = this;
|
|
59
|
-
while (v.parent)
|
|
60
|
-
v = v.parent;
|
|
61
|
-
return v;
|
|
62
|
-
}
|
|
63
|
-
/** Make `e` appear in this view: satisfy own constraint, recursively up. */
|
|
64
|
-
assertContains(e) {
|
|
65
|
-
this.parent?.assertContains(e);
|
|
66
|
-
this.assertSelf(e);
|
|
67
|
-
}
|
|
68
|
-
assertSelf(_e) { }
|
|
69
|
-
filter(pred) {
|
|
70
|
-
return new FilterView(this, pred);
|
|
71
|
-
}
|
|
72
|
-
sortBy(field) {
|
|
73
|
-
return new SortView(this, field);
|
|
74
|
-
}
|
|
75
|
-
groupBy(field, opts) {
|
|
76
|
-
return new GroupView(this, field, opts);
|
|
77
|
-
}
|
|
78
|
-
map(f) {
|
|
79
|
-
return derive(() => this.list.value.map(f));
|
|
80
|
-
}
|
|
81
|
-
/** Remove `e` from the source. */
|
|
82
|
-
remove(e) {
|
|
83
|
-
this.root.removeFromSource(e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/** A writable source collection. */
|
|
87
|
-
export class Coll extends View {
|
|
88
|
-
#src;
|
|
89
|
-
constructor(items, key) {
|
|
90
|
-
const src = cell(items);
|
|
91
|
-
super(src, key, null);
|
|
92
|
-
this.#src = src;
|
|
93
|
-
}
|
|
94
|
-
assertContains(e) {
|
|
95
|
-
if (!this.#src.value.includes(e))
|
|
96
|
-
this.insert(e);
|
|
97
|
-
}
|
|
98
|
-
insert(e, at) {
|
|
99
|
-
const arr = [...this.#src.value];
|
|
100
|
-
if (at == null)
|
|
101
|
-
arr.push(e);
|
|
102
|
-
else
|
|
103
|
-
arr.splice(at, 0, e);
|
|
104
|
-
this.#src.value = arr;
|
|
105
|
-
}
|
|
106
|
-
removeFromSource(e) {
|
|
107
|
-
this.#src.value = this.#src.value.filter(x => x !== e);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
class FilterView extends View {
|
|
111
|
-
#pred;
|
|
112
|
-
constructor(parent, pred) {
|
|
113
|
-
super(derive(() => parent.items.filter(pred)), parent.key, parent);
|
|
114
|
-
this.#pred = pred;
|
|
115
|
-
}
|
|
116
|
-
assertSelf(e) {
|
|
117
|
-
this.#pred.assert?.(e);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
/** Sorted view. `move` writes the order field between the drop neighbours. */
|
|
121
|
-
export class SortView extends View {
|
|
122
|
-
#field;
|
|
123
|
-
constructor(parent, field) {
|
|
124
|
-
super(derive(() => [...parent.items].sort((a, b) => field(a).value - field(b).value)), parent.key, parent);
|
|
125
|
-
this.#field = field;
|
|
126
|
-
}
|
|
127
|
-
move(e, to) {
|
|
128
|
-
const others = this.items.filter(x => x !== e);
|
|
129
|
-
batch(() => {
|
|
130
|
-
this.assertContains(e);
|
|
131
|
-
this.#field(e).value = between(rankAt(others, this.#field, to - 1), rankAt(others, this.#field, to));
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/** Grouped view. `move`/`insert` write the group field (and, with a `sort`
|
|
136
|
-
* field, the order field). Backward composition runs the parent chain. */
|
|
137
|
-
export class GroupView {
|
|
138
|
-
groups;
|
|
139
|
-
#parent;
|
|
140
|
-
#field;
|
|
141
|
-
#order;
|
|
142
|
-
#sort;
|
|
143
|
-
constructor(parent, field, opts = {}) {
|
|
144
|
-
this.#parent = parent;
|
|
145
|
-
this.#field = field;
|
|
146
|
-
this.#order = opts.order ?? [];
|
|
147
|
-
this.#sort = opts.sort;
|
|
148
|
-
this.groups = derive(() => {
|
|
149
|
-
const sort = this.#sort;
|
|
150
|
-
const items = sort
|
|
151
|
-
? [...parent.items].sort((a, b) => sort(a).value - sort(b).value)
|
|
152
|
-
: parent.items;
|
|
153
|
-
return groupItems(items, e => field(e).value, this.#order);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
get value() {
|
|
157
|
-
return this.groups.value;
|
|
158
|
-
}
|
|
159
|
-
map(f) {
|
|
160
|
-
return derive(() => this.value.map(f));
|
|
161
|
-
}
|
|
162
|
-
/** Place `e` in group `toKey` at `index`. Inserts it into the source if
|
|
163
|
-
* it isn't there yet, asserts every upstream filter, then writes the
|
|
164
|
-
* group key and order field — all in one batch. */
|
|
165
|
-
move(e, toKey, index) {
|
|
166
|
-
const target = (this.value.find(g => Object.is(g.key, toKey))?.items ?? []).filter(x => x !== e);
|
|
167
|
-
const sort = this.#sort;
|
|
168
|
-
batch(() => {
|
|
169
|
-
this.#parent.assertContains(e);
|
|
170
|
-
this.#field(e).value = toKey;
|
|
171
|
-
if (sort && index != null)
|
|
172
|
-
sort(e).value = between(rankAt(target, sort, index - 1), rankAt(target, sort, index));
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
insert(e, toKey, index) {
|
|
176
|
-
this.move(e, toKey, index);
|
|
177
|
-
}
|
|
178
|
-
remove(e) {
|
|
179
|
-
this.#parent.remove(e);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function groupItems(items, keyOf, order) {
|
|
183
|
-
const buckets = new Map();
|
|
184
|
-
for (const k of order)
|
|
185
|
-
buckets.set(k, []);
|
|
186
|
-
for (const e of items) {
|
|
187
|
-
const k = keyOf(e);
|
|
188
|
-
const arr = buckets.get(k);
|
|
189
|
-
if (arr)
|
|
190
|
-
arr.push(e);
|
|
191
|
-
else
|
|
192
|
-
buckets.set(k, [e]);
|
|
193
|
-
}
|
|
194
|
-
return [...buckets].map(([key, items]) => ({ key, items }));
|
|
195
|
-
}
|
|
196
|
-
function rankAt(arr, field, i) {
|
|
197
|
-
return i >= 0 && i < arr.length ? field(arr[i]).value : undefined;
|
|
198
|
-
}
|
|
199
|
-
function between(a, b) {
|
|
200
|
-
if (a != null && b != null)
|
|
201
|
-
return (a + b) / 2;
|
|
202
|
-
if (a != null)
|
|
203
|
-
return a + 1;
|
|
204
|
-
if (b != null)
|
|
205
|
-
return b - 1;
|
|
206
|
-
return 0;
|
|
207
|
-
}
|
|
208
|
-
export function coll(items, key) {
|
|
209
|
-
return new Coll(items, key);
|
|
210
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { type Cell, Num, type Read, type Traits, Vec, type Writable } from "../index.js";
|
|
2
|
-
type V = {
|
|
3
|
-
x: number;
|
|
4
|
-
y: number;
|
|
5
|
-
};
|
|
6
|
-
/** Writable centroid; on write, translates every point by the delta.
|
|
7
|
-
* The Vec-specific group-action reading of `mean`. */
|
|
8
|
-
export declare function rigidTranslate(points: readonly Writable<Vec>[]): Writable<Vec>;
|
|
9
|
-
/** Writable angle from `pivot` to `points[0]`; write rotates every input
|
|
10
|
-
* about `pivot` by (target − current) via its `Pivotal` trait.
|
|
11
|
-
*
|
|
12
|
-
* Trait-generic: Vec rotates position; Pose rotates position AND
|
|
13
|
-
* orientation. Rotation-about-pivot fixes the pivot and preserves radial
|
|
14
|
-
* distances, so scale-about-pivot reads unchanged. `pivot` is reactive
|
|
15
|
-
* (re-read per write); pass `rigidTranslate(points)` for rotation about
|
|
16
|
-
* the cluster's own centroid. */
|
|
17
|
-
export declare function rotateAbout<T extends {
|
|
18
|
-
x: number;
|
|
19
|
-
y: number;
|
|
20
|
-
}>(points: readonly Writable<Traits<T, "pivotal"> & Cell<T>>[], pivot: Read<V>): Writable<Num>;
|
|
21
|
-
/** Writable radial distance from pivot to `points[0]`; write scales every
|
|
22
|
-
* input radially about `pivot` (negative target reflects). Exact
|
|
23
|
-
* cross-channel invariance with `rotateAbout`.
|
|
24
|
-
*
|
|
25
|
-
* Complement carries per-point offsets from the pivot at the last
|
|
26
|
-
* non-degenerate state, so a collapse onto the pivot (radius ≈ 0)
|
|
27
|
-
* reinflates from the stored shape. Pose `theta` survives the round-trip
|
|
28
|
-
* (only spatial offset is stored). */
|
|
29
|
-
export declare function scaleAbout<T extends {
|
|
30
|
-
x: number;
|
|
31
|
-
y: number;
|
|
32
|
-
}>(points: readonly Writable<Traits<T, "pivotal"> & Cell<T>>[], pivot: Read<V>): Writable<Num>;
|
|
33
|
-
/** Per-axis scale about a pivot. Vec-specific (Pivotal has no per-axis
|
|
34
|
-
* method yet). Complement carries per-point per-axis fractions of
|
|
35
|
-
* point 0's offset, so a per-axis collapse is recoverable (cf.
|
|
36
|
-
* `bboxLens.size`). */
|
|
37
|
-
export declare function scaleAboutXY(points: readonly Writable<Vec>[], pivot: Read<V>): Writable<Vec>;
|
|
38
|
-
export declare function bestFitLine(points: readonly Writable<Vec>[]): {
|
|
39
|
-
point: Writable<Vec>;
|
|
40
|
-
direction: Writable<Num>;
|
|
41
|
-
};
|
|
42
|
-
export declare function bestFitCircle(points: readonly Writable<Vec>[]): {
|
|
43
|
-
center: Writable<Vec>;
|
|
44
|
-
radius: Writable<Num>;
|
|
45
|
-
};
|
|
46
|
-
export declare function pca(points: readonly Writable<Vec>[]): {
|
|
47
|
-
mean: Writable<Vec>;
|
|
48
|
-
rotation: Writable<Num>;
|
|
49
|
-
majorLength: Writable<Num>;
|
|
50
|
-
minorLength: Writable<Num>;
|
|
51
|
-
};
|
|
52
|
-
/** Writable total over K parts; write scales all parts proportionally,
|
|
53
|
-
* preserving their ratios. A `remember` anchored at zero with a signed
|
|
54
|
-
* sum feature: a collapse to zero reinflates the stored ratios, seeded
|
|
55
|
-
* uniform so an all-zero start splits evenly. */
|
|
56
|
-
export declare function total(parts: readonly Writable<Num>[]): Writable<Num>;
|
|
57
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Num, Vec, type Writable } from "../index.js";
|
|
2
|
-
export declare function meanDiff(a: Num, b: Num): {
|
|
3
|
-
mean: Writable<Num>;
|
|
4
|
-
diff: Writable<Num>;
|
|
5
|
-
};
|
|
6
|
-
export declare function procrustes(points: readonly Writable<Vec>[]): {
|
|
7
|
-
centroid: Writable<Vec>;
|
|
8
|
-
rotation: Writable<Num>;
|
|
9
|
-
scale: Writable<Num>;
|
|
10
|
-
};
|
|
11
|
-
export declare function bbox(points: readonly Writable<Vec>[]): {
|
|
12
|
-
center: Writable<Vec>;
|
|
13
|
-
size: Writable<Vec>;
|
|
14
|
-
};
|