@yuneta/gobj-ui 7.23.153 → 7.23.154

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.
@@ -29326,6 +29326,14 @@ function tidy_tree_lr(nodes, edges, o) {
29326
29326
  * the packing across the depths differs, so switching between the
29327
29327
  * two moves cards sideways and never reorders them.
29328
29328
  *
29329
+ * And a run of three or more LEAF children -- a hall's devices, a
29330
+ * region's places when none is open -- is STACKED: two columns
29331
+ * under the parent with a corridor between them, row after row.
29332
+ * That is what makes it compact when a whole level is open, where
29333
+ * the contour alone gains nothing: every parent needed the whole
29334
+ * row of its children beside it. The tree grows taller instead of
29335
+ * wider. Its elbow edges run down the corridor (elbow_combs).
29336
+ *
29329
29337
  * The contour arithmetic is Moen's ("Drawing dynamic trees", IEEE
29330
29338
  * Software, 1990) in the form mxGraph gave it in mxCompactTreeLayout
29331
29339
  * -- Copyright (c) 2006-2015 JGraph Ltd, Apache License 2.0 -- read in
@@ -29406,10 +29414,81 @@ function contour_merge(p1, p2) {
29406
29414
  p1.lower_head = p2.lower_head;
29407
29415
  return total;
29408
29416
  }
29417
+ function stack_trunk_width(k) {
29418
+ let lanes = Math.max(0, k - 2);
29419
+ return Math.max(36, 20 + Math.max(0, lanes - 1) * 6);
29420
+ }
29421
+ function stack_geometry(t, st, col_x, col_w) {
29422
+ let d = st.depth;
29423
+ let k = st.ids.length;
29424
+ let rows = Math.ceil(k / 2);
29425
+ let side = [0, 0];
29426
+ let row_len = [];
29427
+ for (let i = 0; i < k; i++) {
29428
+ let n = t.by_id.get(st.ids[i]);
29429
+ let r = Math.floor(i / 2);
29430
+ side[i % 2] = Math.max(side[i % 2], n.h);
29431
+ row_len[r] = Math.max(row_len[r] || 0, n.w);
29432
+ }
29433
+ row_len[0] = col_w[d];
29434
+ let trunk = stack_trunk_width(k);
29435
+ let starts = [col_x[d]];
29436
+ for (let r = 1; r < rows; r++) starts[r] = starts[r - 1] + row_len[r - 1] + 40;
29437
+ return {
29438
+ side,
29439
+ trunk,
29440
+ starts,
29441
+ row_len,
29442
+ len: starts[rows - 1] + row_len[rows - 1] - col_x[d],
29443
+ ext: side[0] + trunk + side[1]
29444
+ };
29445
+ }
29446
+ function place_stack(t, st, g, top, pos) {
29447
+ for (let i = 0; i < st.ids.length; i++) {
29448
+ let n = t.by_id.get(st.ids[i]);
29449
+ let r = Math.floor(i / 2);
29450
+ let across = i % 2 === 0 ? top + g.side[0] - n.h / 2 : top + g.side[0] + g.trunk + n.h / 2;
29451
+ pos.set(st.ids[i], {
29452
+ x: g.starts[r] + g.row_len[r] / 2,
29453
+ y: across
29454
+ });
29455
+ }
29456
+ }
29409
29457
  function compact_tree_lr(nodes, edges, o) {
29410
29458
  let t = spanning_tree(nodes, edges);
29411
- let order = walk_order(t);
29412
29459
  let pos = /* @__PURE__ */ new Map();
29460
+ let kids_of = /* @__PURE__ */ new Map();
29461
+ let stacks = /* @__PURE__ */ new Map();
29462
+ for (let [id, kids] of t.children) {
29463
+ let out = [];
29464
+ let run = [];
29465
+ let flush = () => {
29466
+ if (run.length >= 3) {
29467
+ let key = { stack: stacks.size };
29468
+ stacks.set(key, {
29469
+ ids: run,
29470
+ depth: t.depth.get(run[0])
29471
+ });
29472
+ out.push(key);
29473
+ } else out.push(...run);
29474
+ run = [];
29475
+ };
29476
+ for (let kid of kids) if ((t.children.get(kid) || []).length === 0) run.push(kid);
29477
+ else {
29478
+ flush();
29479
+ out.push(kid);
29480
+ }
29481
+ flush();
29482
+ kids_of.set(id, out);
29483
+ }
29484
+ let order = [];
29485
+ let todo = t.roots.slice().reverse();
29486
+ while (todo.length) {
29487
+ let id = todo.pop();
29488
+ order.push(id);
29489
+ let kids = kids_of.get(id) || [];
29490
+ for (let i = kids.length - 1; i >= 0; i--) todo.push(kids[i]);
29491
+ }
29413
29492
  let col_w = [];
29414
29493
  for (let [id, d] of t.depth) col_w[d] = Math.max(col_w[d] || 0, t.by_id.get(id).w);
29415
29494
  let col_x = [];
@@ -29422,18 +29501,32 @@ function compact_tree_lr(nodes, edges, o) {
29422
29501
  let dist = o.nodesep;
29423
29502
  let level_distance = o.ranksep - pad;
29424
29503
  let tn = /* @__PURE__ */ new Map();
29425
- for (let id of order) tn.set(id, {
29426
- len: col_w[t.depth.get(id)],
29427
- ext: t.by_id.get(id).h,
29428
- off_x: 0,
29429
- off_y: 0,
29430
- y: 0,
29431
- contour: null
29432
- });
29504
+ for (let id of order) {
29505
+ let st = stacks.get(id);
29506
+ if (st) {
29507
+ let g = stack_geometry(t, st, col_x, col_w);
29508
+ tn.set(id, {
29509
+ len: g.len,
29510
+ ext: g.ext,
29511
+ geom: g,
29512
+ off_x: 0,
29513
+ off_y: 0,
29514
+ y: 0,
29515
+ contour: null
29516
+ });
29517
+ } else tn.set(id, {
29518
+ len: col_w[t.depth.get(id)],
29519
+ ext: t.by_id.get(id).h,
29520
+ off_x: 0,
29521
+ off_y: 0,
29522
+ y: 0,
29523
+ contour: null
29524
+ });
29525
+ }
29433
29526
  for (let i = order.length - 1; i >= 0; i--) {
29434
29527
  let id = order[i];
29435
29528
  let n = tn.get(id);
29436
- let kids = t.children.get(id) || [];
29529
+ let kids = kids_of.get(id) || [];
29437
29530
  if (!kids.length) {
29438
29531
  let upper = contour_line(n.len + dist, 0);
29439
29532
  let lower_tail = contour_line(0, -n.ext - dist);
@@ -29469,7 +29562,7 @@ function compact_tree_lr(nodes, edges, o) {
29469
29562
  for (let id of order) {
29470
29563
  let n = tn.get(id);
29471
29564
  if (!root_of.has(id)) root_of.set(id, id);
29472
- let kids = t.children.get(id) || [];
29565
+ let kids = kids_of.get(id) || [];
29473
29566
  if (!kids.length) continue;
29474
29567
  let running = n.y + tn.get(kids[0]).off_y;
29475
29568
  tn.get(kids[0]).y = running;
@@ -29496,9 +29589,14 @@ function compact_tree_lr(nodes, edges, o) {
29496
29589
  top += hi.get(r) - lo.get(r) + o.nodesep * 2;
29497
29590
  }
29498
29591
  for (let id of order) {
29499
- let d = t.depth.get(id);
29500
29592
  let n = tn.get(id);
29501
29593
  let y = n.y + shift.get(root_of.get(id));
29594
+ let st = stacks.get(id);
29595
+ if (st) {
29596
+ place_stack(t, st, n.geom, y, pos);
29597
+ continue;
29598
+ }
29599
+ let d = t.depth.get(id);
29502
29600
  pos.set(id, {
29503
29601
  x: col_x[d] + col_w[d] / 2,
29504
29602
  y: y + n.ext / 2
@@ -29663,6 +29761,136 @@ function swap_box(b) {
29663
29761
  y2: b.x2
29664
29762
  };
29665
29763
  }
29764
+ function staggered_turn(lo, hi, st) {
29765
+ let mid = (lo + hi) / 2;
29766
+ let group = null;
29767
+ let k = 0;
29768
+ if (st.dep && st.dep.count > 1) {
29769
+ group = st.dep;
29770
+ k = st.dep.rank;
29771
+ } else if (st.arr && st.arr.count > 1) {
29772
+ group = st.arr;
29773
+ k = st.arr.count - 1 - st.arr.rank;
29774
+ }
29775
+ if (group) {
29776
+ let room = Math.max(0, hi - lo - 12);
29777
+ let step = Math.min(10, room / (group.count - 1));
29778
+ mid = mid - (group.count - 1) * step / 2 + k * step;
29779
+ }
29780
+ return mid;
29781
+ }
29782
+ /************************************************************
29783
+ * The combs of the stacks under one card.
29784
+ *
29785
+ * treedb_layout.js lays a run of leaf children out as two columns
29786
+ * with a corridor between them. A card in the first row is reached
29787
+ * from the channel like any other; one in a lower row cannot be --
29788
+ * the card above it stands in the way -- so its edge runs down
29789
+ * the corridor in a LANE of its own and turns into the gap above
29790
+ * its card, which it shares only with the other card of its row,
29791
+ * reached from the other side.
29792
+ *
29793
+ * targets [{id, box}]: the forward edges leaving one card,
29794
+ * each with the box of the card it reaches
29795
+ * vertical as in elbow_points; false swaps the boxes
29796
+ *
29797
+ * Returns Map id -> {lane, z, top}: the lane, the height of the
29798
+ * gap it turns into, and the top of the stack's first row (where
29799
+ * the channel over the stack ends). Only for a card that stands
29800
+ * under another of the group, within a stack gap and lined up
29801
+ * with it on the corridor side -- what a stack looks like.
29802
+ *
29803
+ * Lanes go outermost first, row by row: a line into a higher row
29804
+ * keeps outside the lines going deeper, and none crosses another.
29805
+ ************************************************************/
29806
+ function elbow_combs(targets, vertical) {
29807
+ let list = vertical ? targets : targets.map((it) => ({
29808
+ id: it.id,
29809
+ box: swap_box(it.box)
29810
+ }));
29811
+ let cy = (b) => (b.y1 + b.y2) / 2;
29812
+ let near = (a, b) => Math.abs(a - b) < 1.5;
29813
+ let under = [];
29814
+ for (let it of list) {
29815
+ let b = it.box;
29816
+ let above = null;
29817
+ for (let o of list) {
29818
+ let a = o.box;
29819
+ if (o === it || a.y2 > b.y1 || b.y1 - a.y2 > 40) continue;
29820
+ if (!(near(a.x2, b.x2) || near(a.x1, b.x1))) continue;
29821
+ if (!above || a.y2 > above.y2) above = a;
29822
+ }
29823
+ if (!above) continue;
29824
+ let side;
29825
+ if (near(above.x2, b.x2) && !near(above.x1, b.x1)) side = 0;
29826
+ else if (near(above.x1, b.x1) && !near(above.x2, b.x2)) side = 1;
29827
+ else side = list.some((o) => o !== it && near(cy(o.box), cy(b)) && o.box.x2 <= b.x1) ? 1 : 0;
29828
+ under.push({
29829
+ id: it.id,
29830
+ box: b,
29831
+ above,
29832
+ side
29833
+ });
29834
+ }
29835
+ let corridor = (u) => {
29836
+ if (u.side === 0) {
29837
+ let l = u.box.x2;
29838
+ let col = list.filter((o) => near(o.box.x2, l));
29839
+ let r = Infinity;
29840
+ for (let c of col) for (let o of list) if (o !== c && near(cy(o.box), cy(c.box)) && o.box.x1 >= l - .5) r = Math.min(r, o.box.x1);
29841
+ return {
29842
+ l,
29843
+ r: isFinite(r) ? r : l + 36,
29844
+ col
29845
+ };
29846
+ }
29847
+ let r = u.box.x1;
29848
+ let col = list.filter((o) => near(o.box.x1, r));
29849
+ let l = -Infinity;
29850
+ for (let c of col) for (let o of list) if (o !== c && near(cy(o.box), cy(c.box)) && o.box.x2 <= r + .5) l = Math.max(l, o.box.x2);
29851
+ return {
29852
+ l: isFinite(l) ? l : r - 36,
29853
+ r,
29854
+ col
29855
+ };
29856
+ };
29857
+ let groups = /* @__PURE__ */ new Map();
29858
+ for (let u of under) {
29859
+ let c = corridor(u);
29860
+ u.top = Math.min(...c.col.map((o) => o.box.y1));
29861
+ let key = `${Math.round(c.l)}|${Math.round(c.r)}`;
29862
+ if (!groups.has(key)) groups.set(key, {
29863
+ l: c.l,
29864
+ r: c.r,
29865
+ left: [],
29866
+ right: []
29867
+ });
29868
+ groups.get(key)[u.side === 0 ? "left" : "right"].push(u);
29869
+ }
29870
+ let out = /* @__PURE__ */ new Map();
29871
+ for (let g of groups.values()) {
29872
+ g.left.sort((a, b) => a.box.y1 - b.box.y1);
29873
+ g.right.sort((a, b) => a.box.y1 - b.box.y1);
29874
+ let n = g.left.length + g.right.length;
29875
+ let room = g.r - g.l - 20;
29876
+ let step = n > 1 ? Math.max(0, Math.min(6, room / (n - 1))) : 0;
29877
+ g.left.forEach((u, i) => {
29878
+ out.set(u.id, {
29879
+ lane: g.l + 10 + i * step,
29880
+ z: (u.above.y2 + u.box.y1) / 2,
29881
+ top: u.top
29882
+ });
29883
+ });
29884
+ g.right.forEach((u, j) => {
29885
+ out.set(u.id, {
29886
+ lane: g.r - 10 - j * step,
29887
+ z: (u.above.y2 + u.box.y1) / 2,
29888
+ top: u.top
29889
+ });
29890
+ });
29891
+ }
29892
+ return out;
29893
+ }
29666
29894
  /************************************************************
29667
29895
  * The control points of an elbow from the port `s` to the port
29668
29896
  * `t` ([x, y] each), knowing the boxes of the two cards
@@ -29670,34 +29898,32 @@ function swap_box(b) {
29670
29898
  *
29671
29899
  * vertical the rows are stacked (top to bottom); false
29672
29900
  * when they are side by side (left to right)
29673
- * stagger {dep, arr}: the edge's rank among the forward
29674
- * edges leaving its source card and among those
29675
- * reaching its target card (elbow_rank). Absent,
29676
- * the channel is the middle.
29901
+ * stagger {dep, arr, comb}: the edge's rank among the
29902
+ * forward edges leaving its source card and among
29903
+ * those reaching its target card (elbow_rank), and
29904
+ * its comb when the target is in a lower row of a
29905
+ * stack (elbow_combs). Absent, the channel is the
29906
+ * middle.
29677
29907
  ************************************************************/
29678
29908
  function elbow_points(s, t, box_s, box_t, lane, vertical, stagger) {
29679
29909
  if (!vertical) return elbow_points(swap(s), swap(t), swap_box(box_s), swap_box(box_t), lane, true, stagger).map(swap);
29680
29910
  let shift = lane * 10;
29681
29911
  if (t[1] - s[1] > 20) {
29912
+ let st = stagger || {};
29682
29913
  let lo = Math.max(s[1], box_s.y2);
29914
+ if (st.comb) {
29915
+ let c = st.comb;
29916
+ let turn = staggered_turn(lo, c.top, { dep: st.dep }) + shift;
29917
+ turn = Math.min(Math.max(turn, lo + 4), c.top - 4);
29918
+ return [
29919
+ [s[0], turn],
29920
+ [c.lane, turn],
29921
+ [c.lane, c.z],
29922
+ [t[0], c.z]
29923
+ ];
29924
+ }
29683
29925
  let hi = Math.min(t[1], box_t.y1);
29684
- let mid = (lo + hi) / 2;
29685
- let st = stagger || {};
29686
- let group = null;
29687
- let k = 0;
29688
- if (st.dep && st.dep.count > 1) {
29689
- group = st.dep;
29690
- k = st.dep.rank;
29691
- } else if (st.arr && st.arr.count > 1) {
29692
- group = st.arr;
29693
- k = st.arr.count - 1 - st.arr.rank;
29694
- }
29695
- if (group) {
29696
- let room = Math.max(0, hi - lo - 12);
29697
- let step = Math.min(10, room / (group.count - 1));
29698
- mid = mid - (group.count - 1) * step / 2 + k * step;
29699
- }
29700
- mid += shift;
29926
+ let mid = staggered_turn(lo, hi, st) + shift;
29701
29927
  mid = Math.min(Math.max(mid, lo + 4), hi - 4);
29702
29928
  return [[s[0], mid], [t[0], mid]];
29703
29929
  }
@@ -29916,6 +30142,7 @@ function orth_search(a, z, boxes) {
29916
30142
  function elbow_route(s, t, box_s, box_t, lane, vertical, boxes, stagger) {
29917
30143
  let others = boxes || [];
29918
30144
  if (!vertical) return elbow_route(swap(s), swap(t), swap_box(box_s), swap_box(box_t), lane, true, others.map(swap_box), stagger).map(swap);
30145
+ if (stagger && stagger.comb) return elbow_points(s, t, box_s, box_t, lane, true, stagger);
29919
30146
  let simple = elbow_points(s, t, box_s, box_t, lane, true, stagger);
29920
30147
  if (!elbow_path_hits([
29921
30148
  s,
@@ -59389,17 +59616,27 @@ function elbow_stagger_of(edge, vertical) {
59389
59616
  try {
59390
59617
  let ps = pos(s);
59391
59618
  let pt = pos(t);
59392
- let dep = graph.getRelatedEdgesData(s, "out").filter((d) => d.source === s && pos(d.target)[along] > ps[along]).map((d) => ({
59619
+ let leaving = graph.getRelatedEdgesData(s, "out").filter((d) => d.source === s && pos(d.target)[along] > ps[along]);
59620
+ let combs = elbow_combs(leaving.map((d) => ({
59393
59621
  id: d.id,
59394
- span: Math.abs(pos(d.target)[across] - port_of(s, d.style && d.style.sourcePort)[across])
59395
- }));
59622
+ box: elbow_box(element.getElement(d.target))
59623
+ })), vertical);
59624
+ let dep = leaving.map((d) => {
59625
+ let comb = combs.get(d.id);
59626
+ let far = comb ? comb.lane : pos(d.target)[across];
59627
+ return {
59628
+ id: d.id,
59629
+ span: Math.abs(far - port_of(s, d.style && d.style.sourcePort)[across])
59630
+ };
59631
+ });
59396
59632
  let arr = graph.getRelatedEdgesData(t, "in").filter((d) => d.target === t && pos(d.source)[along] < pt[along]).map((d) => ({
59397
59633
  id: d.id,
59398
59634
  span: Math.abs(port_of(d.source, d.style && d.style.sourcePort)[across] - pt[across])
59399
59635
  }));
59400
59636
  return {
59401
59637
  dep: elbow_rank(dep, edge.id),
59402
- arr: elbow_rank(arr, edge.id)
59638
+ arr: elbow_rank(arr, edge.id),
59639
+ comb: combs.get(edge.id) || null
59403
59640
  };
59404
59641
  } catch (e) {
59405
59642
  log_error(`elbow edge ${edge.id}: cannot read the edges sharing its ends: ${e}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.153",
3
+ "version": "7.23.154",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -133,7 +133,7 @@ import {
133
133
  fold_root_group_key,
134
134
  } from "./treedb_fold_model.js";
135
135
  import {layout_tree, layout_radial, layout_compact} from "./treedb_layout.js";
136
- import {elbow_lane, elbow_rank, elbow_route} from "./treedb_elbow.js";
136
+ import {elbow_lane, elbow_rank, elbow_route, elbow_combs} from "./treedb_elbow.js";
137
137
 
138
138
  import {
139
139
  BaseLayout,
@@ -4324,13 +4324,22 @@ function elbow_stagger_of(edge, vertical)
4324
4324
  try {
4325
4325
  let ps = pos(s);
4326
4326
  let pt = pos(t);
4327
- let dep = graph.getRelatedEdgesData(s, 'out')
4328
- .filter((d) => d.source === s && pos(d.target)[along] > ps[along])
4329
- .map((d) => ({
4327
+ let leaving = graph.getRelatedEdgesData(s, 'out')
4328
+ .filter((d) => d.source === s && pos(d.target)[along] > ps[along]);
4329
+ /* The cards they reach, for the combs of a stack; a comb's
4330
+ * run ends at its lane, so that is what its span measures. */
4331
+ let combs = elbow_combs(leaving.map((d) => ({
4332
+ id: d.id,
4333
+ box: elbow_box(element.getElement(d.target)),
4334
+ })), vertical);
4335
+ let dep = leaving.map((d) => {
4336
+ let comb = combs.get(d.id);
4337
+ let far = comb? comb.lane : pos(d.target)[across];
4338
+ return {
4330
4339
  id: d.id,
4331
- span: Math.abs(pos(d.target)[across] -
4332
- port_of(s, d.style && d.style.sourcePort)[across]),
4333
- }));
4340
+ span: Math.abs(far - port_of(s, d.style && d.style.sourcePort)[across]),
4341
+ };
4342
+ });
4334
4343
  let arr = graph.getRelatedEdgesData(t, 'in')
4335
4344
  .filter((d) => d.target === t && pos(d.source)[along] < pt[along])
4336
4345
  .map((d) => ({
@@ -4338,7 +4347,11 @@ function elbow_stagger_of(edge, vertical)
4338
4347
  span: Math.abs(port_of(d.source, d.style && d.style.sourcePort)[across] -
4339
4348
  pt[across]),
4340
4349
  }));
4341
- return {dep: elbow_rank(dep, edge.id), arr: elbow_rank(arr, edge.id)};
4350
+ return {
4351
+ dep: elbow_rank(dep, edge.id),
4352
+ arr: elbow_rank(arr, edge.id),
4353
+ comb: combs.get(edge.id) || null,
4354
+ };
4342
4355
  } catch(e) {
4343
4356
  log_error(`elbow edge ${edge.id}: cannot read the edges sharing its ends: ${e}`);
4344
4357
  return null;