@orbcharts/plugin-basic 4.0.0-pre-alpha.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 (208) hide show
  1. package/LICENSE +201 -0
  2. package/dist/orbcharts-plugin-basic.es.js +25335 -0
  3. package/dist/orbcharts-plugin-basic.umd.js +341 -0
  4. package/dist/plugin-basic/src/baseLayers/BaseBars.d.ts +38 -0
  5. package/dist/plugin-basic/src/baseLayers/BaseBarsTriangle.d.ts +37 -0
  6. package/dist/plugin-basic/src/baseLayers/BaseCategoryAxis.d.ts +42 -0
  7. package/dist/plugin-basic/src/baseLayers/BaseDots.d.ts +38 -0
  8. package/dist/plugin-basic/src/baseLayers/BaseLegend.d.ts +31 -0
  9. package/dist/plugin-basic/src/baseLayers/BaseLineAreas.d.ts +36 -0
  10. package/dist/plugin-basic/src/baseLayers/BaseLines.d.ts +36 -0
  11. package/dist/plugin-basic/src/baseLayers/BaseStackedBars.d.ts +41 -0
  12. package/dist/plugin-basic/src/baseLayers/BaseTooltip.d.ts +47 -0
  13. package/dist/plugin-basic/src/baseLayers/BaseValueAxis.d.ts +38 -0
  14. package/dist/plugin-basic/src/baseLayers/BaseXAxis.d.ts +25 -0
  15. package/dist/plugin-basic/src/baseLayers/BaseXZoom.d.ts +22 -0
  16. package/dist/plugin-basic/src/baseLayers/BaseYAxis.d.ts +23 -0
  17. package/dist/plugin-basic/src/baseLayers/types.d.ts +171 -0
  18. package/dist/plugin-basic/src/const/layerIndex.d.ts +10 -0
  19. package/dist/plugin-basic/src/const/sharedPluginParams.d.ts +6 -0
  20. package/dist/plugin-basic/src/index.d.ts +2 -0
  21. package/dist/plugin-basic/src/plugins/CompositionPlot/CompositionPlot.d.ts +22 -0
  22. package/dist/plugin-basic/src/plugins/CompositionPlot/contextObservables.d.ts +40 -0
  23. package/dist/plugin-basic/src/plugins/CompositionPlot/defaults.d.ts +10 -0
  24. package/dist/plugin-basic/src/plugins/CompositionPlot/index.d.ts +3 -0
  25. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Bubbles.d.ts +16 -0
  26. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Indicator.d.ts +0 -0
  27. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Pie.d.ts +16 -0
  28. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/PieEventTexts.d.ts +16 -0
  29. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/PieLabels.d.ts +16 -0
  30. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Rose.d.ts +16 -0
  31. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/RoseLabels.d.ts +16 -0
  32. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Waffle.d.ts +0 -0
  33. package/dist/plugin-basic/src/plugins/CompositionPlot/types.d.ts +110 -0
  34. package/dist/plugin-basic/src/plugins/CompositionPlot/utils.d.ts +19 -0
  35. package/dist/plugin-basic/src/plugins/HierarchyPlot/HierarchyPlot.d.ts +22 -0
  36. package/dist/plugin-basic/src/plugins/HierarchyPlot/contextObservables.d.ts +16 -0
  37. package/dist/plugin-basic/src/plugins/HierarchyPlot/defaults.d.ts +4 -0
  38. package/dist/plugin-basic/src/plugins/HierarchyPlot/index.d.ts +3 -0
  39. package/dist/plugin-basic/src/plugins/HierarchyPlot/layers/TreeMap.d.ts +16 -0
  40. package/dist/plugin-basic/src/plugins/HierarchyPlot/types.d.ts +29 -0
  41. package/dist/plugin-basic/src/plugins/Legend/Legend.d.ts +22 -0
  42. package/dist/plugin-basic/src/plugins/Legend/contextObservables.d.ts +9 -0
  43. package/dist/plugin-basic/src/plugins/Legend/defaults.d.ts +4 -0
  44. package/dist/plugin-basic/src/plugins/Legend/index.d.ts +3 -0
  45. package/dist/plugin-basic/src/plugins/Legend/layers/Legend.d.ts +16 -0
  46. package/dist/plugin-basic/src/plugins/Legend/types.d.ts +31 -0
  47. package/dist/plugin-basic/src/plugins/Legend/utils.d.ts +19 -0
  48. package/dist/plugin-basic/src/plugins/NetworkPlot/NetworkPlot.d.ts +22 -0
  49. package/dist/plugin-basic/src/plugins/NetworkPlot/contextObservables.d.ts +19 -0
  50. package/dist/plugin-basic/src/plugins/NetworkPlot/defaults.d.ts +5 -0
  51. package/dist/plugin-basic/src/plugins/NetworkPlot/index.d.ts +3 -0
  52. package/dist/plugin-basic/src/plugins/NetworkPlot/layers/ForceDirected.d.ts +16 -0
  53. package/dist/plugin-basic/src/plugins/NetworkPlot/layers/ForceDirectedBubbles.d.ts +16 -0
  54. package/dist/plugin-basic/src/plugins/NetworkPlot/types.d.ts +117 -0
  55. package/dist/plugin-basic/src/plugins/ScatterPlot/ScatterPlot.d.ts +22 -0
  56. package/dist/plugin-basic/src/plugins/ScatterPlot/contextObservables.d.ts +140 -0
  57. package/dist/plugin-basic/src/plugins/ScatterPlot/defaults.d.ts +8 -0
  58. package/dist/plugin-basic/src/plugins/ScatterPlot/index.d.ts +3 -0
  59. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/Scatter.d.ts +16 -0
  60. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/ScatterBubbles.d.ts +16 -0
  61. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XYAux.d.ts +16 -0
  62. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XYAxes.d.ts +16 -0
  63. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XZoom.d.ts +16 -0
  64. package/dist/plugin-basic/src/plugins/ScatterPlot/types.d.ts +146 -0
  65. package/dist/plugin-basic/src/plugins/SeriesPlot/SeriesPlot.d.ts +22 -0
  66. package/dist/plugin-basic/src/plugins/SeriesPlot/contextObservables.d.ts +77 -0
  67. package/dist/plugin-basic/src/plugins/SeriesPlot/defaults.d.ts +15 -0
  68. package/dist/plugin-basic/src/plugins/SeriesPlot/index.d.ts +3 -0
  69. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Bars.d.ts +16 -0
  70. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/BarsPN.d.ts +16 -0
  71. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/BarsTriangle.d.ts +16 -0
  72. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryAux.d.ts +16 -0
  73. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryAxis.d.ts +16 -0
  74. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryZoom.d.ts +16 -0
  75. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Dots.d.ts +16 -0
  76. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/LineAreas.d.ts +16 -0
  77. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Lines.d.ts +16 -0
  78. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/StackedBars.d.ts +16 -0
  79. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/StackedValueAxis.d.ts +16 -0
  80. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/ValueAxis.d.ts +16 -0
  81. package/dist/plugin-basic/src/plugins/SeriesPlot/types.d.ts +140 -0
  82. package/dist/plugin-basic/src/plugins/Tooltip/Tooltip.d.ts +22 -0
  83. package/dist/plugin-basic/src/plugins/Tooltip/contextObservables.d.ts +9 -0
  84. package/dist/plugin-basic/src/plugins/Tooltip/defaults.d.ts +4 -0
  85. package/dist/plugin-basic/src/plugins/Tooltip/index.d.ts +3 -0
  86. package/dist/plugin-basic/src/plugins/Tooltip/layers/Tooltip.d.ts +16 -0
  87. package/dist/plugin-basic/src/plugins/Tooltip/types.d.ts +35 -0
  88. package/dist/plugin-basic/src/plugins/Tooltip/utils.d.ts +19 -0
  89. package/dist/plugin-basic/src/plugins/index.d.ts +7 -0
  90. package/dist/plugin-basic/src/types/BaseLayer.d.ts +3 -0
  91. package/dist/plugin-basic/src/types/Common.d.ts +14 -0
  92. package/dist/plugin-basic/src/types/ComputedData.d.ts +27 -0
  93. package/dist/plugin-basic/src/types/PluginParams.d.ts +66 -0
  94. package/dist/plugin-basic/src/types/index.d.ts +3 -0
  95. package/dist/plugin-basic/src/utils/commonUtils.d.ts +3 -0
  96. package/dist/plugin-basic/src/utils/d3Graphics.d.ts +24 -0
  97. package/dist/plugin-basic/src/utils/d3Scale.d.ts +28 -0
  98. package/dist/plugin-basic/src/utils/d3Utils.d.ts +14 -0
  99. package/dist/plugin-basic/src/utils/graphObservables.d.ts +0 -0
  100. package/dist/plugin-basic/src/utils/gridObservables.d.ts +51 -0
  101. package/dist/plugin-basic/src/utils/multivariateObservables.d.ts +74 -0
  102. package/dist/plugin-basic/src/utils/observables.d.ts +34 -0
  103. package/dist/plugin-basic/src/utils/orbchartsUtils.d.ts +26 -0
  104. package/dist/plugin-basic/src/utils/seriesObservables.d.ts +22 -0
  105. package/dist/plugin-basic/vite.config.d.ts +2 -0
  106. package/dist/src/index.d.ts +1 -0
  107. package/package.json +62 -0
  108. package/src/baseLayers/BaseBars.ts +783 -0
  109. package/src/baseLayers/BaseBarsTriangle.ts +692 -0
  110. package/src/baseLayers/BaseCategoryAxis.ts +708 -0
  111. package/src/baseLayers/BaseDots.ts +495 -0
  112. package/src/baseLayers/BaseLegend.ts +684 -0
  113. package/src/baseLayers/BaseLineAreas.ts +644 -0
  114. package/src/baseLayers/BaseLines.ts +721 -0
  115. package/src/baseLayers/BaseStackedBars.ts +818 -0
  116. package/src/baseLayers/BaseTooltip.ts +435 -0
  117. package/src/baseLayers/BaseValueAxis.ts +612 -0
  118. package/src/baseLayers/BaseXAxis.ts +412 -0
  119. package/src/baseLayers/BaseXZoom.ts +250 -0
  120. package/src/baseLayers/BaseYAxis.ts +371 -0
  121. package/src/baseLayers/types.ts +174 -0
  122. package/src/const/layerIndex.ts +36 -0
  123. package/src/const/sharedPluginParams.ts +29 -0
  124. package/src/index.ts +3 -0
  125. package/src/plugins/CompositionPlot/CompositionPlot.ts +308 -0
  126. package/src/plugins/CompositionPlot/contextObservables.ts +251 -0
  127. package/src/plugins/CompositionPlot/defaults.ts +162 -0
  128. package/src/plugins/CompositionPlot/index.ts +3 -0
  129. package/src/plugins/CompositionPlot/layers/Bubbles.ts +808 -0
  130. package/src/plugins/CompositionPlot/layers/Indicator.ts +0 -0
  131. package/src/plugins/CompositionPlot/layers/Pie.ts +776 -0
  132. package/src/plugins/CompositionPlot/layers/PieEventTexts.ts +326 -0
  133. package/src/plugins/CompositionPlot/layers/PieLabels.ts +651 -0
  134. package/src/plugins/CompositionPlot/layers/Rose.ts +546 -0
  135. package/src/plugins/CompositionPlot/layers/RoseLabels.ts +616 -0
  136. package/src/plugins/CompositionPlot/layers/Waffle.ts +0 -0
  137. package/src/plugins/CompositionPlot/types.ts +129 -0
  138. package/src/plugins/CompositionPlot/utils.ts +53 -0
  139. package/src/plugins/HierarchyPlot/HierarchyPlot.ts +190 -0
  140. package/src/plugins/HierarchyPlot/contextObservables.ts +136 -0
  141. package/src/plugins/HierarchyPlot/defaults.ts +31 -0
  142. package/src/plugins/HierarchyPlot/index.ts +3 -0
  143. package/src/plugins/HierarchyPlot/layers/TreeMap.ts +371 -0
  144. package/src/plugins/HierarchyPlot/types.ts +36 -0
  145. package/src/plugins/Legend/Legend.ts +151 -0
  146. package/src/plugins/Legend/contextObservables.ts +55 -0
  147. package/src/plugins/Legend/defaults.ts +37 -0
  148. package/src/plugins/Legend/index.ts +3 -0
  149. package/src/plugins/Legend/layers/Legend.ts +114 -0
  150. package/src/plugins/Legend/types.ts +45 -0
  151. package/src/plugins/Legend/utils.ts +53 -0
  152. package/src/plugins/NetworkPlot/NetworkPlot.ts +228 -0
  153. package/src/plugins/NetworkPlot/contextObservables.ts +123 -0
  154. package/src/plugins/NetworkPlot/defaults.ts +147 -0
  155. package/src/plugins/NetworkPlot/index.ts +3 -0
  156. package/src/plugins/NetworkPlot/layers/ForceDirected.ts +1048 -0
  157. package/src/plugins/NetworkPlot/layers/ForceDirectedBubbles.ts +1318 -0
  158. package/src/plugins/NetworkPlot/types.ts +146 -0
  159. package/src/plugins/ScatterPlot/ScatterPlot.ts +569 -0
  160. package/src/plugins/ScatterPlot/contextObservables.ts +901 -0
  161. package/src/plugins/ScatterPlot/defaults.ts +212 -0
  162. package/src/plugins/ScatterPlot/index.ts +3 -0
  163. package/src/plugins/ScatterPlot/layers/Scatter.ts +518 -0
  164. package/src/plugins/ScatterPlot/layers/ScatterBubbles.ts +670 -0
  165. package/src/plugins/ScatterPlot/layers/XYAux.ts +686 -0
  166. package/src/plugins/ScatterPlot/layers/XYAxes.ts +205 -0
  167. package/src/plugins/ScatterPlot/layers/XZoom.ts +48 -0
  168. package/src/plugins/ScatterPlot/types.ts +179 -0
  169. package/src/plugins/SeriesPlot/SeriesPlot.ts +494 -0
  170. package/src/plugins/SeriesPlot/contextObservables.ts +726 -0
  171. package/src/plugins/SeriesPlot/defaults.ts +142 -0
  172. package/src/plugins/SeriesPlot/index.ts +3 -0
  173. package/src/plugins/SeriesPlot/layers/Bars.ts +84 -0
  174. package/src/plugins/SeriesPlot/layers/BarsPN.ts +85 -0
  175. package/src/plugins/SeriesPlot/layers/BarsTriangle.ts +89 -0
  176. package/src/plugins/SeriesPlot/layers/CategoryAux.ts +1131 -0
  177. package/src/plugins/SeriesPlot/layers/CategoryAxis.ts +92 -0
  178. package/src/plugins/SeriesPlot/layers/CategoryZoom.ts +233 -0
  179. package/src/plugins/SeriesPlot/layers/Dots.ts +91 -0
  180. package/src/plugins/SeriesPlot/layers/LineAreas.ts +82 -0
  181. package/src/plugins/SeriesPlot/layers/Lines.ts +75 -0
  182. package/src/plugins/SeriesPlot/layers/StackedBars.ts +85 -0
  183. package/src/plugins/SeriesPlot/layers/StackedValueAxis.ts +111 -0
  184. package/src/plugins/SeriesPlot/layers/ValueAxis.ts +111 -0
  185. package/src/plugins/SeriesPlot/types.ts +201 -0
  186. package/src/plugins/Tooltip/Tooltip.ts +159 -0
  187. package/src/plugins/Tooltip/contextObservables.ts +55 -0
  188. package/src/plugins/Tooltip/defaults.ts +458 -0
  189. package/src/plugins/Tooltip/index.ts +3 -0
  190. package/src/plugins/Tooltip/layers/Tooltip.ts +90 -0
  191. package/src/plugins/Tooltip/types.ts +55 -0
  192. package/src/plugins/Tooltip/utils.ts +53 -0
  193. package/src/plugins/index.ts +8 -0
  194. package/src/types/BaseLayer.ts +3 -0
  195. package/src/types/Common.ts +20 -0
  196. package/src/types/ComputedData.ts +55 -0
  197. package/src/types/PluginParams.ts +81 -0
  198. package/src/types/index.ts +3 -0
  199. package/src/utils/commonUtils.ts +31 -0
  200. package/src/utils/d3Graphics.ts +177 -0
  201. package/src/utils/d3Scale.ts +198 -0
  202. package/src/utils/d3Utils.ts +92 -0
  203. package/src/utils/graphObservables.ts +0 -0
  204. package/src/utils/gridObservables.ts +637 -0
  205. package/src/utils/multivariateObservables.ts +790 -0
  206. package/src/utils/observables.ts +357 -0
  207. package/src/utils/orbchartsUtils.ts +335 -0
  208. package/src/utils/seriesObservables.ts +172 -0
@@ -0,0 +1,1048 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ first,
6
+ switchMap,
7
+ debounceTime,
8
+ takeUntil,
9
+ distinctUntilChanged,
10
+ shareReplay,
11
+ iif,
12
+ EMPTY,
13
+ Observable,
14
+ Subject,
15
+ BehaviorSubject
16
+ } from 'rxjs'
17
+ import type { Theme, EventData } from '@orbcharts/core'
18
+ import type { NetworkPlotPluginParams, NetworkPlotExtendContext, ForceDirectedParams } from '../types'
19
+ import { defineSVGLayer } from '@orbcharts/core'
20
+ import { validateObject } from '@orbcharts/core'
21
+ import { DEFAULT_FORCE_DIRECTED_PARAMS } from "../defaults"
22
+ import { multivariateSelectionsObservable } from "../../../utils/multivariateObservables"
23
+ import { getColor, getDatumColor } from '../../../utils/orbchartsUtils'
24
+ import { createClassName, createUniID } from '../../../utils/orbchartsUtils'
25
+ import type { ComputedDatumGraphNode, ComputedDatumGraphEdge } from '../../../types/ComputedData'
26
+ import type { ContainerPosition, GraphicStyles, Layout } from '../../../types/PluginParams'
27
+ import { LAYER_INDEX_OF_GRAPHIC } from '../../../const/layerIndex'
28
+
29
+ // interface BubblesDatum extends ComputedDatumGraphNode {
30
+ // x: number
31
+ // y: number
32
+ // r: number
33
+ // _originR: number // 紀錄變化前的r
34
+ // }
35
+
36
+ type Zoom = {
37
+ xOffset: number
38
+ yOffset: number
39
+ scaleExtent: {
40
+ min: number
41
+ max: number
42
+ }
43
+ }
44
+
45
+ // d3 forceSimulation使用的node資料
46
+ type RenderNode = d3.SimulationNodeDatum & ComputedDatumGraphNode
47
+
48
+ // d3 forceSimulation使用的edge資料
49
+ interface RenderEdge extends ComputedDatumGraphEdge {
50
+ _source: RenderNode
51
+ _target: RenderNode
52
+ }
53
+
54
+ // d3 forceSimulation使用的資料
55
+ type RenderData = {
56
+ nodes: (ComputedDatumGraphNode | RenderNode)[] // 經過d3 forceSimulation計算後的node才有座標資訊
57
+ edges: RenderEdge[]
58
+ }
59
+
60
+ interface D3DragEvent {
61
+ active: number
62
+ dx: number
63
+ dy: number
64
+ identifier: string
65
+ sourceEvent: MouseEvent
66
+ subject: RenderNode
67
+ target: any
68
+ type: string
69
+ x: number
70
+ y: number
71
+ }
72
+
73
+ type DragStatus = 'start' | 'drag' | 'end'
74
+
75
+ // type BubblesSimulationDatum = BubblesDatum & d3.SimulationNodeDatum
76
+
77
+ const pluginName = 'NetworkPlot'
78
+ const layerName = 'ForceDirected'
79
+
80
+ const gSelectionClassName = createClassName(pluginName, layerName, 'zoom-area')
81
+ const defsArrowMarkerId = createUniID(pluginName, layerName, 'arrow')
82
+ const defsArrowMarkerClassName = createClassName(pluginName, layerName, 'arrow-marker')
83
+ const edgeListGClassName = createClassName(pluginName, layerName, 'edge-list-g')
84
+ const edgeGClassName = createClassName(pluginName, layerName, 'edge-g')
85
+ const edgeArrowPathClassName = createClassName(pluginName, layerName, 'edge-arrow-path')
86
+ const edgeLabelGClassName = createClassName(pluginName, layerName, 'edge-label-g')
87
+ const edgeLabelClassName = createClassName(pluginName, layerName, 'edge-label')
88
+ const nodeListGClassName = createClassName(pluginName, layerName, 'node-list-g')
89
+ const nodeGClassName = createClassName(pluginName, layerName, 'node-g')
90
+ const nodeCircleClassName = createClassName(pluginName, layerName, 'node-circle')
91
+ const nodeLabelGClassName = createClassName(pluginName, layerName, 'node-label-g')
92
+ const nodeLabelClassName = createClassName(pluginName, layerName, 'node-label')
93
+
94
+
95
+ function createSimulation (layout: Layout, layerParams: ForceDirectedParams) {
96
+ return d3.forceSimulation()
97
+ .velocityDecay(layerParams.force.velocityDecay)
98
+ .alphaDecay(layerParams.force.alphaDecay)
99
+ .force(
100
+ "link",
101
+ d3.forceLink()
102
+ .id((d: d3.SimulationNodeDatum & ComputedDatumGraphNode) => d.id)
103
+ .strength(1)
104
+ .distance((d: d3.SimulationLinkDatum<d3.SimulationNodeDatum & ComputedDatumGraphNode>) => {
105
+ // if (d.direction === 'top') {
106
+ // return 200
107
+ // } else {
108
+ // return 250
109
+ // }
110
+ return layerParams.force.linkDistance
111
+ })
112
+ )
113
+ .force("charge", d3.forceManyBody().strength(layerParams.force.nodeStrength))
114
+ .force("collision", d3.forceCollide(layerParams.dot.radius).strength(1))
115
+ .force("center", d3.forceCenter(layout.width / 2, layout.height / 2))
116
+
117
+ }
118
+
119
+ function translateFn (d: RenderNode): string {
120
+ // console.log('translateFn', d)
121
+ return "translate(" + d.x + "," + d.y + ")";
122
+ }
123
+
124
+ function translateCenterFn (d: RenderEdge): string {
125
+ // console.log('translateCenterFn', d)
126
+ const x = d._source.x + ((d._target.x - d._source.x) / 2) // 置中的話除2
127
+ const y = d._source.y + ((d._target.y - d._source.y) / 2) // 置中的話除2
128
+ return "translate(" + x + "," + y + ")";
129
+ }
130
+
131
+ function linkArcFn (d: RenderEdge): string {
132
+ // console.log('linkArcFn', d)
133
+
134
+ // const dx = d.target.x - d.source.x,
135
+ // dy = d.target.y - d.source.y
136
+ // dr讓方向線變成有弧度的
137
+ // dr = Math.sqrt(dx * dx + dy * dy);
138
+ // return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
139
+
140
+ // 直線
141
+ return "M" + d._source.x + "," + d._source.y + " L" + d._target.x + "," + d._target.y;
142
+
143
+
144
+ }
145
+
146
+
147
+
148
+ function renderArrowMarker (defsSelection: d3.Selection<SVGDefsElement, any, any, unknown>, layerParams: ForceDirectedParams, theme: Theme) {
149
+ return defsSelection
150
+ .selectAll<SVGMarkerElement, any>(`marker.${defsArrowMarkerClassName}`)
151
+ .data([layerParams])
152
+ .join(
153
+ enter => {
154
+ const enterSelection = enter
155
+ .append("marker")
156
+ .classed(defsArrowMarkerClassName, true)
157
+ .attr('id', defsArrowMarkerId)
158
+ .attr('fill', d => getColor(layerParams.arrow.colorType, theme ))
159
+ .attr("viewBox", d => `-${d.arrow.pointerWidth} -${d.arrow.pointerHeight / 2} ${d.arrow.pointerWidth} ${d.arrow.pointerHeight}`)
160
+ .attr("orient", "auto")
161
+ enterSelection.append("path")
162
+ .attr("d", d => `M${-d.arrow.pointerWidth},${-d.arrow.pointerHeight / 2}L0,0L${-d.arrow.pointerWidth},${d.arrow.pointerHeight / 2}`) // 箭頭的尖端為(0,0)
163
+ return enterSelection
164
+ },
165
+ update => {
166
+ return update
167
+ },
168
+ exit => {
169
+ return exit.remove()
170
+ }
171
+ )
172
+ .attr("markerWidth", d => d.arrow.pointerWidth)
173
+ .attr("markerHeight", d => d.arrow.pointerHeight)
174
+ /* refX:修正marker位置(計算出和circle半徑相等的寬度)
175
+ (1)circle半徑需加上 strokeWidth/2 是因為框線是以 circle 的邊緣往內及往外擴展,所以 stroke 多出來的寬度是一半而已
176
+ (2)circle半徑需除以 path 寬度是因為「marker 的位置會受到 path 的stroke-width影響」,所以要進行修正
177
+ (3)- 1 是要修正奇怪的誤差(不知原因)
178
+ */
179
+ .attr('refX', d => ((d.dot.radius + (layerParams.dot.strokeWidth / 2)) / d.arrow.strokeWidth) - 1)
180
+ .attr("refY", 0)
181
+
182
+ }
183
+
184
+ // function drag (): d3.DragBehavior<Element, unknown, unknown> {
185
+ // let originHighlightLockMode: boolean // 拖拽前的highlightLockMode
186
+
187
+ // return d3.drag()
188
+ // .on("start", (event: D3DragEvent) => {
189
+ // console.log('start', event.sourceEvent)
190
+ // // if (this.params.lockMode) {
191
+ // // return
192
+ // // }
193
+ // // if (!d3.event.active) {
194
+ // // this.forceRestart()
195
+ // // }
196
+ // // d.fx = d.x
197
+ // // d.fy = d.y
198
+
199
+ // // // 鎖定模式才不會在拖拽過程式觸發到其他事件造成衝突
200
+ // // originHighlightLockMode = this.highlightLockMode
201
+ // // this.highlightLockMode = true
202
+ // // this.noneStopMode = true
203
+ // // // 動畫會有點卡住所以乾脆拿掉
204
+ // // if(this.tooltip != null) {
205
+ // // this.tooltip.remove()
206
+ // // }
207
+ // })
208
+ // .on("drag", function (event: D3DragEvent) {
209
+ // console.log('drag', event)
210
+ // // if (this.params.lockMode) {
211
+ // // return
212
+ // // }
213
+ // // if (!d3.event.active) {
214
+ // // this.force.alphaTarget(0)
215
+ // // }
216
+ // // d.fx = d3.event.x
217
+ // // d.fy = d3.event.y
218
+ // // d3.select(this).attr({
219
+ // // 'cx': event.x,
220
+ // // 'cy': event.y,
221
+ // // })
222
+ // d3.select(this)
223
+ // .attr('fx', event.x)
224
+ // .attr('fy', event.y)
225
+ // })
226
+ // .on("end", (event: D3DragEvent) => {
227
+ // console.log('end', event)
228
+ // // if (this.params.lockMode) {
229
+ // // return
230
+ // // }
231
+ // // d.fx = null
232
+ // // d.fy = null
233
+
234
+ // // this.highlightLockMode = originHighlightLockMode // 還原拖拽前的highlightLockMode
235
+ // // this.noneStopMode = false
236
+ // // if (this.highlightLockMode) {
237
+ // // this.forceStop()
238
+ // // }
239
+ // })
240
+ // }
241
+
242
+ function drag (simulation: d3.Simulation<d3.SimulationNodeDatum, undefined>, dragStatus$: BehaviorSubject<DragStatus>) {
243
+ function dragstarted (event: D3DragEvent, node: RenderNode) {
244
+ if (!event.active) simulation.alphaTarget(0.3).restart()
245
+ event.subject.fx = event.x
246
+ event.subject.fy = event.y
247
+
248
+ dragStatus$.next('start')
249
+ }
250
+
251
+ function dragged (event: D3DragEvent, node: RenderNode) {
252
+ event.subject.fx = event.x
253
+ event.subject.fy = event.y
254
+
255
+ dragStatus$.next('drag')
256
+ }
257
+
258
+ function dragended (event: D3DragEvent, node: RenderNode) {
259
+ if (!event.active) simulation.alphaTarget(0);
260
+ event.subject.fx = null
261
+ event.subject.fy = null
262
+
263
+ dragStatus$.next('end')
264
+ }
265
+
266
+ return d3.drag()
267
+ .on("start", dragstarted)
268
+ .on("drag", dragged)
269
+ .on("end", dragended)
270
+ }
271
+
272
+ function renderNodeG ({ nodeListGSelection, nodes }: {
273
+ nodeListGSelection: d3.Selection<SVGGElement, any, any, unknown>
274
+ nodes: RenderNode[]
275
+ }) {
276
+ return nodeListGSelection.selectAll<SVGGElement, RenderNode>('g')
277
+ .data(nodes, d => d.id)
278
+ .join(
279
+ enter => {
280
+ const enterSelection = enter
281
+ .append('g')
282
+ .classed(nodeGClassName, true)
283
+ // .attr('cursor', 'pointer')
284
+ return enterSelection
285
+ },
286
+ update => {
287
+ return update
288
+ },
289
+ exit => {
290
+ return exit.remove()
291
+ }
292
+ )
293
+ }
294
+
295
+ function renderNodeCircle ({ nodeGSelection, layerParams, theme }: {
296
+ nodeGSelection: d3.Selection<SVGGElement, RenderNode, any, unknown>
297
+ layerParams: ForceDirectedParams
298
+ theme: Theme
299
+ }) {
300
+ nodeGSelection.each((data,i,g) => {
301
+ const gSelection = d3.select(g[i])
302
+ gSelection.selectAll<SVGCircleElement, ComputedDatumGraphEdge>('circle')
303
+ .data([data])
304
+ .join(
305
+ enter => {
306
+ const enterSelection = enter
307
+ .append('circle')
308
+ .classed(nodeCircleClassName, true)
309
+ .attr('cursor', 'pointer')
310
+ return enterSelection
311
+ },
312
+ update => {
313
+ return update
314
+ },
315
+ exit => {
316
+ return exit.remove()
317
+ }
318
+ )
319
+ .attr('r', layerParams.dot.radius)
320
+ .attr('fill', d => getDatumColor({ datum: d, colorType: layerParams.dot.fillColorType, theme }))
321
+ .attr('stroke', d => getDatumColor({ datum: d, colorType: layerParams.dot.strokeColorType, theme }))
322
+ .attr('stroke-width', layerParams.dot.strokeWidth)
323
+ .attr('style', d => layerParams.dot.styleFn(d))
324
+ })
325
+
326
+ return nodeGSelection.select<SVGCircleElement>(`circle.${nodeCircleClassName}`)
327
+ }
328
+
329
+ function renderNodeLabelG ({ nodeGSelection, layerParams }: {
330
+ nodeGSelection: d3.Selection<SVGGElement, any, any, unknown>
331
+ layerParams: ForceDirectedParams
332
+ }) {
333
+ nodeGSelection.each((data,i,g) => {
334
+ const gSelection = d3.select(g[i])
335
+ gSelection.selectAll<SVGGElement, RenderNode>('g')
336
+ .data([data])
337
+ .join(
338
+ enter => {
339
+ const enterSelection = enter
340
+ .append('g')
341
+ .classed(nodeLabelGClassName, true)
342
+ // .attr('cursor', 'pointer')
343
+ return enterSelection
344
+ },
345
+ update => {
346
+ return update
347
+ },
348
+ exit => {
349
+ return exit.remove()
350
+ }
351
+ )
352
+ .attr('transform', `translate(0, ${- layerParams.dot.radius - 10})`)
353
+ })
354
+
355
+ return nodeGSelection.select<SVGTextElement>(`g.${nodeLabelGClassName}`)
356
+ }
357
+
358
+ function renderNodeLabel ({ nodeLabelGSelection, layerParams, theme }: {
359
+ nodeLabelGSelection: d3.Selection<SVGGElement, RenderNode, any, unknown>
360
+ layerParams: ForceDirectedParams
361
+ theme: Theme
362
+ }) {
363
+ nodeLabelGSelection.each((data,i,g) => {
364
+ const gSelection = d3.select(g[i])
365
+ gSelection.selectAll<SVGTextElement, RenderNode>('text')
366
+ .data([data], d => d.id)
367
+ .join(
368
+ enter => {
369
+ const enterSelection = enter
370
+ .append('text')
371
+ .classed(nodeLabelClassName, true)
372
+ // .attr('cursor', 'pointer')
373
+ .attr('text-anchor', 'middle')
374
+ .attr('pointer-events', 'none')
375
+ return enterSelection
376
+ },
377
+ update => {
378
+ return update
379
+ },
380
+ exit => {
381
+ return exit.remove()
382
+ }
383
+ )
384
+ .text(d => d.name)
385
+ .attr('fill', d => getDatumColor({ datum: d, colorType: layerParams.dotLabel.colorType, theme }))
386
+ .attr('font-size', theme.fontSize)
387
+ .attr('style', d => layerParams.dotLabel.styleFn(d))
388
+ })
389
+
390
+ return nodeLabelGSelection.select<SVGTextElement>(`text.${nodeLabelClassName}`)
391
+ }
392
+
393
+ function renderEdgeG ({ edgeListGSelection, edges }: {
394
+ edgeListGSelection: d3.Selection<SVGGElement, any, any, unknown>
395
+ edges: RenderEdge[]
396
+ }) {
397
+ return edgeListGSelection.selectAll<SVGGElement, RenderEdge>('g')
398
+ .data(edges, d => d.id)
399
+ .join(
400
+ enter => {
401
+ const enterSelection = enter
402
+ .append('g')
403
+ .classed(edgeGClassName, true)
404
+ // .attr('cursor', 'pointer')
405
+ return enterSelection
406
+ },
407
+ update => {
408
+ return update
409
+ },
410
+ exit => {
411
+ return exit.remove()
412
+ }
413
+ )
414
+ }
415
+
416
+ function renderEdgeArrowPath ({ edgeGSelection, layerParams, theme }: {
417
+ edgeGSelection: d3.Selection<SVGGElement, RenderEdge, any, unknown>
418
+ layerParams: ForceDirectedParams
419
+ theme: Theme
420
+ }) {
421
+ edgeGSelection.each((data,i,g) => {
422
+ const gSelection = d3.select(g[i])
423
+ gSelection.selectAll<SVGPathElement, ComputedDatumGraphEdge>('path')
424
+ .data([data])
425
+ .join(
426
+ enter => {
427
+ return enter
428
+ .append('path')
429
+ .classed(edgeArrowPathClassName, true)
430
+ .attr('marker-end', `url(#${defsArrowMarkerId})`)
431
+ },
432
+ update => {
433
+ return update
434
+ },
435
+ exit => {
436
+ return exit.remove()
437
+ }
438
+ )
439
+ .attr('stroke', d => getDatumColor({ datum: d.data, colorType: layerParams.arrow.colorType, theme }))
440
+ .attr('stroke-width', layerParams.arrow.strokeWidth)
441
+ .attr('style', d => layerParams.arrow.styleFn(d))
442
+ })
443
+
444
+ return edgeGSelection.select<SVGPathElement>(`path.${edgeArrowPathClassName}`)
445
+ }
446
+
447
+ function renderEdgeLabelG ({ edgeGSelection }: {
448
+ edgeGSelection: d3.Selection<SVGGElement, any, any, unknown>
449
+ }) {
450
+ edgeGSelection.each((data,i,g) => {
451
+ const gSelection = d3.select(g[i])
452
+ gSelection.selectAll<SVGGElement, RenderEdge>('g')
453
+ .data([data])
454
+ .join(
455
+ enter => {
456
+ const enterSelection = enter
457
+ .append('g')
458
+ .classed(edgeLabelGClassName, true)
459
+ // .attr('cursor', 'pointer')
460
+ return enterSelection
461
+ },
462
+ update => {
463
+ return update
464
+ },
465
+ exit => {
466
+ return exit.remove()
467
+ }
468
+ )
469
+ })
470
+
471
+ return edgeGSelection.select<SVGTextElement>(`g.${edgeLabelGClassName}`)
472
+ }
473
+
474
+ function renderEdgeLabel ({ edgeLabelGSelection, layerParams, theme }: {
475
+ edgeLabelGSelection: d3.Selection<SVGGElement, RenderEdge, any, unknown>
476
+ layerParams: ForceDirectedParams
477
+ theme: Theme
478
+ }) {
479
+ edgeLabelGSelection.each((data,i,g) => {
480
+ const gSelection = d3.select(g[i])
481
+ gSelection.selectAll<SVGTextElement, RenderEdge>('text')
482
+ .data([data], d => d.id)
483
+ .join(
484
+ enter => {
485
+ const enterSelection = enter
486
+ .append('text')
487
+ .classed(edgeLabelClassName, true)
488
+ // .attr('cursor', 'pointer')
489
+ .attr('text-anchor', 'middle')
490
+ .attr('pointer-events', 'none')
491
+ return enterSelection
492
+ },
493
+ update => {
494
+ return update
495
+ },
496
+ exit => {
497
+ return exit.remove()
498
+ }
499
+ )
500
+ .text(d => d.name)
501
+ .attr('fill', d => getDatumColor({ datum: d, colorType: layerParams.arrowLabel.colorType, theme }))
502
+ .attr('font-size', theme.fontSize)
503
+ .attr('style', d => layerParams.arrowLabel.styleFn(d))
504
+ })
505
+
506
+ return edgeLabelGSelection.select<SVGTextElement>(`text.${edgeLabelClassName}`)
507
+ }
508
+
509
+ function highlightNodes ({ nodeGSelection, edgeGSelection, highlightIds, styles }: {
510
+ nodeGSelection: d3.Selection<SVGGElement, RenderNode, SVGGElement, any>
511
+ edgeGSelection: d3.Selection<SVGGElement, RenderEdge, SVGGElement, any>
512
+ styles: GraphicStyles
513
+ highlightIds: string[]
514
+ }) {
515
+ nodeGSelection.interrupt('highlight')
516
+ edgeGSelection.interrupt('highlight')
517
+ // console.log(highlightIds)
518
+ if (!highlightIds.length) {
519
+ nodeGSelection
520
+ .transition('highlight')
521
+ .style('opacity', 1)
522
+ edgeGSelection
523
+ .transition('highlight')
524
+ .style('opacity', 1)
525
+ return
526
+ }
527
+
528
+ edgeGSelection
529
+ .style('opacity', styles.unhighlightedOpacity)
530
+
531
+ nodeGSelection.each((d, i, n) => {
532
+ const segment = d3.select(n[i])
533
+
534
+ if (highlightIds.includes(d.id)) {
535
+ segment
536
+ .style('opacity', 1)
537
+ .transition('highlight')
538
+ .ease(d3.easeElastic)
539
+ .duration(500)
540
+ } else {
541
+ // 取消
542
+ segment
543
+ .style('opacity', styles.unhighlightedOpacity)
544
+ }
545
+ })
546
+ }
547
+
548
+ export const ForceDirected = defineSVGLayer<NetworkPlotExtendContext, NetworkPlotPluginParams, ForceDirectedParams>({
549
+ name: layerName,
550
+ defaultParams: DEFAULT_FORCE_DIRECTED_PARAMS,
551
+ layerIndex: LAYER_INDEX_OF_GRAPHIC,
552
+ initShow: true,
553
+ validator: (params) => {
554
+ const result = validateObject(params, {
555
+ dot: {
556
+ toBeTypes: ['object']
557
+ },
558
+ dotLabel: {
559
+ toBeTypes: ['object']
560
+ },
561
+ arrow: {
562
+ toBeTypes: ['object']
563
+ },
564
+ arrowLabel: {
565
+ toBeTypes: ['object']
566
+ },
567
+ force: {
568
+ toBeTypes: ['object']
569
+ },
570
+ zoomable: {
571
+ toBeTypes: ['boolean']
572
+ },
573
+ transform: {
574
+ toBeTypes: ['object']
575
+ },
576
+ scaleExtent: {
577
+ toBeTypes: ['object']
578
+ }
579
+ })
580
+ if (params.dot) {
581
+ const dotResult = validateObject(params.dot, {
582
+ radius: {
583
+ toBeTypes: ['number']
584
+ },
585
+ fillColorType: {
586
+ toBeOption: 'ColorType'
587
+ },
588
+ strokeColorType: {
589
+ toBeOption: 'ColorType'
590
+ },
591
+ strokeWidth: {
592
+ toBeTypes: ['number']
593
+ },
594
+ styleFn: {
595
+ toBeTypes: ['Function']
596
+ },
597
+ })
598
+ if (dotResult.status === 'error') {
599
+ return dotResult
600
+ }
601
+ }
602
+ if (params.dotLabel) {
603
+ const dotLabelResult = validateObject(params.dotLabel, {
604
+ colorType: {
605
+ toBeOption: 'ColorType'
606
+ },
607
+ sizeFixed: {
608
+ toBeTypes: ['boolean']
609
+ },
610
+ styleFn: {
611
+ toBeTypes: ['Function']
612
+ },
613
+ })
614
+ if (dotLabelResult.status === 'error') {
615
+ return dotLabelResult
616
+ }
617
+ }
618
+ if (params.arrow) {
619
+ const arrowResult = validateObject(params.arrow, {
620
+ colorType: {
621
+ toBeOption: 'ColorType'
622
+ },
623
+ strokeWidth: {
624
+ toBeTypes: ['number']
625
+ },
626
+ pointerWidth: {
627
+ toBeTypes: ['number']
628
+ },
629
+ pointerHeight: {
630
+ toBeTypes: ['number']
631
+ },
632
+ styleFn: {
633
+ toBeTypes: ['Function']
634
+ },
635
+ })
636
+ if (arrowResult.status === 'error') {
637
+ return arrowResult
638
+ }
639
+ }
640
+ if (params.arrowLabel) {
641
+ const arrowLabelResult = validateObject(params.arrowLabel, {
642
+ colorType: {
643
+ toBeOption: 'ColorType'
644
+ },
645
+ sizeFixed: {
646
+ toBeTypes: ['boolean']
647
+ },
648
+ styleFn: {
649
+ toBeTypes: ['Function']
650
+ },
651
+ })
652
+ if (arrowLabelResult.status === 'error') {
653
+ return arrowLabelResult
654
+ }
655
+ }
656
+ if (params.force) {
657
+ const forceResult = validateObject(params.force, {
658
+ nodeStrength: {
659
+ toBeTypes: ['number']
660
+ },
661
+ linkDistance: {
662
+ toBeTypes: ['number']
663
+ },
664
+ velocityDecay: {
665
+ toBeTypes: ['number']
666
+ },
667
+ alphaDecay: {
668
+ toBeTypes: ['number']
669
+ },
670
+ })
671
+ if (forceResult.status === 'error') {
672
+ return forceResult
673
+ }
674
+ }
675
+ if (params.transform) {
676
+ const transformResult = validateObject(params.transform, {
677
+ x: {
678
+ toBeTypes: ['number']
679
+ },
680
+ y: {
681
+ toBeTypes: ['number']
682
+ },
683
+ k: {
684
+ toBeTypes: ['number']
685
+ },
686
+ })
687
+ if (transformResult.status === 'error') {
688
+ return transformResult
689
+ }
690
+ }
691
+ if (params.scaleExtent) {
692
+ const scaleExtentResult = validateObject(params.scaleExtent, {
693
+ min: {
694
+ toBeTypes: ['number']
695
+ },
696
+ max: {
697
+ toBeTypes: ['number']
698
+ },
699
+ })
700
+ if (scaleExtentResult.status === 'error') {
701
+ return scaleExtentResult
702
+ }
703
+ }
704
+ return result
705
+ },
706
+ setup: ({ svgG, pluginParams$, layerParams$, context }) => {
707
+
708
+ const destroy$ = new Subject()
709
+
710
+ const rootSelection = d3.select(context.svg)
711
+ const selection = d3.select(svgG)
712
+ const gSelection = selection.append('g').classed(gSelectionClassName, true)
713
+ const defsSelection = gSelection.append('defs')
714
+ const edgeListGSelection = gSelection.append('g').classed(edgeListGClassName, true)
715
+ const nodeListGSelection = gSelection.append('g').classed(nodeListGClassName, true)
716
+
717
+ let nodeGSelection: d3.Selection<SVGGElement, RenderNode, SVGGElement, any> | undefined
718
+ let nodeCircleSelection: d3.Selection<SVGCircleElement, RenderNode, SVGGElement, any> | undefined
719
+ let nodeLabelGSelection: d3.Selection<SVGGElement, RenderNode, SVGGElement, any> | undefined
720
+ let nodeLabelSelection: d3.Selection<SVGTextElement, RenderNode, SVGGElement, any> | undefined
721
+ let edgeGSelection: d3.Selection<SVGGElement, RenderEdge, SVGGElement, any> | undefined
722
+ let edgeArrowSelection: d3.Selection<SVGPathElement, RenderEdge, SVGGElement, any> | undefined
723
+ let edgeLabelGSelection: d3.Selection<SVGGElement, RenderEdge, SVGGElement, any> | undefined
724
+ let edgeLabelSelection: d3.Selection<SVGTextElement, RenderEdge, SVGGElement, any> | undefined
725
+
726
+ const dragStatus$ = new BehaviorSubject<DragStatus>('end') // start, drag, end
727
+ const mouseEvent$ = new Subject<EventData>()
728
+
729
+ context.layout$
730
+ .pipe(
731
+ takeUntil(destroy$)
732
+ )
733
+ .subscribe(layout => {
734
+ selection
735
+ .attr('transform', `translate(${layout.left}, ${layout.top})`)
736
+ })
737
+
738
+ // <marker> marker selection
739
+ combineLatest({
740
+ layerParams: layerParams$,
741
+ theme: context.theme$
742
+ }).pipe(
743
+ takeUntil(destroy$),
744
+ debounceTime(0),
745
+ map(({ layerParams, theme }) => {
746
+ return renderArrowMarker(defsSelection, layerParams, theme)
747
+ })
748
+ ).subscribe()
749
+
750
+ // init zoom
751
+ const d3Zoom$ = layerParams$.pipe(
752
+ takeUntil(destroy$),
753
+ // map(d => d.scaleExtent),
754
+ // distinctUntilChanged((a, b) => String(a) === String(b)),
755
+ // first(),
756
+ map(data => {
757
+ let d3Zoom = data.zoomable
758
+ ? d3.zoom().on('zoom', (event) => {
759
+ // console.log(event)
760
+ // this.svgGroup.attr('transform', `translate(
761
+ // ${event.transform.x + (this.zoom.xOffset * event.transform.k)},
762
+ // ${event.transform.y + (this.zoom.yOffset * event.transform.k)}
763
+ // ) scale(
764
+ // ${event.transform.k}
765
+ // )`)
766
+ gSelection.attr('transform', `translate(
767
+ ${event.transform.x},
768
+ ${event.transform.y}
769
+ ) scale(
770
+ ${event.transform.k}
771
+ )`)
772
+
773
+ if (data.dotLabel.sizeFixed && nodeLabelSelection) {
774
+ // 反向 scale 抵消掉放大縮小
775
+ nodeLabelSelection.attr('transform', `scale(${1 / event.transform.k})`)
776
+ }
777
+ if (data.arrowLabel.sizeFixed && edgeLabelSelection) {
778
+ // 反向 scale 抵消掉放大縮小
779
+ edgeLabelSelection.attr('transform', `scale(${1 / event.transform.k})`)
780
+ }
781
+ })
782
+ : d3.zoom().on('zoom', null)
783
+ if (data.scaleExtent) {
784
+ d3Zoom.scaleExtent([data.scaleExtent.min, data.scaleExtent.max])
785
+ }
786
+ rootSelection.call(d3Zoom)
787
+
788
+ return d3Zoom
789
+ }),
790
+ // shareReplay(1)
791
+ )
792
+
793
+ // zoom transform
794
+ combineLatest({
795
+ d3Zoom: d3Zoom$,
796
+ transform: layerParams$.pipe(
797
+ takeUntil(destroy$),
798
+ map(d => d.transform),
799
+ )
800
+ }).pipe(
801
+ takeUntil(destroy$),
802
+ debounceTime(0)
803
+ ).subscribe(data => {
804
+ // console.log('call')
805
+ selection.call(
806
+ data.d3Zoom.transform, d3.zoomIdentity
807
+ .translate(data.transform.x, data.transform.y)
808
+ .scale(data.transform.k)
809
+ )
810
+ })
811
+
812
+
813
+ const simulation$: Observable<d3.Simulation<d3.SimulationNodeDatum, undefined>> = combineLatest({
814
+ layout: context.layout$.pipe(
815
+ first() // 只使用第一次的尺寸(置中)
816
+ ),
817
+ layerParams: layerParams$
818
+ }).pipe(
819
+ takeUntil(destroy$),
820
+ debounceTime(0),
821
+ map(data => createSimulation(data.layout, data.layerParams)),
822
+ shareReplay(1)
823
+ )
824
+
825
+ const renderData$: Observable<RenderData> = combineLatest({
826
+ visibleComputedData: context.visibleComputedData$,
827
+ NodeMap: context.NodeMap$,
828
+ }).pipe(
829
+ takeUntil(destroy$),
830
+ map(data => {
831
+ return {
832
+ nodes: data.visibleComputedData.nodes,
833
+ edges: data.visibleComputedData.edges.map(_d => {
834
+ let d: RenderEdge = _d as RenderEdge
835
+ // d.source = _d.startNode
836
+ // d.target = _d.endNode
837
+ d._source = data.NodeMap.get(_d.source)! as RenderNode
838
+ d._target = data.NodeMap.get(_d.target)! as RenderNode
839
+ return d
840
+ })
841
+ }
842
+ }),
843
+ shareReplay(1)
844
+ )
845
+
846
+ combineLatest({
847
+ renderData: renderData$,
848
+ // computedData: context.computedData$,
849
+ // CategoryNodeMap: context.CategoryNodeMap$,
850
+ simulation: simulation$,
851
+ layerParams: layerParams$,
852
+ theme: context.theme$
853
+ }).pipe(
854
+ takeUntil(destroy$),
855
+ debounceTime(0),
856
+ ).subscribe(data => {
857
+
858
+ nodeGSelection = renderNodeG({
859
+ nodeListGSelection: nodeListGSelection,
860
+ nodes: data.renderData.nodes,
861
+ })
862
+
863
+ nodeCircleSelection = renderNodeCircle({
864
+ nodeGSelection: nodeGSelection,
865
+ layerParams: data.layerParams,
866
+ theme: data.theme
867
+ })
868
+ nodeGSelection.call(drag(data.simulation, dragStatus$))
869
+
870
+ nodeLabelGSelection = renderNodeLabelG({
871
+ nodeGSelection: nodeGSelection,
872
+ layerParams: data.layerParams
873
+ })
874
+
875
+ nodeLabelSelection = renderNodeLabel({
876
+ nodeLabelGSelection: nodeLabelGSelection,
877
+ layerParams: data.layerParams,
878
+ theme: data.theme
879
+ })
880
+
881
+ edgeGSelection = renderEdgeG({
882
+ edgeListGSelection: edgeListGSelection,
883
+ edges: data.renderData.edges
884
+ })
885
+
886
+ edgeArrowSelection = renderEdgeArrowPath({
887
+ edgeGSelection: edgeGSelection,
888
+ layerParams: data.layerParams,
889
+ theme: data.theme
890
+ })
891
+
892
+ edgeLabelGSelection = renderEdgeLabelG({
893
+ edgeGSelection: edgeGSelection,
894
+ })
895
+
896
+ edgeLabelSelection = renderEdgeLabel({
897
+ edgeLabelGSelection: edgeLabelGSelection,
898
+ layerParams: data.layerParams,
899
+ theme: data.theme
900
+ })
901
+
902
+ data.simulation.nodes(data.renderData.nodes)
903
+ .on('tick', () => {
904
+ edgeArrowSelection.attr('d', linkArcFn)
905
+ nodeGSelection.attr('transform', translateFn)
906
+ // nodeLabelGSelection.attr('transform', d => translateFn({
907
+ // x: d.x,
908
+ // y: d.y - data.layerParams.dot.radius - 10
909
+ // }))
910
+ edgeLabelGSelection.attr('transform', d => translateCenterFn(d))
911
+ })
912
+ ;(data.simulation.force("link") as any).links(data.renderData.edges)
913
+
914
+ data.simulation.alpha(0.3).restart()
915
+
916
+ nodeCircleSelection
917
+ .on('mouseover', (event, datum) => {
918
+ event.stopPropagation()
919
+
920
+ mouseEvent$.next({
921
+ // type: 'relationship',
922
+ // eventName: 'mouseover',
923
+ // pluginName,
924
+ // highlightTarget: data.fullChartParams.highlightTarget,
925
+ // datum: datum,
926
+ // category: data.CategoryNodeMap.get(datum.categoryLabel)!,
927
+ // categoryIndex: datum.categoryIndex,
928
+ // categoryLabel: datum.categoryLabel,
929
+ // event,
930
+ // data: data.computedData
931
+ eventName: 'mouseover',
932
+ pluginName,
933
+ layerName,
934
+ target: datum,
935
+ event,
936
+ })
937
+ })
938
+ .on('mousemove', (event, datum) => {
939
+ event.stopPropagation()
940
+
941
+ mouseEvent$.next({
942
+ // type: 'relationship',
943
+ // eventName: 'mousemove',
944
+ // pluginName,
945
+ // highlightTarget: data.fullChartParams.highlightTarget,
946
+ // datum: datum,
947
+ // category: data.CategoryNodeMap.get(datum.categoryLabel)!,
948
+ // categoryIndex: datum.categoryIndex,
949
+ // categoryLabel: datum.categoryLabel,
950
+ // event,
951
+ // data: data.computedData
952
+ eventName: 'mousemove',
953
+ pluginName,
954
+ layerName,
955
+ target: datum,
956
+ event,
957
+ })
958
+ })
959
+ .on('mouseout', (event, datum) => {
960
+ event.stopPropagation()
961
+
962
+ mouseEvent$.next({
963
+ // type: 'relationship',
964
+ // eventName: 'mouseout',
965
+ // pluginName,
966
+ // highlightTarget: data.fullChartParams.highlightTarget,
967
+ // datum: datum,
968
+ // category: data.CategoryNodeMap.get(datum.categoryLabel)!,
969
+ // categoryIndex: datum.categoryIndex,
970
+ // categoryLabel: datum.categoryLabel,
971
+ // event,
972
+ // data: data.computedData
973
+ eventName: 'mouseout',
974
+ pluginName,
975
+ layerName,
976
+ target: datum,
977
+ event,
978
+ })
979
+ })
980
+ .on('click', (event, datum) => {
981
+ event.stopPropagation()
982
+
983
+ mouseEvent$.next({
984
+ // type: 'relationship',
985
+ // eventName: 'click',
986
+ // pluginName,
987
+ // highlightTarget: data.fullChartParams.highlightTarget,
988
+ // datum: datum,
989
+ // category: data.CategoryNodeMap.get(datum.categoryLabel)!,
990
+ // categoryIndex: datum.categoryIndex,
991
+ // categoryLabel: datum.categoryLabel,
992
+ // event,
993
+ // data: data.computedData
994
+ eventName: 'click',
995
+ pluginName,
996
+ layerName,
997
+ target: datum,
998
+ event,
999
+ })
1000
+ })
1001
+ })
1002
+
1003
+ dragStatus$.pipe(
1004
+ distinctUntilChanged((a, b) => a === b),
1005
+ // 只有沒有托曳時才執行
1006
+ switchMap(d => iif(() => d === 'end', mouseEvent$, EMPTY))
1007
+ ).subscribe(data => {
1008
+ context.eventTrigger$.next(data)
1009
+ })
1010
+
1011
+ combineLatest({
1012
+ renderData: renderData$,
1013
+ highlightNodes: context.graphHighlightNodes$.pipe(
1014
+ map(data => data.map(d => d.id))
1015
+ ),
1016
+ // highlightEdges: context.graphHighlightEdges$.pipe(
1017
+ // map(data => data.map(d => d.id))
1018
+ // ),
1019
+ styles: pluginParams$.pipe(
1020
+ map(d => d.styles)
1021
+ ),
1022
+ // layerParams: layerParams$,
1023
+ }).pipe(
1024
+ takeUntil(destroy$),
1025
+ debounceTime(0)
1026
+ ).subscribe(data => {
1027
+ if (!nodeGSelection || !edgeGSelection) {
1028
+ return
1029
+ }
1030
+
1031
+ highlightNodes({
1032
+ nodeGSelection,
1033
+ edgeGSelection,
1034
+ styles: data.styles,
1035
+ highlightIds: data.highlightNodes,
1036
+ })
1037
+ // highlightEdges({
1038
+ // edgeGSelection,
1039
+ // highlightIds: data.highlightEdges,
1040
+ // fullChartParams: data.fullChartParams
1041
+ // })
1042
+ })
1043
+
1044
+ return () => {
1045
+ destroy$.next(undefined)
1046
+ }
1047
+ }
1048
+ })