@hpcc-js/graph 3.2.1 → 3.3.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.
Files changed (97) hide show
  1. package/dist/assets/{dagre-D0bY8RPD.js.map → dagre-BVMQn91n.js.map} +1 -1
  2. package/dist/assets/graphviz-CMhlTALo.js.map +1 -1
  3. package/dist/index.js +4741 -1992
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.umd.cjs +148 -7
  6. package/dist/index.umd.cjs.map +1 -1
  7. package/package.json +4 -3
  8. package/src/__tests__/test5.ts +1 -1
  9. package/src/common/graphT.css +33 -0
  10. package/src/{graph2 → common}/graphT.ts +78 -47
  11. package/src/common/index.ts +3 -0
  12. package/src/{graph2 → common}/layouts/dagre.ts +16 -3
  13. package/src/{graph2 → common}/layouts/graphviz.ts +17 -4
  14. package/src/{graph2 → common}/layouts/layout.ts +48 -5
  15. package/src/common/layouts/pathIntersection.ts +67 -0
  16. package/src/{graph2 → common}/layouts/placeholders.ts +12 -0
  17. package/src/{graph2 → common}/layouts/tree.ts +2 -4
  18. package/src/{graph2 → common}/sankeyGraph.ts +2 -2
  19. package/src/html/annotation.ts +71 -0
  20. package/src/html/component.ts +14 -0
  21. package/src/html/graphHtml.ts +10 -0
  22. package/src/html/graphHtmlT.ts +49 -0
  23. package/src/html/icon.ts +64 -0
  24. package/src/html/image.ts +26 -0
  25. package/src/html/imageChar.ts +18 -0
  26. package/src/html/index.ts +8 -0
  27. package/src/html/shape.ts +136 -0
  28. package/src/html/text.ts +59 -0
  29. package/src/html/textBox.ts +41 -0
  30. package/src/html/vertex.ts +65 -0
  31. package/src/index.ts +3 -1
  32. package/src/{graph2 → react}/dataGraph.ts +11 -11
  33. package/src/{graph2/graph.ts → react/graphReact.ts} +65 -22
  34. package/src/{graph2 → react}/graphReactT.ts +1 -1
  35. package/src/react/index.ts +4 -0
  36. package/src/{graph2 → react}/subgraph.tsx +2 -1
  37. package/src/{graph2 → react}/vertex.tsx +2 -1
  38. package/types/{graph2 → common}/graphT.d.ts +9 -11
  39. package/types/common/index.d.ts +3 -0
  40. package/types/{graph2 → common}/layouts/layout.d.ts +18 -1
  41. package/types/common/layouts/pathIntersection.d.ts +24 -0
  42. package/types/{graph2 → common}/layouts/placeholders.d.ts +6 -0
  43. package/types/{graph2 → common}/sankeyGraph.d.ts +2 -2
  44. package/types/html/annotation.d.ts +13 -0
  45. package/types/html/component.d.ts +10 -0
  46. package/types/html/graphHtml.d.ts +5 -0
  47. package/types/html/graphHtmlT.d.ts +9 -0
  48. package/types/html/icon.d.ts +15 -0
  49. package/types/html/image.d.ts +8 -0
  50. package/types/html/imageChar.d.ts +2 -0
  51. package/types/html/index.d.ts +8 -0
  52. package/types/html/shape.d.ts +31 -0
  53. package/types/html/text.d.ts +10 -0
  54. package/types/html/textBox.d.ts +11 -0
  55. package/types/html/vertex.d.ts +17 -0
  56. package/types/index.d.ts +3 -1
  57. package/types/{graph2 → react}/dataGraph.d.ts +2 -2
  58. package/types/{graph2/graph.d.ts → react/graphReact.d.ts} +13 -3
  59. package/types/{graph2 → react}/graphReactT.d.ts +1 -1
  60. package/types/react/index.d.ts +4 -0
  61. package/types/{graph2 → react}/subgraph.d.ts +1 -1
  62. package/types/{graph2 → react}/vertex.d.ts +1 -1
  63. package/src/graph2/graph.css +0 -34
  64. package/src/graph2/index.ts +0 -7
  65. package/src/test.ts +0 -649
  66. package/types/graph2/index.d.ts +0 -7
  67. /package/src/{graph2 → common}/layouts/circle.ts +0 -0
  68. /package/src/{graph2 → common}/layouts/dagreWorker.ts +0 -0
  69. /package/src/{graph2 → common}/layouts/forceDirected.ts +0 -0
  70. /package/src/{graph2 → common}/layouts/forceDirectedWorker.ts +0 -0
  71. /package/src/{graph2 → common}/layouts/geoForceDirected.ts +0 -0
  72. /package/src/{graph2 → common}/layouts/graphvizWorker.ts +0 -0
  73. /package/src/{graph2 → common}/layouts/index.ts +0 -0
  74. /package/src/{graph2 → common}/layouts/null.ts +0 -0
  75. /package/src/{graph2 → common}/layouts/workers/dagre.ts +0 -0
  76. /package/src/{graph2 → common}/layouts/workers/dagreOptions.ts +0 -0
  77. /package/src/{graph2 → common}/layouts/workers/forceDirected.ts +0 -0
  78. /package/src/{graph2 → common}/layouts/workers/forceDirectedOptions.ts +0 -0
  79. /package/src/{graph2 → common}/layouts/workers/graphviz.ts +0 -0
  80. /package/src/{graph2 → common}/layouts/workers/graphvizOptions.ts +0 -0
  81. /package/src/{graph2 → common}/liteMap.ts +0 -0
  82. /package/src/{graph2 → common}/liteSVGZooom.ts +0 -0
  83. /package/src/{graph2 → common}/sankeyGraph.css +0 -0
  84. /package/types/{graph2 → common}/layouts/circle.d.ts +0 -0
  85. /package/types/{graph2 → common}/layouts/dagre.d.ts +0 -0
  86. /package/types/{graph2 → common}/layouts/dagreWorker.d.ts +0 -0
  87. /package/types/{graph2 → common}/layouts/forceDirected.d.ts +0 -0
  88. /package/types/{graph2 → common}/layouts/forceDirectedWorker.d.ts +0 -0
  89. /package/types/{graph2 → common}/layouts/geoForceDirected.d.ts +0 -0
  90. /package/types/{graph2 → common}/layouts/graphviz.d.ts +0 -0
  91. /package/types/{graph2 → common}/layouts/graphvizWorker.d.ts +0 -0
  92. /package/types/{graph2 → common}/layouts/index.d.ts +0 -0
  93. /package/types/{graph2 → common}/layouts/null.d.ts +0 -0
  94. /package/types/{graph2 → common}/layouts/tree.d.ts +0 -0
  95. /package/types/{graph2 → common}/layouts/workers/dagreOptions.d.ts +0 -0
  96. /package/types/{graph2 → common}/layouts/workers/forceDirectedOptions.d.ts +0 -0
  97. /package/types/{graph2 → common}/layouts/workers/graphvizOptions.d.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hpcc-js/graph",
3
- "version": "3.2.1",
3
+ "version": "3.3.0",
4
4
  "description": "hpcc-js - Viz Graph",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.cjs",
@@ -56,7 +56,8 @@
56
56
  "d3-shape": "^1",
57
57
  "d3-tile": "^1",
58
58
  "d3-transition": "^1",
59
- "dagre": "0.8.5"
59
+ "dagre": "0.8.5",
60
+ "lit-html": "3.2.1"
60
61
  },
61
62
  "repository": {
62
63
  "type": "git",
@@ -69,5 +70,5 @@
69
70
  "url": "https://github.com/hpcc-systems/Visualization/issues"
70
71
  },
71
72
  "homepage": "https://github.com/hpcc-systems/Visualization",
72
- "gitHead": "ed4453ec6a4a8af9c0beb11b46c7f4365bc0669e"
73
+ "gitHead": "220704148d2a9cc72a4bfa5160107d6fdf37c914"
73
74
  }
@@ -1,5 +1,5 @@
1
1
  import { Vertex4, CentroidVertex4 } from "@hpcc-js/react";
2
- import { Graph2 } from "../graph2/graph";
2
+ import { GraphReact } from "../react/graphReact.ts";
3
3
 
4
4
  const VERTEX_ARR = [{
5
5
  id: 0,
@@ -0,0 +1,33 @@
1
+ .graph_GraphT .graphVertex {
2
+ cursor: pointer;
3
+ }
4
+
5
+ .graph_GraphT .allowDragging .graphVertex {
6
+ cursor: pointer;
7
+ }
8
+
9
+ .graph_GraphT .allowDragging .graphVertex.grabbed {
10
+ cursor: grabbing;
11
+ }
12
+
13
+ .graph_GraphT .graphEdge {
14
+ stroke: darkgray;
15
+ fill: none;
16
+ }
17
+
18
+ .graph_GraphT .graphEdge>text {
19
+ stroke: none;
20
+ fill: black;
21
+ }
22
+
23
+ .graph_GraphT .graphEdge.hide-text>text {
24
+ display: none;
25
+ }
26
+
27
+ .graph_GraphT g.selected rect {
28
+ stroke: navy !important;
29
+ }
30
+
31
+ .graph_GraphT g.selected circle {
32
+ stroke: navy !important;
33
+ }
@@ -1,5 +1,4 @@
1
- import { d3Event, drag as d3Drag, Palette, select as d3Select, Selection, Spacer, SVGGlowFilter, SVGZoomWidget, ToggleButton, Utility, Widget, BaseType as D3BaseType } from "@hpcc-js/common";
2
- import { IconEx, Icons, render } from "@hpcc-js/react";
1
+ import { d3Event, drag as d3Drag, Palette, select as d3Select, Selection, Spacer, SVGGlowFilter, SVGZoomWidget, Button, ToggleButton, Utility, Widget } from "@hpcc-js/common";
3
2
  import { Graph2 as GraphCollection, hashSum } from "@hpcc-js/util";
4
3
  import { HTMLTooltip } from "@hpcc-js/html";
5
4
  import { interpolateNumberArray as d3InterpolateNumberArray } from "d3-interpolate";
@@ -7,17 +6,18 @@ import "d3-transition";
7
6
  import { interpolatePath as d3InterpolatePath } from "d3-interpolate-path";
8
7
  import { Circle, Dagre, ForceDirected, ForceDirectedAnimated, Graphviz, ILayout, Null } from "./layouts/index.ts";
9
8
  import { Options as FDOptions } from "./layouts/forceDirectedWorker.ts";
10
- import type { VertexBaseProps, EdgeBaseProps, GraphDataProps, HierarchyBase, SubgraphBaseProps } from "./layouts/placeholders.ts";
9
+ import type { BaseProps, VertexBaseProps, EdgeBaseProps, GraphDataProps, HierarchyBase, SubgraphBaseProps } from "./layouts/placeholders.ts";
11
10
  import { EdgePlaceholder, SubgraphPlaceholder, VertexPlaceholder, isEdgePlaceholder } from "./layouts/placeholders.ts";
12
11
  import { Engine, graphviz as gvWorker } from "./layouts/graphvizWorker.ts";
13
12
  import { Tree, RadialTree, Dendrogram, RadialDendrogram } from "./layouts/tree.ts";
14
13
 
15
- import "../../src/graph2/graph.css";
14
+ import "./graphT.css";
16
15
 
17
16
  let scriptDir = (globalThis?.document?.currentScript as HTMLScriptElement)?.src ?? "./dummy.js";
18
17
  scriptDir = scriptDir.substring(0, scriptDir.replace(/[?#].*/, "").lastIndexOf("/") + 1);
19
18
 
20
19
  export {
20
+ BaseProps,
21
21
  GraphDataProps,
22
22
  SubgraphBaseProps,
23
23
  VertexBaseProps,
@@ -43,7 +43,7 @@ function dragEnd<V extends VertexBaseProps>(n: VertexPlaceholder<V>) {
43
43
  n.fy = n.sy = undefined;
44
44
  }
45
45
 
46
- export type RendererT<T> = (props: T, element: SVGGElement) => void;
46
+ export type RendererT<T> = (props: T, element: SVGGElement) => unknown;
47
47
 
48
48
  export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E extends EdgeBaseProps<V>> extends SVGZoomWidget {
49
49
 
@@ -121,6 +121,8 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
121
121
  ];
122
122
  this._iconBar.buttons(buttons.concat(this._iconBar.buttons()));
123
123
 
124
+ this.selectionGlowColor("navy");
125
+
124
126
  this._dragHandler
125
127
  .on("start", function (d) {
126
128
  if (context.allowDragging()) {
@@ -241,24 +243,6 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
241
243
  return this._iconBar.buttons();
242
244
  }
243
245
 
244
- protected _categories: IconEx[] = [];
245
- categories(): IconEx[];
246
- categories(_: IconEx[]): this;
247
- categories(_?: IconEx[]): IconEx[] | this {
248
- if (_ === void 0) return this._categories;
249
- this._categories = _;
250
- return this;
251
- }
252
-
253
- protected _annotations: IconEx[] = [];
254
- annotations(): IconEx[];
255
- annotations(_: IconEx[]): this;
256
- annotations(_?: IconEx[]): IconEx[] | this {
257
- if (_ === void 0) return this._annotations;
258
- this._annotations = _;
259
- return this;
260
- }
261
-
262
246
  private _origData: GraphDataProps<SG, V, E> = {
263
247
  subgraphs: [],
264
248
  vertices: [],
@@ -485,7 +469,7 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
485
469
  ...context.vertexMapper(d.props, d.props.origData)
486
470
  }
487
471
  );
488
- context._vertexRenderer(props, this);
472
+ d.renderResult = context._vertexRenderer(props, this);
489
473
  }
490
474
  })
491
475
  .transition().duration(this.transitionDuration())
@@ -579,7 +563,10 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
579
563
 
580
564
  moveEdgePlaceholder(ep: EdgePlaceholder<V, E>, transition: boolean): this {
581
565
  const edgeLayout = {
582
- ...this._layoutAlgo.edgePath(ep as any, this.edgeArcDepth())
566
+ ...this._layoutAlgo.edgePath(ep as any, this.edgeArcDepth()),
567
+ markerStart: `url(#${this.id()}_circleFoot)`,
568
+ markerEnd: `url(#${this.id()}_arrowHead)`,
569
+
583
570
  };
584
571
  const context = this;
585
572
  if (this._edgeRenderer && ep.element) {
@@ -596,7 +583,7 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
596
583
  strokeWidth: ep.props.strokeWidth ?? context.edgeStrokeWidth(),
597
584
  color: ep.props.stroke ?? context.edgeColor()
598
585
  };
599
- context._edgeRenderer({ ...edgeLayout, ...ep.props, ...updated }, ep.element.node());
586
+ ep.renderResult = context._edgeRenderer({ graphInstance: context, ...edgeLayout, ...ep.props, ...updated }, ep.element.node());
600
587
  };
601
588
  });
602
589
  }
@@ -639,7 +626,7 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
639
626
  if (this._transformScale > this.maxScale() + (this._transformScale - this.maxScale()) / 2) {
640
627
  scale = this.maxScale() + (this._transformScale - this.maxScale()) / 2;
641
628
  } else if (this._transformScale < this.minScale() - (this._transformScale - this.minScale()) / 13) {
642
- // scale = this.minScale() - (this._transformScale - this.minScale()) / 13;
629
+ scale = this.minScale() - (this._transformScale - this.minScale()) / 13;
643
630
  }
644
631
  }
645
632
  return Math.round(pos * scale * rf) / rf;
@@ -654,7 +641,14 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
654
641
  projectPlacholder(vp: VertexPlaceholder<V>) {
655
642
  return {
656
643
  x: this.project(vp.fx !== undefined ? vp.fx : vp.x),
657
- y: this.project(vp.fy !== undefined ? vp.fy : vp.y)
644
+ y: this.project(vp.fy !== undefined ? vp.fy : vp.y),
645
+ };
646
+ }
647
+
648
+ rprojectPlacholder(vp: VertexPlaceholder<V>) {
649
+ return {
650
+ x: this.rproject(vp.fx !== undefined ? vp.fx : vp.x),
651
+ y: this.rproject(vp.fy !== undefined ? vp.fy : vp.y),
658
652
  };
659
653
  }
660
654
 
@@ -663,26 +657,9 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
663
657
  }
664
658
 
665
659
  updateCategories() {
666
- render(Icons, {
667
- icons: this._categories.map((c): IconEx => ({
668
- ...c,
669
- id: this.categoryID(c.id),
670
- fill: c.fill || "transparent",
671
- imageCharFill: c.imageCharFill || this._catPalette(c.id)
672
- }))
673
- }, this._svgDefsCat.node());
674
660
  }
675
661
 
676
662
  updateAnnotations() {
677
- render(Icons, {
678
- icons: this._annotations.map((c): IconEx => ({
679
- ...c,
680
- id: this.categoryID(c.id, "ann"),
681
- shape: c.shape || "square",
682
- height: c.height || 12,
683
- fill: c.fill || this._catPalette(c.id)
684
- }))
685
- }, this._svgDefsAnn.node());
686
663
  }
687
664
 
688
665
  private _edgeRenderer: RendererT<E>;
@@ -831,7 +808,7 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
831
808
  ...context.vertexMapper(d.props, d.props.origData)
832
809
  }
833
810
  );
834
- context._vertexRenderer(props, this);
811
+ d.renderResult = context._vertexRenderer(props, this);
835
812
  })
836
813
  ;
837
814
  return this;
@@ -900,15 +877,66 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
900
877
  return this;
901
878
  }
902
879
 
880
+ addMarkers(clearFirst: boolean = false) {
881
+ if (clearFirst) {
882
+ this._svgDefs.select("#" + this._id + "_arrowHead").remove();
883
+ this._svgDefs.select("#" + this._id + "_circleFoot").remove();
884
+ this._svgDefs.select("#" + this._id + "_circleHead").remove();
885
+ }
886
+ this._svgDefs.append("marker")
887
+ .attr("class", "marker")
888
+ .attr("id", this._id + "_arrowHead")
889
+ .attr("viewBox", "0 0 10 10")
890
+ .attr("refX", 10)
891
+ .attr("refY", 5)
892
+ .attr("markerWidth", 8)
893
+ .attr("markerHeight", 8)
894
+ .attr("markerUnits", "strokeWidth")
895
+ .attr("orient", "auto")
896
+ .append("polyline")
897
+ .attr("points", "0,0 10,5 0,10 1,5")
898
+ ;
899
+ this._svgDefs.append("marker")
900
+ .attr("class", "marker")
901
+ .attr("id", this._id + "_circleFoot")
902
+ .attr("viewBox", "0 0 10 10")
903
+ .attr("refX", 1)
904
+ .attr("refY", 5)
905
+ .attr("markerWidth", 7)
906
+ .attr("markerHeight", 7)
907
+ .attr("markerUnits", "strokeWidth")
908
+ .attr("orient", "auto")
909
+ .append("circle")
910
+ .attr("cx", 5)
911
+ .attr("cy", 5)
912
+ .attr("r", 4)
913
+ ;
914
+ this._svgDefs.append("marker")
915
+ .attr("class", "marker")
916
+ .attr("id", this._id + "_circleHead")
917
+ .attr("viewBox", "0 0 10 10")
918
+ .attr("refX", 9)
919
+ .attr("refY", 5)
920
+ .attr("markerWidth", 7)
921
+ .attr("markerHeight", 7)
922
+ .attr("markerUnits", "strokeWidth")
923
+ .attr("orient", "auto")
924
+ .append("circle")
925
+ .attr("cx", 5)
926
+ .attr("cy", 5)
927
+ .attr("r", 4)
928
+ ;
929
+ }
930
+
903
931
  enter(domNode, element) {
904
932
  super.enter(domNode, element);
905
933
 
906
934
  const svg = this.locateSVGNode(domNode);
907
935
  this._svgDefs = d3Select(svg).select<SVGDefsElement>("defs");
908
936
  this._centroidFilter = new SVGGlowFilter(this._svgDefs, this._id + "_glow");
909
-
910
937
  this._svgDefsCat = this._svgDefs.append("g");
911
938
  this._svgDefsAnn = this._svgDefs.append("g");
939
+ this.addMarkers();
912
940
  this._subgraphG = this._renderElement.append("g");
913
941
  this._edgeG = this._renderElement.append("g");
914
942
  this._vertexG = this._renderElement.append("g");
@@ -1036,6 +1064,9 @@ export class GraphT<SG extends SubgraphBaseProps, V extends VertexBaseProps, E e
1036
1064
  super.update(domNode, element);
1037
1065
 
1038
1066
  this._renderElement.classed("allowDragging", this.allowDragging());
1067
+ if (this._centroidFilter) {
1068
+ this._centroidFilter.update(this.selectionGlowColor());
1069
+ }
1039
1070
 
1040
1071
  this.updateCategories();
1041
1072
  this.updateAnnotations();
@@ -0,0 +1,3 @@
1
+ export * from "../react/dataGraph.ts";
2
+ export * from "./graphT.ts";
3
+ export * from "./sankeyGraph.ts";
@@ -111,9 +111,22 @@ export class Dagre extends Layout {
111
111
  let points = [];
112
112
  let hasNaN = false;
113
113
  if (ep.points) {
114
- points = ep.points.map(p => {
115
- const x = this._graph.project(p[0], false);
116
- const y = this._graph.project(p[1], false);
114
+ const line = this.edgeLine(ep);
115
+ points = ep.points.map((p, idx) => {
116
+ let x = NaN;
117
+ let y = NaN;
118
+ if (idx === 0) {
119
+ x = this._graph.rproject(line.source.x);
120
+ y = this._graph.rproject(line.source.y);
121
+ } else if (idx === ep.points.length - 1) {
122
+ x = this._graph.rproject(line.target.x);
123
+ y = this._graph.rproject(line.target.y);
124
+ } else {
125
+ x = p[0];
126
+ y = p[1];
127
+ }
128
+ x = this._graph.project(x, false);
129
+ y = this._graph.project(y, false);
117
130
  if (isNaN(x) || isNaN(y)) {
118
131
  hasNaN = true;
119
132
  }
@@ -38,7 +38,7 @@ export class Graphviz extends Layout {
38
38
  case "vertex":
39
39
  delete item["fx"];
40
40
  delete item["fy"];
41
- const bbox = item.element.node().getBBox();
41
+ const bbox = this.vertexSize(item);
42
42
  const retVal = {
43
43
  id: item.id,
44
44
  text: item.props.text,
@@ -102,9 +102,22 @@ export class Graphviz extends Layout {
102
102
  let points = [];
103
103
  let hasNaN = false;
104
104
  if (ep.points) {
105
- points = ep.points.map(p => {
106
- const x = this._graph.project(p[0], false);
107
- const y = this._graph.project(p[1], false);
105
+ const line = this.edgeLine(ep);
106
+ points = ep.points.map((p, idx) => {
107
+ let x = NaN;
108
+ let y = NaN;
109
+ if (idx === 0) {
110
+ x = this._graph.rproject(line.source.x);
111
+ y = this._graph.rproject(line.source.y);
112
+ } else if (idx === ep.points.length - 1) {
113
+ x = this._graph.rproject(line.target.x);
114
+ y = this._graph.rproject(line.target.y);
115
+ } else {
116
+ x = p[0];
117
+ y = p[1];
118
+ }
119
+ x = this._graph.project(x, false);
120
+ y = this._graph.project(y, false);
108
121
  if (isNaN(x) || isNaN(y)) {
109
122
  hasNaN = true;
110
123
  }
@@ -2,9 +2,20 @@ import { Graph2 as GraphCollection } from "@hpcc-js/util";
2
2
  import { curveBasis as d3CurveBasis, curveCardinal as d3CurveCardinal, line as d3Line } from "d3-shape";
3
3
  import { EdgePlaceholder, SubgraphPlaceholder, VertexPlaceholder } from "./placeholders.ts";
4
4
  import { EdgeLayout } from "./tree.ts";
5
+ import { intersection } from "./pathIntersection.ts";
5
6
 
6
7
  export type Point = [number, number];
7
8
 
9
+ interface Position {
10
+ x: number;
11
+ y: number;
12
+ }
13
+
14
+ interface Line {
15
+ source: Position;
16
+ target: Position;
17
+ }
18
+
8
19
  const lineBasis = d3Line<Point>()
9
20
  .x(d => d[0])
10
21
  .y(d => d[1])
@@ -27,11 +38,15 @@ export interface ILayout {
27
38
 
28
39
  export type Size = { width: number, height: number };
29
40
  export interface IGraph {
41
+ id(): string;
30
42
  size(): Size;
31
43
  graphData(): GraphCollection<VertexPlaceholder, EdgePlaceholder, SubgraphPlaceholder>;
32
44
 
33
- project(pos: number, clip: boolean);
45
+ project(pos: number, clip?: boolean);
46
+ rproject(pos: number);
47
+
34
48
  projectPlacholder(vp: VertexPlaceholder);
49
+ rprojectPlacholder(vp: VertexPlaceholder);
35
50
 
36
51
  moveSubgraphs(transition: boolean): this;
37
52
 
@@ -78,10 +93,38 @@ export class Layout implements ILayout {
78
93
  return [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
79
94
  }
80
95
 
96
+ vertexSize(vp: VertexPlaceholder): { width: number, height: number } {
97
+ const size = vp.renderResult?.extent ? vp.renderResult.extent : vp.element.node().getBBox();
98
+ const retVal = {
99
+ width: this._graph.rproject(size.width),
100
+ height: this._graph.rproject(size.height)
101
+ };
102
+ return retVal;
103
+ }
104
+
105
+ edgeLine(ep: EdgePlaceholder): Line {
106
+ const sPos = { ...this._graph.projectPlacholder(ep.source), w: ep.source?.renderResult?.extent?.width ?? 0, h: ep.source?.renderResult?.extent?.height ?? 0 };
107
+ const tPos = { ...this._graph.projectPlacholder(ep.target), w: ep.target?.renderResult?.extent?.width ?? 0, h: ep.target?.renderResult?.extent?.height ?? 0 };
108
+ const sIntersect = intersection(sPos, { start: sPos, end: tPos });
109
+ const tIntersect = intersection(tPos, { start: sPos, end: tPos });
110
+ return {
111
+ source: {
112
+ x: sIntersect ? sIntersect.x : sPos.x,
113
+ y: sIntersect ? sIntersect.y : sPos.y
114
+ },
115
+ target: {
116
+ x: tIntersect ? tIntersect.x : tPos.x,
117
+ y: tIntersect ? tIntersect.y : tPos.y
118
+ }
119
+ };
120
+ }
121
+
81
122
  edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
82
- const sPos = this._graph.projectPlacholder(ep.source);
83
- const tPos = this._graph.projectPlacholder(ep.target);
84
- const points: Point[] = [[sPos.x, sPos.y], [tPos.x, tPos.y]];
123
+ const line = this.edgeLine(ep);
124
+ const points: Point[] = [
125
+ [line.source.x, line.source.y],
126
+ [line.target.x, line.target.y]
127
+ ];
85
128
 
86
129
  if (curveDepth) {
87
130
  const dx = points[0][0] - points[1][0];
@@ -92,7 +135,7 @@ export class Layout implements ILayout {
92
135
  const midY = (points[0][1] + points[1][1]) / 2 + dx * curveDepth / 100;
93
136
  return {
94
137
  path: lineCardinal([points[0], [midX, midY], points[1]]),
95
- labelPos: [midX, midY]
138
+ labelPos: [midX, midY],
96
139
  };
97
140
  }
98
141
  }
@@ -0,0 +1,67 @@
1
+ export interface Pos {
2
+ x: number;
3
+ y: number;
4
+ }
5
+
6
+ export interface Segment {
7
+ start: Pos;
8
+ end: Pos;
9
+ }
10
+
11
+ export interface Rectangle {
12
+ topLeft: Pos;
13
+ topRight: Pos;
14
+ bottomLeft: Pos;
15
+ bottomRight: Pos;
16
+ }
17
+
18
+ export interface Rectangle2 {
19
+ x: number;
20
+ y: number;
21
+ w: number;
22
+ h: number;
23
+ }
24
+
25
+ function rectEdges(rect: Rectangle) {
26
+ return [
27
+ { start: rect.topLeft, end: rect.topRight },
28
+ { start: rect.topRight, end: rect.bottomRight },
29
+ { start: rect.bottomRight, end: rect.bottomLeft },
30
+ { start: rect.bottomLeft, end: rect.topLeft }
31
+ ];
32
+ }
33
+
34
+ function lineIntersection(segment1: Segment, segment2: Segment) {
35
+ const { x: x1, y: y1 } = segment1.start;
36
+ const { x: x2, y: y2 } = segment1.end;
37
+ const { x: x3, y: y3 } = segment2.start;
38
+ const { x: x4, y: y4 } = segment2.end;
39
+
40
+ const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
41
+ if (denom === 0) return null; // Parallel lines
42
+
43
+ const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
44
+ const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
45
+
46
+ if (t > 0 && t < 1 && u > 0 && u < 1) {
47
+ return {
48
+ x: x1 + t * (x2 - x1),
49
+ y: y1 + t * (y2 - y1)
50
+ };
51
+ }
52
+
53
+ return null; // No intersection
54
+ }
55
+
56
+ export function intersection(rect: Rectangle2, line: Segment) {
57
+ for (const edge of rectEdges({
58
+ topLeft: { x: rect.x - rect.w / 2, y: rect.y - rect.h / 2 },
59
+ topRight: { x: rect.x + rect.w / 2, y: rect.y - rect.h / 2 },
60
+ bottomRight: { x: rect.x + rect.w / 2, y: rect.y + rect.h / 2 },
61
+ bottomLeft: { x: rect.x - rect.w / 2, y: rect.y + rect.h / 2 }
62
+ })) {
63
+ const intersectionPoint = lineIntersection(edge, line);
64
+ if (intersectionPoint) return intersectionPoint;
65
+ }
66
+ return null;
67
+ }
@@ -31,7 +31,10 @@ export interface EdgeBaseProps<V extends VertexBaseProps = VertexBaseProps> exte
31
31
  strokeDasharray?: string;
32
32
  strokeWidth?: number;
33
33
  stroke?: string;
34
+ path?: string;
34
35
  fontFamily?: string;
36
+ markerStart?: string;
37
+ markerEnd?: string;
35
38
  }
36
39
 
37
40
  export interface HierarchyBase<SG extends SubgraphBaseProps, V extends VertexBaseProps> {
@@ -52,6 +55,9 @@ export interface SubgraphPlaceholder<SG extends SubgraphBaseProps = SubgraphBase
52
55
  element?: Selection<SVGGElement, SubgraphPlaceholder<SG>, SVGGElement, any>;
53
56
  props: SG;
54
57
 
58
+ // render result properties ---
59
+ renderResult?: unknown;
60
+
55
61
  // Dagre / Graphviz Properties ---
56
62
  x?: number; // The node’s current x-position
57
63
  y?: number; // The node’s current y-position
@@ -62,6 +68,9 @@ export interface VertexPlaceholder<V extends VertexBaseProps = VertexBaseProps>
62
68
  element?: Selection<SVGGElement, VertexPlaceholder<V>, SVGGElement, any>;
63
69
  props: V;
64
70
 
71
+ // render result properties ---
72
+ renderResult?: any;
73
+
65
74
  // D3 Assigned Properties ---
66
75
  index?: number; // The node’s zero-based index into nodes
67
76
  x?: number; // The node’s current x-position
@@ -91,6 +100,9 @@ export interface EdgePlaceholder<V extends VertexBaseProps = VertexBaseProps, E
91
100
  source: VertexPlaceholder<V>; // The link’s source node
92
101
  target: VertexPlaceholder<V>; // The link’s target node
93
102
 
103
+ // render result properties ---
104
+ renderResult?: any;
105
+
94
106
  // D3 Assigned Properties ---
95
107
  index?: number; // The zero-based index into links, assigned by this method
96
108
 
@@ -236,8 +236,7 @@ export class Tree extends TidyTreeBase {
236
236
  }
237
237
 
238
238
  edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
239
- const source = this._graph.projectPlacholder(ep.source);
240
- const target = this._graph.projectPlacholder(ep.target);
239
+ const { source, target } = this.edgeLine(ep);
241
240
  return {
242
241
  path: linkHorizontal({ source, target }),
243
242
  labelPos: this.center([[source.x, source.y], [target.x, target.y]])
@@ -294,8 +293,7 @@ export class Dendrogram extends TidyTreeBase {
294
293
  }
295
294
 
296
295
  edgePath(ep: EdgePlaceholder, curveDepth: number): EdgeLayout {
297
- const source = this._graph.projectPlacholder(ep.source);
298
- const target = this._graph.projectPlacholder(ep.target);
296
+ const { source, target } = this.edgeLine(ep);
299
297
  return {
300
298
  path: linkHorizontal({ source, target }),
301
299
  labelPos: this.center([[source.x, source.y], [target.x, target.y]])
@@ -1,9 +1,9 @@
1
1
  import { Palette, SVGWidget, Utility, select as d3Select } from "@hpcc-js/common";
2
2
  import { compare2 } from "@hpcc-js/util";
3
3
  import { sankey as d3Sankey, sankeyLinkHorizontal as d3SankeyLinkHorizontal } from "d3-sankey";
4
- import { AnnotationColumn, toJsonObj } from "./dataGraph.ts";
4
+ import { AnnotationColumn, toJsonObj } from "../react/dataGraph.ts";
5
5
 
6
- import "../../src/graph2/sankeyGraph.css";
6
+ import "./sankeyGraph.css";
7
7
  import { EdgeBaseProps, VertexBaseProps } from "./graphT.ts";
8
8
 
9
9
  export class SankeyGraph extends SVGWidget {
@@ -0,0 +1,71 @@
1
+ import { svg } from "lit-html";
2
+ import { Palette, Utility } from "@hpcc-js/common";
3
+ import { extend } from "./component.ts";
4
+ import { TextBox } from "./textBox.ts";
5
+
6
+ export interface AnnotationProps {
7
+ text: string;
8
+ fontFamily?: string;
9
+ fontSize?: number;
10
+ fill?: string;
11
+ stroke?: string;
12
+ }
13
+
14
+ export const annotation = ({
15
+ text,
16
+ fontFamily = "FontAwesome",
17
+ fontSize = 8,
18
+ fill = "gray",
19
+ stroke = "darkgray"
20
+ }: AnnotationProps) => {
21
+ const renderChar = fontFamily === "FontAwesome" ? Utility.faChar(text) : text;
22
+ const textBoxTpl = TextBox({
23
+ text: {
24
+ text: renderChar,
25
+ fill: Palette.textColor(fill),
26
+ fontFamily,
27
+ fontSize,
28
+ dominantBaseline: fontFamily === "FontAwesome" ? "ideographic" : undefined
29
+ },
30
+ padding: 3,
31
+ fill,
32
+ stroke,
33
+ cornerRadius: 0
34
+ });
35
+ return extend(svg`\
36
+ <g data-click="annotation" data-click-data=${JSON.stringify({ text, fill, fontFamily, fontSize, stroke })}>
37
+ ${textBoxTpl}
38
+ </g>`, textBoxTpl.extent.width, textBoxTpl.extent.height);
39
+ };
40
+
41
+ export interface AnnotationsProps {
42
+ annotations: AnnotationProps[];
43
+ padding?: number;
44
+ }
45
+
46
+ export const annotations = ({
47
+ annotations,
48
+ padding = 3,
49
+ }: AnnotationsProps) => {
50
+ let xOffset = 0;
51
+ const items = annotations.map(annotationProp => {
52
+ const ann = annotation(annotationProp);
53
+ const itemSvg = extend(svg`\
54
+ <g transform="translate(${xOffset + ann.extent.width / 2} 0)">
55
+ ${ann}
56
+ </g>`, ann.extent.width, ann.extent.height);
57
+ xOffset += ann.extent.width + padding;
58
+ return itemSvg;
59
+ });
60
+ const { width, height } = items.reduce((acc, item) => {
61
+ return {
62
+ width: acc.width + item.extent.width,
63
+ height: Math.max(acc.height, item.extent.height)
64
+ };
65
+ }, { width: (items.length - 1) * padding, height: 0 });
66
+ return extend(svg`\
67
+ <g transform="translate(${-width / 2} 0)">
68
+ ${items}
69
+ </g>`, width, height);
70
+ };
71
+