@leafer/path 1.0.0-beta.15 → 1.0.0-beta.16

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 CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@leafer/path",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.16",
4
4
  "description": "@leafer/path",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
7
7
  "main": "src/index.ts",
8
8
  "types": "types/index.d.ts",
9
9
  "files": [
10
+ "src",
10
11
  "types",
11
12
  "dist"
12
13
  ],
@@ -21,10 +22,10 @@
21
22
  "leaferjs"
22
23
  ],
23
24
  "dependencies": {
24
- "@leafer/math": "1.0.0-beta.15",
25
- "@leafer/debug": "1.0.0-beta.15"
25
+ "@leafer/math": "1.0.0-beta.16",
26
+ "@leafer/debug": "1.0.0-beta.16"
26
27
  },
27
28
  "devDependencies": {
28
- "@leafer/interface": "1.0.0-beta.15"
29
+ "@leafer/interface": "1.0.0-beta.16"
29
30
  }
30
31
  }
@@ -0,0 +1,256 @@
1
+ import { IPointData, ITwoPointBoundsData, IPathCommandData } from '@leafer/interface'
2
+ import { OneRadian, PI2, PI_2, PointHelper, TwoPointBoundsHelper } from '@leafer/math'
3
+
4
+ import { PathCommandMap } from './PathCommandMap'
5
+ import { RectHelper } from './RectHelper'
6
+ import { PathHelper } from './PathHelper'
7
+
8
+ const { sin, cos, atan2, ceil, abs, PI, sqrt, pow } = Math
9
+ const { setPoint, addPoint } = TwoPointBoundsHelper
10
+ const { set } = PointHelper
11
+ const { M, L, C, Q, Z } = PathCommandMap
12
+ const tempPoint = {} as IPointData
13
+
14
+ export const BezierHelper = {
15
+
16
+ points(data: IPathCommandData, points: number[], curve?: boolean | number, close?: boolean): void {
17
+ data.push(M, points[0], points[1])
18
+
19
+ if (curve && points.length > 5) {
20
+
21
+ let aX: number, aY: number, bX: number, bY: number, cX: number, cY: number, c1X: number, c1Y: number, c2X: number, c2Y: number
22
+ let ba: number, cb: number, d: number, len = points.length
23
+ const t = curve === true ? 0.5 : curve as number
24
+
25
+ if (close) {
26
+ points = [points[len - 2], points[len - 1], ...points, points[0], points[1], points[2], points[3]]
27
+ len = points.length
28
+ }
29
+
30
+ for (let i = 2; i < len - 2; i += 2) {
31
+ aX = points[i - 2]
32
+ aY = points[i - 1]
33
+
34
+ bX = points[i]
35
+ bY = points[i + 1]
36
+
37
+ cX = points[i + 2]
38
+ cY = points[i + 3]
39
+
40
+ ba = sqrt(pow(bX - aX, 2) + pow(bY - aY, 2))
41
+ cb = sqrt(pow(cX - bX, 2) + pow(cY - bY, 2))
42
+
43
+ d = ba + cb
44
+ ba = (t * ba) / d
45
+ cb = (t * cb) / d
46
+
47
+ cX -= aX
48
+ cY -= aY
49
+
50
+ c1X = bX - ba * cX
51
+ c1Y = bY - ba * cY
52
+
53
+ if (i === 2) {
54
+ if (!close) data.push(Q, c1X, c1Y, bX, bY)
55
+ } else {
56
+ data.push(C, c2X, c2Y, c1X, c1Y, bX, bY)
57
+ }
58
+
59
+ c2X = bX + cb * cX
60
+ c2Y = bY + cb * cY
61
+ }
62
+
63
+ if (!close) data.push(Q, c2X, c2Y, points[len - 2], points[len - 1])
64
+
65
+ } else {
66
+
67
+ for (let i = 2, len = points.length; i < len; i += 2) {
68
+ data.push(L, points[i], points[i + 1])
69
+ }
70
+
71
+ }
72
+
73
+ if (close) data.push(Z)
74
+ },
75
+
76
+ rect(data: IPathCommandData, x: number, y: number, width: number, height: number) {
77
+ PathHelper.creator.path = data
78
+ PathHelper.creator.moveTo(x, y).lineTo(x + width, y).lineTo(x + width, y + height).lineTo(x, y + height).lineTo(x, y)
79
+ },
80
+
81
+ roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, radius: number | number[]): void {
82
+ PathHelper.creator.path = []
83
+ RectHelper.drawRoundRect(PathHelper.creator, x, y, width, height, radius)
84
+ data.push(...PathHelper.convertToCanvasData(PathHelper.creator.path, true))
85
+ },
86
+
87
+ 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 {
88
+ const BAx = x1 - fromX
89
+ const BAy = y1 - fromY
90
+ const CBx = toX - x1
91
+ const CBy = toY - y1
92
+
93
+ let startRadian = atan2(BAy, BAx)
94
+ let endRadian = atan2(CBy, CBx)
95
+ let totalRadian = endRadian - startRadian
96
+ if (totalRadian < 0) totalRadian += PI2
97
+
98
+ if (totalRadian === PI || (abs(BAx + BAy) < 1.e-12) || (abs(CBx + CBy) < 1.e-12)) { // invalid
99
+ if (data) data.push(L, x1, y1)
100
+ if (setPointBounds) {
101
+ setPoint(setPointBounds, fromX, fromY)
102
+ addPoint(setPointBounds, x1, y1)
103
+ }
104
+ if (setStartPoint) set(setStartPoint, fromX, fromY)
105
+ if (setEndPoint) set(setEndPoint, x1, y1)
106
+ return
107
+ }
108
+
109
+ const anticlockwise = BAx * CBy - CBx * BAy < 0
110
+ const sign = anticlockwise ? -1 : 1
111
+ const c = radius / cos(totalRadian / 2)
112
+
113
+ const centerX = x1 + c * cos(startRadian + totalRadian / 2 + PI_2 * sign)
114
+ const centerY = y1 + c * sin(startRadian + totalRadian / 2 + PI_2 * sign)
115
+ startRadian -= PI_2 * sign
116
+ endRadian -= PI_2 * sign
117
+
118
+ return ellipse(data, centerX, centerY, radius, radius, 0, startRadian / OneRadian, endRadian / OneRadian, anticlockwise, setPointBounds, setEndPoint, setStartPoint)
119
+ },
120
+
121
+ arc(data: IPathCommandData | null | void, x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void {
122
+ return ellipse(data, x, y, radius, radius, 0, startAngle, endAngle, anticlockwise, setPointBounds, setEndPoint, setStartPoint)
123
+ },
124
+
125
+ 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 {
126
+ const rotationRadian = rotation * OneRadian
127
+ const rotationSin = sin(rotationRadian)
128
+ const rotationCos = cos(rotationRadian)
129
+
130
+ let startRadian = startAngle * OneRadian
131
+ let endRadian = endAngle * OneRadian
132
+
133
+ if (startRadian > PI) startRadian -= PI2
134
+ if (endRadian < 0) endRadian += PI2
135
+
136
+ let totalRadian = endRadian - startRadian
137
+ if (totalRadian < 0) totalRadian += PI2
138
+ else if (totalRadian > PI2) totalRadian -= PI2
139
+
140
+ if (anticlockwise) totalRadian -= PI2
141
+
142
+
143
+ const parts = ceil(abs(totalRadian / PI_2))
144
+ const partRadian = totalRadian / parts
145
+ const partRadian4Sin = sin(partRadian / 4)
146
+ const control = 8 / 3 * partRadian4Sin * partRadian4Sin / sin(partRadian / 2)
147
+
148
+ endRadian = startRadian + partRadian
149
+
150
+ let startCos = cos(startRadian)
151
+ let startSin = sin(startRadian)
152
+ let endCos: number, endSin: number
153
+
154
+ let x: number, y: number, x1: number, y1: number, x2: number, y2: number
155
+
156
+ let startX = x = rotationCos * radiusX * startCos - rotationSin * radiusY * startSin
157
+ let startY = y = rotationSin * radiusX * startCos + rotationCos * radiusY * startSin
158
+
159
+ let fromX = cx + x, fromY = cy + y
160
+
161
+ if (data) data.push(L, fromX, fromY)
162
+ if (setPointBounds) setPoint(setPointBounds, fromX, fromY)
163
+ if (setStartPoint) set(setStartPoint, fromX, fromY)
164
+
165
+ for (let i = 0; i < parts; i++) {
166
+
167
+ endCos = cos(endRadian)
168
+ endSin = sin(endRadian)
169
+
170
+ x = rotationCos * radiusX * endCos - rotationSin * radiusY * endSin
171
+ y = rotationSin * radiusX * endCos + rotationCos * radiusY * endSin
172
+ x1 = cx + startX - control * (rotationCos * radiusX * startSin + rotationSin * radiusY * startCos)
173
+ y1 = cy + startY - control * (rotationSin * radiusX * startSin - rotationCos * radiusY * startCos)
174
+ x2 = cx + x + control * (rotationCos * radiusX * endSin + rotationSin * radiusY * endCos)
175
+ y2 = cy + y + control * (rotationSin * radiusX * endSin - rotationCos * radiusY * endCos)
176
+
177
+ if (data) data.push(C, x1, y1, x2, y2, cx + x, cy + y)
178
+ if (setPointBounds) toTwoPointBounds(cx + startX, cy + startY, x1, y1, x2, y2, cx + x, cy + y, setPointBounds, true)
179
+
180
+ startX = x
181
+ startY = y
182
+ startCos = endCos
183
+ startSin = endSin
184
+ startRadian = endRadian
185
+ endRadian += partRadian
186
+ }
187
+
188
+ if (setEndPoint) set(setEndPoint, cx + x, cy + y)
189
+
190
+ },
191
+
192
+ quadraticCurveTo(data: IPathCommandData, fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number): void {
193
+ data.push(C, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY)
194
+ },
195
+
196
+ toTwoPointBoundsByQuadraticCurve(fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void {
197
+ toTwoPointBounds(fromX, fromY, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY, pointBounds, addMode)
198
+ },
199
+
200
+ toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void {
201
+
202
+ const tList = []
203
+ let a, b, c, t, t1, t2, v, sqrtV
204
+ let f = fromX, z1 = x1, z2 = x2, o = toX
205
+
206
+ for (let i = 0; i < 2; ++i) {
207
+
208
+ if (i == 1) {
209
+ f = fromY, z1 = y1, z2 = y2, o = toY
210
+ }
211
+
212
+ a = -3 * f + 9 * z1 - 9 * z2 + 3 * o
213
+ b = 6 * f - 12 * z1 + 6 * z2
214
+ c = 3 * z1 - 3 * f
215
+
216
+ if (Math.abs(a) < 1e-12) {
217
+ if (Math.abs(b) < 1e-12) continue
218
+ t = -c / b
219
+ if (0 < t && t < 1) tList.push(t)
220
+ continue
221
+ }
222
+
223
+ v = b * b - 4 * c * a
224
+ sqrtV = Math.sqrt(v)
225
+ if (v < 0) continue
226
+
227
+ t1 = (-b + sqrtV) / (2 * a)
228
+ if (0 < t1 && t1 < 1) tList.push(t1)
229
+ t2 = (-b - sqrtV) / (2 * a)
230
+ if (0 < t2 && t2 < 1) tList.push(t2)
231
+ }
232
+
233
+ addMode ? addPoint(pointBounds, fromX, fromY) : setPoint(pointBounds, fromX, fromY)
234
+ addPoint(pointBounds, toX, toY)
235
+
236
+ for (let i = 0, len = tList.length; i < len; i++) {
237
+ getPointAndSet(tList[i], fromX, fromY, x1, y1, x2, y2, toX, toY, tempPoint)
238
+ addPoint(pointBounds, tempPoint.x, tempPoint.y)
239
+ }
240
+
241
+ },
242
+
243
+ getPointAndSet(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, setPoint: IPointData): void {
244
+ const o = 1 - t, a = o * o * o, b = 3 * o * o * t, c = 3 * o * t * t, d = t * t * t
245
+ setPoint.x = a * fromX + b * x1 + c * x2 + d * toX
246
+ setPoint.y = a * fromY + b * y1 + c * y2 + d * toY
247
+ },
248
+
249
+ getPoint(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number): IPointData {
250
+ const point = {} as IPointData
251
+ getPointAndSet(t, fromX, fromY, x1, y1, x2, y2, toX, toY, point)
252
+ return point
253
+ }
254
+ }
255
+
256
+ 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,105 @@
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
+ arcTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, radius: number): void {
77
+ data.push(U, x1, y1, x2, y2, radius)
78
+ },
79
+
80
+ // new
81
+
82
+ drawEllipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
83
+ if (rotation === undefined) rotation = 0
84
+ if (startAngle === undefined) startAngle = 0
85
+ if (endAngle === undefined) endAngle = 360
86
+ BezierHelper.ellipse(null, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise, null, null, startPoint)
87
+ data.push(M, startPoint.x, startPoint.y)
88
+ ellipse(data, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
89
+ },
90
+
91
+ drawArc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void {
92
+ if (startAngle === undefined) startAngle = 0
93
+ if (endAngle === undefined) endAngle = 360
94
+ BezierHelper.arc(null, x, y, radius, startAngle, endAngle, anticlockwise, null, null, startPoint)
95
+ data.push(M, startPoint.x, startPoint.y)
96
+ arc(data, x, y, radius, startAngle, endAngle, anticlockwise)
97
+ },
98
+
99
+ drawPoints(data: IPathCommandData, points: number[], curve?: boolean | number, close?: boolean): void {
100
+ BezierHelper.points(data, points, curve, close)
101
+ }
102
+
103
+ }
104
+
105
+ const { ellipse, arc } = PathCommandDataHelper
@@ -0,0 +1,126 @@
1
+ import { INumberMap, IStringMap } from '@leafer/interface'
2
+
3
+ export const CanvasCommandOnlyMap: INumberMap = {
4
+
5
+ N: 21, // rect
6
+ D: 22, // roundRect
7
+ X: 23, // simple roundRect
8
+ G: 24, // ellipse
9
+ F: 25, // simple ellipse
10
+ O: 26, // arc
11
+ P: 27, // simple arc
12
+ U: 28 // arcTo
13
+
14
+ }
15
+
16
+ export const PathCommandMap: INumberMap = {
17
+
18
+ // svg and canvas
19
+
20
+ M: 1, // moveto
21
+ m: 10,
22
+ L: 2, // lineto
23
+ l: 20,
24
+ H: 3, // horizontal lineto
25
+ h: 30,
26
+ V: 4, // vertical lineto
27
+ v: 40,
28
+ C: 5, // curveto
29
+ c: 50,
30
+ S: 6, // smooth curveto
31
+ s: 60,
32
+ Q: 7, // quadratic Belzier curve
33
+ q: 70,
34
+ T: 8, // smooth quadratic Belzier curveto
35
+ t: 80,
36
+ A: 9, //e lliptical Arc
37
+ a: 90,
38
+ Z: 11, // closepath
39
+ z: 11,
40
+
41
+ R: 12, // Catmull Rom
42
+
43
+ // canvas
44
+ ...CanvasCommandOnlyMap
45
+ }
46
+
47
+ export const PathCommandLengthMap: INumberMap = {
48
+
49
+ M: 3, //moveto
50
+ m: 3,
51
+ L: 3, //lineto
52
+ l: 3,
53
+ H: 2, //horizontal lineto
54
+ h: 2,
55
+ V: 2, //vertical lineto
56
+ v: 2,
57
+ C: 7, //curveto
58
+ c: 7,
59
+ S: 5, //smooth curveto
60
+ s: 5,
61
+ Q: 5, //quadratic Belzier curve
62
+ q: 5,
63
+ T: 3, //smooth quadratic Belzier curveto
64
+ t: 3,
65
+ A: 8, //elliptical Arc
66
+ a: 8,
67
+ Z: 1, //closepath
68
+ z: 1,
69
+
70
+ // canvas
71
+
72
+ N: 5, // rect
73
+ D: 9, // roundRect
74
+ X: 6, // simple roundRect
75
+ G: 9, // ellipse
76
+ F: 5, // simple ellipse
77
+ O: 7, // arc
78
+ P: 4, // simple arc
79
+ U: 6 // arcTo
80
+
81
+ }
82
+
83
+ export const NeedConvertToCanvasCommandMap: INumberMap = { // convert to: M L C Q Z
84
+
85
+ // M: 1, //moveto
86
+ m: 10,
87
+ // L: 2, //lineto
88
+ l: 20,
89
+ H: 3, //horizontal lineto
90
+ h: 30,
91
+ V: 4, //vertical lineto
92
+ v: 40,
93
+ // C: 5, //curveto
94
+ c: 50,
95
+ S: 6, //smooth curveto
96
+ s: 60,
97
+ // Q: 7, //quadratic Belzier curve
98
+ q: 70,
99
+ T: 8, //smooth quadratic Belzier curveto
100
+ t: 80,
101
+ A: 9, //elliptical Arc
102
+ a: 90,
103
+ // Z: 11, //closepath
104
+ // z: 11
105
+
106
+ }
107
+
108
+
109
+ export const NeedConvertToCurveCommandMap: INumberMap = {
110
+ ...NeedConvertToCanvasCommandMap,
111
+ ...CanvasCommandOnlyMap
112
+ }
113
+
114
+ const P = PathCommandMap
115
+
116
+ export const PathNumberCommandMap: IStringMap = {}
117
+ for (let key in P) {
118
+ PathNumberCommandMap[P[key]] = key
119
+ }
120
+ // {1: 'M'}
121
+
122
+ export const PathNumberCommandLengthMap: INumberMap = {}
123
+ for (let key in P) {
124
+ PathNumberCommandLengthMap[P[key]] = PathCommandLengthMap[key]
125
+ }
126
+ // {1: 3}
@@ -0,0 +1,325 @@
1
+ import { IPathCommandData, IPointData } from '@leafer/interface'
2
+ import { StringNumberMap } from '@leafer/math'
3
+ import { Debug } from '@leafer/debug'
4
+
5
+ import { PathCommandMap as Command, NeedConvertToCanvasCommandMap, NeedConvertToCurveCommandMap, PathCommandLengthMap, PathNumberCommandMap, PathNumberCommandLengthMap } from './PathCommandMap'
6
+ import { BezierHelper } from './BezierHelper'
7
+ import { EllipseHelper } from './EllipseHelper'
8
+
9
+
10
+ interface ICurrentCommand {
11
+ name?: number
12
+ length?: number
13
+ index?: number
14
+ dot?: number
15
+ }
16
+
17
+
18
+ const { M, m, L, l, H, h, V, v, C, c, S, s, Q, q, T, t, A, a, Z, z, N, D, X, G, F, O, P, U } = Command
19
+ const { rect, roundRect, arcTo, arc, ellipse, quadraticCurveTo } = BezierHelper
20
+ const { ellipticalArc } = EllipseHelper
21
+ const debug = Debug.get('PathConvert')
22
+
23
+ const setEndPoint = {} as IPointData
24
+
25
+ export const PathConvert = {
26
+
27
+ current: { dot: 0 } as ICurrentCommand,
28
+
29
+ stringify(data: IPathCommandData): string {
30
+ let i = 0, len = data.length, count: number, str: string = '', command: number, lastCommand: number
31
+ while (i < len) {
32
+ command = data[i]
33
+ count = PathNumberCommandLengthMap[command]
34
+ if (command === lastCommand) {
35
+ str += ' ' // 重复的命令可以省略
36
+ } else {
37
+ str += PathNumberCommandMap[command]
38
+ }
39
+
40
+ for (let j = 1; j < count; j++) {
41
+ str += data[i + j];
42
+ (j === count - 1) || (str += ' ')
43
+ }
44
+
45
+ lastCommand = command
46
+ i += count
47
+ }
48
+ return str
49
+ },
50
+
51
+ parse(pathString: string, curveMode?: boolean): IPathCommandData {
52
+
53
+ let needConvert: boolean, char: string, lastChar: string, num = ''
54
+ const data: IPathCommandData = []
55
+ const convertCommand = curveMode ? NeedConvertToCurveCommandMap : NeedConvertToCanvasCommandMap
56
+
57
+ for (let i = 0, len = pathString.length; i < len; i++) {
58
+
59
+ char = pathString[i]
60
+
61
+ if (StringNumberMap[char]) {
62
+
63
+ if (char === '.') {
64
+ current.dot++
65
+ if (current.dot > 1) {
66
+ pushData(data, num); num = '' // .375.375
67
+ }
68
+ }
69
+
70
+ num += char
71
+
72
+ } else if (Command[char]) {
73
+
74
+ if (num) { pushData(data, num); num = '' }
75
+
76
+ current.name = Command[char]
77
+ current.length = PathCommandLengthMap[char]
78
+ current.index = 0
79
+ pushData(data, current.name)
80
+
81
+ if (!needConvert && convertCommand[char]) needConvert = true
82
+
83
+ } else {
84
+
85
+ if (char === '-' || char === '+') {
86
+
87
+ if (lastChar === 'e' || lastChar === 'E') { // L45e-12 21e+22
88
+ num += char
89
+ } else {
90
+ if (num) pushData(data, num) // L-34-35 L+12+28
91
+ num = char
92
+ }
93
+
94
+ } else {
95
+ if (num) { pushData(data, num); num = '' }
96
+ }
97
+
98
+ }
99
+
100
+ lastChar = char
101
+
102
+ }
103
+
104
+ if (num) pushData(data, num)
105
+
106
+ return needConvert ? PathConvert.toCanvasData(data, curveMode) : data
107
+ },
108
+
109
+ toCanvasData(old: IPathCommandData, curveMode?: boolean): IPathCommandData {
110
+
111
+ let x = 0, y = 0, x1 = 0, y1 = 0, i = 0, len = old.length, controlX: number, controlY: number, command: number, lastCommand: number, smooth: boolean
112
+ const data: IPathCommandData = []
113
+
114
+ while (i < len) {
115
+
116
+ command = old[i]
117
+
118
+ switch (command) {
119
+ //moveto(x, y)
120
+ case m:
121
+ old[i + 1] += x
122
+ old[i + 2] += y
123
+ case M:
124
+ x = old[i + 1]
125
+ y = old[i + 2]
126
+ data.push(M, x, y)
127
+ i += 3
128
+ break
129
+
130
+ //horizontal lineto(x)
131
+ case h:
132
+ old[i + 1] += x
133
+ case H:
134
+ x = old[i + 1]
135
+ data.push(L, x, y)
136
+ i += 2
137
+ break
138
+
139
+ //vertical lineto(y)
140
+ case v:
141
+ old[i + 1] += y
142
+ case V:
143
+ y = old[i + 1]
144
+ data.push(L, x, y)
145
+ i += 2
146
+ break
147
+
148
+ //lineto(x,y)
149
+ case l:
150
+ old[i + 1] += x
151
+ old[i + 2] += y
152
+ case L:
153
+ x = old[i + 1]
154
+ y = old[i + 2]
155
+ data.push(L, x, y)
156
+ i += 3
157
+ break
158
+
159
+ //smooth bezierCurveTo(x2, y2, x, y)
160
+ case s: //smooth
161
+ old[i + 1] += x
162
+ old[i + 2] += y
163
+ old[i + 3] += x
164
+ old[i + 4] += y
165
+ command = S
166
+ case S:
167
+ smooth = (lastCommand === C) || (lastCommand === S)
168
+ x1 = smooth ? (x * 2 - controlX) : old[i + 1]
169
+ y1 = smooth ? (y * 2 - controlY) : old[i + 2]
170
+ controlX = old[i + 1]
171
+ controlY = old[i + 2]
172
+ x = old[i + 3]
173
+ y = old[i + 4]
174
+ data.push(C, x1, y1, controlX, controlY, x, y)
175
+ i += 5
176
+ break
177
+
178
+ //bezierCurveTo(x1, y1, x2, y2, x, y)
179
+ case c:
180
+ old[i + 1] += x
181
+ old[i + 2] += y
182
+ old[i + 3] += x
183
+ old[i + 4] += y
184
+ old[i + 5] += x
185
+ old[i + 6] += y
186
+ command = C
187
+ case C:
188
+ controlX = old[i + 3]
189
+ controlY = old[i + 4]
190
+ x = old[i + 5]
191
+ y = old[i + 6]
192
+ data.push(C, old[i + 1], old[i + 2], controlX, controlY, x, y)
193
+ i += 7
194
+ break
195
+
196
+ //smooth quadraticCurveTo(x, y)
197
+ case t:
198
+ old[i + 1] += x
199
+ old[i + 2] += y
200
+ command = T
201
+ case T: //smooth
202
+ smooth = (lastCommand === Q) || (lastCommand === T)
203
+ controlX = smooth ? (x * 2 - controlX) : old[i + 1]
204
+ controlY = smooth ? (y * 2 - controlY) : old[i + 2]
205
+ curveMode ? quadraticCurveTo(data, x, y, controlX, controlY, old[i + 1], old[i + 2]) : data.push(Q, controlX, controlY, old[i + 1], old[i + 2])
206
+ x = old[i + 1]
207
+ y = old[i + 2]
208
+ i += 3
209
+ break
210
+
211
+ //quadraticCurveTo(x1, y1, x, y)
212
+ case q:
213
+ old[i + 1] += x
214
+ old[i + 2] += y
215
+ old[i + 3] += x
216
+ old[i + 4] += y
217
+ command = Q
218
+ case Q:
219
+ controlX = old[i + 1]
220
+ controlY = old[i + 2]
221
+ curveMode ? quadraticCurveTo(data, x, y, controlX, controlY, old[i + 3], old[i + 4]) : data.push(Q, controlX, controlY, old[i + 3], old[i + 4])
222
+ x = old[i + 3]
223
+ y = old[i + 4]
224
+ i += 5
225
+ break
226
+
227
+ //ellipticalArc(rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y)
228
+ case a:
229
+ old[i + 6] += x
230
+ old[i + 7] += y
231
+ case A:
232
+ ellipticalArc(data, x, y, old[i + 1], old[i + 2], old[i + 3], old[i + 4], old[i + 5], old[i + 6], old[i + 7], curveMode) // convert to canvas ellipse or curve
233
+ x = old[i + 6]
234
+ y = old[i + 7]
235
+ i += 8
236
+ break
237
+ case z:
238
+ case Z:
239
+ data.push(Z)
240
+ i++
241
+ break
242
+
243
+
244
+ // canvas command
245
+
246
+ case N: // rect(x, y, width, height)
247
+ x = old[i + 1]
248
+ y = old[i + 2]
249
+ curveMode ? rect(data, x, y, old[i + 3], old[i + 4]) : copyData(data, old, i, 5)
250
+ i += 5
251
+ break
252
+ case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4)
253
+ x = old[i + 1]
254
+ y = old[i + 2]
255
+ curveMode ? roundRect(data, x, y, old[i + 3], old[i + 4], [old[i + 5], old[i + 6], old[i + 7], old[i + 8]]) : copyData(data, old, i, 9)
256
+ i += 9
257
+ break
258
+ case X: // simple roundRect(x, y, width, height, radius)
259
+ x = old[i + 1]
260
+ y = old[i + 2]
261
+ curveMode ? roundRect(data, x, y, old[i + 3], old[i + 4], old[i + 5]) : copyData(data, old, i, 6)
262
+ i += 6
263
+ break
264
+ case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
265
+ ellipse(curveMode ? data : copyData(data, old, i, 9), old[i + 1], old[i + 2], old[i + 3], old[i + 4], old[i + 5], old[i + 6], old[i + 7], old[i + 8] as unknown as boolean, null, setEndPoint)
266
+ x = setEndPoint.x
267
+ y = setEndPoint.y
268
+ i += 9
269
+ break
270
+ case F: // simple ellipse(x, y, radiusX, radiusY)
271
+ curveMode ? ellipse(data, old[i + 1], old[i + 2], old[i + 3], old[i + 4], 0, 0, 360, false) : copyData(data, old, i, 5)
272
+ x = old[i + 1] + old[i + 3]
273
+ y = old[i + 2]
274
+ i += 5
275
+ break
276
+ case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise)
277
+ arc(curveMode ? data : copyData(data, old, i, 7), old[i + 1], old[i + 2], old[i + 3], old[i + 4], old[i + 5], old[i + 6] as unknown as boolean, null, setEndPoint)
278
+ x = setEndPoint.x
279
+ y = setEndPoint.y
280
+ i += 7
281
+ break
282
+ case P: // simple arc(x, y, radius)
283
+ curveMode ? arc(data, old[i + 1], old[i + 2], old[i + 3], 0, 360, false) : copyData(data, old, i, 4)
284
+ x = old[i + 1] + old[i + 3]
285
+ y = old[i + 2]
286
+ i += 4
287
+ break
288
+ case U: // arcTo(x1, y1, x2, y2, radius)
289
+ arcTo(curveMode ? data : copyData(data, old, i, 6), x, y, old[i + 1], old[i + 2], old[i + 3], old[i + 4], old[i + 5], null, setEndPoint)
290
+ x = setEndPoint.x
291
+ y = setEndPoint.y
292
+ i += 6
293
+ break
294
+ default:
295
+ debug.error(`command: ${command} [index:${i}]`, old)
296
+ return data
297
+ }
298
+
299
+ lastCommand = command
300
+ }
301
+
302
+ return data
303
+
304
+ },
305
+
306
+ copyData(data: IPathCommandData, old: IPathCommandData, index: number, count: number): void {
307
+ for (let i = index, end = index + count; i < end; i++) {
308
+ data.push(old[i])
309
+ }
310
+ },
311
+
312
+ pushData(data: IPathCommandData, strNum: string | number) {
313
+ if (current.index === current.length) { // 单个命令,多个数据的情况
314
+ current.index = 1
315
+ data.push(current.name)
316
+ }
317
+
318
+ data.push(Number(strNum))
319
+ current.index++
320
+ current.dot = 0
321
+ }
322
+
323
+ }
324
+
325
+ const { current, pushData, copyData } = PathConvert
@@ -0,0 +1,10 @@
1
+ import { IPathCommandData } from '@leafer/interface'
2
+
3
+
4
+ export const PathCorner = {
5
+
6
+ smooth(data: IPathCommandData, _cornerRadius: number, _cornerSmoothing?: number): IPathCommandData {
7
+ return data
8
+ }
9
+
10
+ }
@@ -0,0 +1,96 @@
1
+ import { IPathCommandData, IPathDrawer, IPathString } from '@leafer/interface'
2
+ import { PathCommandDataHelper } from './PathCommandDataHelper'
3
+ import { PathHelper } from './PathHelper'
4
+
5
+
6
+ const { moveTo, lineTo, quadraticCurveTo, bezierCurveTo, closePath, beginPath, rect, roundRect, ellipse, arc, arcTo, drawEllipse, drawArc, drawPoints } = PathCommandDataHelper
7
+
8
+ export class PathCreator implements IPathDrawer {
9
+
10
+ public path: IPathCommandData
11
+
12
+ constructor(path?: IPathCommandData | IPathString) {
13
+ if (path) {
14
+ this.path = typeof path === 'string' ? PathHelper.parse(path) : path
15
+ } else {
16
+ this.path = []
17
+ }
18
+ }
19
+
20
+ public beginPath(): PathCreator {
21
+ beginPath(this.path)
22
+ return this
23
+ }
24
+
25
+ // svg and canvas
26
+
27
+ public moveTo(x: number, y: number): PathCreator {
28
+ moveTo(this.path, x, y)
29
+ return this
30
+ }
31
+
32
+ public lineTo(x: number, y: number): PathCreator {
33
+ lineTo(this.path, x, y)
34
+ return this
35
+ }
36
+
37
+ public bezierCurveTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number): PathCreator {
38
+ bezierCurveTo(this.path, x1, y1, x2, y2, x, y)
39
+ return this
40
+ }
41
+
42
+ public quadraticCurveTo(x1: number, y1: number, x: number, y: number): PathCreator {
43
+ quadraticCurveTo(this.path, x1, y1, x, y)
44
+ return this
45
+ }
46
+
47
+ public closePath(): PathCreator {
48
+ closePath(this.path)
49
+ return this
50
+ }
51
+
52
+ // canvas
53
+
54
+ public rect(x: number, y: number, width: number, height: number): PathCreator {
55
+ rect(this.path, x, y, width, height)
56
+ return this
57
+ }
58
+
59
+ public roundRect(x: number, y: number, width: number, height: number, cornerRadius: number | number[]): PathCreator {
60
+ roundRect(this.path, x, y, width, height, cornerRadius)
61
+ return this
62
+ }
63
+
64
+ public ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator {
65
+ ellipse(this.path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
66
+ return this
67
+ }
68
+
69
+ public arc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator {
70
+ arc(this.path, x, y, radius, startAngle, endAngle, anticlockwise)
71
+ return this
72
+ }
73
+
74
+ public arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): PathCreator {
75
+ arcTo(this.path, x1, y1, x2, y2, radius)
76
+ return this
77
+ }
78
+
79
+ // moveTo, then draw
80
+
81
+ public drawEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator {
82
+ drawEllipse(this.path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
83
+ return this
84
+ }
85
+
86
+ public drawArc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator {
87
+ drawArc(this.path, x, y, radius, startAngle, endAngle, anticlockwise)
88
+ return this
89
+ }
90
+
91
+ public drawPoints(points: number[], curve?: boolean | number, close?: boolean): PathCreator {
92
+ drawPoints(this.path, points, curve, close)
93
+ return this
94
+ }
95
+
96
+ }
@@ -0,0 +1,85 @@
1
+ import { IPathDrawer, IPathCommandData } from '@leafer/interface'
2
+ import { OneRadian, PI2 } from '@leafer/math'
3
+ import { Debug } from '@leafer/debug'
4
+
5
+ import { PathCommandMap as Command } from './PathCommandMap'
6
+
7
+
8
+ const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = Command
9
+ const debug = Debug.get('PathDrawer')
10
+
11
+ export const PathDrawer = {
12
+
13
+ drawPathByData(drawer: IPathDrawer, data: IPathCommandData): void {
14
+ if (!data) return
15
+
16
+ let command: number
17
+ let i = 0, len = data.length
18
+
19
+ while (i < len) {
20
+ command = data[i]
21
+ switch (command) {
22
+ case M: //moveto(x, y)
23
+ drawer.moveTo(data[i + 1], data[i + 2])
24
+ i += 3
25
+ break
26
+ case L: //lineto(x, y)
27
+ drawer.lineTo(data[i + 1], data[i + 2])
28
+ i += 3
29
+ break
30
+ case C: //bezierCurveTo(x1, y1, x2, y2, x, y)
31
+ drawer.bezierCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6])
32
+ i += 7
33
+ break
34
+ case Q: //quadraticCurveTo(x1, y1, x, y)
35
+ drawer.quadraticCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4])
36
+ i += 5
37
+ break
38
+ case Z: //closepath()
39
+ drawer.closePath()
40
+ i += 1
41
+ break
42
+
43
+ // canvas command
44
+
45
+ case N: // rect(x, y, width, height)
46
+ drawer.rect(data[i + 1], data[i + 2], data[i + 3], data[i + 4])
47
+ i += 5
48
+ break
49
+ case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4)
50
+ drawer.roundRect(data[i + 1], data[i + 2], data[i + 3], data[i + 4], [data[i + 5], data[i + 6], data[i + 7], data[i + 8]])
51
+ i += 9
52
+ break
53
+ case X: // simple roundRect(x, y, width, height, radius)
54
+ drawer.roundRect(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5])
55
+ i += 6
56
+ break
57
+ case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
58
+ drawer.ellipse(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5] * OneRadian, data[i + 6] * OneRadian, data[i + 7] * OneRadian, data[i + 8] as unknown as boolean)
59
+ i += 9
60
+ break
61
+ case F: // simple ellipse(x, y, radiusX, radiusY)
62
+ drawer.ellipse(data[i + 1], data[i + 2], data[i + 3], data[i + 4], 0, 0, PI2, false)
63
+ i += 5
64
+ break
65
+ case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise)
66
+ drawer.arc(data[i + 1], data[i + 2], data[i + 3], data[i + 4] * OneRadian, data[i + 5] * OneRadian, data[i + 6] as unknown as boolean)
67
+ i += 7
68
+ break
69
+ case P: // simple arc(x, y, radius)
70
+ drawer.arc(data[i + 1], data[i + 2], data[i + 3], 0, PI2, false)
71
+ i += 4
72
+ break
73
+ case U: // arcTo(x1, y1, x2, y2, radius)
74
+ drawer.arcTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5])
75
+ i += 6
76
+ break
77
+ default:
78
+ debug.error(`command: ${command} [index:${i}]`, data)
79
+ return
80
+ }
81
+ }
82
+
83
+ }
84
+
85
+ }
@@ -0,0 +1,8 @@
1
+ import { IPathCommandData, IPathCreator } from '@leafer/interface'
2
+
3
+ export const PathHelper = {
4
+ // index.ts rewrite
5
+ creator: {} as IPathCreator,
6
+ parse(_pathString: string, _curveMode?: boolean): IPathCommandData { return undefined },
7
+ convertToCanvasData(_old: IPathCommandData, _curveMode?: boolean): IPathCommandData { return undefined }
8
+ }
@@ -0,0 +1,22 @@
1
+ import { IPathDrawer } from '@leafer/interface'
2
+ import { MathHelper } from '@leafer/math'
3
+
4
+ export const RectHelper = {
5
+
6
+ drawRoundRect(drawer: IPathDrawer, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void {
7
+ let [topLeft, topRight, bottomRight, bottomLeft] = MathHelper.fourNumber(cornerRadius)
8
+
9
+ const max = Math.min(width / 2, height / 2)
10
+ if (topLeft > max) topLeft = max
11
+ if (topRight > max) topRight = max
12
+ if (bottomRight > max) bottomRight = max
13
+ if (bottomLeft > max) bottomLeft = max
14
+
15
+ topLeft ? drawer.moveTo(x + topLeft, y) : drawer.moveTo(x, y)
16
+ topRight ? drawer.arcTo(x + width, y, x + width, y + height, topRight) : drawer.lineTo(x + width, y)
17
+ bottomRight ? drawer.arcTo(x + width, y + height, x, y + height, bottomRight) : drawer.lineTo(x + width, y + height)
18
+ bottomLeft ? drawer.arcTo(x, y + height, x, y, bottomLeft) : drawer.lineTo(x, y + height)
19
+ topLeft ? drawer.arcTo(x, y, x + width, y, topLeft) : drawer.lineTo(x, y)
20
+ }
21
+
22
+ }