@yuneta/gobj-ui 7.23.155 → 7.23.157

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.
@@ -6,9 +6,7 @@
6
6
  * tidy tree's rows and centring, the radial tree's rings.
7
7
  ***********************************************************************/
8
8
  import { describe, test, expect } from "vitest";
9
- import {
10
- spanning_tree, layout_tree, layout_radial, layout_compact, stack_trunk_width, STACK_GAP,
11
- } from "./treedb_layout.js";
9
+ import { spanning_tree, layout_tree, layout_radial } from "./treedb_layout.js";
12
10
 
13
11
  /* es -> (norte, sur); norte -> nave; nave -> d0, d1 (hook rank 1) and
14
12
  * c1 (hook rank 2); c1 -> d0 as well (second parent). */
@@ -99,177 +97,6 @@ describe("the tidy tree, read down (the default)", () => {
99
97
  });
100
98
  });
101
99
 
102
- /* Every two cards of one row stand at least `gap` apart. */
103
- function expect_no_overlap_in_rows(list, pos, gap)
104
- {
105
- for(let i = 0; i < list.length; i++) {
106
- for(let j = i + 1; j < list.length; j++) {
107
- let a = list[i];
108
- let b = list[j];
109
- let pa = pos.get(a.id);
110
- let pb = pos.get(b.id);
111
- if(Math.abs(pa.y - pb.y) > 1e-6) {
112
- continue;
113
- }
114
- expect(Math.abs(pa.x - pb.x) + 1e-6).toBeGreaterThanOrEqual((a.w + b.w) / 2 + gap);
115
- }
116
- }
117
- }
118
-
119
- function width_of(list, pos)
120
- {
121
- let lo = Infinity;
122
- let hi = -Infinity;
123
- for(let n of list) {
124
- let p = pos.get(n.id);
125
- lo = Math.min(lo, p.x - n.w / 2);
126
- hi = Math.max(hi, p.x + n.w / 2);
127
- }
128
- return hi - lo;
129
- }
130
-
131
- /* r -> a, b; a -> hall -> six devices; b is a closed sibling. */
132
- const wide = [N("r", 172, 96), N("a", 172, 96), N("b", 172, 96), N("hall", 172, 96)]
133
- .concat([0, 1, 2, 3, 4, 5].map((i) => N(`d${i}`, 116, 40)));
134
- const wide_edges = [
135
- {source: "r", target: "a"}, {source: "r", target: "b"}, {source: "a", target: "hall"},
136
- ].concat([0, 1, 2, 3, 4, 5].map((i) => ({source: "hall", target: `d${i}`})));
137
-
138
- describe("the compact tree", () => {
139
- test("the rows are the tidy tree's rows; a stack starts on its row", () => {
140
- let tidy = layout_tree(nodes, edges, {nodesep: 10, ranksep: 100});
141
- let pos = layout_compact(nodes, edges, {nodesep: 10, ranksep: 100});
142
- /* nave's three leaves (d0, d1, c1) are a stack: d0 and d1 are
143
- * its first row, on the tidy row; c1 opens the next one. */
144
- for(let id of ["es", "norte", "sur", "nave", "d0", "d1"]) {
145
- expect(pos.get(id).y).toBe(tidy.get(id).y);
146
- }
147
- expect(pos.get("c1").y).toBeGreaterThan(pos.get("d0").y);
148
- });
149
-
150
- test("no two cards of a row are closer than the gap", () => {
151
- expect_no_overlap_in_rows(nodes, layout_compact(nodes, edges, {nodesep: 10, ranksep: 100}), 10);
152
- expect_no_overlap_in_rows(wide, layout_compact(wide, wide_edges, {nodesep: 18, ranksep: 90}), 18);
153
- });
154
-
155
- test("two leaves stay side by side, and the parent is centred over them", () => {
156
- let two = [N("p", 172, 96), N("a", 116, 40), N("b", 116, 40)];
157
- let pos = layout_compact(two, [{source: "p", target: "a"}, {source: "p", target: "b"}],
158
- {nodesep: 10, ranksep: 100});
159
- expect(pos.get("a").y).toBe(pos.get("b").y);
160
- expect(pos.get("b").x - pos.get("a").x).toBe(116 + 10);
161
- expect(pos.get("p").x).toBeCloseTo((pos.get("a").x + pos.get("b").x) / 2, 6);
162
- });
163
-
164
- test("a closed sibling sits beside an open branch, not beside its block", () => {
165
- let opts = {nodesep: 18, ranksep: 90};
166
- let tidy = layout_tree(wide, wide_edges, opts);
167
- let pos = layout_compact(wide, wide_edges, opts);
168
- /* In the tidy tree `b` waits for the whole fan of devices; here it
169
- * only has to clear `a`, the card beside it. */
170
- expect(pos.get("b").x - pos.get("a").x).toBeLessThan(tidy.get("b").x - tidy.get("a").x);
171
- expect(pos.get("b").x - pos.get("a").x).toBeGreaterThanOrEqual(172 + 18 - 1e-6);
172
- expect(width_of(wide, pos)).toBeLessThanOrEqual(width_of(wide, tidy));
173
- });
174
-
175
- test("two trees sit side by side, not overlapped", () => {
176
- let pos = layout_compact([N("a", 100, 50), N("b", 100, 50)], [], {nodesep: 10});
177
- expect(pos.get("b").x - pos.get("a").x).toBe(100 + 20);
178
- });
179
-
180
- test("read right, it is the same tree with the axes swapped", () => {
181
- let down = layout_compact(wide, wide_edges, {nodesep: 18, ranksep: 90});
182
- let swapped = wide.map((n) => N(n.id, n.h, n.w));
183
- let right = layout_compact(swapped, wide_edges, {nodesep: 18, ranksep: 90, direction: "LR"});
184
- for(let n of wide) {
185
- expect(right.get(n.id).x).toBeCloseTo(down.get(n.id).y, 6);
186
- expect(right.get(n.id).y).toBeCloseTo(down.get(n.id).x, 6);
187
- }
188
- });
189
-
190
- test("a tree as deep as the data places every node, without recursion", () => {
191
- let chain = [];
192
- let links = [];
193
- for(let i = 0; i < 5000; i++) {
194
- chain.push(N(`n${i}`, 40, 20));
195
- if(i > 0) {
196
- links.push({source: `n${i - 1}`, target: `n${i}`});
197
- }
198
- }
199
- let pos = layout_compact(chain, links, {});
200
- expect(pos.size).toBe(5000);
201
- expect(Number.isFinite(pos.get("n4999").x)).toBe(true);
202
- });
203
- });
204
-
205
- describe("the compact tree stacks a run of leaves", () => {
206
- const P6 = N("p", 172, 96);
207
- const L6 = [0, 1, 2, 3, 4, 5].map((i) => N(`l${i}`, 116, 40));
208
- const E6 = L6.map((l) => ({source: "p", target: l.id}));
209
- const OPTS = {nodesep: 18, ranksep: 90};
210
-
211
- test("in two columns, row after row, against a corridor", () => {
212
- let pos = layout_compact([P6, ...L6], E6, OPTS);
213
- let y = (id) => pos.get(id).y;
214
- let x = (id) => pos.get(id).x;
215
- expect(y("l0")).toBe(y("l1"));
216
- expect(y("l2")).toBe(y("l3"));
217
- expect(y("l2") - y("l0")).toBe(40 + STACK_GAP);
218
- expect(y("l4") - y("l2")).toBe(40 + STACK_GAP);
219
- /* The first column lines up on its right edge, the second on
220
- * its left, and between them the corridor. */
221
- expect(x("l0")).toBe(x("l2"));
222
- expect(x("l1")).toBe(x("l5"));
223
- expect((x("l1") - 58) - (x("l0") + 58)).toBe(stack_trunk_width(6));
224
- /* The parent over the block. */
225
- expect(x("p")).toBeCloseTo(((x("l0") - 58) + (x("l1") + 58)) / 2, 6);
226
- });
227
-
228
- test("no two cards overlap", () => {
229
- let all = [P6, ...L6];
230
- let pos = layout_compact(all, E6, OPTS);
231
- for(let i = 0; i < all.length; i++) {
232
- for(let j = i + 1; j < all.length; j++) {
233
- let a = all[i];
234
- let b = all[j];
235
- let pa = pos.get(a.id);
236
- let pb = pos.get(b.id);
237
- let apart_x = Math.abs(pa.x - pb.x) >= (a.w + b.w) / 2;
238
- let apart_y = Math.abs(pa.y - pb.y) >= (a.h + b.h) / 2;
239
- expect(apart_x || apart_y).toBe(true);
240
- }
241
- }
242
- });
243
-
244
- test("a whole level of leaves takes a fraction of the row it took", () => {
245
- let tidy = layout_tree([P6, ...L6], E6, OPTS);
246
- let pos = layout_compact([P6, ...L6], E6, OPTS);
247
- let span = (p) => {
248
- let xs = L6.map((l) => p.get(l.id).x);
249
- return Math.max(...xs) - Math.min(...xs) + 116;
250
- };
251
- expect(span(pos)).toBeLessThan(span(tidy) / 2);
252
- });
253
-
254
- test("a run is broken by a child with children, and the order is kept", () => {
255
- /* p -> a, b, c (leaves), m (has a child), d, e (leaves). */
256
- let list = [N("p", 172, 96), N("a", 116, 40), N("b", 116, 40), N("c", 116, 40),
257
- N("m", 172, 96), N("d", 116, 40), N("e", 116, 40), N("mk", 116, 40)];
258
- let links = ["a", "b", "c", "m", "d", "e"].map((k) => ({source: "p", target: k}))
259
- .concat([{source: "m", target: "mk"}]);
260
- let pos = layout_compact(list, links, OPTS);
261
- /* a, b, c: a stack (c under a); d, e: a pair on the row. */
262
- expect(pos.get("c").y).toBeGreaterThan(pos.get("a").y);
263
- expect(pos.get("c").x).toBe(pos.get("a").x);
264
- expect(pos.get("d").y).toBe(pos.get("a").y);
265
- expect(pos.get("e").y).toBe(pos.get("a").y);
266
- /* Left to right: the stack, m, then d and e. */
267
- expect(pos.get("b").x).toBeLessThan(pos.get("m").x);
268
- expect(pos.get("m").x).toBeLessThan(pos.get("d").x);
269
- expect(pos.get("d").x).toBeLessThan(pos.get("e").x);
270
- });
271
- });
272
-
273
100
  describe("the tidy tree, read right", () => {
274
101
  test("depths are columns; a column is as wide as its widest card", () => {
275
102
  let pos = layout_tree(nodes, edges, {nodesep: 10, ranksep: 100, direction: "LR"});