@yuneta/gobj-ui 7.23.82 → 7.23.84
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 +40 -11
- package/dist/gobj-ui.cjs.js +266 -126
- package/dist/gobj-ui.es.js +266 -126
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +162 -38
- package/src/c_yui_treedb_graph.js +97 -29
- package/src/treedb_layout.js +55 -16
- package/src/treedb_layout.test.js +15 -0
package/src/treedb_layout.js
CHANGED
|
@@ -51,8 +51,10 @@
|
|
|
51
51
|
*
|
|
52
52
|
* `radial`: the root in the middle and a ring per depth, each
|
|
53
53
|
* subtree owning a sector proportional to its leaves; the radius
|
|
54
|
-
* of a ring grows until its neighbours sit side by side
|
|
55
|
-
* nothing overlaps by
|
|
54
|
+
* of a ring grows until its neighbours sit side by side AND
|
|
55
|
+
* until it clears the ring inside it, so nothing overlaps by
|
|
56
|
+
* construction, across the ring or along the radius (see
|
|
57
|
+
* `layout_radial`).
|
|
56
58
|
*
|
|
57
59
|
* Pure: takes plain nodes and edges, returns positions. The G6
|
|
58
60
|
* classes in c_g6_nodes_tree.js are two thin adapters over it.
|
|
@@ -272,6 +274,18 @@ function tidy_tree_lr(nodes, edges, o)
|
|
|
272
274
|
return pos;
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
/* How much of a card lies along the direction `a`: the projection
|
|
278
|
+
* of its rectangle on that direction. Full width when the card is
|
|
279
|
+
* read along its width, full height across it, and the honest
|
|
280
|
+
* mixture in between. */
|
|
281
|
+
function extent_at(n, a)
|
|
282
|
+
{
|
|
283
|
+
if(!n) {
|
|
284
|
+
return 0;
|
|
285
|
+
}
|
|
286
|
+
return Math.abs(n.w * Math.cos(a)) + Math.abs(n.h * Math.sin(a));
|
|
287
|
+
}
|
|
288
|
+
|
|
275
289
|
/************************************************************
|
|
276
290
|
* The radial tree: the root in the middle, a ring per depth.
|
|
277
291
|
*
|
|
@@ -333,10 +347,10 @@ export function layout_radial(nodes, edges, opts)
|
|
|
333
347
|
/* An explicit stack, not recursion: the sectors of a ring are
|
|
334
348
|
* sorted by angle in pass 2, so the order they are pushed in does
|
|
335
349
|
* not matter -- only that every node gets its own. */
|
|
336
|
-
let sector = (root_id, root_from, root_span) => {
|
|
337
|
-
let stack = [{id: root_id, from: root_from, span: root_span}];
|
|
350
|
+
let sector = (root_id, root_from, root_span, root_parent) => {
|
|
351
|
+
let stack = [{id: root_id, from: root_from, span: root_span, parent: root_parent}];
|
|
338
352
|
while(stack.length) {
|
|
339
|
-
let {id, from, span} = stack.pop();
|
|
353
|
+
let {id, from, span, parent} = stack.pop();
|
|
340
354
|
let a = from + span / 2;
|
|
341
355
|
angle.set(id, a);
|
|
342
356
|
let ring = ring_of(id);
|
|
@@ -344,11 +358,21 @@ export function layout_radial(nodes, edges, opts)
|
|
|
344
358
|
rings[ring] = [];
|
|
345
359
|
}
|
|
346
360
|
let n = t.by_id.get(id);
|
|
347
|
-
rings[ring].push({
|
|
361
|
+
rings[ring].push({
|
|
362
|
+
id: id,
|
|
363
|
+
angle: a,
|
|
364
|
+
diag: Math.hypot(n.w, n.h),
|
|
365
|
+
/* How much of the card lies along the RADIUS at this
|
|
366
|
+
* angle -- the projection of the rectangle on that
|
|
367
|
+
* direction -- for itself and for the node it hangs
|
|
368
|
+
* from, which is the one it has to clear. */
|
|
369
|
+
ext: extent_at(n, a),
|
|
370
|
+
parent_ext: parent? extent_at(t.by_id.get(parent), a) : 0,
|
|
371
|
+
});
|
|
348
372
|
let cursor = from;
|
|
349
373
|
for(let kid of (t.children.get(id) || [])) {
|
|
350
374
|
let part = span * leaves.get(kid) / leaves.get(id);
|
|
351
|
-
stack.push({id: kid, from: cursor, span: part});
|
|
375
|
+
stack.push({id: kid, from: cursor, span: part, parent: id});
|
|
352
376
|
cursor += part;
|
|
353
377
|
}
|
|
354
378
|
}
|
|
@@ -359,28 +383,43 @@ export function layout_radial(nodes, edges, opts)
|
|
|
359
383
|
let cursor = -Math.PI / 2; /* the first child at the top */
|
|
360
384
|
for(let kid of (t.children.get(root) || [])) {
|
|
361
385
|
let part = 2 * Math.PI * leaves.get(kid) / leaves.get(root);
|
|
362
|
-
sector(kid, cursor, part);
|
|
386
|
+
sector(kid, cursor, part, root);
|
|
363
387
|
cursor += part;
|
|
364
388
|
}
|
|
365
389
|
} else {
|
|
366
390
|
let cursor = -Math.PI / 2;
|
|
367
391
|
for(let root of t.roots) {
|
|
368
392
|
let part = 2 * Math.PI * leaves.get(root) / total;
|
|
369
|
-
sector(root, cursor, part);
|
|
393
|
+
sector(root, cursor, part, null);
|
|
370
394
|
cursor += part;
|
|
371
395
|
}
|
|
372
396
|
}
|
|
373
397
|
|
|
374
|
-
/* Pass 2, the RADII
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
398
|
+
/* Pass 2, the RADII. Two constraints, and the larger wins.
|
|
399
|
+
*
|
|
400
|
+
* ACROSS the ring: its two closest neighbours have to sit side
|
|
401
|
+
* by side -- the arc between two adjacent centres is r·Δangle,
|
|
402
|
+
* and it has to hold half of each card plus the gap. Measured on
|
|
403
|
+
* NEIGHBOURS, not on the ring's total length: a leafless root
|
|
404
|
+
* squeezed between two big subtrees gets a sliver of angle, and
|
|
405
|
+
* it is the sliver that sets the radius.
|
|
406
|
+
*
|
|
407
|
+
* ALONG the radius: `ranksep` is a step between CENTRES, so a
|
|
408
|
+
* ring one step out was one step out whatever the cards
|
|
409
|
+
* measured -- 180 between the centres of two cards that reach
|
|
410
|
+
* 95 each way is two cards touching. The tidy tree never had
|
|
411
|
+
* this: its `ranksep` is a gap between COLUMNS, and a column
|
|
412
|
+
* carries its own width. Here every card is measured against
|
|
413
|
+
* the one it HANGS FROM, in the direction it hangs in -- the
|
|
414
|
+
* two are radially aligned, or nearly, since a child lives
|
|
415
|
+
* inside its parent's sector -- and the ring goes out far
|
|
416
|
+
* enough for the worst of them. */
|
|
381
417
|
let radius = [0];
|
|
382
418
|
for(let ring = 1; ring < rings.length; ring++) {
|
|
383
419
|
let r = radius[ring - 1] + o.ranksep;
|
|
420
|
+
for(let n of (rings[ring] || [])) {
|
|
421
|
+
r = Math.max(r, radius[ring - 1] + (n.parent_ext + n.ext) / 2 + o.nodesep);
|
|
422
|
+
}
|
|
384
423
|
let list = (rings[ring] || []).slice().sort((a, b) => a.angle - b.angle);
|
|
385
424
|
for(let i = 0; i < list.length && list.length > 1; i++) {
|
|
386
425
|
let a = list[i];
|
|
@@ -181,6 +181,21 @@ describe("the radial tree", () => {
|
|
|
181
181
|
expect(d).toBeGreaterThan(190);
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
test("a ring clears the ring inside it: a card does not sit on its parent", () => {
|
|
185
|
+
/* One child, straight below its root: `ranksep` alone is a
|
|
186
|
+
* step between CENTRES, so two cards 96 tall 100 apart had
|
|
187
|
+
* 48 + 48 of card and 4 of air. The ring adds the halves. */
|
|
188
|
+
let pos = layout_radial(
|
|
189
|
+
[N("root", 172, 96), N("kid", 172, 96)],
|
|
190
|
+
[{source: "root", target: "kid", rank: 0}],
|
|
191
|
+
{nodesep: 18, ranksep: 100}
|
|
192
|
+
);
|
|
193
|
+
let d = Math.hypot(
|
|
194
|
+
pos.get("kid").x - pos.get("root").x, pos.get("kid").y - pos.get("root").y
|
|
195
|
+
);
|
|
196
|
+
expect(d).toBeGreaterThanOrEqual(48 + 48 + 18);
|
|
197
|
+
});
|
|
198
|
+
|
|
184
199
|
test("several roots share the circle around an empty centre", () => {
|
|
185
200
|
let pos = layout_radial(
|
|
186
201
|
[N("a", 100, 50), N("b", 100, 50), N("c", 100, 50)], [], {ranksep: 100}
|