@leafer/path 1.0.0-alpha.7 → 1.0.0-bate
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/package.json +4 -3
- package/src/BezierHelper.ts +117 -79
- package/src/EllipseHelper.ts +70 -0
- package/src/PathBounds.ts +138 -0
- package/src/PathCommandDataHelper.ts +98 -0
- package/src/PathCommandMap.ts +52 -33
- package/src/PathConvert.ts +104 -29
- package/src/PathCorner.ts +10 -0
- package/src/PathCreator.ts +71 -43
- package/src/PathDrawer.ts +85 -0
- package/src/PathHelper.ts +5 -110
- package/src/RectHelper.ts +22 -0
- package/src/index.ts +18 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leafer/path",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-bate",
|
|
4
4
|
"description": "@leafer/path",
|
|
5
5
|
"author": "Chao (Leafer) Wan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
"leaferjs"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@leafer/math": "1.0.0-
|
|
22
|
+
"@leafer/math": "1.0.0-bate",
|
|
23
|
+
"@leafer/debug": "1.0.0-bate"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
|
-
"@leafer/interface": "1.0.0-
|
|
26
|
+
"@leafer/interface": "1.0.0-bate"
|
|
26
27
|
}
|
|
27
28
|
}
|
package/src/BezierHelper.ts
CHANGED
|
@@ -1,104 +1,142 @@
|
|
|
1
1
|
import { IPointData, ITwoPointBoundsData, IPathCommandData } from '@leafer/interface'
|
|
2
|
-
import { TwoPointBoundsHelper } from '@leafer/math'
|
|
2
|
+
import { OneRadian, PI2, PI_2, PointHelper, TwoPointBoundsHelper } from '@leafer/math'
|
|
3
3
|
|
|
4
4
|
import { PathCommandMap } from './PathCommandMap'
|
|
5
|
+
import { RectHelper } from './RectHelper'
|
|
6
|
+
import { PathHelper } from './PathHelper'
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
const tempPoint = {} as IPointData
|
|
8
|
-
const { sin, cos, sqrt, atan2, ceil, abs, PI } = Math
|
|
8
|
+
const { sin, cos, atan2, ceil, abs, PI } = Math
|
|
9
9
|
const { setPoint, addPoint } = TwoPointBoundsHelper
|
|
10
|
+
const { set } = PointHelper
|
|
11
|
+
const tempPoint = {} as IPointData
|
|
10
12
|
|
|
11
13
|
export const BezierHelper = {
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
15
|
+
rect(data: IPathCommandData, x: number, y: number, width: number, height: number) {
|
|
16
|
+
PathHelper.creator.path = data
|
|
17
|
+
PathHelper.creator.moveTo(x, y).lineTo(x + width, y).lineTo(x + width, y + height).lineTo(x, y + height).lineTo(x, y)
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, radius: number | number[]): void {
|
|
21
|
+
PathHelper.creator.path = []
|
|
22
|
+
RectHelper.drawRoundRect(PathHelper.creator, x, y, width, height, radius)
|
|
23
|
+
data.push(...PathHelper.convertToCanvasData(PathHelper.creator.path, true))
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
arcTo(data: IPathCommandData | null | void, fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number, radius: number, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void {
|
|
27
|
+
const BAx = x1 - fromX
|
|
28
|
+
const BAy = y1 - fromY
|
|
29
|
+
const CBx = toX - x1
|
|
30
|
+
const CBy = toY - y1
|
|
31
|
+
|
|
32
|
+
let startRadian = atan2(BAy, BAx)
|
|
33
|
+
let endRadian = atan2(CBy, CBx)
|
|
34
|
+
let totalRadian = endRadian - startRadian
|
|
35
|
+
if (totalRadian < 0) totalRadian += PI2
|
|
36
|
+
|
|
37
|
+
if (totalRadian === PI || (abs(BAx + BAy) < 1.e-12) || (abs(CBx + CBy) < 1.e-12)) { // invalid
|
|
38
|
+
if (data) data.push(PathCommandMap.L, x1, y1)
|
|
39
|
+
if (setPointBounds) {
|
|
40
|
+
setPoint(setPointBounds, fromX, fromY)
|
|
41
|
+
addPoint(setPointBounds, x1, y1)
|
|
42
|
+
}
|
|
43
|
+
if (setStartPoint) set(setStartPoint, fromX, fromY)
|
|
44
|
+
if (setEndPoint) set(setEndPoint, x1, y1)
|
|
45
|
+
return
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const cy1 = sinRotation * cx + cosRotation * cy + localToY * 0.5
|
|
48
|
+
const anticlockwise = BAx * CBy - CBx * BAy < 0
|
|
49
|
+
const sign = anticlockwise ? -1 : 1
|
|
50
|
+
const c = radius / cos(totalRadian / 2)
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const centerX = x1 + c * cos(startRadian + totalRadian / 2 + PI_2 * sign)
|
|
53
|
+
const centerY = y1 + c * sin(startRadian + totalRadian / 2 + PI_2 * sign)
|
|
54
|
+
startRadian -= PI_2 * sign
|
|
55
|
+
endRadian -= PI_2 * sign
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
return ellipse(data, centerX, centerY, radius, radius, 0, startRadian / OneRadian, endRadian / OneRadian, anticlockwise, setPointBounds, setEndPoint, setStartPoint)
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
arc(data: IPathCommandData | null | void, x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void {
|
|
61
|
+
return ellipse(data, x, y, radius, radius, 0, startAngle, endAngle, anticlockwise, setPointBounds, setEndPoint, setStartPoint)
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
ellipse(data: IPathCommandData | null | void, cx: number, cy: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void {
|
|
65
|
+
const rotationRadian = rotation * OneRadian
|
|
66
|
+
const rotationSin = sin(rotationRadian)
|
|
67
|
+
const rotationCos = cos(rotationRadian)
|
|
68
|
+
|
|
69
|
+
let startRadian = startAngle * OneRadian
|
|
70
|
+
let endRadian = endAngle * OneRadian
|
|
71
|
+
|
|
72
|
+
if (startRadian > PI) startRadian -= PI2
|
|
73
|
+
if (endRadian < 0) endRadian += PI2
|
|
74
|
+
|
|
75
|
+
let totalRadian = endRadian - startRadian
|
|
76
|
+
if (totalRadian < 0) totalRadian += PI2
|
|
77
|
+
else if (totalRadian > PI2) totalRadian -= PI2
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
let
|
|
66
|
-
let
|
|
67
|
-
let
|
|
68
|
-
let sinStart = sin(startRadian)
|
|
69
|
-
let cosEnd: number, sinEnd: number
|
|
70
|
-
let startX = 0, startY = 0
|
|
79
|
+
if (anticlockwise) totalRadian -= PI2
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const parts = ceil(abs(totalRadian / PI_2))
|
|
83
|
+
const partRadian = totalRadian / parts
|
|
84
|
+
const partRadian4Sin = sin(partRadian / 4)
|
|
85
|
+
const control = 8 / 3 * partRadian4Sin * partRadian4Sin / sin(partRadian / 2)
|
|
86
|
+
|
|
87
|
+
endRadian = startRadian + partRadian
|
|
88
|
+
|
|
89
|
+
let startCos = cos(startRadian)
|
|
90
|
+
let startSin = sin(startRadian)
|
|
91
|
+
let endCos: number, endSin: number
|
|
71
92
|
|
|
72
93
|
let x: number, y: number, x1: number, y1: number, x2: number, y2: number
|
|
73
|
-
for (let i = 0; i < segments; i++) {
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
let startX = x = rotationCos * radiusX * startCos - rotationSin * radiusY * startSin
|
|
96
|
+
let startY = y = rotationSin * radiusX * startCos + rotationCos * radiusY * startSin
|
|
97
|
+
|
|
98
|
+
let fromX = cx + x, fromY = cy + y
|
|
99
|
+
|
|
100
|
+
if (data) data.push(PathCommandMap.L, fromX, fromY)
|
|
101
|
+
if (setPointBounds) setPoint(setPointBounds, fromX, fromY)
|
|
102
|
+
if (setStartPoint) set(setStartPoint, fromX, fromY)
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < parts; i++) {
|
|
105
|
+
|
|
106
|
+
endCos = cos(endRadian)
|
|
107
|
+
endSin = sin(endRadian)
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
y2 = y + controlRadian * (sinRotation * rx * sinEnd - cosRotation * ry * cosEnd)
|
|
109
|
+
x = rotationCos * radiusX * endCos - rotationSin * radiusY * endSin
|
|
110
|
+
y = rotationSin * radiusX * endCos + rotationCos * radiusY * endSin
|
|
111
|
+
x1 = cx + startX - control * (rotationCos * radiusX * startSin + rotationSin * radiusY * startCos)
|
|
112
|
+
y1 = cy + startY - control * (rotationSin * radiusX * startSin - rotationCos * radiusY * startCos)
|
|
113
|
+
x2 = cx + x + control * (rotationCos * radiusX * endSin + rotationSin * radiusY * endCos)
|
|
114
|
+
y2 = cy + y + control * (rotationSin * radiusX * endSin - rotationCos * radiusY * endCos)
|
|
85
115
|
|
|
86
|
-
data.push(PathCommandMap.C, x1
|
|
116
|
+
if (data) data.push(PathCommandMap.C, x1, y1, x2, y2, cx + x, cy + y)
|
|
117
|
+
if (setPointBounds) toTwoPointBounds(cx + startX, cy + startY, x1, y1, x2, y2, cx + x, cy + y, setPointBounds, true)
|
|
87
118
|
|
|
88
119
|
startX = x
|
|
89
120
|
startY = y
|
|
121
|
+
startCos = endCos
|
|
122
|
+
startSin = endSin
|
|
90
123
|
startRadian = endRadian
|
|
91
|
-
|
|
92
|
-
sinStart = sinEnd
|
|
93
|
-
endRadian += segmentRadian
|
|
94
|
-
|
|
124
|
+
endRadian += partRadian
|
|
95
125
|
}
|
|
96
126
|
|
|
97
|
-
|
|
127
|
+
if (setEndPoint) set(setEndPoint, cx + x, cy + y)
|
|
128
|
+
|
|
129
|
+
},
|
|
98
130
|
|
|
131
|
+
quadraticCurveTo(data: IPathCommandData, fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number): void {
|
|
132
|
+
data.push(PathCommandMap.C, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY)
|
|
99
133
|
},
|
|
100
134
|
|
|
101
|
-
|
|
135
|
+
toTwoPointBoundsByQuadraticCurve(fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void {
|
|
136
|
+
toTwoPointBounds(fromX, fromY, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY, pointBounds, addMode)
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void {
|
|
102
140
|
|
|
103
141
|
const tList = []
|
|
104
142
|
let a, b, c, t, t1, t2, v, sqrtV
|
|
@@ -131,13 +169,14 @@ export const BezierHelper = {
|
|
|
131
169
|
if (0 < t2 && t2 < 1) tList.push(t2)
|
|
132
170
|
}
|
|
133
171
|
|
|
134
|
-
setPoint(pointBounds, fromX, fromY)
|
|
172
|
+
addMode ? addPoint(pointBounds, fromX, fromY) : setPoint(pointBounds, fromX, fromY)
|
|
135
173
|
addPoint(pointBounds, toX, toY)
|
|
136
174
|
|
|
137
175
|
for (let i = 0, len = tList.length; i < len; i++) {
|
|
138
|
-
|
|
176
|
+
getPointAndSet(tList[i], fromX, fromY, x1, y1, x2, y2, toX, toY, tempPoint)
|
|
139
177
|
addPoint(pointBounds, tempPoint.x, tempPoint.y)
|
|
140
178
|
}
|
|
179
|
+
|
|
141
180
|
},
|
|
142
181
|
|
|
143
182
|
getPointAndSet(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, setPoint: IPointData): void {
|
|
@@ -148,10 +187,9 @@ export const BezierHelper = {
|
|
|
148
187
|
|
|
149
188
|
getPoint(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number): IPointData {
|
|
150
189
|
const point = {} as IPointData
|
|
151
|
-
|
|
190
|
+
getPointAndSet(t, fromX, fromY, x1, y1, x2, y2, toX, toY, point)
|
|
152
191
|
return point
|
|
153
192
|
}
|
|
154
|
-
|
|
155
193
|
}
|
|
156
194
|
|
|
157
|
-
const
|
|
195
|
+
const { getPointAndSet, toTwoPointBounds, ellipse } = BezierHelper
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { IPathCommandData } from '@leafer/interface'
|
|
2
|
+
import { OneRadian, PI2 } from '@leafer/math'
|
|
3
|
+
|
|
4
|
+
import { PathCommandMap } from './PathCommandMap'
|
|
5
|
+
import { BezierHelper } from './BezierHelper'
|
|
6
|
+
|
|
7
|
+
const { sin, cos, sqrt, atan2 } = Math
|
|
8
|
+
const { ellipse } = BezierHelper
|
|
9
|
+
|
|
10
|
+
export const EllipseHelper = {
|
|
11
|
+
|
|
12
|
+
// svg
|
|
13
|
+
ellipticalArc(data: IPathCommandData, fromX: number, fromY: number, radiusX: number, radiusY: number, rotation: number, largeFlag: number, sweepFlag: number, toX: number, toY: number, curveMode?: boolean): void {
|
|
14
|
+
|
|
15
|
+
const halfX = (toX - fromX) / 2
|
|
16
|
+
const halfY = (toY - fromY) / 2
|
|
17
|
+
|
|
18
|
+
const rotationRadian = rotation * OneRadian
|
|
19
|
+
const rotationSin = sin(rotationRadian)
|
|
20
|
+
const rotationCos = cos(rotationRadian)
|
|
21
|
+
|
|
22
|
+
const px = -rotationCos * halfX - rotationSin * halfY
|
|
23
|
+
const py = -rotationCos * halfY + rotationSin * halfX
|
|
24
|
+
const rxSquare = radiusX * radiusX
|
|
25
|
+
const rySquare = radiusY * radiusY
|
|
26
|
+
const pySquare = py * py
|
|
27
|
+
const pxSquare = px * px
|
|
28
|
+
|
|
29
|
+
const a = rxSquare * rySquare - rxSquare * pySquare - rySquare * pxSquare
|
|
30
|
+
let s = 0
|
|
31
|
+
|
|
32
|
+
if (a < 0) {
|
|
33
|
+
const t = sqrt(1 - a / (rxSquare * rySquare))
|
|
34
|
+
radiusX *= t
|
|
35
|
+
radiusY *= t
|
|
36
|
+
} else {
|
|
37
|
+
s = (largeFlag === sweepFlag ? -1 : 1) * sqrt(a / (rxSquare * pySquare + rySquare * pxSquare))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cx = s * radiusX * py / radiusY
|
|
41
|
+
const cy = -s * radiusY * px / radiusX
|
|
42
|
+
|
|
43
|
+
const startRadian = atan2((py - cy) / radiusY, (px - cx) / radiusX)
|
|
44
|
+
const endRadian = atan2((-py - cy) / radiusY, (-px - cx) / radiusX)
|
|
45
|
+
|
|
46
|
+
let totalRadian = endRadian - startRadian
|
|
47
|
+
|
|
48
|
+
if (sweepFlag === 0 && totalRadian > 0) {
|
|
49
|
+
totalRadian -= PI2
|
|
50
|
+
} else if (sweepFlag === 1 && totalRadian < 0) {
|
|
51
|
+
totalRadian += PI2
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const centerX = fromX + halfX + rotationCos * cx - rotationSin * cy
|
|
55
|
+
const centerY = fromY + halfY + rotationSin * cx + rotationCos * cy
|
|
56
|
+
|
|
57
|
+
const anticlockwise = totalRadian < 0 ? 1 : 0
|
|
58
|
+
|
|
59
|
+
if (curveMode) {
|
|
60
|
+
ellipse(data, centerX, centerY, radiusX, radiusY, rotation, startRadian / OneRadian, endRadian / OneRadian, anticlockwise as unknown as boolean)
|
|
61
|
+
} else {
|
|
62
|
+
if (radiusX === radiusY && !rotation) {
|
|
63
|
+
data.push(PathCommandMap.O, centerX, centerY, radiusX, startRadian / OneRadian, endRadian / OneRadian, anticlockwise)
|
|
64
|
+
} else {
|
|
65
|
+
data.push(PathCommandMap.G, centerX, centerY, radiusX, radiusY, rotation, startRadian / OneRadian, endRadian / OneRadian, anticlockwise)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ITwoPointBoundsData, IPathCommandData, IBoundsData, IPointData } from '@leafer/interface'
|
|
2
|
+
import { TwoPointBoundsHelper } from '@leafer/math'
|
|
3
|
+
import { Debug } from '@leafer/debug'
|
|
4
|
+
|
|
5
|
+
import { BezierHelper } from './BezierHelper'
|
|
6
|
+
import { PathCommandMap as Command } from './PathCommandMap'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = Command
|
|
10
|
+
const { toTwoPointBounds, toTwoPointBoundsByQuadraticCurve, arcTo, arc, ellipse } = BezierHelper
|
|
11
|
+
const { add, copy, addPoint, setPoint, addBounds, toBounds } = TwoPointBoundsHelper
|
|
12
|
+
const debug = Debug.get('PathBounds')
|
|
13
|
+
|
|
14
|
+
let radius: number, radiusX: number, radiusY: number
|
|
15
|
+
const tempPointBounds = {} as ITwoPointBoundsData
|
|
16
|
+
const setPointBounds = {} as ITwoPointBoundsData
|
|
17
|
+
const setEndPoint = {} as IPointData
|
|
18
|
+
|
|
19
|
+
export const PathBounds = {
|
|
20
|
+
|
|
21
|
+
toBounds(data: IPathCommandData, setBounds: IBoundsData): void {
|
|
22
|
+
PathBounds.toTwoPointBounds(data, setPointBounds)
|
|
23
|
+
toBounds(setPointBounds, setBounds)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
toTwoPointBounds(data: IPathCommandData, setPointBounds: ITwoPointBoundsData): void {
|
|
27
|
+
if (!data || !data.length) return setPoint(setPointBounds, 0, 0)
|
|
28
|
+
|
|
29
|
+
let command: number
|
|
30
|
+
let i: number = 0, x: number = 0, y: number = 0, x1: number, y1: number, toX: number, toY: number
|
|
31
|
+
|
|
32
|
+
const len = data.length
|
|
33
|
+
|
|
34
|
+
while (i < len) {
|
|
35
|
+
command = data[i]
|
|
36
|
+
|
|
37
|
+
if (i === 0) {
|
|
38
|
+
if (command === Z || command === C || command === Q) {
|
|
39
|
+
setPoint(setPointBounds, x, y)
|
|
40
|
+
} else {
|
|
41
|
+
setPoint(setPointBounds, data[i + 1], data[i + 2])
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
switch (command) {
|
|
46
|
+
case M: //moveto(x, y)
|
|
47
|
+
case L: //lineto(x, y)
|
|
48
|
+
x = data[i + 1]
|
|
49
|
+
y = data[i + 2]
|
|
50
|
+
addPoint(setPointBounds, x, y)
|
|
51
|
+
i += 3
|
|
52
|
+
break
|
|
53
|
+
case C: //bezierCurveTo(x1, y1, x2, y2, x,y)
|
|
54
|
+
toX = data[i + 5]
|
|
55
|
+
toY = data[i + 6]
|
|
56
|
+
toTwoPointBounds(x, y, data[i + 1], data[i + 2], data[i + 3], data[i + 4], toX, toY, tempPointBounds)
|
|
57
|
+
add(setPointBounds, tempPointBounds)
|
|
58
|
+
x = toX
|
|
59
|
+
y = toY
|
|
60
|
+
i += 7
|
|
61
|
+
break
|
|
62
|
+
case Q: //quadraticCurveTo(x1, y1, x, y)
|
|
63
|
+
x1 = data[i + 1]
|
|
64
|
+
y1 = data[i + 2]
|
|
65
|
+
toX = data[i + 3]
|
|
66
|
+
toY = data[i + 4]
|
|
67
|
+
toTwoPointBoundsByQuadraticCurve(x, y, x1, y1, toX, toY, tempPointBounds)
|
|
68
|
+
add(setPointBounds, tempPointBounds)
|
|
69
|
+
x = toX
|
|
70
|
+
y = toY
|
|
71
|
+
i += 5
|
|
72
|
+
break
|
|
73
|
+
case Z: //closepath()
|
|
74
|
+
i += 1
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
// canvas command
|
|
78
|
+
|
|
79
|
+
case N: // rect(x, y, width, height)
|
|
80
|
+
x = data[i + 1]
|
|
81
|
+
y = data[i + 2]
|
|
82
|
+
addBounds(setPointBounds, x, y, data[i + 3], data[i + 4])
|
|
83
|
+
i += 5
|
|
84
|
+
break
|
|
85
|
+
case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4)
|
|
86
|
+
case X: // simple roundRect(x, y, width, height, radius)
|
|
87
|
+
x = data[i + 1]
|
|
88
|
+
y = data[i + 2]
|
|
89
|
+
addBounds(setPointBounds, x, y, data[i + 3], data[i + 4])
|
|
90
|
+
i += (command === D ? 9 : 6)
|
|
91
|
+
break
|
|
92
|
+
case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
|
|
93
|
+
ellipse(null, data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6], data[i + 7], data[i + 8] as unknown as boolean, tempPointBounds, setEndPoint)
|
|
94
|
+
i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds)
|
|
95
|
+
x = setEndPoint.x
|
|
96
|
+
y = setEndPoint.y
|
|
97
|
+
i += 9
|
|
98
|
+
break
|
|
99
|
+
case F: // simple ellipse(x, y, radiusX, radiusY)
|
|
100
|
+
x = data[i + 1]
|
|
101
|
+
y = data[i + 2]
|
|
102
|
+
radiusX = data[i + 3]
|
|
103
|
+
radiusY = data[i + 4]
|
|
104
|
+
addBounds(setPointBounds, x - radiusX, y - radiusY, radiusX * 2, radiusY * 2)
|
|
105
|
+
x += radiusX
|
|
106
|
+
i += 5
|
|
107
|
+
break
|
|
108
|
+
case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise)
|
|
109
|
+
arc(null, data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6] as unknown as boolean, tempPointBounds, setEndPoint)
|
|
110
|
+
i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds)
|
|
111
|
+
x = setEndPoint.x
|
|
112
|
+
y = setEndPoint.y
|
|
113
|
+
i += 7
|
|
114
|
+
break
|
|
115
|
+
case P: // simple arc(x, y, radius)
|
|
116
|
+
x = data[i + 1]
|
|
117
|
+
y = data[i + 2]
|
|
118
|
+
radius = data[i + 3]
|
|
119
|
+
addBounds(setPointBounds, x - radius, y - radius, radius * 2, radius * 2)
|
|
120
|
+
x += radius
|
|
121
|
+
i += 4
|
|
122
|
+
break
|
|
123
|
+
case U: // arcTo(x1, y1, x2, y2, radius)
|
|
124
|
+
arcTo(null, x, y, data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], tempPointBounds, setEndPoint)
|
|
125
|
+
i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds)
|
|
126
|
+
x = setEndPoint.x
|
|
127
|
+
y = setEndPoint.y
|
|
128
|
+
i += 6
|
|
129
|
+
break
|
|
130
|
+
default:
|
|
131
|
+
debug.error(`command: ${command} [index:${i}]`, data)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { IPathCommandData, IPointData } from '@leafer/interface'
|
|
2
|
+
import { PathCommandMap } from './PathCommandMap'
|
|
3
|
+
import { BezierHelper } from './BezierHelper'
|
|
4
|
+
import { MathHelper } from '@leafer/math'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = PathCommandMap
|
|
8
|
+
const startPoint = {} as IPointData
|
|
9
|
+
|
|
10
|
+
export const PathCommandDataHelper = {
|
|
11
|
+
|
|
12
|
+
beginPath(data: IPathCommandData): void {
|
|
13
|
+
data.length = 0
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// svg and canvas
|
|
17
|
+
|
|
18
|
+
moveTo(data: IPathCommandData, x: number, y: number): void {
|
|
19
|
+
data.push(M, x, y)
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
lineTo(data: IPathCommandData, x: number, y: number): void {
|
|
23
|
+
data.push(L, x, y)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
bezierCurveTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, x: number, y: number): void {
|
|
27
|
+
data.push(C, x1, y1, x2, y2, x, y)
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
quadraticCurveTo(data: IPathCommandData, x1: number, y1: number, x: number, y: number): void {
|
|
31
|
+
data.push(Q, x1, y1, x, y)
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
closePath(data: IPathCommandData): void {
|
|
35
|
+
data.push(Z)
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// canvas
|
|
39
|
+
|
|
40
|
+
rect(data: IPathCommandData, x: number, y: number, width: number, height: number): void {
|
|
41
|
+
data.push(N, x, y, width, height)
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void {
|
|
45
|
+
if (typeof cornerRadius === 'number') {
|
|
46
|
+
data.push(X, x, y, width, height, cornerRadius)
|
|
47
|
+
} else {
|
|
48
|
+
const fourCorners = MathHelper.fourNumber(cornerRadius)
|
|
49
|
+
if (fourCorners) {
|
|
50
|
+
data.push(D, x, y, width, height, ...fourCorners)
|
|
51
|
+
} else {
|
|
52
|
+
data.push(N, x, y, width, height)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
ellipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
|
|
58
|
+
if (rotation === undefined) {
|
|
59
|
+
data.push(F, x, y, radiusX, radiusY)
|
|
60
|
+
} else {
|
|
61
|
+
if (startAngle === undefined) startAngle = 0
|
|
62
|
+
if (endAngle === undefined) endAngle = 360
|
|
63
|
+
data.push(G, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise ? 1 : 0)
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
arc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
|
|
68
|
+
if (startAngle === undefined) {
|
|
69
|
+
data.push(P, x, y, radius)
|
|
70
|
+
} else {
|
|
71
|
+
if (endAngle === undefined) endAngle = 360
|
|
72
|
+
data.push(O, x, y, radius, startAngle, endAngle, anticlockwise ? 1 : 0)
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
moveToEllipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
|
|
77
|
+
if (rotation === undefined) rotation = 0
|
|
78
|
+
if (startAngle === undefined) startAngle = 0
|
|
79
|
+
if (endAngle === undefined) endAngle = 360
|
|
80
|
+
BezierHelper.ellipse(null, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise, null, null, startPoint)
|
|
81
|
+
data.push(M, startPoint.x, startPoint.y)
|
|
82
|
+
ellipse(data, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
moveToArc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
|
|
86
|
+
if (startAngle === undefined) startAngle = 0
|
|
87
|
+
if (endAngle === undefined) endAngle = 360
|
|
88
|
+
BezierHelper.arc(null, x, y, radius, startAngle, endAngle, anticlockwise, null, null, startPoint)
|
|
89
|
+
data.push(M, startPoint.x, startPoint.y)
|
|
90
|
+
arc(data, x, y, radius, startAngle, endAngle, anticlockwise)
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
arcTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, radius: number): void {
|
|
94
|
+
data.push(U, x1, y1, x2, y2, radius)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { ellipse, arc } = PathCommandDataHelper
|