@leafer/path 1.0.0-alpha.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023-present, Chao (Leafer) Wan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # @leafer/path
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@leafer/path",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "@leafer/path",
5
+ "author": "Chao (Leafer) Wan",
6
+ "license": "MIT",
7
+ "main": "src/index.ts",
8
+ "files": ["src"],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/leaferjs/leafer.git"
12
+ },
13
+ "homepage": "https://github.com/leaferjs/leafer/tree/main/packages/path",
14
+ "bugs": "https://github.com/leaferjs/leafer/issues",
15
+ "keywords": [
16
+ "leafer",
17
+ "leaferjs"
18
+ ],
19
+ "dependencies": {
20
+ "@leafer/math": "1.0.0-alpha.1"
21
+ },
22
+ "devDependencies": {
23
+ "@leafer/interface": "1.0.0-alpha.1"
24
+ }
25
+ }
@@ -0,0 +1,157 @@
1
+ import { IPointData, ITwoPointBoundsData, IPathCommandData } from '@leafer/interface'
2
+ import { TwoPointBoundsHelper } from '@leafer/math'
3
+
4
+ import { PathCommandMap } from './PathCommandMap'
5
+
6
+
7
+ const tempPoint = {} as IPointData
8
+ const { sin, cos, sqrt, atan2, ceil, abs, PI } = Math
9
+ const { setPoint, addPoint } = TwoPointBoundsHelper
10
+
11
+ export const BezierHelper = {
12
+
13
+ getFromACommand(fromX: number, fromY: number, rx: number, ry: number, rotateAngle: number, largeFlag: number, sweepFlag: number, toX: number, toY: number): IPathCommandData { // [...CCommandData, ...CCommandData]
14
+
15
+ const localToX = toX - fromX
16
+ const localToY = toY - fromY
17
+
18
+ const rotation = rotateAngle * PI / 180
19
+ const sinRotation = sin(rotation)
20
+ const cosRotation = cos(rotation)
21
+
22
+ const ax = -cosRotation * localToX * 0.5 - sinRotation * localToY * 0.5
23
+ const ay = -cosRotation * localToY * 0.5 + sinRotation * localToX * 0.5
24
+ const rxSquare = rx * rx
25
+ const rySquare = ry * ry
26
+ const pySquare = ay * ay
27
+ const pxSquare = ax * ax
28
+ const a = rxSquare * rySquare - rxSquare * pySquare - rySquare * pxSquare
29
+ let sr = 0
30
+
31
+ if (a < 0) {
32
+ const scale = sqrt(1 - a / (rxSquare * rySquare))
33
+ rx *= scale
34
+ ry *= scale
35
+ } else {
36
+ const sign = largeFlag === sweepFlag ? -1 : 1
37
+ sr = sign * sqrt(a / (rxSquare * pySquare + rySquare * pxSquare))
38
+ }
39
+
40
+ const cx = sr * rx * ay / ry
41
+ const cy = -sr * ry * ax / rx
42
+ const cx1 = cosRotation * cx - sinRotation * cy + localToX * 0.5
43
+ const cy1 = sinRotation * cx + cosRotation * cy + localToY * 0.5
44
+
45
+ let r1 = atan2((ay - cy) / ry, (ax - cx) / rx)
46
+ let r2 = atan2((-ay - cy) / ry, (-ax - cx) / rx)
47
+ let totalRadian = r2 - r1
48
+
49
+ if (sweepFlag === 0 && totalRadian > 0) {
50
+ totalRadian -= 2 * PI
51
+ } else if (sweepFlag === 1 && totalRadian < 0) {
52
+ totalRadian += 2 * PI
53
+ }
54
+
55
+ // segments arc
56
+ const data: IPathCommandData = []
57
+ const segments = ceil(abs(totalRadian / PI * 2))
58
+ const segmentRadian = totalRadian / segments
59
+ const sinSegmentRadian2 = sin(segmentRadian / 2)
60
+ const sinSegmentRadian4 = sin(segmentRadian / 4)
61
+ const controlRadian = 8 / 3 * sinSegmentRadian4 * sinSegmentRadian4 / sinSegmentRadian2
62
+
63
+ r1 = 0
64
+ r2 = atan2((ay - cy) / ry, (ax - cx) / rx)
65
+ let startRadian = r2 - r1
66
+ let endRadian = startRadian + segmentRadian
67
+ let cosStart = cos(startRadian)
68
+ let sinStart = sin(startRadian)
69
+ let cosEnd: number, sinEnd: number
70
+ let startX = 0, startY = 0
71
+
72
+ let x: number, y: number, x1: number, y1: number, x2: number, y2: number
73
+ for (let i = 0; i < segments; i++) {
74
+
75
+ cosEnd = cos(endRadian)
76
+ sinEnd = sin(endRadian)
77
+
78
+ // segment bezier
79
+ x = cosRotation * rx * cosEnd - sinRotation * ry * sinEnd + cx1
80
+ y = sinRotation * rx * cosEnd + cosRotation * ry * sinEnd + cy1
81
+ x1 = startX + controlRadian * (-cosRotation * rx * sinStart - sinRotation * ry * cosStart)
82
+ y1 = startY + controlRadian * (-sinRotation * rx * sinStart + cosRotation * ry * cosStart)
83
+ x2 = x + controlRadian * (cosRotation * rx * sinEnd + sinRotation * ry * cosEnd)
84
+ y2 = y + controlRadian * (sinRotation * rx * sinEnd - cosRotation * ry * cosEnd)
85
+
86
+ data.push(PathCommandMap.C, x1 + fromX, y1 + fromY, x2 + fromX, y2 + fromY, x + fromX, y + fromY)
87
+
88
+ startX = x
89
+ startY = y
90
+ startRadian = endRadian
91
+ cosStart = cosEnd
92
+ sinStart = sinEnd
93
+ endRadian += segmentRadian
94
+
95
+ }
96
+
97
+ return data
98
+
99
+ },
100
+
101
+ toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData): void {
102
+
103
+ const tList = []
104
+ let a, b, c, t, t1, t2, v, sqrtV
105
+ let f = fromX, z1 = x1, z2 = x2, o = toX
106
+
107
+ for (let i = 0; i < 2; ++i) {
108
+
109
+ if (i == 1) {
110
+ f = fromY, z1 = y1, z2 = y2, o = toY
111
+ }
112
+
113
+ a = -3 * f + 9 * z1 - 9 * z2 + 3 * o
114
+ b = 6 * f - 12 * z1 + 6 * z2
115
+ c = 3 * z1 - 3 * f
116
+
117
+ if (Math.abs(a) < 1e-12) {
118
+ if (Math.abs(b) < 1e-12) continue
119
+ t = -c / b
120
+ if (0 < t && t < 1) tList.push(t)
121
+ continue
122
+ }
123
+
124
+ v = b * b - 4 * c * a
125
+ sqrtV = Math.sqrt(v)
126
+ if (v < 0) continue
127
+
128
+ t1 = (-b + sqrtV) / (2 * a)
129
+ if (0 < t1 && t1 < 1) tList.push(t1)
130
+ t2 = (-b - sqrtV) / (2 * a)
131
+ if (0 < t2 && t2 < 1) tList.push(t2)
132
+ }
133
+
134
+ setPoint(pointBounds, fromX, fromY)
135
+ addPoint(pointBounds, toX, toY)
136
+
137
+ for (let i = 0, len = tList.length; i < len; i++) {
138
+ B.getPointAndSet(tList[i], fromX, fromY, x1, y1, x2, y2, toX, toY, tempPoint)
139
+ addPoint(pointBounds, tempPoint.x, tempPoint.y)
140
+ }
141
+ },
142
+
143
+ getPointAndSet(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, setPoint: IPointData): void {
144
+ const o = 1 - t, a = o * o * o, b = 3 * o * o * t, c = 3 * o * t * t, d = t * t * t
145
+ setPoint.x = a * fromX + b * x1 + c * x2 + d * toX
146
+ setPoint.y = a * fromY + b * y1 + c * y2 + d * toY
147
+ },
148
+
149
+ getPoint(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number): IPointData {
150
+ const point = {} as IPointData
151
+ B.getPointAndSet(t, fromX, fromY, x1, y1, x2, y2, toX, toY, point)
152
+ return point
153
+ }
154
+
155
+ }
156
+
157
+ const B = BezierHelper
@@ -0,0 +1,107 @@
1
+ import { INumberMap, IStringMap } from '@leafer/interface'
2
+
3
+
4
+ export const PathCommandMap: INumberMap = {
5
+ M: 1, //moveto
6
+ m: 10,
7
+ L: 2, //lineto
8
+ l: 20,
9
+ H: 3, //horizontal lineto
10
+ h: 30,
11
+ V: 4, //vertical lineto
12
+ v: 40,
13
+ C: 5, //curveto
14
+ c: 50,
15
+ S: 6, //smooth curveto
16
+ s: 60,
17
+ Q: 7, //quadratic Belzier curve
18
+ q: 70,
19
+ T: 8, //smooth quadratic Belzier curveto
20
+ t: 80,
21
+ A: 9, //elliptical Arc
22
+ a: 90,
23
+ Z: 11, //closepath
24
+ z: 11,
25
+
26
+ // 非svg标准的canvas绘图命令
27
+ rect: 100,
28
+ roundRect: 101,
29
+ ellipse: 102,
30
+ arc: 103,
31
+ arcTo: 104
32
+ }
33
+
34
+ export const PathCommandLengthMap: INumberMap = {
35
+ M: 3, //moveto
36
+ m: 3,
37
+ L: 3, //lineto
38
+ l: 3,
39
+ H: 2, //horizontal lineto
40
+ h: 2,
41
+ V: 2, //vertical lineto
42
+ v: 2,
43
+ C: 7, //curveto
44
+ c: 7,
45
+ S: 5, //smooth curveto
46
+ s: 5,
47
+ Q: 5, //quadratic Belzier curve
48
+ q: 5,
49
+ T: 3, //smooth quadratic Belzier curveto
50
+ t: 3,
51
+ A: 8, //elliptical Arc
52
+ a: 8,
53
+ Z: 1, //closepath
54
+ z: 1,
55
+
56
+ // 非svg标准的canvas绘图命令
57
+ rect: 5,
58
+ roundRect: 6,
59
+ ellipse: 9,
60
+ arc: 7,
61
+ arcTo: 6
62
+ }
63
+
64
+ export const PathCommandNeedConvertMap: INumberMap = { // convert to: M L C Q Z
65
+ // M: 1, //moveto
66
+ m: 10,
67
+ // L: 2, //lineto
68
+ l: 20,
69
+ H: 3, //horizontal lineto
70
+ h: 30,
71
+ V: 4, //vertical lineto
72
+ v: 40,
73
+ // C: 5, //curveto
74
+ c: 50,
75
+ S: 6, //smooth curveto
76
+ s: 60,
77
+ // Q: 7, //quadratic Belzier curve
78
+ q: 70,
79
+ T: 8, //smooth quadratic Belzier curveto
80
+ t: 80,
81
+ A: 9, //elliptical Arc
82
+ a: 90,
83
+ // Z: 11, //closepath
84
+ // z: 11
85
+
86
+ // 非svg标准的canvas绘图命令
87
+ rect: 100,
88
+ roundRect: 101,
89
+ ellipse: 102,
90
+ arc: 103,
91
+ arcTo: 104
92
+ }
93
+
94
+
95
+ const P = PathCommandMap
96
+
97
+ export const NumberPathCommandMap: IStringMap = {}
98
+ for (let key in P) {
99
+ NumberPathCommandMap[P[key]] = key
100
+ }
101
+ // {1: 'M'}
102
+
103
+ export const NumberPathCommandLengthMap: INumberMap = {}
104
+ for (let key in P) {
105
+ NumberPathCommandLengthMap[P[key]] = PathCommandLengthMap[key]
106
+ }
107
+ // {1: 3}
@@ -0,0 +1,241 @@
1
+ import { IPathCommandData } from '@leafer/interface'
2
+ import { StringNumberMap } from '@leafer/math'
3
+ import { PathCommandMap as Command, PathCommandNeedConvertMap as NeedConvertCommand, PathCommandLengthMap as CommandLength, NumberPathCommandMap as CommandName, NumberPathCommandLengthMap as NumberCommandLength } from './PathCommandMap'
4
+
5
+ import { BezierHelper } from './BezierHelper'
6
+
7
+
8
+ interface ICurrentCommand {
9
+ name?: number
10
+ length?: number
11
+ index?: number
12
+ }
13
+
14
+
15
+ const { M, m, L, l, H, h, V, v, C, c, S, s, Q, q, T, t, A, a, Z, z } = Command
16
+ const { getFromACommand } = BezierHelper
17
+
18
+ export const PathConvert = {
19
+
20
+ current: {} as ICurrentCommand,
21
+
22
+ stringify(data: IPathCommandData): string {
23
+ let i = 0, len = data.length, count: number, str: string = '', command: number, lastCommand: number
24
+ while (i < len) {
25
+ command = data[i]
26
+ count = NumberCommandLength[command]
27
+ if (command === lastCommand) {
28
+ str += ' ' // 重复的命令可以省略
29
+ } else {
30
+ str += CommandName[command]
31
+ }
32
+
33
+ for (let j = 1; j < count; j++) {
34
+ str += data[i + j];
35
+ (j === count - 1) || (str += ' ')
36
+ }
37
+
38
+ lastCommand = command
39
+ i += count
40
+ }
41
+ return str
42
+ },
43
+
44
+ parse(pathString: string, convert: boolean = true): IPathCommandData {
45
+
46
+ let needConvert: boolean, char: string, num = ''
47
+ const data: IPathCommandData = []
48
+
49
+ for (let i = 0, len = pathString.length; i < len; i++) {
50
+
51
+ char = pathString[i]
52
+
53
+ if (StringNumberMap[char]) {
54
+
55
+ num += char
56
+
57
+ } else if (Command[char]) {
58
+
59
+ if (num) { pushData(data, Number(num)); num = '' }
60
+
61
+ current.name = Command[char]
62
+ current.length = CommandLength[char]
63
+ current.index = 0
64
+ pushData(data, current.name)
65
+
66
+ if (!needConvert && NeedConvertCommand[char]) needConvert = true
67
+
68
+ } else {
69
+
70
+ if (char === '-') { // L-34-35
71
+ if (num) { pushData(data, Number(num)) }
72
+ num = char
73
+ } else {
74
+ if (num) { pushData(data, Number(num)); num = '' }
75
+ }
76
+
77
+ }
78
+
79
+ }
80
+
81
+ if (num) pushData(data, Number(num))
82
+
83
+ //console.log(pathString, P._data)
84
+ return (convert && needConvert) ? PathConvert.convertToSimple(data) : data
85
+ },
86
+
87
+ convertToSimple(old: IPathCommandData): IPathCommandData {
88
+
89
+ let x = 0, y = 0, x1 = 0, y1 = 0, i = 0, len = old.length, controlX: number, controlY: number, command: number, lastCommand: number, smooth: boolean
90
+ const data: IPathCommandData = []
91
+
92
+ while (i < len) {
93
+
94
+ command = old[i]
95
+
96
+ switch (command) {
97
+ //moveto x,y
98
+ case m:
99
+ old[i + 1] += x
100
+ old[i + 2] += y
101
+ case M:
102
+ x = old[i + 1]
103
+ y = old[i + 2]
104
+ data.push(M, x, y)
105
+ i += 3
106
+ break
107
+
108
+ //horizontal lineto x
109
+ case h:
110
+ old[i + 1] += x
111
+ case H:
112
+ x = old[i + 1]
113
+ data.push(L, x, y)
114
+ i += 2
115
+ break
116
+
117
+ //vertical lineto y
118
+ case v:
119
+ old[i + 1] += y
120
+ case V:
121
+ y = old[i + 1]
122
+ data.push(L, x, y)
123
+ i += 2
124
+ break
125
+
126
+ //lineto x,y
127
+ case l:
128
+ old[i + 1] += x
129
+ old[i + 2] += y
130
+ case L:
131
+ x = old[i + 1]
132
+ y = old[i + 2]
133
+ data.push(L, x, y)
134
+ i += 3
135
+ break
136
+
137
+ //smooth bezierCurveTo x2,y2,x,y
138
+ case s: //smooth
139
+ old[i + 1] += x
140
+ old[i + 2] += y
141
+ old[i + 3] += x
142
+ old[i + 4] += y
143
+ command = S
144
+ case S:
145
+ smooth = (lastCommand === C) || (lastCommand === S)
146
+ x1 = smooth ? (x * 2 - controlX) : old[i + 1]
147
+ y1 = smooth ? (y * 2 - controlY) : old[i + 2]
148
+ controlX = old[i + 1]
149
+ controlY = old[i + 2]
150
+ x = old[i + 3]
151
+ y = old[i + 4]
152
+ data.push(C, x1, y1, controlX, controlY, x, y)
153
+ i += 5
154
+ break
155
+
156
+ //bezierCurveTo x1,y1,x2,y2,x,y
157
+ case c:
158
+ old[i + 1] += x
159
+ old[i + 2] += y
160
+ old[i + 3] += x
161
+ old[i + 4] += y
162
+ old[i + 5] += x
163
+ old[i + 6] += y
164
+ command = C
165
+ case C:
166
+ controlX = old[i + 3]
167
+ controlY = old[i + 4]
168
+ x = old[i + 5]
169
+ y = old[i + 6]
170
+ data.push(C, old[i + 1], old[i + 2], controlX, controlY, x, y)
171
+ i += 7
172
+ break
173
+
174
+ //smooth quadraticCurveTo x,y
175
+ case t:
176
+ old[i + 1] += x
177
+ old[i + 2] += y
178
+ command = T
179
+ case T: //smooth
180
+ smooth = (lastCommand === Q) || (lastCommand === T)
181
+ controlX = smooth ? (x * 2 - controlX) : old[i + 1]
182
+ controlY = smooth ? (y * 2 - controlY) : old[i + 2]
183
+ x = old[i + 1]
184
+ y = old[i + 2]
185
+ data.push(Q, controlX, controlY, x, y)
186
+ i += 3
187
+ break
188
+
189
+ //quadraticCurveTo x1,y1,x,y
190
+ case q:
191
+ old[i + 1] += x
192
+ old[i + 2] += y
193
+ old[i + 3] += x
194
+ old[i + 4] += y
195
+ command = Q
196
+ case Q:
197
+ controlX = old[i + 1]
198
+ controlY = old[i + 2]
199
+ x = old[i + 3]
200
+ y = old[i + 4]
201
+ data.push(Q, controlX, controlY, x, y)
202
+ i += 5
203
+ break
204
+
205
+ //ellipticalArc rx ry x-axis-rotation large-arc-flag sweep-flag x y
206
+ case a:
207
+ old[i + 6] += x
208
+ old[i + 7] += y
209
+ case A:
210
+ data.push(...getFromACommand(x, y, old[i + 1], old[i + 2], old[i + 3], old[i + 4], old[i + 5], old[i + 6], old[i + 7])) // convert bezier
211
+ x = old[i + 6]
212
+ y = old[i + 7]
213
+ i += 8
214
+ break
215
+ case z:
216
+ case Z:
217
+ data.push(Z)
218
+ i++
219
+ break
220
+ }
221
+
222
+ lastCommand = command
223
+ }
224
+
225
+ return data
226
+
227
+ },
228
+
229
+ pushData(data: IPathCommandData, num: number) {
230
+ if (current.index === current.length) { // 单个命令,多个数据的情况
231
+ current.index = 1
232
+ data.push(current.name)
233
+ }
234
+
235
+ data.push(num)
236
+ current.index++
237
+ }
238
+
239
+ }
240
+
241
+ const { current, pushData } = PathConvert
@@ -0,0 +1,64 @@
1
+ import { IPathCommandData } from '@leafer/interface'
2
+ import { PathCommandMap } from './PathCommandMap'
3
+
4
+
5
+ let data: IPathCommandData
6
+ const { M, L, C, Q, Z, rect, roundRect, ellipse, arc, arcTo } = PathCommandMap
7
+
8
+ export const PathCreator = {
9
+
10
+ begin(commandData: IPathCommandData): void {
11
+ data = commandData
12
+ },
13
+
14
+ end(): void {
15
+ data = undefined
16
+ },
17
+
18
+ // draw
19
+
20
+ moveTo(x: number, y: number): void {
21
+ data.push(M, x, y)
22
+ },
23
+
24
+ lineTo(x: number, y: number): void {
25
+ data.push(L, x, y)
26
+ },
27
+
28
+ bezierCurveTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number): void {
29
+ data.push(C, x1, y1, x2, y2, x, y)
30
+ },
31
+
32
+ quadraticCurveTo(x1: number, y1: number, x: number, y: number): void {
33
+ data.push(Q, x1, y1, x, y)
34
+ },
35
+
36
+ close(end?: boolean): void {
37
+ data.push(Z)
38
+ if (end) data = undefined
39
+ },
40
+
41
+
42
+ // 非svg标准的canvas绘图命令
43
+
44
+ rect(x: number, y: number, width: number, height: number): void {
45
+ data.push(rect, x, y, width, height)
46
+ },
47
+
48
+ roundRect(x: number, y: number, width: number, height: number, cornerRadius?: number | number[]): void {
49
+ data.push(roundRect, x, y, width, height, cornerRadius as unknown as number)
50
+ },
51
+
52
+ ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void {
53
+ data.push(ellipse, x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise as unknown as number)
54
+ },
55
+
56
+ arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void {
57
+ data.push(arc, x, y, radius, startAngle, endAngle, counterclockwise as unknown as number)
58
+ },
59
+
60
+ arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void {
61
+ data.push(arcTo, x1, y1, x2, y2, radius)
62
+ }
63
+
64
+ }
@@ -0,0 +1,113 @@
1
+ import { ICanvasDrawPath, ITwoPointBoundsData, IPathCommandData } from '@leafer/interface'
2
+ import { TwoPointBoundsHelper } from '@leafer/math'
3
+
4
+ import { BezierHelper } from './BezierHelper'
5
+ import { PathCommandMap as Command } from './PathCommandMap'
6
+
7
+
8
+ const { M, L, C, Q, Z, ellipse: E } = Command
9
+ const { toTwoPointBounds } = BezierHelper
10
+ const { add, addPoint, setPoint } = TwoPointBoundsHelper
11
+
12
+ const tempPointBounds = {} as ITwoPointBoundsData
13
+
14
+ export const PathHelper = {
15
+
16
+ applyCorner(data: IPathCommandData, cornerRadius: number, cornerSmoothing?: number): IPathCommandData {
17
+ return data
18
+ },
19
+
20
+ toTwoPointBounds(data: IPathCommandData, setPointBounds: ITwoPointBoundsData): void {
21
+
22
+ let command: number
23
+ let i: number = 0, x: number, y: number, x1: number, y1: number, toX: number, toY: number
24
+
25
+ const len = data.length
26
+
27
+ while (i < len) {
28
+ command = data[i]
29
+
30
+ if (i === 0) {
31
+ (command === M) ? setPoint(setPointBounds, data[1], data[2]) : setPoint(setPointBounds, 0, 0)
32
+ }
33
+
34
+ switch (command) {
35
+ case M: //moveto x,y
36
+ case L: //lineto x,y
37
+ x = data[i + 1]
38
+ y = data[i + 2]
39
+ addPoint(setPointBounds, x, y)
40
+ i += 3
41
+ break
42
+ case C: //bezierCurveTo x1,y1,x2,y2,x,y
43
+ toX = data[i + 5]
44
+ toY = data[i + 6]
45
+ toTwoPointBounds(x, y, data[i + 1], data[i + 2], data[i + 3], data[i + 4], toX, toY, tempPointBounds)
46
+ add(setPointBounds, tempPointBounds)
47
+ x = toX
48
+ y = toY
49
+ i += 7
50
+ break
51
+ case Q: //quadraticCurveTo x1,y1,x,y
52
+ x1 = data[i + 1]
53
+ y1 = data[i + 2]
54
+ toX = data[i + 3]
55
+ toY = data[i + 4]
56
+ toTwoPointBounds(x, y, x1, y1, x1, y1, toX, toY, tempPointBounds)
57
+ add(setPointBounds, tempPointBounds)
58
+ x = toX
59
+ y = toY
60
+ i += 5
61
+ break
62
+ case Z: //closepath
63
+ i += 1
64
+ break
65
+ }
66
+ }
67
+
68
+ // 增加1px的扩展,否则会有问题
69
+ setPointBounds.minX--
70
+ setPointBounds.minY--
71
+ setPointBounds.maxX++
72
+ setPointBounds.maxY++
73
+
74
+ },
75
+
76
+ drawData(drawer: ICanvasDrawPath, data: IPathCommandData): void {
77
+ let command: number
78
+ let i = 0, len = data.length
79
+
80
+ while (i < len) {
81
+ command = data[i]
82
+ switch (command) {
83
+ case M: //moveto x,y
84
+ drawer.moveTo(data[i + 1], data[i + 2])
85
+ i += 3
86
+ break
87
+ case L: //lineto x,y
88
+ drawer.lineTo(data[i + 1], data[i + 2])
89
+ i += 3
90
+ break
91
+ case C: //bezierCurveTo x1,y1,x2,y2,x,y
92
+ drawer.bezierCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6])
93
+ i += 7
94
+ break
95
+ case Q: //quadraticCurveTo x1,y1,x,y
96
+ drawer.quadraticCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4])
97
+ i += 5
98
+ break
99
+ case Z: //closepath
100
+ drawer.closePath()
101
+ i += 1
102
+ break
103
+
104
+ // 非svg标准的canvas绘图命令
105
+ case E:
106
+ drawer.ellipse(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)
107
+ i += 9
108
+ break
109
+ }
110
+ }
111
+ }
112
+
113
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { PathConvert } from './PathConvert'
2
+ export { PathHelper } from './PathHelper'
3
+ export { PathCreator } from './PathCreator'
4
+ export { BezierHelper } from './BezierHelper'
5
+ export { PathCommandMap, PathCommandNeedConvertMap, NumberPathCommandMap, NumberPathCommandLengthMap } from './PathCommandMap'