@poe2-toolkit/tree-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.
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/dist/geometry/centre.d.ts +17 -0
- package/dist/geometry/centre.d.ts.map +1 -0
- package/dist/geometry/centre.js +59 -0
- package/dist/geometry/centre.js.map +1 -0
- package/dist/geometry/framing.d.ts +27 -0
- package/dist/geometry/framing.d.ts.map +1 -0
- package/dist/geometry/framing.js +101 -0
- package/dist/geometry/framing.js.map +1 -0
- package/dist/geometry/orbit.d.ts +23 -0
- package/dist/geometry/orbit.d.ts.map +1 -0
- package/dist/geometry/orbit.js +28 -0
- package/dist/geometry/orbit.js.map +1 -0
- package/dist/geometry/project.d.ts +20 -0
- package/dist/geometry/project.d.ts.map +1 -0
- package/dist/geometry/project.js +149 -0
- package/dist/geometry/project.js.map +1 -0
- package/dist/ggg/normalize.d.ts +109 -0
- package/dist/ggg/normalize.d.ts.map +1 -0
- package/dist/ggg/normalize.js +279 -0
- package/dist/ggg/normalize.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/scene/allocate.d.ts +61 -0
- package/dist/scene/allocate.d.ts.map +1 -0
- package/dist/scene/allocate.js +239 -0
- package/dist/scene/allocate.js.map +1 -0
- package/dist/scene/buildScene.d.ts +21 -0
- package/dist/scene/buildScene.d.ts.map +1 -0
- package/dist/scene/buildScene.js +212 -0
- package/dist/scene/buildScene.js.map +1 -0
- package/dist/scene/connections.d.ts +13 -0
- package/dist/scene/connections.d.ts.map +1 -0
- package/dist/scene/connections.js +75 -0
- package/dist/scene/connections.js.map +1 -0
- package/dist/scene/nodeSize.d.ts +26 -0
- package/dist/scene/nodeSize.d.ts.map +1 -0
- package/dist/scene/nodeSize.js +72 -0
- package/dist/scene/nodeSize.js.map +1 -0
- package/dist/types.d.ts +426 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalise GGG's official skill-tree export (`data.json`) into the engine's
|
|
3
|
+
* {@link TreeData}.
|
|
4
|
+
*
|
|
5
|
+
* This is the only place that knows GGG's field names and quirks. Everything
|
|
6
|
+
* downstream works against the clean {@link TreeData} contract. The transform is
|
|
7
|
+
* deliberately tolerant: optional fields come and go across patches and missing
|
|
8
|
+
* data should never throw.
|
|
9
|
+
*
|
|
10
|
+
* The GGG quirks handled here:
|
|
11
|
+
* - node positions are **baked** (`node.x`/`node.y`); the export ships none of
|
|
12
|
+
* the orbit-radius constants needed to recompute them, so they are read as-is;
|
|
13
|
+
* - edges are split across `in` + `out` id lists, merged into one set here;
|
|
14
|
+
* - arc geometry lives in a separate top-level `edges` table, not on the node;
|
|
15
|
+
* - attribute choices live in a global `skillOverrides` table, not per node;
|
|
16
|
+
* - ascendancy ids are internal (`Ranger1`) and mapped to display names here;
|
|
17
|
+
* - centre/ring dimensions are absent — filled from stable atlas frame sizes.
|
|
18
|
+
*/
|
|
19
|
+
// --- centre/ring sizes (GGG atlas frame dimensions, stable across classes) --
|
|
20
|
+
/** Class portrait atlas frame (`background-<class>` Class0..3), 1500². */
|
|
21
|
+
const PORTRAIT_SIZE = 1500;
|
|
22
|
+
/** Ornate + rotating ring atlas frames (`group-background`), 2000². */
|
|
23
|
+
const RING_SIZE = 2000;
|
|
24
|
+
/** Ascendancy disc portrait, 1500². */
|
|
25
|
+
const ASCENDANCY_DISC_SIZE = 1500;
|
|
26
|
+
/** Central opening radius — the game's `PSSCentreInnerRadius` constant. */
|
|
27
|
+
const CENTRE_INNER_RADIUS = 130;
|
|
28
|
+
/**
|
|
29
|
+
* @param raw parsed GGG `data.json`
|
|
30
|
+
* @param version tree/patch version (e.g. "0_5") — GGG does not store it inside
|
|
31
|
+
* the file, so it is supplied here
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeGggTree(raw, version) {
|
|
34
|
+
const ascendancyNames = mapAscendancyNames(raw.classes);
|
|
35
|
+
const attributeOptions = mapAttributeOptions(raw.skillOverrides);
|
|
36
|
+
const classNames = raw.classes.map((cls) => cls.name);
|
|
37
|
+
const edgeArcs = mapEdgeArcs(raw.edges);
|
|
38
|
+
const nodes = normalizeNodes(raw, ascendancyNames, attributeOptions, classNames, edgeArcs);
|
|
39
|
+
const ascendancyStarts = mapAscendancyStarts(raw.nodes);
|
|
40
|
+
const startNodeByClass = mapClassStartNodes(raw.nodes);
|
|
41
|
+
return {
|
|
42
|
+
version,
|
|
43
|
+
constants: { centreInnerRadius: CENTRE_INNER_RADIUS },
|
|
44
|
+
groups: normalizeGroups(raw.groups),
|
|
45
|
+
nodes,
|
|
46
|
+
classes: raw.classes.map((cls, index) => normalizeClass(cls, index, startNodeByClass, ascendancyStarts)),
|
|
47
|
+
jewelSlots: (raw.jewelSlots ?? []).map(Number),
|
|
48
|
+
bounds: { minX: raw.min_x, minY: raw.min_y, maxX: raw.max_x, maxY: raw.max_y },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** GGG `groups` is keyed by group id (1-based numeric strings) with no holes. */
|
|
52
|
+
function normalizeGroups(raw) {
|
|
53
|
+
const groups = {};
|
|
54
|
+
for (const [key, group] of Object.entries(raw)) {
|
|
55
|
+
groups[Number(key)] = {
|
|
56
|
+
x: group.x,
|
|
57
|
+
y: group.y,
|
|
58
|
+
orbits: group.orbits ?? [],
|
|
59
|
+
nodes: (group.nodes ?? []).map(Number),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return groups;
|
|
63
|
+
}
|
|
64
|
+
/** Undirected key for an edge between two skill ids. */
|
|
65
|
+
function edgeKey(a, b) {
|
|
66
|
+
return a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* GGG's top-level `edges` table marks arcs and gives each arc's world centre
|
|
70
|
+
* directly (`orbitX`/`orbitY`). Map it by undirected edge key; only entries with
|
|
71
|
+
* a centre are kept (arcs without one fall back to the geometric rule).
|
|
72
|
+
*/
|
|
73
|
+
function mapEdgeArcs(edges) {
|
|
74
|
+
const arcs = new Map();
|
|
75
|
+
for (const edge of edges ?? []) {
|
|
76
|
+
if (edge.orbitX !== undefined && edge.orbitY !== undefined) {
|
|
77
|
+
arcs.set(edgeKey(Number(edge.from), Number(edge.to)), { x: edge.orbitX, y: edge.orbitY });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return arcs;
|
|
81
|
+
}
|
|
82
|
+
function normalizeNodes(raw, ascendancyNames, attributeOptions, classNames, edgeArcs) {
|
|
83
|
+
const nodes = {};
|
|
84
|
+
for (const [key, node] of Object.entries(raw.nodes)) {
|
|
85
|
+
const skill = node.skill ?? Number(key);
|
|
86
|
+
nodes[skill] = normalizeNode(skill, node, ascendancyNames, attributeOptions, classNames, edgeArcs);
|
|
87
|
+
}
|
|
88
|
+
return nodes;
|
|
89
|
+
}
|
|
90
|
+
function normalizeNode(skill, raw, ascendancyNames, attributeOptions, classNames, edgeArcs) {
|
|
91
|
+
const name = raw.name ?? '';
|
|
92
|
+
// GGG ships edges split across `in` and `out`; merge + dedupe into one set.
|
|
93
|
+
const neighbours = new Set([
|
|
94
|
+
...(raw.in ?? []).map(Number),
|
|
95
|
+
...(raw.out ?? []).map(Number),
|
|
96
|
+
]);
|
|
97
|
+
const ascendancyName = raw.ascendancyId
|
|
98
|
+
? ascendancyNames.get(raw.ascendancyId) ?? raw.ascendancyId
|
|
99
|
+
: undefined;
|
|
100
|
+
return {
|
|
101
|
+
skill,
|
|
102
|
+
group: raw.group ?? -1,
|
|
103
|
+
orbit: raw.orbit ?? 0,
|
|
104
|
+
orbitIndex: raw.orbitIndex ?? 0,
|
|
105
|
+
x: raw.x ?? 0,
|
|
106
|
+
y: raw.y ?? 0,
|
|
107
|
+
connections: [...neighbours].map((id) => {
|
|
108
|
+
const arcCentre = edgeArcs.get(edgeKey(skill, id));
|
|
109
|
+
return arcCentre ? { id, arcCentre } : { id };
|
|
110
|
+
}),
|
|
111
|
+
name,
|
|
112
|
+
icon: raw.icon ?? '',
|
|
113
|
+
stats: cleanStats(raw.stats),
|
|
114
|
+
// Kind flags and metadata are added only when present, so absence stays
|
|
115
|
+
// absent (exactOptionalPropertyTypes-friendly).
|
|
116
|
+
...(raw.isNotable ? { isNotable: true } : {}),
|
|
117
|
+
...(raw.isKeystone ? { isKeystone: true } : {}),
|
|
118
|
+
...(raw.isJewelSocket ? { isJewelSocket: true } : {}),
|
|
119
|
+
// GGG has no `noRadius`; the hidden Sinister sockets are identified by name.
|
|
120
|
+
...(raw.isJewelSocket && name.includes('SinisterJewelSocket') ? { noRadius: true } : {}),
|
|
121
|
+
...(raw.isMastery ? { isMastery: true } : {}),
|
|
122
|
+
...(raw.isAscendancyStart ? { isAscendancyStart: true } : {}),
|
|
123
|
+
...(raw.isGenericAttribute ? { isAttribute: true } : {}),
|
|
124
|
+
...(raw.activeEffectImage !== undefined ? { activeEffectImage: raw.activeEffectImage } : {}),
|
|
125
|
+
...(ascendancyName !== undefined ? { ascendancyName } : {}),
|
|
126
|
+
...(raw.flavourText !== undefined ? { flavourText: joinFlavour(raw.flavourText) } : {}),
|
|
127
|
+
...(raw.recipe !== undefined ? { recipe: raw.recipe } : {}),
|
|
128
|
+
// Generic +attribute nodes share the three global Str/Dex/Int choices.
|
|
129
|
+
...(raw.isGenericAttribute && attributeOptions.length > 0 ? { options: attributeOptions } : {}),
|
|
130
|
+
...(raw.unlockConstraint ? { conditional: true } : {}),
|
|
131
|
+
...(raw.unlockConstraint?.ascendancy !== undefined ? { unlockAscendancy: raw.unlockConstraint.ascendancy } : {}),
|
|
132
|
+
...(raw.classStartIndex && raw.classStartIndex.length > 0
|
|
133
|
+
? { classesStart: raw.classStartIndex.map((i) => classNames[i] ?? String(i)) }
|
|
134
|
+
: {}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** GGG ships keystone flavour text as an array of lines; join into one string. */
|
|
138
|
+
function joinFlavour(flavour) {
|
|
139
|
+
return Array.isArray(flavour) ? flavour.join('\n') : flavour;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* GGG stat lines carry inline reference tags: `[ref|display]` renders as
|
|
143
|
+
* `display`, `[ref]` as `ref` (e.g. `[Critical|Critical Hit Chance]` ->
|
|
144
|
+
* "Critical Hit Chance", `[Curse|Curses]` -> "Curses", `[Shock]` -> "Shock").
|
|
145
|
+
* The `display` side is the player-facing text, including the correct plural —
|
|
146
|
+
* strip the tags down to it.
|
|
147
|
+
*/
|
|
148
|
+
function cleanStatText(line) {
|
|
149
|
+
return line.replace(/\[(?:[^|\]]*\|)?([^\]]+)\]/g, '$1');
|
|
150
|
+
}
|
|
151
|
+
/** Apply {@link cleanStatText} across a list of stat lines. */
|
|
152
|
+
function cleanStats(stats) {
|
|
153
|
+
return (stats ?? []).map(cleanStatText);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Map GGG's internal ascendancy ids ("Ranger1") to display names ("Deadeye").
|
|
157
|
+
* The display name is what a build's chosen ascendancy is keyed by, so node and
|
|
158
|
+
* allocation agree. Unreleased ascendancies carry no name and are skipped.
|
|
159
|
+
*/
|
|
160
|
+
function mapAscendancyNames(classes) {
|
|
161
|
+
const names = new Map();
|
|
162
|
+
for (const cls of classes) {
|
|
163
|
+
for (const asc of cls.ascendancies ?? []) {
|
|
164
|
+
if (asc.name) {
|
|
165
|
+
names.set(asc.id, asc.name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return names;
|
|
170
|
+
}
|
|
171
|
+
/** Preferred display order of attribute choices (GGG keys them by numeric id). */
|
|
172
|
+
const ATTRIBUTE_ORDER = ['Strength', 'Dexterity', 'Intelligence'];
|
|
173
|
+
/**
|
|
174
|
+
* The three shared attribute choices (Str / Dex / Int), read from GGG's global
|
|
175
|
+
* `skillOverrides` table. That table also holds unrelated overrides (Pathfinder
|
|
176
|
+
* alternates, per-class node swaps), so it is filtered to the `generic_attribute`
|
|
177
|
+
* entries. Every generic-attribute node reuses these, ordered Str/Dex/Int.
|
|
178
|
+
*/
|
|
179
|
+
function mapAttributeOptions(overrides) {
|
|
180
|
+
if (!overrides) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
return Object.entries(overrides)
|
|
184
|
+
.filter(([, override]) => override.id?.startsWith('generic_attribute'))
|
|
185
|
+
.map(([key, override]) => ({
|
|
186
|
+
id: override.skill ?? Number(key),
|
|
187
|
+
name: override.name ?? '',
|
|
188
|
+
stats: cleanStats(override.stats),
|
|
189
|
+
icon: override.icon ?? '',
|
|
190
|
+
}))
|
|
191
|
+
.sort((a, b) => orderIndex(a.name) - orderIndex(b.name));
|
|
192
|
+
}
|
|
193
|
+
function orderIndex(name) {
|
|
194
|
+
const index = ATTRIBUTE_ORDER.indexOf(name);
|
|
195
|
+
return index === -1 ? ATTRIBUTE_ORDER.length : index;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The world position of each ascendancy's start node (the diamond), keyed by
|
|
199
|
+
* GGG ascendancy id. GGG bakes every ascendancy at a far-flung cluster; the
|
|
200
|
+
* start node is the anchor the renderer relocates onto the hub. Ascendancies
|
|
201
|
+
* with no nodes (e.g. unreleased "Abyssal Lich") are simply absent.
|
|
202
|
+
*/
|
|
203
|
+
function mapAscendancyStarts(nodes) {
|
|
204
|
+
const starts = new Map();
|
|
205
|
+
for (const node of Object.values(nodes)) {
|
|
206
|
+
if (node.isAscendancyStart && node.ascendancyId && node.x !== undefined && node.y !== undefined) {
|
|
207
|
+
starts.set(node.ascendancyId, { x: node.x, y: node.y });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return starts;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Build a class-index -> start-node-skill-id map. A class-start node lists the
|
|
214
|
+
* class indices that start there in `classStartIndex` (often two); the same node
|
|
215
|
+
* can serve several classes.
|
|
216
|
+
*/
|
|
217
|
+
function mapClassStartNodes(nodes) {
|
|
218
|
+
const byClass = new Map();
|
|
219
|
+
for (const [key, node] of Object.entries(nodes)) {
|
|
220
|
+
const skill = node.skill ?? Number(key);
|
|
221
|
+
for (const classIndex of node.classStartIndex ?? []) {
|
|
222
|
+
if (!byClass.has(classIndex)) {
|
|
223
|
+
byClass.set(classIndex, skill);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return byClass;
|
|
228
|
+
}
|
|
229
|
+
function normalizeClass(raw, index, startNodeByClass, ascendancyStarts) {
|
|
230
|
+
return {
|
|
231
|
+
name: raw.name,
|
|
232
|
+
id: index,
|
|
233
|
+
baseStr: raw.base_str,
|
|
234
|
+
baseDex: raw.base_dex,
|
|
235
|
+
baseInt: raw.base_int,
|
|
236
|
+
startNode: startNodeByClass.get(index) ?? -1,
|
|
237
|
+
centre: {
|
|
238
|
+
image: `Classes${raw.name}`,
|
|
239
|
+
x: 0,
|
|
240
|
+
y: 0,
|
|
241
|
+
art: { width: PORTRAIT_SIZE, height: PORTRAIT_SIZE },
|
|
242
|
+
active: { width: RING_SIZE, height: RING_SIZE },
|
|
243
|
+
frame: { width: RING_SIZE, height: RING_SIZE },
|
|
244
|
+
},
|
|
245
|
+
// Keep only released ascendancies — ones with a name AND a start node (i.e.
|
|
246
|
+
// actual nodes; e.g. "Abyssal Lich" is named but has none yet, so dropped).
|
|
247
|
+
ascendancies: (raw.ascendancies ?? [])
|
|
248
|
+
.filter((asc) => Boolean(asc.name) && ascendancyStarts.has(asc.id))
|
|
249
|
+
.map((asc) => normalizeAscendancy(asc, ascendancyStarts)),
|
|
250
|
+
...(raw.overridePairs ? { overridePairs: normalizeOverrides(raw.overridePairs) } : {}),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** Normalise the override map's string keys to numeric skill ids. */
|
|
254
|
+
function normalizeOverrides(raw) {
|
|
255
|
+
const out = {};
|
|
256
|
+
for (const [base, target] of Object.entries(raw)) {
|
|
257
|
+
out[Number(base)] = target;
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
function normalizeAscendancy(raw, ascendancyStarts) {
|
|
262
|
+
const start = ascendancyStarts.get(raw.id) ?? { x: 0, y: 0 };
|
|
263
|
+
// The renderer relocates each disc node by `centre − worldAnchor`. GGG's
|
|
264
|
+
// `offsetX/offsetY` is where the start diamond must land in the hub — on the
|
|
265
|
+
// class's quatrefoil axis, 1332u out (verified against the original PoB
|
|
266
|
+
// layout, exact to ~10u). So worldAnchor = startNode + offset places the
|
|
267
|
+
// diamond on the quatrefoil axis and the rest of the cluster around it.
|
|
268
|
+
const offsetX = raw.offsetX ?? 0;
|
|
269
|
+
const offsetY = raw.offsetY ?? 0;
|
|
270
|
+
return {
|
|
271
|
+
id: raw.name,
|
|
272
|
+
name: raw.name,
|
|
273
|
+
internalId: raw.id,
|
|
274
|
+
image: `Classes${raw.name.replace(/\s+/g, '')}`,
|
|
275
|
+
worldAnchor: { x: start.x + offsetX, y: start.y + offsetY },
|
|
276
|
+
size: { width: ASCENDANCY_DISC_SIZE, height: ASCENDANCY_DISC_SIZE },
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/ggg/normalize.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@poe2-toolkit/tree-core` — headless geometry engine for the Path of Exile 2 passive
|
|
3
|
+
* tree. Pure TypeScript, zero runtime dependencies, no DOM, no canvas.
|
|
4
|
+
*
|
|
5
|
+
* This entry point is **source-agnostic**: it works against the {@link TreeData}
|
|
6
|
+
* contract and knows nothing about where the data came from. The pipeline is
|
|
7
|
+
* `TreeData` → `buildScene` → `project` / `nodeAt`, plus the helpers a UI needs
|
|
8
|
+
* (interactive allocation, hit-testing, view framing).
|
|
9
|
+
*
|
|
10
|
+
* Producing `TreeData` is a separate, swappable adapter. The one for GGG's
|
|
11
|
+
* official export lives in the `@poe2-toolkit/tree-core/ggg` subpath, so the engine
|
|
12
|
+
* itself carries no dependency on any particular source shape.
|
|
13
|
+
*/
|
|
14
|
+
export type { TreeData, TreeConstants, Group, TreeNode, NodeOption, AttributeChoice, JewelInfo, NodeConnection, Size, ClassDef, CentreArt, AscendancyDef, SpriteFrame, SpriteManifest, Point, WorldRect, Scene, PlacedNode, NodeKind, PlacedConnection, PlacedEffect, CentreLayout, ClassAnchor, BuildAllocation, SceneOptions, Viewport, ScreenScene, ScreenNode, ScreenConnection, ScreenEffect, } from './types.js';
|
|
15
|
+
export { nodePosition } from './geometry/orbit.js';
|
|
16
|
+
export { computeCentreLayout } from './geometry/centre.js';
|
|
17
|
+
export { project, projectPoint, screenToWorld, nodeAt } from './geometry/project.js';
|
|
18
|
+
export { allocatedBounds, allocatedBoundsWithCentre, classBounds } from './geometry/framing.js';
|
|
19
|
+
export { buildScene, chosenAttributeOption, classOverrideNode } from './scene/buildScene.js';
|
|
20
|
+
export { ascendancyStartNode, buildAscendancyGraph, buildTreeGraph, pathToNode, reachable, removalSet, toggleAllocation, toggleAscendancyAllocation, } from './scene/allocate.js';
|
|
21
|
+
export type { TreeGraph } from './scene/allocate.js';
|
|
22
|
+
export { placeConnection } from './scene/connections.js';
|
|
23
|
+
export { classifyNode, nodeTargetSize } from './scene/nodeSize.js';
|
|
24
|
+
export type { NodeSize } from './scene/nodeSize.js';
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EACV,QAAQ,EACR,aAAa,EACb,KAAK,EACL,QAAQ,EACR,UAAU,EACV,eAAe,EACf,SAAS,EACT,cAAc,EACd,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,aAAa,EACb,WAAW,EACX,cAAc,EACd,KAAK,EACL,SAAS,EACT,KAAK,EACL,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,UAAU,EACV,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@poe2-toolkit/tree-core` — headless geometry engine for the Path of Exile 2 passive
|
|
3
|
+
* tree. Pure TypeScript, zero runtime dependencies, no DOM, no canvas.
|
|
4
|
+
*
|
|
5
|
+
* This entry point is **source-agnostic**: it works against the {@link TreeData}
|
|
6
|
+
* contract and knows nothing about where the data came from. The pipeline is
|
|
7
|
+
* `TreeData` → `buildScene` → `project` / `nodeAt`, plus the helpers a UI needs
|
|
8
|
+
* (interactive allocation, hit-testing, view framing).
|
|
9
|
+
*
|
|
10
|
+
* Producing `TreeData` is a separate, swappable adapter. The one for GGG's
|
|
11
|
+
* official export lives in the `@poe2-toolkit/tree-core/ggg` subpath, so the engine
|
|
12
|
+
* itself carries no dependency on any particular source shape.
|
|
13
|
+
*/
|
|
14
|
+
export { nodePosition } from './geometry/orbit.js';
|
|
15
|
+
export { computeCentreLayout } from './geometry/centre.js';
|
|
16
|
+
export { project, projectPoint, screenToWorld, nodeAt } from './geometry/project.js';
|
|
17
|
+
export { allocatedBounds, allocatedBoundsWithCentre, classBounds } from './geometry/framing.js';
|
|
18
|
+
export { buildScene, chosenAttributeOption, classOverrideNode } from './scene/buildScene.js';
|
|
19
|
+
export { ascendancyStartNode, buildAscendancyGraph, buildTreeGraph, pathToNode, reachable, removalSet, toggleAllocation, toggleAscendancyAllocation, } from './scene/allocate.js';
|
|
20
|
+
export { placeConnection } from './scene/connections.js';
|
|
21
|
+
export { classifyNode, nodeTargetSize } from './scene/nodeSize.js';
|
|
22
|
+
// Data-source adapters live in their own subpaths (e.g. `@poe2-toolkit/tree-core/ggg`)
|
|
23
|
+
// so this entry point stays source-agnostic — see ./ggg/normalize.ts.
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAmCH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,UAAU,EACV,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGnE,uFAAuF;AACvF,sEAAsE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive allocation over the passive tree graph: click a node to allocate
|
|
3
|
+
* the shortest path to it from the class start (and the current allocation), or
|
|
4
|
+
* click an allocated node to remove it and anything it orphaned.
|
|
5
|
+
*
|
|
6
|
+
* Pure graph logic — no rendering. The page owns the allocation state and feeds
|
|
7
|
+
* clicks through {@link toggleAllocation}.
|
|
8
|
+
*/
|
|
9
|
+
import type { TreeData } from '../types.js';
|
|
10
|
+
/** Adjacency list of the walkable tree: node id -> connected node ids. */
|
|
11
|
+
export type TreeGraph = Map<number, Set<number>>;
|
|
12
|
+
/**
|
|
13
|
+
* Build the undirected adjacency graph of walkable nodes. Class-start edges are
|
|
14
|
+
* kept (unlike in the drawn scene) so paths can root at the start node.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildTreeGraph(data: TreeData): TreeGraph;
|
|
17
|
+
/**
|
|
18
|
+
* The start node of an ascendancy panel (its pathing root), or undefined when
|
|
19
|
+
* the ascendancy has none in the data.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ascendancyStartNode(data: TreeData, ascendancy: string): number | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Adjacency graph of a single ascendancy's nodes (its start node included as the
|
|
24
|
+
* root). Ascendancy points are separate from the main tree, so the editor paths
|
|
25
|
+
* within this self-contained subgraph — never across the main-tree boundary.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildAscendancyGraph(data: TreeData, ascendancy: string): TreeGraph;
|
|
28
|
+
/**
|
|
29
|
+
* Shortest path (BFS) from any of `sources` to `target`, as the list of nodes
|
|
30
|
+
* to add — excluding the sources, including the target. `[]` when the target is
|
|
31
|
+
* already a source; `null` when unreachable.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pathToNode(graph: TreeGraph, sources: ReadonlySet<number>, target: number): number[] | null;
|
|
34
|
+
/**
|
|
35
|
+
* The nodes a click on an allocated `target` removes: everything beyond it (the
|
|
36
|
+
* nodes that lose their connection to the start once it's cut), keeping the
|
|
37
|
+
* target itself — clicking shortens the path *back* to the clicked node. When
|
|
38
|
+
* nothing lies beyond (the target is a tip), the target itself is removed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function removalSet(graph: TreeGraph, startNode: number, allocated: ReadonlySet<number>, target: number): Set<number>;
|
|
41
|
+
/** The subset of `allowed` still reachable from `roots` through the graph. */
|
|
42
|
+
export declare function reachable(graph: TreeGraph, roots: Iterable<number>, allowed: ReadonlySet<number>): Set<number>;
|
|
43
|
+
/**
|
|
44
|
+
* Toggle a node in a manual build:
|
|
45
|
+
* - allocated target -> remove it and prune any node it orphaned from the start
|
|
46
|
+
* - unallocated target -> allocate the shortest path to it
|
|
47
|
+
*
|
|
48
|
+
* Returns the new allocated node ids (start node excluded — it's implicit).
|
|
49
|
+
* Pass a prebuilt `graph` to avoid recomputing it per click.
|
|
50
|
+
*/
|
|
51
|
+
export declare function toggleAllocation(data: TreeData, startNode: number, allocated: ReadonlySet<number>, target: number, graph?: TreeGraph): number[];
|
|
52
|
+
/**
|
|
53
|
+
* Toggle an ascendancy node, pathing only within that ascendancy's own subgraph
|
|
54
|
+
* (rooted at its start node). The main-tree allocation is carried through
|
|
55
|
+
* untouched, so this is the ascendancy counterpart to {@link toggleAllocation}
|
|
56
|
+
* — clicks allocate the path from the ascendancy start, or remove beyond.
|
|
57
|
+
*
|
|
58
|
+
* Returns the new full allocated set (main tree + this ascendancy's nodes).
|
|
59
|
+
*/
|
|
60
|
+
export declare function toggleAscendancyAllocation(data: TreeData, ascendancy: string, allocated: ReadonlySet<number>, target: number, graph?: TreeGraph): number[];
|
|
61
|
+
//# sourceMappingURL=allocate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocate.d.ts","sourceRoot":"","sources":["../../src/scene/allocate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,aAAa,CAAC;AAEtD,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AA+BjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAmCxD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ1F;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,CAmClF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAqC1G;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,CAAC,CAkBb;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAyB9G;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,SAAgC,GACtC,MAAM,EAAE,CAoBV;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,SAAkD,GACxD,MAAM,EAAE,CAsBV"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive allocation over the passive tree graph: click a node to allocate
|
|
3
|
+
* the shortest path to it from the class start (and the current allocation), or
|
|
4
|
+
* click an allocated node to remove it and anything it orphaned.
|
|
5
|
+
*
|
|
6
|
+
* Pure graph logic — no rendering. The page owns the allocation state and feeds
|
|
7
|
+
* clicks through {@link toggleAllocation}.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Walkable for pathing: real main-tree nodes plus the class-start roots.
|
|
11
|
+
* Masteries (not pathed through), ascendancy nodes (separate panel), hidden
|
|
12
|
+
* sockets and conditional/unlock nodes are excluded.
|
|
13
|
+
*
|
|
14
|
+
* The GGG tree's synthetic centre node (keyed `root`, so it has no numeric skill
|
|
15
|
+
* id and parses to NaN) wires every class start together through the hub. Left
|
|
16
|
+
* walkable, a path could shortcut across the centre and surface at whichever rim
|
|
17
|
+
* gateway is fewest hops from the target — even another class's — so it looks
|
|
18
|
+
* like it starts at "the nearest point of the circle". Pathing must root at the
|
|
19
|
+
* class start (or the nearest allocated node) and stay in the tree, never cross
|
|
20
|
+
* the hub, so the centre node is excluded.
|
|
21
|
+
*/
|
|
22
|
+
function isWalkable(node) {
|
|
23
|
+
if (Number.isNaN(node.skill)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (node.ascendancyName || node.isMastery || node.conditional) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (node.noRadius && node.isJewelSocket) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the undirected adjacency graph of walkable nodes. Class-start edges are
|
|
36
|
+
* kept (unlike in the drawn scene) so paths can root at the start node.
|
|
37
|
+
*/
|
|
38
|
+
export function buildTreeGraph(data) {
|
|
39
|
+
const graph = new Map();
|
|
40
|
+
const link = (from, to) => {
|
|
41
|
+
let set = graph.get(from);
|
|
42
|
+
if (!set) {
|
|
43
|
+
set = new Set();
|
|
44
|
+
graph.set(from, set);
|
|
45
|
+
}
|
|
46
|
+
set.add(to);
|
|
47
|
+
};
|
|
48
|
+
for (const node of Object.values(data.nodes)) {
|
|
49
|
+
if (!isWalkable(node)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!graph.has(node.skill)) {
|
|
53
|
+
graph.set(node.skill, new Set());
|
|
54
|
+
}
|
|
55
|
+
for (const conn of node.connections) {
|
|
56
|
+
const target = data.nodes[conn.id];
|
|
57
|
+
if (!target || !isWalkable(target)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
link(node.skill, conn.id);
|
|
61
|
+
link(conn.id, node.skill);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return graph;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The start node of an ascendancy panel (its pathing root), or undefined when
|
|
68
|
+
* the ascendancy has none in the data.
|
|
69
|
+
*/
|
|
70
|
+
export function ascendancyStartNode(data, ascendancy) {
|
|
71
|
+
for (const node of Object.values(data.nodes)) {
|
|
72
|
+
if (node.isAscendancyStart && node.ascendancyName === ascendancy) {
|
|
73
|
+
return node.skill;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Adjacency graph of a single ascendancy's nodes (its start node included as the
|
|
80
|
+
* root). Ascendancy points are separate from the main tree, so the editor paths
|
|
81
|
+
* within this self-contained subgraph — never across the main-tree boundary.
|
|
82
|
+
*/
|
|
83
|
+
export function buildAscendancyGraph(data, ascendancy) {
|
|
84
|
+
const graph = new Map();
|
|
85
|
+
const link = (from, to) => {
|
|
86
|
+
let set = graph.get(from);
|
|
87
|
+
if (!set) {
|
|
88
|
+
set = new Set();
|
|
89
|
+
graph.set(from, set);
|
|
90
|
+
}
|
|
91
|
+
set.add(to);
|
|
92
|
+
};
|
|
93
|
+
for (const node of Object.values(data.nodes)) {
|
|
94
|
+
if (node.ascendancyName !== ascendancy) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!graph.has(node.skill)) {
|
|
98
|
+
graph.set(node.skill, new Set());
|
|
99
|
+
}
|
|
100
|
+
for (const conn of node.connections) {
|
|
101
|
+
const target = data.nodes[conn.id];
|
|
102
|
+
if (!target || target.ascendancyName !== ascendancy) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
link(node.skill, conn.id);
|
|
106
|
+
link(conn.id, node.skill);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return graph;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Shortest path (BFS) from any of `sources` to `target`, as the list of nodes
|
|
113
|
+
* to add — excluding the sources, including the target. `[]` when the target is
|
|
114
|
+
* already a source; `null` when unreachable.
|
|
115
|
+
*/
|
|
116
|
+
export function pathToNode(graph, sources, target) {
|
|
117
|
+
if (sources.has(target)) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
const prev = new Map();
|
|
121
|
+
const seen = new Set(sources);
|
|
122
|
+
const queue = [...sources];
|
|
123
|
+
for (let head = 0; head < queue.length; head++) {
|
|
124
|
+
const current = queue[head];
|
|
125
|
+
for (const next of graph.get(current) ?? []) {
|
|
126
|
+
if (seen.has(next)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
seen.add(next);
|
|
130
|
+
prev.set(next, current);
|
|
131
|
+
if (next === target) {
|
|
132
|
+
const path = [];
|
|
133
|
+
let step = target;
|
|
134
|
+
while (step !== undefined && !sources.has(step)) {
|
|
135
|
+
path.push(step);
|
|
136
|
+
step = prev.get(step);
|
|
137
|
+
}
|
|
138
|
+
return path.reverse();
|
|
139
|
+
}
|
|
140
|
+
queue.push(next);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The nodes a click on an allocated `target` removes: everything beyond it (the
|
|
147
|
+
* nodes that lose their connection to the start once it's cut), keeping the
|
|
148
|
+
* target itself — clicking shortens the path *back* to the clicked node. When
|
|
149
|
+
* nothing lies beyond (the target is a tip), the target itself is removed.
|
|
150
|
+
*/
|
|
151
|
+
export function removalSet(graph, startNode, allocated, target) {
|
|
152
|
+
if (!allocated.has(target)) {
|
|
153
|
+
return new Set();
|
|
154
|
+
}
|
|
155
|
+
const remaining = new Set(allocated);
|
|
156
|
+
remaining.delete(target);
|
|
157
|
+
const keep = reachable(graph, [startNode], remaining);
|
|
158
|
+
const beyond = new Set();
|
|
159
|
+
for (const id of allocated) {
|
|
160
|
+
if (id !== target && !keep.has(id)) {
|
|
161
|
+
beyond.add(id);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return beyond.size > 0 ? beyond : new Set([target]);
|
|
165
|
+
}
|
|
166
|
+
/** The subset of `allowed` still reachable from `roots` through the graph. */
|
|
167
|
+
export function reachable(graph, roots, allowed) {
|
|
168
|
+
const kept = new Set();
|
|
169
|
+
const seen = new Set();
|
|
170
|
+
const queue = [];
|
|
171
|
+
for (const root of roots) {
|
|
172
|
+
seen.add(root);
|
|
173
|
+
queue.push(root);
|
|
174
|
+
}
|
|
175
|
+
for (let head = 0; head < queue.length; head++) {
|
|
176
|
+
const current = queue[head];
|
|
177
|
+
for (const next of graph.get(current) ?? []) {
|
|
178
|
+
if (seen.has(next) || !allowed.has(next)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
seen.add(next);
|
|
182
|
+
kept.add(next);
|
|
183
|
+
queue.push(next);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return kept;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Toggle a node in a manual build:
|
|
190
|
+
* - allocated target -> remove it and prune any node it orphaned from the start
|
|
191
|
+
* - unallocated target -> allocate the shortest path to it
|
|
192
|
+
*
|
|
193
|
+
* Returns the new allocated node ids (start node excluded — it's implicit).
|
|
194
|
+
* Pass a prebuilt `graph` to avoid recomputing it per click.
|
|
195
|
+
*/
|
|
196
|
+
export function toggleAllocation(data, startNode, allocated, target, graph = buildTreeGraph(data)) {
|
|
197
|
+
if (target === startNode) {
|
|
198
|
+
return [...allocated];
|
|
199
|
+
}
|
|
200
|
+
if (allocated.has(target)) {
|
|
201
|
+
const removed = removalSet(graph, startNode, allocated, target);
|
|
202
|
+
return [...allocated].filter((id) => !removed.has(id));
|
|
203
|
+
}
|
|
204
|
+
const sources = new Set(allocated);
|
|
205
|
+
sources.add(startNode);
|
|
206
|
+
const path = pathToNode(graph, sources, target);
|
|
207
|
+
if (!path) {
|
|
208
|
+
return [...allocated];
|
|
209
|
+
}
|
|
210
|
+
return [...new Set([...allocated, ...path])];
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Toggle an ascendancy node, pathing only within that ascendancy's own subgraph
|
|
214
|
+
* (rooted at its start node). The main-tree allocation is carried through
|
|
215
|
+
* untouched, so this is the ascendancy counterpart to {@link toggleAllocation}
|
|
216
|
+
* — clicks allocate the path from the ascendancy start, or remove beyond.
|
|
217
|
+
*
|
|
218
|
+
* Returns the new full allocated set (main tree + this ascendancy's nodes).
|
|
219
|
+
*/
|
|
220
|
+
export function toggleAscendancyAllocation(data, ascendancy, allocated, target, graph = buildAscendancyGraph(data, ascendancy)) {
|
|
221
|
+
const start = ascendancyStartNode(data, ascendancy);
|
|
222
|
+
if (start === undefined) {
|
|
223
|
+
return [...allocated];
|
|
224
|
+
}
|
|
225
|
+
// Split off this ascendancy's slice; everything else stays exactly as-is.
|
|
226
|
+
const ascAllocated = new Set();
|
|
227
|
+
const rest = [];
|
|
228
|
+
for (const id of allocated) {
|
|
229
|
+
if (graph.has(id)) {
|
|
230
|
+
ascAllocated.add(id);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
rest.push(id);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const nextAsc = toggleAllocation(data, start, ascAllocated, target, graph);
|
|
237
|
+
return [...rest, ...nextAsc];
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=allocate.js.map
|