@yuneta/gobj-ui 7.23.151 → 7.23.152

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 CHANGED
@@ -1457,8 +1457,11 @@ the lane, plus the two ends; a turn costs 40px of line), over the cards within
1457
1457
  400px first and all of them if that finds no way; with no way at all the
1458
1458
  simple elbow stays. A parent and the row of children under it keep their bus;
1459
1459
  what gets routed is a detour with a third card in its way, an edge dropping
1460
- several rows, an edge between two cards of one row. Ports are not obstacles,
1461
- only cards. G6's `shortest-path` router is not used: it is not exported, it
1460
+ several rows, an edge between two cards of one row. Since `7.23.152` a node is
1461
+ measured **whole** — card, ports and label — so a line keeps clear of the
1462
+ ports as well; the edge's own ports stick out of its own cards, so its first
1463
+ and last segments are not tested against their own box. G6's `shortest-path`
1464
+ router is not used: it is not exported, it
1462
1465
  falls back in silence to a route that crosses cards, and it rewrites its
1463
1466
  module defaults with every config it is given.
1464
1467
 
@@ -29669,8 +29669,8 @@ function elbow_points(s, t, box_s, box_t, lane, vertical) {
29669
29669
  }
29670
29670
  let out = 16 + Math.abs(shift);
29671
29671
  let side = lane < 0 ? Math.min(box_s.x1, box_t.x1) - 24 - Math.abs(shift) : Math.max(box_s.x2, box_t.x2) + 24 + Math.abs(shift);
29672
- let y1 = s[1] + out;
29673
- let y2 = t[1] - out;
29672
+ let y1 = Math.max(s[1], box_s.y2) + out;
29673
+ let y2 = Math.min(t[1], box_t.y1) - out;
29674
29674
  return [
29675
29675
  [s[0], y1],
29676
29676
  [side, y1],
@@ -29699,15 +29699,30 @@ function segment_hits(a, c, b) {
29699
29699
  let y2 = Math.max(a[1], c[1]);
29700
29700
  return x1 < b.x2 && x2 > b.x1 && y1 < b.y2 && y2 > b.y1;
29701
29701
  }
29702
+ function same_box(a, b) {
29703
+ return !!a && !!b && a.x1 === b.x1 && a.y1 === b.y1 && a.x2 === b.x2 && a.y2 === b.y2;
29704
+ }
29702
29705
  /************************************************************
29703
29706
  * Does the polyline `points` (its ends included) cross any of
29704
- * `boxes`? A card is a box shrunk by a pixel, so a line along
29705
- * its border or leaving from a port on it is not a hit.
29707
+ * `boxes`? A box is shrunk by a pixel, so a line along its
29708
+ * border is not a hit.
29709
+ *
29710
+ * A box is a whole node, its ports included, and the edge's own
29711
+ * ports stick out of its own two cards: its first segment starts
29712
+ * inside `box_s` and its last one ends inside `box_t`. Those two
29713
+ * segments are not tested against their own box; every other
29714
+ * segment is, so a line may not run back over its own cards.
29706
29715
  ************************************************************/
29707
- function elbow_path_hits(points, boxes) {
29716
+ function elbow_path_hits(points, boxes, box_s, box_t) {
29717
+ let last = points.length - 2;
29708
29718
  for (let b of boxes) {
29709
29719
  let inner = box_expand(b, -1);
29710
- for (let i = 0; i + 1 < points.length; i++) if (segment_hits(points[i], points[i + 1], inner)) return true;
29720
+ let own_s = same_box(b, box_s);
29721
+ let own_t = same_box(b, box_t);
29722
+ for (let i = 0; i <= last; i++) {
29723
+ if (own_s && i === 0 || own_t && i === last) continue;
29724
+ if (segment_hits(points[i], points[i + 1], inner)) return true;
29725
+ }
29711
29726
  }
29712
29727
  return false;
29713
29728
  }
@@ -29849,8 +29864,11 @@ function orth_search(a, z, boxes) {
29849
29864
  * port, the shortest way along the gaps between the cards, in.
29850
29865
  * A parent and the row of children under it keep their bus.
29851
29866
  *
29852
- * `boxes` are all the cards on screen, the two of the edge
29853
- * included. The route keeps a clearance from every card, which
29867
+ * `boxes` are all the nodes on screen, the two of the edge
29868
+ * included, each as the box of the WHOLE node -- card, ports and
29869
+ * label -- so a line clears the ports too. `box_s` and `box_t`
29870
+ * are the edge's own two, measured the same way. The route keeps
29871
+ * a clearance from every box, which
29854
29872
  * grows with the lane so that two routed edges of one pair do not
29855
29873
  * coincide. Nearby cards first; all of them if that finds no way;
29856
29874
  * the simple elbow if nothing does.
@@ -29863,10 +29881,10 @@ function elbow_route(s, t, box_s, box_t, lane, vertical, boxes) {
29863
29881
  s,
29864
29882
  ...simple,
29865
29883
  t
29866
- ], others)) return simple;
29884
+ ], others, box_s, box_t)) return simple;
29867
29885
  let m = 12 + Math.abs(lane) * 10;
29868
- let a = [s[0], s[1] + m + 4];
29869
- let z = [t[0], t[1] - m - 4];
29886
+ let a = [s[0], Math.max(s[1], box_s.y2) + m + 1];
29887
+ let z = [t[0], Math.min(t[1], box_t.y1) - m - 1];
29870
29888
  let region = box_expand({
29871
29889
  x1: Math.min(a[0], z[0], box_s.x1, box_t.x1),
29872
29890
  y1: Math.min(a[1], z[1], box_s.y1, box_t.y1),
@@ -59265,7 +59283,8 @@ var TreedbTreeLayout = class extends _antv_g6.BaseLayout {
59265
59283
  var ELBOW_RADIUS = 6;
59266
59284
  function elbow_box(node) {
59267
59285
  try {
59268
- let b = node.getShape("key").getBounds();
59286
+ let b = node.getRenderBounds();
59287
+ if (!b || b.isEmpty && b.isEmpty()) b = node.getShape("key").getBounds();
59269
59288
  return {
59270
59289
  x1: b.min[0],
59271
59290
  y1: b.min[1],
@@ -29664,8 +29664,8 @@ function elbow_points(s, t, box_s, box_t, lane, vertical) {
29664
29664
  }
29665
29665
  let out = 16 + Math.abs(shift);
29666
29666
  let side = lane < 0 ? Math.min(box_s.x1, box_t.x1) - 24 - Math.abs(shift) : Math.max(box_s.x2, box_t.x2) + 24 + Math.abs(shift);
29667
- let y1 = s[1] + out;
29668
- let y2 = t[1] - out;
29667
+ let y1 = Math.max(s[1], box_s.y2) + out;
29668
+ let y2 = Math.min(t[1], box_t.y1) - out;
29669
29669
  return [
29670
29670
  [s[0], y1],
29671
29671
  [side, y1],
@@ -29694,15 +29694,30 @@ function segment_hits(a, c, b) {
29694
29694
  let y2 = Math.max(a[1], c[1]);
29695
29695
  return x1 < b.x2 && x2 > b.x1 && y1 < b.y2 && y2 > b.y1;
29696
29696
  }
29697
+ function same_box(a, b) {
29698
+ return !!a && !!b && a.x1 === b.x1 && a.y1 === b.y1 && a.x2 === b.x2 && a.y2 === b.y2;
29699
+ }
29697
29700
  /************************************************************
29698
29701
  * Does the polyline `points` (its ends included) cross any of
29699
- * `boxes`? A card is a box shrunk by a pixel, so a line along
29700
- * its border or leaving from a port on it is not a hit.
29702
+ * `boxes`? A box is shrunk by a pixel, so a line along its
29703
+ * border is not a hit.
29704
+ *
29705
+ * A box is a whole node, its ports included, and the edge's own
29706
+ * ports stick out of its own two cards: its first segment starts
29707
+ * inside `box_s` and its last one ends inside `box_t`. Those two
29708
+ * segments are not tested against their own box; every other
29709
+ * segment is, so a line may not run back over its own cards.
29701
29710
  ************************************************************/
29702
- function elbow_path_hits(points, boxes) {
29711
+ function elbow_path_hits(points, boxes, box_s, box_t) {
29712
+ let last = points.length - 2;
29703
29713
  for (let b of boxes) {
29704
29714
  let inner = box_expand(b, -1);
29705
- for (let i = 0; i + 1 < points.length; i++) if (segment_hits(points[i], points[i + 1], inner)) return true;
29715
+ let own_s = same_box(b, box_s);
29716
+ let own_t = same_box(b, box_t);
29717
+ for (let i = 0; i <= last; i++) {
29718
+ if (own_s && i === 0 || own_t && i === last) continue;
29719
+ if (segment_hits(points[i], points[i + 1], inner)) return true;
29720
+ }
29706
29721
  }
29707
29722
  return false;
29708
29723
  }
@@ -29844,8 +29859,11 @@ function orth_search(a, z, boxes) {
29844
29859
  * port, the shortest way along the gaps between the cards, in.
29845
29860
  * A parent and the row of children under it keep their bus.
29846
29861
  *
29847
- * `boxes` are all the cards on screen, the two of the edge
29848
- * included. The route keeps a clearance from every card, which
29862
+ * `boxes` are all the nodes on screen, the two of the edge
29863
+ * included, each as the box of the WHOLE node -- card, ports and
29864
+ * label -- so a line clears the ports too. `box_s` and `box_t`
29865
+ * are the edge's own two, measured the same way. The route keeps
29866
+ * a clearance from every box, which
29849
29867
  * grows with the lane so that two routed edges of one pair do not
29850
29868
  * coincide. Nearby cards first; all of them if that finds no way;
29851
29869
  * the simple elbow if nothing does.
@@ -29858,10 +29876,10 @@ function elbow_route(s, t, box_s, box_t, lane, vertical, boxes) {
29858
29876
  s,
29859
29877
  ...simple,
29860
29878
  t
29861
- ], others)) return simple;
29879
+ ], others, box_s, box_t)) return simple;
29862
29880
  let m = 12 + Math.abs(lane) * 10;
29863
- let a = [s[0], s[1] + m + 4];
29864
- let z = [t[0], t[1] - m - 4];
29881
+ let a = [s[0], Math.max(s[1], box_s.y2) + m + 1];
29882
+ let z = [t[0], Math.min(t[1], box_t.y1) - m - 1];
29865
29883
  let region = box_expand({
29866
29884
  x1: Math.min(a[0], z[0], box_s.x1, box_t.x1),
29867
29885
  y1: Math.min(a[1], z[1], box_s.y1, box_t.y1),
@@ -59260,7 +59278,8 @@ var TreedbTreeLayout = class extends BaseLayout {
59260
59278
  var ELBOW_RADIUS = 6;
59261
59279
  function elbow_box(node) {
59262
59280
  try {
59263
- let b = node.getShape("key").getBounds();
59281
+ let b = node.getRenderBounds();
59282
+ if (!b || b.isEmpty && b.isEmpty()) b = node.getShape("key").getBounds();
59264
59283
  return {
59265
59284
  x1: b.min[0],
59266
59285
  y1: b.min[1],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.151",
3
+ "version": "7.23.152",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -4229,11 +4229,17 @@ class TreedbTreeLayout extends BaseLayout
4229
4229
  ************************************************************/
4230
4230
  const ELBOW_RADIUS = 6;
4231
4231
 
4232
- /* The box of a card on the canvas, for a detour to go round it. */
4232
+ /* The box of a WHOLE node on the canvas -- the card, its ports and
4233
+ * its label -- for an elbow to keep clear of. The ports are the
4234
+ * reason it is not the key shape's box: they stick out of the
4235
+ * card, and a line over a port reads as a link to it. */
4233
4236
  function elbow_box(node)
4234
4237
  {
4235
4238
  try {
4236
- let b = node.getShape('key').getBounds();
4239
+ let b = node.getRenderBounds();
4240
+ if(!b || b.isEmpty && b.isEmpty()) {
4241
+ b = node.getShape('key').getBounds();
4242
+ }
4237
4243
  return {x1: b.min[0], y1: b.min[1], x2: b.max[0], y2: b.max[1]};
4238
4244
  } catch(e) {
4239
4245
  log_error(`elbow edge: no bounds for node ${node && node.id}: ${e}`);
@@ -96,8 +96,10 @@ export function elbow_points(s, t, box_s, box_t, lane, vertical)
96
96
  let side = (lane < 0)
97
97
  ? Math.min(box_s.x1, box_t.x1) - ELBOW_CLEAR - Math.abs(shift)
98
98
  : Math.max(box_s.x2, box_t.x2) + ELBOW_CLEAR + Math.abs(shift);
99
- let y1 = s[1] + out; /* a hook port is on the bottom edge */
100
- let y2 = t[1] - out; /* an fkey port is on the top edge */
99
+ /* From the border of the whole box, which holds the ports: a
100
+ * port sticks out of its card, and the run has to clear it. */
101
+ let y1 = Math.max(s[1], box_s.y2) + out; /* a hook port is on the bottom edge */
102
+ let y2 = Math.min(t[1], box_t.y1) - out; /* an fkey port is on the top edge */
101
103
  return [[s[0], y1], [side, y1], [side, y2], [t[0], y2]];
102
104
  }
103
105
 
@@ -133,16 +135,33 @@ function segment_hits(a, c, b)
133
135
  return x1 < b.x2 && x2 > b.x1 && y1 < b.y2 && y2 > b.y1;
134
136
  }
135
137
 
138
+ function same_box(a, b)
139
+ {
140
+ return !!a && !!b && a.x1 === b.x1 && a.y1 === b.y1 && a.x2 === b.x2 && a.y2 === b.y2;
141
+ }
142
+
136
143
  /************************************************************
137
144
  * Does the polyline `points` (its ends included) cross any of
138
- * `boxes`? A card is a box shrunk by a pixel, so a line along
139
- * its border or leaving from a port on it is not a hit.
145
+ * `boxes`? A box is shrunk by a pixel, so a line along its
146
+ * border is not a hit.
147
+ *
148
+ * A box is a whole node, its ports included, and the edge's own
149
+ * ports stick out of its own two cards: its first segment starts
150
+ * inside `box_s` and its last one ends inside `box_t`. Those two
151
+ * segments are not tested against their own box; every other
152
+ * segment is, so a line may not run back over its own cards.
140
153
  ************************************************************/
141
- export function elbow_path_hits(points, boxes)
154
+ export function elbow_path_hits(points, boxes, box_s, box_t)
142
155
  {
156
+ let last = points.length - 2;
143
157
  for(let b of boxes) {
144
158
  let inner = box_expand(b, -1);
145
- for(let i = 0; i + 1 < points.length; i++) {
159
+ let own_s = same_box(b, box_s);
160
+ let own_t = same_box(b, box_t);
161
+ for(let i = 0; i <= last; i++) {
162
+ if((own_s && i === 0) || (own_t && i === last)) {
163
+ continue;
164
+ }
146
165
  if(segment_hits(points[i], points[i + 1], inner)) {
147
166
  return true;
148
167
  }
@@ -332,8 +351,11 @@ function orth_search(a, z, boxes)
332
351
  * port, the shortest way along the gaps between the cards, in.
333
352
  * A parent and the row of children under it keep their bus.
334
353
  *
335
- * `boxes` are all the cards on screen, the two of the edge
336
- * included. The route keeps a clearance from every card, which
354
+ * `boxes` are all the nodes on screen, the two of the edge
355
+ * included, each as the box of the WHOLE node -- card, ports and
356
+ * label -- so a line clears the ports too. `box_s` and `box_t`
357
+ * are the edge's own two, measured the same way. The route keeps
358
+ * a clearance from every box, which
337
359
  * grows with the lane so that two routed edges of one pair do not
338
360
  * coincide. Nearby cards first; all of them if that finds no way;
339
361
  * the simple elbow if nothing does.
@@ -347,13 +369,14 @@ export function elbow_route(s, t, box_s, box_t, lane, vertical, boxes)
347
369
  }
348
370
 
349
371
  let simple = elbow_points(s, t, box_s, box_t, lane, true);
350
- if(!elbow_path_hits([s, ...simple, t], others)) {
372
+ if(!elbow_path_hits([s, ...simple, t], others, box_s, box_t)) {
351
373
  return simple;
352
374
  }
353
375
 
354
376
  let m = ELBOW_CLEAR / 2 + Math.abs(lane) * ELBOW_STEP;
355
- let a = [s[0], s[1] + m + 4]; /* out of a bottom port */
356
- let z = [t[0], t[1] - m - 4]; /* into a top port */
377
+ /* Just outside the grown box of each end, which holds its ports. */
378
+ let a = [s[0], Math.max(s[1], box_s.y2) + m + 1]; /* out of a bottom port */
379
+ let z = [t[0], Math.min(t[1], box_t.y1) - m - 1]; /* into a top port */
357
380
  let region = box_expand({
358
381
  x1: Math.min(a[0], z[0], box_s.x1, box_t.x1),
359
382
  y1: Math.min(a[1], z[1], box_s.y1, box_t.y1),
@@ -165,6 +165,43 @@ describe("the route round the other cards", () => {
165
165
  });
166
166
  });
167
167
 
168
+ describe("the ports are obstacles too", () => {
169
+ /* Whole-node boxes: a port of 14px sticks 7px out of the bottom
170
+ * and the top of each card. */
171
+ const AP = {x1: 0, y1: -7, x2: 100, y2: 47};
172
+ const BP = {x1: 350, y1: 123, x2: 450, y2: 177};
173
+ const S = [50, 40];
174
+ const T2 = [400, 130];
175
+
176
+ test("an edge's own ports do not make its line a hit", () => {
177
+ let simple = elbow_points(S, T2, AP, BP, 0, true);
178
+ expect(elbow_path_hits([S, ...simple, T2], [AP, BP], AP, BP)).toBe(false);
179
+ expect(elbow_route(S, T2, AP, BP, 0, true, [AP, BP])).toEqual(simple);
180
+ });
181
+
182
+ test("a port of a third card in the channel is gone round", () => {
183
+ /* F's card ends at y 80, above the channel (85); its port
184
+ * reaches down to 95, into it. */
185
+ const F_CARD = {x1: 200, y1: 0, x2: 300, y2: 80};
186
+ const F = {x1: 200, y1: -7, x2: 300, y2: 95};
187
+ let simple = elbow_points(S, T2, AP, BP, 0, true);
188
+ expect(elbow_path_hits([S, ...simple, T2], [AP, BP, F_CARD], AP, BP)).toBe(false);
189
+ expect(elbow_path_hits([S, ...simple, T2], [AP, BP, F], AP, BP)).toBe(true);
190
+ let route = elbow_route(S, T2, AP, BP, 0, true, [AP, BP, F]);
191
+ expect(elbow_path_hits([S, ...route, T2], [AP, BP, F], AP, BP)).toBe(false);
192
+ });
193
+
194
+ test("a detour clears the ports of its own two cards", () => {
195
+ /* B below A, both with their ports: B -> A goes round. */
196
+ const AB = {x1: 0, y1: -7, x2: 100, y2: 47};
197
+ const BB = {x1: 0, y1: 123, x2: 100, y2: 177};
198
+ let pts = elbow_points([60, 170], [50, 0], BB, AB, 0, true);
199
+ expect(pts[0][1]).toBeGreaterThan(BB.y2);
200
+ expect(pts[3][1]).toBeLessThan(AB.y1);
201
+ expect(elbow_path_hits([[60, 170], ...pts, [50, 0]], [AB, BB], BB, AB)).toBe(false);
202
+ });
203
+ });
204
+
168
205
  describe("read left to right", () => {
169
206
  test("is the same geometry with the axes swapped", () => {
170
207
  let sw = (p) => [p[1], p[0]];