@logicflow/core 2.2.0-alpha.2 → 2.2.0-alpha.3
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/.turbo/turbo-build$colon$dev.log +2 -2
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +6 -0
- package/dist/index.css +3 -2
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/LogicFlow.d.ts +9 -0
- package/es/constant/index.d.ts +1 -1
- package/es/constant/index.js +1 -1
- package/es/constant/theme.d.ts +136 -0
- package/es/constant/theme.js +680 -0
- package/es/index.css +3 -2
- package/es/model/GraphModel.d.ts +9 -2
- package/es/model/GraphModel.js +17 -6
- package/es/model/TransformModel.js +9 -9
- package/es/model/edge/BaseEdgeModel.js +7 -2
- package/es/model/edge/PolylineEdgeModel.d.ts +6 -0
- package/es/model/edge/PolylineEdgeModel.js +23 -1
- package/es/model/node/BaseNodeModel.d.ts +12 -1
- package/es/model/node/BaseNodeModel.js +6 -1
- package/es/model/node/HtmlNodeModel.d.ts +12 -0
- package/es/model/node/HtmlNodeModel.js +19 -0
- package/es/model/node/PolygonNodeModel.js +3 -3
- package/es/options.d.ts +1 -1
- package/es/style/index.css +3 -2
- package/es/style/index.less +3 -2
- package/es/style/raw.d.ts +1 -1
- package/es/style/raw.js +1 -1
- package/es/tool/MultipleSelectTool.js +6 -5
- package/es/util/edge.d.ts +1 -1
- package/es/util/edge.js +2 -2
- package/es/util/geometry.d.ts +8 -0
- package/es/util/geometry.js +79 -0
- package/es/util/theme.d.ts +2 -65
- package/es/util/theme.js +4 -281
- package/es/view/Control.d.ts +5 -0
- package/es/view/Control.js +44 -57
- package/es/view/edge/PolylineEdge.js +13 -2
- package/es/view/node/BaseNode.d.ts +1 -0
- package/es/view/node/BaseNode.js +14 -10
- package/es/view/node/HtmlNode.js +2 -4
- package/es/view/overlay/Grid.d.ts +12 -1
- package/es/view/overlay/Grid.js +85 -23
- package/es/view/overlay/OutlineOverlay.d.ts +1 -0
- package/es/view/overlay/OutlineOverlay.js +17 -16
- package/es/view/overlay/gridConfig.d.ts +46 -0
- package/es/view/overlay/gridConfig.js +99 -0
- package/es/view/shape/Polygon.d.ts +0 -7
- package/es/view/shape/Polygon.js +12 -43
- package/lib/LogicFlow.d.ts +9 -0
- package/lib/constant/index.d.ts +1 -1
- package/lib/constant/index.js +16 -2
- package/lib/constant/theme.d.ts +136 -0
- package/lib/constant/theme.js +683 -0
- package/lib/index.css +3 -2
- package/lib/model/GraphModel.d.ts +9 -2
- package/lib/model/GraphModel.js +18 -7
- package/lib/model/TransformModel.js +9 -9
- package/lib/model/edge/BaseEdgeModel.js +7 -2
- package/lib/model/edge/PolylineEdgeModel.d.ts +6 -0
- package/lib/model/edge/PolylineEdgeModel.js +23 -1
- package/lib/model/node/BaseNodeModel.d.ts +12 -1
- package/lib/model/node/BaseNodeModel.js +6 -1
- package/lib/model/node/HtmlNodeModel.d.ts +12 -0
- package/lib/model/node/HtmlNodeModel.js +19 -0
- package/lib/model/node/PolygonNodeModel.js +3 -3
- package/lib/options.d.ts +1 -1
- package/lib/style/index.css +3 -2
- package/lib/style/index.less +3 -2
- package/lib/style/raw.d.ts +1 -1
- package/lib/style/raw.js +1 -1
- package/lib/tool/MultipleSelectTool.js +6 -5
- package/lib/util/edge.d.ts +1 -1
- package/lib/util/edge.js +2 -2
- package/lib/util/geometry.d.ts +8 -0
- package/lib/util/geometry.js +81 -1
- package/lib/util/theme.d.ts +2 -65
- package/lib/util/theme.js +15 -292
- package/lib/view/Control.d.ts +5 -0
- package/lib/view/Control.js +44 -57
- package/lib/view/edge/PolylineEdge.js +13 -2
- package/lib/view/node/BaseNode.d.ts +1 -0
- package/lib/view/node/BaseNode.js +14 -10
- package/lib/view/node/HtmlNode.js +1 -3
- package/lib/view/overlay/Grid.d.ts +12 -1
- package/lib/view/overlay/Grid.js +83 -21
- package/lib/view/overlay/OutlineOverlay.d.ts +1 -0
- package/lib/view/overlay/OutlineOverlay.js +17 -16
- package/lib/view/overlay/gridConfig.d.ts +46 -0
- package/lib/view/overlay/gridConfig.js +104 -0
- package/lib/view/shape/Polygon.d.ts +0 -7
- package/lib/view/shape/Polygon.js +13 -45
- package/package.json +1 -1
- package/src/LogicFlow.tsx +10 -0
- package/src/constant/index.ts +2 -2
- package/src/constant/theme.ts +708 -0
- package/src/model/GraphModel.ts +19 -7
- package/src/model/TransformModel.ts +9 -9
- package/src/model/edge/BaseEdgeModel.ts +10 -2
- package/src/model/edge/PolylineEdgeModel.ts +26 -1
- package/src/model/node/BaseNodeModel.ts +9 -1
- package/src/model/node/HtmlNodeModel.ts +14 -0
- package/src/model/node/PolygonNodeModel.ts +2 -0
- package/src/options.ts +1 -1
- package/src/style/index.less +3 -2
- package/src/style/raw.ts +3 -2
- package/src/tool/MultipleSelectTool.tsx +6 -5
- package/src/util/edge.ts +2 -1
- package/src/util/geometry.ts +99 -0
- package/src/util/theme.ts +12 -303
- package/src/view/Control.tsx +61 -63
- package/src/view/edge/PolylineEdge.tsx +14 -2
- package/src/view/node/BaseNode.tsx +8 -3
- package/src/view/node/HtmlNode.tsx +27 -10
- package/src/view/overlay/Grid.tsx +187 -30
- package/src/view/overlay/OutlineOverlay.tsx +35 -47
- package/src/view/overlay/gridConfig.ts +103 -0
- package/src/view/shape/Polygon.tsx +12 -49
- package/stats.html +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Component } from 'preact/compat'
|
|
2
|
-
import { cloneDeep, assign } from 'lodash-es'
|
|
2
|
+
import { cloneDeep, assign, isNil } from 'lodash-es'
|
|
3
3
|
import { observer } from '../..'
|
|
4
|
+
import { mergeMajorBoldConfig, MajorBoldConfig } from './gridConfig'
|
|
4
5
|
import { createUuid } from '../../util'
|
|
5
6
|
import { GraphModel } from '../../model'
|
|
6
|
-
import {
|
|
7
|
+
import { defaultGrid } from '../../constant'
|
|
7
8
|
|
|
8
9
|
type IProps = {
|
|
9
10
|
graphModel: GraphModel
|
|
@@ -39,25 +40,93 @@ export class Grid extends Component<IProps> {
|
|
|
39
40
|
)
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// 计算与 size 整除的虚线周期,使边长能被 (dash + gap) 完整分割
|
|
44
|
+
private getDashArrayForSize(dashCfg?: { pattern: number[] }): string {
|
|
45
|
+
// 若提供了直接可用的 pattern,则优先使用
|
|
46
|
+
const direct = dashCfg?.pattern?.filter(
|
|
47
|
+
(n) => typeof n === 'number' && n > 0,
|
|
48
|
+
)
|
|
49
|
+
return direct?.join(',') || '2,1'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 计算一个周期内的最大加粗索引(作为周期大小)
|
|
53
|
+
private getPeriod(advanced: any): number {
|
|
54
|
+
const list = Array.isArray(advanced?.boldIndices)
|
|
55
|
+
? advanced.boldIndices.filter((n: any) => typeof n === 'number' && n > 0)
|
|
56
|
+
: []
|
|
57
|
+
return list.length ? Math.max(...list) : 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 计算加粗线宽,优先使用自定义;否则根据周期与厚度估算
|
|
61
|
+
private getBoldStrokeWidth(
|
|
62
|
+
advanced: any,
|
|
63
|
+
size: number,
|
|
64
|
+
thickness?: number,
|
|
65
|
+
period?: number,
|
|
66
|
+
): number {
|
|
67
|
+
if (typeof advanced?.customBoldWidth === 'number')
|
|
68
|
+
return advanced.customBoldWidth
|
|
69
|
+
const baseThickness = Math.max(1, thickness ?? 1)
|
|
70
|
+
const p = Math.max(1, period ?? this.getPeriod(advanced))
|
|
71
|
+
return Math.min(baseThickness, (size * p) / 2) / 2
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 渲染 mesh 类型四条边的虚线,减少重复代码
|
|
75
|
+
private renderMeshEdgeLines(
|
|
76
|
+
size: number,
|
|
77
|
+
color: string,
|
|
78
|
+
strokeWidth: number,
|
|
79
|
+
opacity: number,
|
|
80
|
+
dash?: string,
|
|
81
|
+
) {
|
|
82
|
+
const segments = [
|
|
83
|
+
{ d: `M 0 0 H ${size}` },
|
|
84
|
+
{ d: `M 0 ${size} H ${size}` },
|
|
85
|
+
{ d: `M 0 0 V ${size}` },
|
|
86
|
+
{ d: `M ${size} 0 V ${size}` },
|
|
87
|
+
]
|
|
88
|
+
return (
|
|
89
|
+
<g opacity={opacity} fill="transparent">
|
|
90
|
+
{segments.map((seg) => (
|
|
91
|
+
<path
|
|
92
|
+
d={seg.d}
|
|
93
|
+
stroke={color}
|
|
94
|
+
strokeWidth={strokeWidth / 2}
|
|
95
|
+
strokeDasharray={dash}
|
|
96
|
+
strokeLinecap="butt"
|
|
97
|
+
fill="transparent"
|
|
98
|
+
/>
|
|
99
|
+
))}
|
|
100
|
+
</g>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
42
104
|
// 网格类型为交叉线
|
|
43
105
|
// todo: 采用背景缩放的方式,实现更好的体验
|
|
44
106
|
renderMesh() {
|
|
45
|
-
const {
|
|
46
|
-
|
|
107
|
+
const {
|
|
108
|
+
config,
|
|
109
|
+
size = 1,
|
|
110
|
+
visible,
|
|
111
|
+
majorBold,
|
|
112
|
+
} = this.gridOptions as Grid.GridOptions & {
|
|
113
|
+
majorBold?: boolean | MajorBoldConfig
|
|
114
|
+
}
|
|
115
|
+
const { config: advanced } = mergeMajorBoldConfig(majorBold)
|
|
116
|
+
const { opacity: baseOpacity } = advanced
|
|
117
|
+
const color: string = (config?.color ?? '#D7DEEB') as string
|
|
118
|
+
const thickness: number = (config?.thickness ?? 1) as number
|
|
47
119
|
|
|
48
120
|
// 对于交叉线网格,线的宽度不能大于网格大小的一半
|
|
49
121
|
const strokeWidth = Math.min(Math.max(1, thickness), size / 2)
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
fill="transparent"
|
|
59
|
-
/>
|
|
60
|
-
)
|
|
122
|
+
const opacity = visible ? baseOpacity : 0
|
|
123
|
+
|
|
124
|
+
// 根据 size 自动计算合适的 dash/gap 周期,使 size 能被 (dash + gap) 整除
|
|
125
|
+
const dash =
|
|
126
|
+
majorBold === false
|
|
127
|
+
? undefined
|
|
128
|
+
: this.getDashArrayForSize(advanced.dashArrayConfig)
|
|
129
|
+
return this.renderMeshEdgeLines(size, color, strokeWidth, opacity, dash)
|
|
61
130
|
}
|
|
62
131
|
|
|
63
132
|
render() {
|
|
@@ -65,7 +134,16 @@ export class Grid extends Component<IProps> {
|
|
|
65
134
|
graphModel: { transformModel, grid },
|
|
66
135
|
} = this.props
|
|
67
136
|
this.gridOptions = grid
|
|
68
|
-
const {
|
|
137
|
+
const {
|
|
138
|
+
type,
|
|
139
|
+
config = {},
|
|
140
|
+
size = 1,
|
|
141
|
+
majorBold,
|
|
142
|
+
} = this.gridOptions as Grid.GridOptions & {
|
|
143
|
+
majorBold?: boolean | MajorBoldConfig
|
|
144
|
+
}
|
|
145
|
+
const showMajorBold = majorBold !== false && !isNil(majorBold)
|
|
146
|
+
const { config: advanced } = mergeMajorBoldConfig(majorBold)
|
|
69
147
|
const { SCALE_X, SKEW_Y, SKEW_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y } =
|
|
70
148
|
transformModel
|
|
71
149
|
const matrixString = [
|
|
@@ -77,9 +155,8 @@ export class Grid extends Component<IProps> {
|
|
|
77
155
|
TRANSLATE_Y,
|
|
78
156
|
].join(',')
|
|
79
157
|
const transform = `matrix(${matrixString})`
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// };
|
|
158
|
+
const radius = Math.min(Math.max(2, config.thickness ?? 1), size / 4)
|
|
159
|
+
const opacity = showMajorBold ? advanced.opacity : 1
|
|
83
160
|
return (
|
|
84
161
|
<div className="lf-grid">
|
|
85
162
|
<svg
|
|
@@ -101,8 +178,91 @@ export class Grid extends Component<IProps> {
|
|
|
101
178
|
{type === 'dot' && this.renderDot()}
|
|
102
179
|
{type === 'mesh' && this.renderMesh()}
|
|
103
180
|
</pattern>
|
|
181
|
+
{type === 'dot' && advanced.boldIndices.length ? (
|
|
182
|
+
<pattern
|
|
183
|
+
id={`${this.id}-dot-major`}
|
|
184
|
+
patternUnits="userSpaceOnUse"
|
|
185
|
+
patternTransform={transform}
|
|
186
|
+
x="0"
|
|
187
|
+
y="0"
|
|
188
|
+
width={size * this.getPeriod(advanced)}
|
|
189
|
+
height={size * this.getPeriod(advanced)}
|
|
190
|
+
>
|
|
191
|
+
<g
|
|
192
|
+
fill={this.gridOptions.config?.color ?? '#D7DEEB'}
|
|
193
|
+
opacity={this.gridOptions.visible ? opacity : 0}
|
|
194
|
+
>
|
|
195
|
+
<circle cx={0} cy={0} r={(radius * 1.5) / 2} />
|
|
196
|
+
<circle
|
|
197
|
+
cx={size * this.getPeriod(advanced)}
|
|
198
|
+
cy={0}
|
|
199
|
+
r={(radius * 1.5) / 2}
|
|
200
|
+
/>
|
|
201
|
+
<circle
|
|
202
|
+
cx={0}
|
|
203
|
+
cy={size * this.getPeriod(advanced)}
|
|
204
|
+
r={(radius * 1.5) / 2}
|
|
205
|
+
/>
|
|
206
|
+
<circle
|
|
207
|
+
cx={size * this.getPeriod(advanced)}
|
|
208
|
+
cy={size * this.getPeriod(advanced)}
|
|
209
|
+
r={(radius * 1.5) / 2}
|
|
210
|
+
/>
|
|
211
|
+
</g>
|
|
212
|
+
</pattern>
|
|
213
|
+
) : null}
|
|
214
|
+
{type === 'mesh' && advanced.boldIndices.length ? (
|
|
215
|
+
<pattern
|
|
216
|
+
id={`${this.id}-major`}
|
|
217
|
+
patternUnits="userSpaceOnUse"
|
|
218
|
+
patternTransform={transform}
|
|
219
|
+
x="0"
|
|
220
|
+
y="0"
|
|
221
|
+
width={size * this.getPeriod(advanced)}
|
|
222
|
+
height={size * this.getPeriod(advanced)}
|
|
223
|
+
>
|
|
224
|
+
{advanced.boldIndices.map((i: number) => (
|
|
225
|
+
<g>
|
|
226
|
+
<path
|
|
227
|
+
d={`M ${size * i} 0 V ${size * this.getPeriod(advanced)}`}
|
|
228
|
+
stroke={this.gridOptions.config?.color ?? '#D7DEEB'}
|
|
229
|
+
strokeWidth={this.getBoldStrokeWidth(
|
|
230
|
+
advanced,
|
|
231
|
+
size,
|
|
232
|
+
(this.gridOptions.config ?? {}).thickness,
|
|
233
|
+
this.getPeriod(advanced),
|
|
234
|
+
)}
|
|
235
|
+
opacity={this.gridOptions.visible ? opacity : 0}
|
|
236
|
+
fill="transparent"
|
|
237
|
+
/>
|
|
238
|
+
<path
|
|
239
|
+
d={`M 0 ${size * i} H ${size * this.getPeriod(advanced)}`}
|
|
240
|
+
stroke={this.gridOptions.config?.color ?? '#D7DEEB'}
|
|
241
|
+
strokeWidth={this.getBoldStrokeWidth(
|
|
242
|
+
advanced,
|
|
243
|
+
size,
|
|
244
|
+
(this.gridOptions.config ?? {}).thickness,
|
|
245
|
+
this.getPeriod(advanced),
|
|
246
|
+
)}
|
|
247
|
+
opacity={this.gridOptions.visible ? opacity : 0}
|
|
248
|
+
fill="transparent"
|
|
249
|
+
/>
|
|
250
|
+
</g>
|
|
251
|
+
))}
|
|
252
|
+
</pattern>
|
|
253
|
+
) : null}
|
|
104
254
|
</defs>
|
|
105
255
|
<rect width="100%" height="100%" fill={`url(#${this.id})`} />
|
|
256
|
+
{type === 'dot' && showMajorBold && advanced.boldIndices.length ? (
|
|
257
|
+
<rect
|
|
258
|
+
width="100%"
|
|
259
|
+
height="100%"
|
|
260
|
+
fill={`url(#${this.id}-dot-major)`}
|
|
261
|
+
/>
|
|
262
|
+
) : null}
|
|
263
|
+
{type === 'mesh' && showMajorBold && advanced.boldIndices.length ? (
|
|
264
|
+
<rect width="100%" height="100%" fill={`url(#${this.id}-major)`} />
|
|
265
|
+
) : null}
|
|
106
266
|
</svg>
|
|
107
267
|
</div>
|
|
108
268
|
)
|
|
@@ -137,20 +297,17 @@ export namespace Grid {
|
|
|
137
297
|
*/
|
|
138
298
|
thickness?: number
|
|
139
299
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
color: '#ababab',
|
|
148
|
-
thickness: 1,
|
|
149
|
-
},
|
|
300
|
+
/**
|
|
301
|
+
* 行为配置:支持 boolean 或高级对象
|
|
302
|
+
* - false: 禁用特殊样式,opacity=1,无加粗,无虚线
|
|
303
|
+
* - true: 启用默认样式,opacity=0.75,每第5个加粗/实线,虚线动态计算
|
|
304
|
+
* - object: 高级配置,详见 MajorBoldConfig
|
|
305
|
+
*/
|
|
306
|
+
majorBold?: boolean | MajorBoldConfig
|
|
150
307
|
}
|
|
151
308
|
|
|
152
309
|
export function getGridOptions(options: number | boolean | GridOptions) {
|
|
153
|
-
const defaultOptions = cloneDeep(
|
|
310
|
+
const defaultOptions = cloneDeep(defaultGrid) as GridOptions
|
|
154
311
|
if (typeof options === 'number') {
|
|
155
312
|
return assign(defaultOptions, { size: options })
|
|
156
313
|
} else if (typeof options === 'boolean') {
|
|
@@ -16,6 +16,26 @@ type IProps = {
|
|
|
16
16
|
|
|
17
17
|
@observer
|
|
18
18
|
export class OutlineOverlay extends Component<IProps> {
|
|
19
|
+
// 通用渲染函数:根据点集合与样式计算包围盒并返回矩形轮廓
|
|
20
|
+
private renderRectOutline(
|
|
21
|
+
pointsList: any[],
|
|
22
|
+
style: Record<string, unknown>,
|
|
23
|
+
className: string,
|
|
24
|
+
defaultOffsets: { widthOffset: number; heightOffset: number },
|
|
25
|
+
) {
|
|
26
|
+
const {
|
|
27
|
+
widthOffset = defaultOffsets.widthOffset,
|
|
28
|
+
heightOffset = defaultOffsets.heightOffset,
|
|
29
|
+
} = (style || {}) as any
|
|
30
|
+
const { x, y, width, height } = getBBoxOfPoints(
|
|
31
|
+
pointsList,
|
|
32
|
+
widthOffset,
|
|
33
|
+
heightOffset,
|
|
34
|
+
)
|
|
35
|
+
return (
|
|
36
|
+
<Rect className={className} {...{ x, y, width, height }} {...style} />
|
|
37
|
+
)
|
|
38
|
+
}
|
|
19
39
|
// 节点outline
|
|
20
40
|
getNodesOutline() {
|
|
21
41
|
const { graphModel } = this.props
|
|
@@ -52,10 +72,8 @@ export class OutlineOverlay extends Component<IProps> {
|
|
|
52
72
|
{...{
|
|
53
73
|
x,
|
|
54
74
|
y,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
width: width + 10,
|
|
58
|
-
height: height + 10,
|
|
75
|
+
width: width + 4,
|
|
76
|
+
height: height + 4,
|
|
59
77
|
}}
|
|
60
78
|
{...attributes}
|
|
61
79
|
/>,
|
|
@@ -96,22 +114,12 @@ export class OutlineOverlay extends Component<IProps> {
|
|
|
96
114
|
// 直线outline
|
|
97
115
|
getLineOutline(line: LineEdgeModel) {
|
|
98
116
|
const { startPoint, endPoint } = line
|
|
99
|
-
const x = (startPoint.x + endPoint.x) / 2
|
|
100
|
-
const y = (startPoint.y + endPoint.y) / 2
|
|
101
|
-
const width = Math.abs(startPoint.x - endPoint.x) + 10
|
|
102
|
-
const height = Math.abs(startPoint.y - endPoint.y) + 10
|
|
103
117
|
const style = line.getOutlineStyle()
|
|
104
|
-
return (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
y,
|
|
110
|
-
width,
|
|
111
|
-
height,
|
|
112
|
-
}}
|
|
113
|
-
{...style}
|
|
114
|
-
/>
|
|
118
|
+
return this.renderRectOutline(
|
|
119
|
+
[startPoint, endPoint],
|
|
120
|
+
style,
|
|
121
|
+
'lf-outline-edge',
|
|
122
|
+
{ widthOffset: 10, heightOffset: 10 },
|
|
115
123
|
)
|
|
116
124
|
}
|
|
117
125
|
|
|
@@ -119,42 +127,22 @@ export class OutlineOverlay extends Component<IProps> {
|
|
|
119
127
|
getPolylineOutline(polyline: PolylineEdgeModel) {
|
|
120
128
|
const { points } = polyline
|
|
121
129
|
const pointsList = points2PointsList(points)
|
|
122
|
-
const bbox = getBBoxOfPoints(pointsList, 8)
|
|
123
|
-
const { x, y, width, height } = bbox
|
|
124
130
|
const style = polyline.getOutlineStyle()
|
|
125
|
-
return (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
x,
|
|
130
|
-
y,
|
|
131
|
-
width,
|
|
132
|
-
height,
|
|
133
|
-
}}
|
|
134
|
-
{...style}
|
|
135
|
-
/>
|
|
136
|
-
)
|
|
131
|
+
return this.renderRectOutline(pointsList, style, 'lf-outline', {
|
|
132
|
+
widthOffset: 8,
|
|
133
|
+
heightOffset: 16,
|
|
134
|
+
})
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
// 曲线outline
|
|
140
138
|
getBezierOutline(bezier: BezierEdgeModel) {
|
|
141
139
|
const { path } = bezier
|
|
142
140
|
const pointsList = getBezierPoints(path)
|
|
143
|
-
const bbox = getBBoxOfPoints(pointsList, 8)
|
|
144
|
-
const { x, y, width, height } = bbox
|
|
145
141
|
const style = bezier.getOutlineStyle()
|
|
146
|
-
return (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
x,
|
|
151
|
-
y,
|
|
152
|
-
width,
|
|
153
|
-
height,
|
|
154
|
-
}}
|
|
155
|
-
{...style}
|
|
156
|
-
/>
|
|
157
|
-
)
|
|
142
|
+
return this.renderRectOutline(pointsList, style, 'lf-outline', {
|
|
143
|
+
widthOffset: 8,
|
|
144
|
+
heightOffset: 16,
|
|
145
|
+
})
|
|
158
146
|
}
|
|
159
147
|
|
|
160
148
|
render() {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 网格高级 majorBold 配置。
|
|
3
|
+
*
|
|
4
|
+
* 示例(布尔值):
|
|
5
|
+
* - false:禁用特殊样式,opacity=1,无加粗,无虚线
|
|
6
|
+
* - true:启用默认值,opacity=0.75,每第 5 条线/点加粗/实线,
|
|
7
|
+
* 虚线图案通过 getDashArrayForSize(size) 计算
|
|
8
|
+
*
|
|
9
|
+
* 示例(对象):
|
|
10
|
+
* {
|
|
11
|
+
* opacity: 0.6,
|
|
12
|
+
* boldIndices: [4, 8],
|
|
13
|
+
* dashArrayConfig: { pattern: [4,2,4] },
|
|
14
|
+
* customBoldWidth: 2,
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
export type MajorBoldConfig = {
|
|
18
|
+
/** 默认网格透明度 (0-1) */
|
|
19
|
+
opacity: number
|
|
20
|
+
/** 一个周期内需要加粗/实线的索引列表 */
|
|
21
|
+
boldIndices: number[]
|
|
22
|
+
/** 虚线样式的计算配置 */
|
|
23
|
+
dashArrayConfig: {
|
|
24
|
+
/** 固定的虚线模式(可选) */
|
|
25
|
+
pattern: number[]
|
|
26
|
+
}
|
|
27
|
+
/** mesh 网格的自定义加粗线宽(可选) */
|
|
28
|
+
customBoldWidth?: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const defaultGridConfig: MajorBoldConfig = {
|
|
32
|
+
opacity: 1,
|
|
33
|
+
boldIndices: [5],
|
|
34
|
+
dashArrayConfig: {
|
|
35
|
+
pattern: [2, 1],
|
|
36
|
+
},
|
|
37
|
+
customBoldWidth: 2,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 校验并规范化 GridConfig。
|
|
42
|
+
* - 将 opacity 限制在 [0,1] 范围
|
|
43
|
+
* - boldIndices 过滤为正整数并去重
|
|
44
|
+
*/
|
|
45
|
+
export function validateGridConfig(cfg: MajorBoldConfig): MajorBoldConfig {
|
|
46
|
+
const opacity = Math.max(
|
|
47
|
+
0,
|
|
48
|
+
Math.min(1, cfg.opacity ?? defaultGridConfig.opacity),
|
|
49
|
+
)
|
|
50
|
+
const boldIndicesSet = new Set<number>()
|
|
51
|
+
const boldRaw = Array.isArray(cfg.boldIndices)
|
|
52
|
+
? cfg.boldIndices
|
|
53
|
+
: defaultGridConfig.boldIndices
|
|
54
|
+
for (const n of boldRaw) {
|
|
55
|
+
if (typeof n === 'number' && Number.isFinite(n) && n > 0) {
|
|
56
|
+
boldIndicesSet.add(Math.floor(n))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const boldIndices = Array.from(boldIndicesSet)
|
|
60
|
+
const pattern = Array.isArray(cfg.dashArrayConfig?.pattern)
|
|
61
|
+
? cfg.dashArrayConfig.pattern
|
|
62
|
+
: defaultGridConfig.dashArrayConfig.pattern
|
|
63
|
+
const dashArrayConfig = {
|
|
64
|
+
pattern,
|
|
65
|
+
}
|
|
66
|
+
const customBoldWidth = cfg.customBoldWidth
|
|
67
|
+
return {
|
|
68
|
+
opacity,
|
|
69
|
+
boldIndices: boldIndices.length
|
|
70
|
+
? boldIndices
|
|
71
|
+
: defaultGridConfig.boldIndices,
|
|
72
|
+
dashArrayConfig,
|
|
73
|
+
customBoldWidth,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 合并用户行为配置与默认值。
|
|
79
|
+
* - false → 关闭模式:opacity=1,bold=[],关闭虚线
|
|
80
|
+
* - true → 默认模式:采用 defaultGridConfig
|
|
81
|
+
* - object → 自定义模式:校验后使用用户配置
|
|
82
|
+
*/
|
|
83
|
+
export function mergeMajorBoldConfig(input?: boolean | MajorBoldConfig): {
|
|
84
|
+
mode: 'disabled' | 'default' | 'custom'
|
|
85
|
+
config: MajorBoldConfig
|
|
86
|
+
} {
|
|
87
|
+
if (input === false) {
|
|
88
|
+
return {
|
|
89
|
+
mode: 'disabled',
|
|
90
|
+
config: {
|
|
91
|
+
opacity: 1,
|
|
92
|
+
boldIndices: [],
|
|
93
|
+
dashArrayConfig: {
|
|
94
|
+
pattern: [],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (input === true || input == null) {
|
|
100
|
+
return { mode: 'default', config: { ...defaultGridConfig } }
|
|
101
|
+
}
|
|
102
|
+
return { mode: 'custom', config: validateGridConfig(input) }
|
|
103
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createElement as h } from 'preact/compat'
|
|
2
2
|
import { forEach, toPairs } from 'lodash-es'
|
|
3
3
|
import { LogicFlow } from '../..'
|
|
4
|
+
import { generateRoundedCorners } from '../../util/geometry'
|
|
4
5
|
|
|
5
6
|
export type IPolygonProps = {
|
|
6
7
|
points: LogicFlow.PointTuple[]
|
|
@@ -9,52 +10,6 @@ export type IPolygonProps = {
|
|
|
9
10
|
className?: string
|
|
10
11
|
radius?: number
|
|
11
12
|
}
|
|
12
|
-
/**
|
|
13
|
-
* 生成带圆角的多边形路径
|
|
14
|
-
* @param points 多边形顶点坐标数组
|
|
15
|
-
* @param radius 圆角半径
|
|
16
|
-
* @returns SVG 路径字符串
|
|
17
|
-
*/
|
|
18
|
-
export function createRoundedPolygonPath(points, radius): string {
|
|
19
|
-
const pointList = points.map((point) => ({ x: point[0], y: point[1] }))
|
|
20
|
-
const len = pointList.length
|
|
21
|
-
if (len < 3) return ''
|
|
22
|
-
|
|
23
|
-
const r = Math.abs(radius)
|
|
24
|
-
let path = ''
|
|
25
|
-
|
|
26
|
-
for (let i = 0; i < len; i++) {
|
|
27
|
-
const prev = pointList[(i - 1 + len) % len]
|
|
28
|
-
const curr = pointList[i]
|
|
29
|
-
const next = pointList[(i + 1) % len]
|
|
30
|
-
|
|
31
|
-
// 向量
|
|
32
|
-
const v1 = { x: curr.x - prev.x, y: curr.y - prev.y }
|
|
33
|
-
const v2 = { x: next.x - curr.x, y: next.y - curr.y }
|
|
34
|
-
|
|
35
|
-
// 单位向量
|
|
36
|
-
const len1 = Math.hypot(v1.x, v1.y)
|
|
37
|
-
const len2 = Math.hypot(v2.x, v2.y)
|
|
38
|
-
const u1 = { x: v1.x / len1, y: v1.y / len1 }
|
|
39
|
-
const u2 = { x: v2.x / len2, y: v2.y / len2 }
|
|
40
|
-
|
|
41
|
-
// 起点 = curr - u1 * r,终点 = curr + u2 * r
|
|
42
|
-
const start = { x: curr.x - u1.x * r, y: curr.y - u1.y * r }
|
|
43
|
-
const end = { x: curr.x + u2.x * r, y: curr.y + u2.y * r }
|
|
44
|
-
|
|
45
|
-
if (i === 0) {
|
|
46
|
-
path += `M ${start.x} ${start.y} `
|
|
47
|
-
} else {
|
|
48
|
-
path += `L ${start.x} ${start.y} `
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Q 控制点是当前拐角点
|
|
52
|
-
path += `Q ${curr.x} ${curr.y} ${end.x} ${end.y} `
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
path += 'Z'
|
|
56
|
-
return path
|
|
57
|
-
}
|
|
58
13
|
|
|
59
14
|
export function Polygon(props: IPolygonProps): h.JSX.Element {
|
|
60
15
|
const { points = [], className, radius } = props
|
|
@@ -74,13 +29,21 @@ export function Polygon(props: IPolygonProps): h.JSX.Element {
|
|
|
74
29
|
})
|
|
75
30
|
|
|
76
31
|
if (className) {
|
|
77
|
-
attrs.
|
|
32
|
+
attrs.className = `lf-basic-shape ${className}`
|
|
78
33
|
} else {
|
|
79
34
|
attrs.className = 'lf-basic-shape'
|
|
80
35
|
}
|
|
81
36
|
if (radius) {
|
|
82
|
-
const
|
|
83
|
-
|
|
37
|
+
const pointList = points.map((point) => ({ x: point[0], y: point[1] }))
|
|
38
|
+
const rounded = generateRoundedCorners(pointList, radius, true)
|
|
39
|
+
const d = rounded.length
|
|
40
|
+
? `M ${rounded[0].x} ${rounded[0].y} ${rounded
|
|
41
|
+
.slice(1)
|
|
42
|
+
.map((p) => `L ${p.x} ${p.y}`)
|
|
43
|
+
.join(' ')} Z`
|
|
44
|
+
: ''
|
|
45
|
+
attrs.d = d
|
|
46
|
+
delete attrs.points
|
|
84
47
|
return <path {...attrs} />
|
|
85
48
|
} else {
|
|
86
49
|
attrs.points = points.map((point) => point.join(',')).join(' ')
|