@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer/path",
3
- "version": "1.0.0-alpha.7",
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-alpha.7"
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-alpha.7"
26
+ "@leafer/interface": "1.0.0-bate"
26
27
  }
27
28
  }
@@ -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
- 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))
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 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
48
+ const anticlockwise = BAx * CBy - CBx * BAy < 0
49
+ const sign = anticlockwise ? -1 : 1
50
+ const c = radius / cos(totalRadian / 2)
44
51
 
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
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
- if (sweepFlag === 0 && totalRadian > 0) {
50
- totalRadian -= 2 * PI
51
- } else if (sweepFlag === 1 && totalRadian < 0) {
52
- totalRadian += 2 * PI
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
- // 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
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
- cosEnd = cos(endRadian)
76
- sinEnd = sin(endRadian)
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
- // 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)
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 + fromX, y1 + fromY, x2 + fromX, y2 + fromY, x + fromX, y + fromY)
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
- cosStart = cosEnd
92
- sinStart = sinEnd
93
- endRadian += segmentRadian
94
-
124
+ endRadian += partRadian
95
125
  }
96
126
 
97
- return data
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
- toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData): void {
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
- B.getPointAndSet(tList[i], fromX, fromY, x1, y1, x2, y2, toX, toY, tempPoint)
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
- B.getPointAndSet(t, fromX, fromY, x1, y1, x2, y2, toX, toY, point)
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 B = BezierHelper
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