@leafer/path
Advanced tools
+100
| import { IPathCreator, IPathCommandData, IPathDrawer, IPathString, IBoundsData, ITwoPointBoundsData, IPointData, INumberMap, IStringMap } from '@leafer/interface'; | ||
| declare const PathHelper: { | ||
| creator: IPathCreator; | ||
| parse(_pathString: string, _curveMode?: boolean): IPathCommandData; | ||
| convertToCanvasData(_old: IPathCommandData, _curveMode?: boolean): IPathCommandData; | ||
| }; | ||
| interface ICurrentCommand { | ||
| name?: number; | ||
| length?: number; | ||
| index?: number; | ||
| dot?: number; | ||
| } | ||
| declare const PathConvert: { | ||
| current: ICurrentCommand; | ||
| stringify(data: IPathCommandData): string; | ||
| parse(pathString: string, curveMode?: boolean): IPathCommandData; | ||
| toCanvasData(old: IPathCommandData, curveMode?: boolean): IPathCommandData; | ||
| copyData(data: IPathCommandData, old: IPathCommandData, index: number, count: number): void; | ||
| pushData(data: IPathCommandData, strNum: string | number): void; | ||
| }; | ||
| declare class PathCreator implements IPathDrawer { | ||
| path: IPathCommandData; | ||
| constructor(path?: IPathCommandData | IPathString); | ||
| beginPath(): PathCreator; | ||
| moveTo(x: number, y: number): PathCreator; | ||
| lineTo(x: number, y: number): PathCreator; | ||
| bezierCurveTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number): PathCreator; | ||
| quadraticCurveTo(x1: number, y1: number, x: number, y: number): PathCreator; | ||
| closePath(): PathCreator; | ||
| rect(x: number, y: number, width: number, height: number): PathCreator; | ||
| roundRect(x: number, y: number, width: number, height: number, cornerRadius: number | number[]): PathCreator; | ||
| ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator; | ||
| arc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator; | ||
| arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): PathCreator; | ||
| drawEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator; | ||
| drawArc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator; | ||
| drawPoints(points: number[], curve?: boolean | number, close?: boolean): PathCreator; | ||
| } | ||
| declare const PathCommandDataHelper: { | ||
| beginPath(data: IPathCommandData): void; | ||
| moveTo(data: IPathCommandData, x: number, y: number): void; | ||
| lineTo(data: IPathCommandData, x: number, y: number): void; | ||
| bezierCurveTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, x: number, y: number): void; | ||
| quadraticCurveTo(data: IPathCommandData, x1: number, y1: number, x: number, y: number): void; | ||
| closePath(data: IPathCommandData): void; | ||
| rect(data: IPathCommandData, x: number, y: number, width: number, height: number): void; | ||
| roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void; | ||
| ellipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void; | ||
| arc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void; | ||
| arcTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, radius: number): void; | ||
| drawEllipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void; | ||
| drawArc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void; | ||
| drawPoints(data: IPathCommandData, points: number[], curve?: boolean | number, close?: boolean): void; | ||
| }; | ||
| declare const PathDrawer: { | ||
| drawPathByData(drawer: IPathDrawer, data: IPathCommandData): void; | ||
| }; | ||
| declare const PathBounds: { | ||
| toBounds(data: IPathCommandData, setBounds: IBoundsData): void; | ||
| toTwoPointBounds(data: IPathCommandData, setPointBounds: ITwoPointBoundsData): void; | ||
| }; | ||
| declare const PathCorner: { | ||
| smooth(data: IPathCommandData, _cornerRadius: number, _cornerSmoothing?: number): IPathCommandData; | ||
| }; | ||
| declare const BezierHelper: { | ||
| points(data: IPathCommandData, points: number[], curve?: boolean | number, close?: boolean): void; | ||
| rect(data: IPathCommandData, x: number, y: number, width: number, height: number): void; | ||
| roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, radius: number | number[]): void; | ||
| 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; | ||
| arc(data: IPathCommandData | null | void, x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void; | ||
| 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; | ||
| quadraticCurveTo(data: IPathCommandData, fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number): void; | ||
| toTwoPointBoundsByQuadraticCurve(fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void; | ||
| toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void; | ||
| getPointAndSet(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, setPoint: IPointData): void; | ||
| getPoint(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number): IPointData; | ||
| }; | ||
| declare const EllipseHelper: { | ||
| ellipticalArc(data: IPathCommandData, fromX: number, fromY: number, radiusX: number, radiusY: number, rotation: number, largeFlag: number, sweepFlag: number, toX: number, toY: number, curveMode?: boolean): void; | ||
| }; | ||
| declare const RectHelper: { | ||
| drawRoundRect(drawer: IPathDrawer, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void; | ||
| }; | ||
| declare const PathCommandMap: INumberMap; | ||
| declare const NeedConvertToCanvasCommandMap: INumberMap; | ||
| declare const PathNumberCommandMap: IStringMap; | ||
| declare const PathNumberCommandLengthMap: INumberMap; | ||
| export { BezierHelper, EllipseHelper, NeedConvertToCanvasCommandMap, PathBounds, PathCommandDataHelper, PathCommandMap, PathConvert, PathCorner, PathCreator, PathDrawer, PathHelper, PathNumberCommandLengthMap, PathNumberCommandMap, RectHelper }; |
+7
-5
| { | ||
| "name": "@leafer/path", | ||
| "version": "1.0.0-beta.12", | ||
| "version": "1.0.0-beta.15", | ||
| "description": "@leafer/path", | ||
@@ -8,4 +8,6 @@ "author": "Chao (Leafer) Wan", | ||
| "main": "src/index.ts", | ||
| "types": "types/index.d.ts", | ||
| "files": [ | ||
| "src" | ||
| "types", | ||
| "dist" | ||
| ], | ||
@@ -23,8 +25,8 @@ "repository": { | ||
| "dependencies": { | ||
| "@leafer/math": "1.0.0-beta.12", | ||
| "@leafer/debug": "1.0.0-beta.12" | ||
| "@leafer/math": "1.0.0-beta.15", | ||
| "@leafer/debug": "1.0.0-beta.15" | ||
| }, | ||
| "devDependencies": { | ||
| "@leafer/interface": "1.0.0-beta.12" | ||
| "@leafer/interface": "1.0.0-beta.15" | ||
| } | ||
| } |
| import { IPointData, ITwoPointBoundsData, IPathCommandData } from '@leafer/interface' | ||
| import { OneRadian, PI2, PI_2, PointHelper, TwoPointBoundsHelper } from '@leafer/math' | ||
| import { PathCommandMap } from './PathCommandMap' | ||
| import { RectHelper } from './RectHelper' | ||
| import { PathHelper } from './PathHelper' | ||
| const { sin, cos, atan2, ceil, abs, PI } = Math | ||
| const { setPoint, addPoint } = TwoPointBoundsHelper | ||
| const { set } = PointHelper | ||
| const tempPoint = {} as IPointData | ||
| export const BezierHelper = { | ||
| rect(data: IPathCommandData, x: number, y: number, width: number, height: number) { | ||
| PathHelper.creator.path = data | ||
| PathHelper.creator.moveTo(x, y).lineTo(x + width, y).lineTo(x + width, y + height).lineTo(x, y + height).lineTo(x, y) | ||
| }, | ||
| roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, radius: number | number[]): void { | ||
| PathHelper.creator.path = [] | ||
| RectHelper.drawRoundRect(PathHelper.creator, x, y, width, height, radius) | ||
| data.push(...PathHelper.convertToCanvasData(PathHelper.creator.path, true)) | ||
| }, | ||
| 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 { | ||
| const BAx = x1 - fromX | ||
| const BAy = y1 - fromY | ||
| const CBx = toX - x1 | ||
| const CBy = toY - y1 | ||
| let startRadian = atan2(BAy, BAx) | ||
| let endRadian = atan2(CBy, CBx) | ||
| let totalRadian = endRadian - startRadian | ||
| if (totalRadian < 0) totalRadian += PI2 | ||
| if (totalRadian === PI || (abs(BAx + BAy) < 1.e-12) || (abs(CBx + CBy) < 1.e-12)) { // invalid | ||
| if (data) data.push(PathCommandMap.L, x1, y1) | ||
| if (setPointBounds) { | ||
| setPoint(setPointBounds, fromX, fromY) | ||
| addPoint(setPointBounds, x1, y1) | ||
| } | ||
| if (setStartPoint) set(setStartPoint, fromX, fromY) | ||
| if (setEndPoint) set(setEndPoint, x1, y1) | ||
| return | ||
| } | ||
| const anticlockwise = BAx * CBy - CBx * BAy < 0 | ||
| const sign = anticlockwise ? -1 : 1 | ||
| const c = radius / cos(totalRadian / 2) | ||
| const centerX = x1 + c * cos(startRadian + totalRadian / 2 + PI_2 * sign) | ||
| const centerY = y1 + c * sin(startRadian + totalRadian / 2 + PI_2 * sign) | ||
| startRadian -= PI_2 * sign | ||
| endRadian -= PI_2 * sign | ||
| return ellipse(data, centerX, centerY, radius, radius, 0, startRadian / OneRadian, endRadian / OneRadian, anticlockwise, setPointBounds, setEndPoint, setStartPoint) | ||
| }, | ||
| arc(data: IPathCommandData | null | void, x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean, setPointBounds?: ITwoPointBoundsData, setEndPoint?: IPointData, setStartPoint?: IPointData): void { | ||
| return ellipse(data, x, y, radius, radius, 0, startAngle, endAngle, anticlockwise, setPointBounds, setEndPoint, setStartPoint) | ||
| }, | ||
| 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 { | ||
| const rotationRadian = rotation * OneRadian | ||
| const rotationSin = sin(rotationRadian) | ||
| const rotationCos = cos(rotationRadian) | ||
| let startRadian = startAngle * OneRadian | ||
| let endRadian = endAngle * OneRadian | ||
| if (startRadian > PI) startRadian -= PI2 | ||
| if (endRadian < 0) endRadian += PI2 | ||
| let totalRadian = endRadian - startRadian | ||
| if (totalRadian < 0) totalRadian += PI2 | ||
| else if (totalRadian > PI2) totalRadian -= PI2 | ||
| if (anticlockwise) totalRadian -= PI2 | ||
| const parts = ceil(abs(totalRadian / PI_2)) | ||
| const partRadian = totalRadian / parts | ||
| const partRadian4Sin = sin(partRadian / 4) | ||
| const control = 8 / 3 * partRadian4Sin * partRadian4Sin / sin(partRadian / 2) | ||
| endRadian = startRadian + partRadian | ||
| let startCos = cos(startRadian) | ||
| let startSin = sin(startRadian) | ||
| let endCos: number, endSin: number | ||
| let x: number, y: number, x1: number, y1: number, x2: number, y2: number | ||
| let startX = x = rotationCos * radiusX * startCos - rotationSin * radiusY * startSin | ||
| let startY = y = rotationSin * radiusX * startCos + rotationCos * radiusY * startSin | ||
| let fromX = cx + x, fromY = cy + y | ||
| if (data) data.push(PathCommandMap.L, fromX, fromY) | ||
| if (setPointBounds) setPoint(setPointBounds, fromX, fromY) | ||
| if (setStartPoint) set(setStartPoint, fromX, fromY) | ||
| for (let i = 0; i < parts; i++) { | ||
| endCos = cos(endRadian) | ||
| endSin = sin(endRadian) | ||
| x = rotationCos * radiusX * endCos - rotationSin * radiusY * endSin | ||
| y = rotationSin * radiusX * endCos + rotationCos * radiusY * endSin | ||
| x1 = cx + startX - control * (rotationCos * radiusX * startSin + rotationSin * radiusY * startCos) | ||
| y1 = cy + startY - control * (rotationSin * radiusX * startSin - rotationCos * radiusY * startCos) | ||
| x2 = cx + x + control * (rotationCos * radiusX * endSin + rotationSin * radiusY * endCos) | ||
| y2 = cy + y + control * (rotationSin * radiusX * endSin - rotationCos * radiusY * endCos) | ||
| if (data) data.push(PathCommandMap.C, x1, y1, x2, y2, cx + x, cy + y) | ||
| if (setPointBounds) toTwoPointBounds(cx + startX, cy + startY, x1, y1, x2, y2, cx + x, cy + y, setPointBounds, true) | ||
| startX = x | ||
| startY = y | ||
| startCos = endCos | ||
| startSin = endSin | ||
| startRadian = endRadian | ||
| endRadian += partRadian | ||
| } | ||
| if (setEndPoint) set(setEndPoint, cx + x, cy + y) | ||
| }, | ||
| quadraticCurveTo(data: IPathCommandData, fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number): void { | ||
| data.push(PathCommandMap.C, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY) | ||
| }, | ||
| toTwoPointBoundsByQuadraticCurve(fromX: number, fromY: number, x1: number, y1: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void { | ||
| toTwoPointBounds(fromX, fromY, (fromX + 2 * x1) / 3, (fromY + 2 * y1) / 3, (toX + 2 * x1) / 3, (toY + 2 * y1) / 3, toX, toY, pointBounds, addMode) | ||
| }, | ||
| toTwoPointBounds(fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, pointBounds: ITwoPointBoundsData, addMode?: boolean): void { | ||
| const tList = [] | ||
| let a, b, c, t, t1, t2, v, sqrtV | ||
| let f = fromX, z1 = x1, z2 = x2, o = toX | ||
| for (let i = 0; i < 2; ++i) { | ||
| if (i == 1) { | ||
| f = fromY, z1 = y1, z2 = y2, o = toY | ||
| } | ||
| a = -3 * f + 9 * z1 - 9 * z2 + 3 * o | ||
| b = 6 * f - 12 * z1 + 6 * z2 | ||
| c = 3 * z1 - 3 * f | ||
| if (Math.abs(a) < 1e-12) { | ||
| if (Math.abs(b) < 1e-12) continue | ||
| t = -c / b | ||
| if (0 < t && t < 1) tList.push(t) | ||
| continue | ||
| } | ||
| v = b * b - 4 * c * a | ||
| sqrtV = Math.sqrt(v) | ||
| if (v < 0) continue | ||
| t1 = (-b + sqrtV) / (2 * a) | ||
| if (0 < t1 && t1 < 1) tList.push(t1) | ||
| t2 = (-b - sqrtV) / (2 * a) | ||
| if (0 < t2 && t2 < 1) tList.push(t2) | ||
| } | ||
| addMode ? addPoint(pointBounds, fromX, fromY) : setPoint(pointBounds, fromX, fromY) | ||
| addPoint(pointBounds, toX, toY) | ||
| for (let i = 0, len = tList.length; i < len; i++) { | ||
| getPointAndSet(tList[i], fromX, fromY, x1, y1, x2, y2, toX, toY, tempPoint) | ||
| addPoint(pointBounds, tempPoint.x, tempPoint.y) | ||
| } | ||
| }, | ||
| getPointAndSet(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number, setPoint: IPointData): void { | ||
| const o = 1 - t, a = o * o * o, b = 3 * o * o * t, c = 3 * o * t * t, d = t * t * t | ||
| setPoint.x = a * fromX + b * x1 + c * x2 + d * toX | ||
| setPoint.y = a * fromY + b * y1 + c * y2 + d * toY | ||
| }, | ||
| getPoint(t: number, fromX: number, fromY: number, x1: number, y1: number, x2: number, y2: number, toX: number, toY: number): IPointData { | ||
| const point = {} as IPointData | ||
| getPointAndSet(t, fromX, fromY, x1, y1, x2, y2, toX, toY, point) | ||
| return point | ||
| } | ||
| } | ||
| const { getPointAndSet, toTwoPointBounds, ellipse } = BezierHelper |
| import { IPathCommandData } from '@leafer/interface' | ||
| import { OneRadian, PI2 } from '@leafer/math' | ||
| import { PathCommandMap } from './PathCommandMap' | ||
| import { BezierHelper } from './BezierHelper' | ||
| const { sin, cos, sqrt, atan2 } = Math | ||
| const { ellipse } = BezierHelper | ||
| export const EllipseHelper = { | ||
| // svg | ||
| ellipticalArc(data: IPathCommandData, fromX: number, fromY: number, radiusX: number, radiusY: number, rotation: number, largeFlag: number, sweepFlag: number, toX: number, toY: number, curveMode?: boolean): void { | ||
| const halfX = (toX - fromX) / 2 | ||
| const halfY = (toY - fromY) / 2 | ||
| const rotationRadian = rotation * OneRadian | ||
| const rotationSin = sin(rotationRadian) | ||
| const rotationCos = cos(rotationRadian) | ||
| const px = -rotationCos * halfX - rotationSin * halfY | ||
| const py = -rotationCos * halfY + rotationSin * halfX | ||
| const rxSquare = radiusX * radiusX | ||
| const rySquare = radiusY * radiusY | ||
| const pySquare = py * py | ||
| const pxSquare = px * px | ||
| const a = rxSquare * rySquare - rxSquare * pySquare - rySquare * pxSquare | ||
| let s = 0 | ||
| if (a < 0) { | ||
| const t = sqrt(1 - a / (rxSquare * rySquare)) | ||
| radiusX *= t | ||
| radiusY *= t | ||
| } else { | ||
| s = (largeFlag === sweepFlag ? -1 : 1) * sqrt(a / (rxSquare * pySquare + rySquare * pxSquare)) | ||
| } | ||
| const cx = s * radiusX * py / radiusY | ||
| const cy = -s * radiusY * px / radiusX | ||
| const startRadian = atan2((py - cy) / radiusY, (px - cx) / radiusX) | ||
| const endRadian = atan2((-py - cy) / radiusY, (-px - cx) / radiusX) | ||
| let totalRadian = endRadian - startRadian | ||
| if (sweepFlag === 0 && totalRadian > 0) { | ||
| totalRadian -= PI2 | ||
| } else if (sweepFlag === 1 && totalRadian < 0) { | ||
| totalRadian += PI2 | ||
| } | ||
| const centerX = fromX + halfX + rotationCos * cx - rotationSin * cy | ||
| const centerY = fromY + halfY + rotationSin * cx + rotationCos * cy | ||
| const anticlockwise = totalRadian < 0 ? 1 : 0 | ||
| if (curveMode) { | ||
| ellipse(data, centerX, centerY, radiusX, radiusY, rotation, startRadian / OneRadian, endRadian / OneRadian, anticlockwise as unknown as boolean) | ||
| } else { | ||
| if (radiusX === radiusY && !rotation) { | ||
| data.push(PathCommandMap.O, centerX, centerY, radiusX, startRadian / OneRadian, endRadian / OneRadian, anticlockwise) | ||
| } else { | ||
| data.push(PathCommandMap.G, centerX, centerY, radiusX, radiusY, rotation, startRadian / OneRadian, endRadian / OneRadian, anticlockwise) | ||
| } | ||
| } | ||
| } | ||
| } |
| import { ITwoPointBoundsData, IPathCommandData, IBoundsData, IPointData } from '@leafer/interface' | ||
| import { TwoPointBoundsHelper } from '@leafer/math' | ||
| import { Debug } from '@leafer/debug' | ||
| import { BezierHelper } from './BezierHelper' | ||
| import { PathCommandMap as Command } from './PathCommandMap' | ||
| const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = Command | ||
| const { toTwoPointBounds, toTwoPointBoundsByQuadraticCurve, arcTo, arc, ellipse } = BezierHelper | ||
| const { add, copy, addPoint, setPoint, addBounds, toBounds } = TwoPointBoundsHelper | ||
| const debug = Debug.get('PathBounds') | ||
| let radius: number, radiusX: number, radiusY: number | ||
| const tempPointBounds = {} as ITwoPointBoundsData | ||
| const setPointBounds = {} as ITwoPointBoundsData | ||
| const setEndPoint = {} as IPointData | ||
| export const PathBounds = { | ||
| toBounds(data: IPathCommandData, setBounds: IBoundsData): void { | ||
| PathBounds.toTwoPointBounds(data, setPointBounds) | ||
| toBounds(setPointBounds, setBounds) | ||
| }, | ||
| toTwoPointBounds(data: IPathCommandData, setPointBounds: ITwoPointBoundsData): void { | ||
| if (!data || !data.length) return setPoint(setPointBounds, 0, 0) | ||
| let command: number | ||
| let i: number = 0, x: number = 0, y: number = 0, x1: number, y1: number, toX: number, toY: number | ||
| const len = data.length | ||
| while (i < len) { | ||
| command = data[i] | ||
| if (i === 0) { | ||
| if (command === Z || command === C || command === Q) { | ||
| setPoint(setPointBounds, x, y) | ||
| } else { | ||
| setPoint(setPointBounds, data[i + 1], data[i + 2]) | ||
| } | ||
| } | ||
| switch (command) { | ||
| case M: //moveto(x, y) | ||
| case L: //lineto(x, y) | ||
| x = data[i + 1] | ||
| y = data[i + 2] | ||
| addPoint(setPointBounds, x, y) | ||
| i += 3 | ||
| break | ||
| case C: //bezierCurveTo(x1, y1, x2, y2, x,y) | ||
| toX = data[i + 5] | ||
| toY = data[i + 6] | ||
| toTwoPointBounds(x, y, data[i + 1], data[i + 2], data[i + 3], data[i + 4], toX, toY, tempPointBounds) | ||
| add(setPointBounds, tempPointBounds) | ||
| x = toX | ||
| y = toY | ||
| i += 7 | ||
| break | ||
| case Q: //quadraticCurveTo(x1, y1, x, y) | ||
| x1 = data[i + 1] | ||
| y1 = data[i + 2] | ||
| toX = data[i + 3] | ||
| toY = data[i + 4] | ||
| toTwoPointBoundsByQuadraticCurve(x, y, x1, y1, toX, toY, tempPointBounds) | ||
| add(setPointBounds, tempPointBounds) | ||
| x = toX | ||
| y = toY | ||
| i += 5 | ||
| break | ||
| case Z: //closepath() | ||
| i += 1 | ||
| break | ||
| // canvas command | ||
| case N: // rect(x, y, width, height) | ||
| x = data[i + 1] | ||
| y = data[i + 2] | ||
| addBounds(setPointBounds, x, y, data[i + 3], data[i + 4]) | ||
| i += 5 | ||
| break | ||
| case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4) | ||
| case X: // simple roundRect(x, y, width, height, radius) | ||
| x = data[i + 1] | ||
| y = data[i + 2] | ||
| addBounds(setPointBounds, x, y, data[i + 3], data[i + 4]) | ||
| i += (command === D ? 9 : 6) | ||
| break | ||
| case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 9 | ||
| break | ||
| case F: // simple ellipse(x, y, radiusX, radiusY) | ||
| x = data[i + 1] | ||
| y = data[i + 2] | ||
| radiusX = data[i + 3] | ||
| radiusY = data[i + 4] | ||
| addBounds(setPointBounds, x - radiusX, y - radiusY, radiusX * 2, radiusY * 2) | ||
| x += radiusX | ||
| i += 5 | ||
| break | ||
| case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 7 | ||
| break | ||
| case P: // simple arc(x, y, radius) | ||
| x = data[i + 1] | ||
| y = data[i + 2] | ||
| radius = data[i + 3] | ||
| addBounds(setPointBounds, x - radius, y - radius, radius * 2, radius * 2) | ||
| x += radius | ||
| i += 4 | ||
| break | ||
| case U: // arcTo(x1, y1, x2, y2, radius) | ||
| arcTo(null, x, y, data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], tempPointBounds, setEndPoint) | ||
| i === 0 ? copy(setPointBounds, tempPointBounds) : add(setPointBounds, tempPointBounds) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 6 | ||
| break | ||
| default: | ||
| debug.error(`command: ${command} [index:${i}]`, data) | ||
| return | ||
| } | ||
| } | ||
| } | ||
| } |
| import { IPathCommandData, IPointData } from '@leafer/interface' | ||
| import { PathCommandMap } from './PathCommandMap' | ||
| import { BezierHelper } from './BezierHelper' | ||
| import { MathHelper } from '@leafer/math' | ||
| const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = PathCommandMap | ||
| const startPoint = {} as IPointData | ||
| export const PathCommandDataHelper = { | ||
| beginPath(data: IPathCommandData): void { | ||
| data.length = 0 | ||
| }, | ||
| // svg and canvas | ||
| moveTo(data: IPathCommandData, x: number, y: number): void { | ||
| data.push(M, x, y) | ||
| }, | ||
| lineTo(data: IPathCommandData, x: number, y: number): void { | ||
| data.push(L, x, y) | ||
| }, | ||
| bezierCurveTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, x: number, y: number): void { | ||
| data.push(C, x1, y1, x2, y2, x, y) | ||
| }, | ||
| quadraticCurveTo(data: IPathCommandData, x1: number, y1: number, x: number, y: number): void { | ||
| data.push(Q, x1, y1, x, y) | ||
| }, | ||
| closePath(data: IPathCommandData): void { | ||
| data.push(Z) | ||
| }, | ||
| // canvas | ||
| rect(data: IPathCommandData, x: number, y: number, width: number, height: number): void { | ||
| data.push(N, x, y, width, height) | ||
| }, | ||
| roundRect(data: IPathCommandData, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void { | ||
| if (typeof cornerRadius === 'number') { | ||
| data.push(X, x, y, width, height, cornerRadius) | ||
| } else { | ||
| const fourCorners = MathHelper.fourNumber(cornerRadius) | ||
| if (fourCorners) { | ||
| data.push(D, x, y, width, height, ...fourCorners) | ||
| } else { | ||
| data.push(N, x, y, width, height) | ||
| } | ||
| } | ||
| }, | ||
| ellipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void { | ||
| if (rotation === undefined) { | ||
| data.push(F, x, y, radiusX, radiusY) | ||
| } else { | ||
| if (startAngle === undefined) startAngle = 0 | ||
| if (endAngle === undefined) endAngle = 360 | ||
| data.push(G, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise ? 1 : 0) | ||
| } | ||
| }, | ||
| arc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void { | ||
| if (startAngle === undefined) { | ||
| data.push(P, x, y, radius) | ||
| } else { | ||
| if (endAngle === undefined) endAngle = 360 | ||
| data.push(O, x, y, radius, startAngle, endAngle, anticlockwise ? 1 : 0) | ||
| } | ||
| }, | ||
| moveToEllipse(data: IPathCommandData, x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void { | ||
| if (rotation === undefined) rotation = 0 | ||
| if (startAngle === undefined) startAngle = 0 | ||
| if (endAngle === undefined) endAngle = 360 | ||
| BezierHelper.ellipse(null, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise, null, null, startPoint) | ||
| data.push(M, startPoint.x, startPoint.y) | ||
| ellipse(data, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| }, | ||
| moveToArc(data: IPathCommandData, x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): void { | ||
| if (startAngle === undefined) startAngle = 0 | ||
| if (endAngle === undefined) endAngle = 360 | ||
| BezierHelper.arc(null, x, y, radius, startAngle, endAngle, anticlockwise, null, null, startPoint) | ||
| data.push(M, startPoint.x, startPoint.y) | ||
| arc(data, x, y, radius, startAngle, endAngle, anticlockwise) | ||
| }, | ||
| arcTo(data: IPathCommandData, x1: number, y1: number, x2: number, y2: number, radius: number): void { | ||
| data.push(U, x1, y1, x2, y2, radius) | ||
| } | ||
| } | ||
| const { ellipse, arc } = PathCommandDataHelper |
| import { INumberMap, IStringMap } from '@leafer/interface' | ||
| export const CanvasCommandOnlyMap: INumberMap = { | ||
| N: 21, // rect | ||
| D: 22, // roundRect | ||
| X: 23, // simple roundRect | ||
| G: 24, // ellipse | ||
| F: 25, // simple ellipse | ||
| O: 26, // arc | ||
| P: 27, // simple arc | ||
| U: 28 // arcTo | ||
| } | ||
| export const PathCommandMap: INumberMap = { | ||
| // svg and canvas | ||
| M: 1, // moveto | ||
| m: 10, | ||
| L: 2, // lineto | ||
| l: 20, | ||
| H: 3, // horizontal lineto | ||
| h: 30, | ||
| V: 4, // vertical lineto | ||
| v: 40, | ||
| C: 5, // curveto | ||
| c: 50, | ||
| S: 6, // smooth curveto | ||
| s: 60, | ||
| Q: 7, // quadratic Belzier curve | ||
| q: 70, | ||
| T: 8, // smooth quadratic Belzier curveto | ||
| t: 80, | ||
| A: 9, //e lliptical Arc | ||
| a: 90, | ||
| Z: 11, // closepath | ||
| z: 11, | ||
| R: 12, // Catmull Rom | ||
| // canvas | ||
| ...CanvasCommandOnlyMap | ||
| } | ||
| export const PathCommandLengthMap: INumberMap = { | ||
| M: 3, //moveto | ||
| m: 3, | ||
| L: 3, //lineto | ||
| l: 3, | ||
| H: 2, //horizontal lineto | ||
| h: 2, | ||
| V: 2, //vertical lineto | ||
| v: 2, | ||
| C: 7, //curveto | ||
| c: 7, | ||
| S: 5, //smooth curveto | ||
| s: 5, | ||
| Q: 5, //quadratic Belzier curve | ||
| q: 5, | ||
| T: 3, //smooth quadratic Belzier curveto | ||
| t: 3, | ||
| A: 8, //elliptical Arc | ||
| a: 8, | ||
| Z: 1, //closepath | ||
| z: 1, | ||
| // canvas | ||
| N: 5, // rect | ||
| D: 9, // roundRect | ||
| X: 6, // simple roundRect | ||
| G: 9, // ellipse | ||
| F: 5, // simple ellipse | ||
| O: 7, // arc | ||
| P: 4, // simple arc | ||
| U: 6 // arcTo | ||
| } | ||
| export const NeedConvertToCanvasCommandMap: INumberMap = { // convert to: M L C Q Z | ||
| // M: 1, //moveto | ||
| m: 10, | ||
| // L: 2, //lineto | ||
| l: 20, | ||
| H: 3, //horizontal lineto | ||
| h: 30, | ||
| V: 4, //vertical lineto | ||
| v: 40, | ||
| // C: 5, //curveto | ||
| c: 50, | ||
| S: 6, //smooth curveto | ||
| s: 60, | ||
| // Q: 7, //quadratic Belzier curve | ||
| q: 70, | ||
| T: 8, //smooth quadratic Belzier curveto | ||
| t: 80, | ||
| A: 9, //elliptical Arc | ||
| a: 90, | ||
| // Z: 11, //closepath | ||
| // z: 11 | ||
| } | ||
| export const NeedConvertToCurveCommandMap: INumberMap = { | ||
| ...NeedConvertToCanvasCommandMap, | ||
| ...CanvasCommandOnlyMap | ||
| } | ||
| const P = PathCommandMap | ||
| export const PathNumberCommandMap: IStringMap = {} | ||
| for (let key in P) { | ||
| PathNumberCommandMap[P[key]] = key | ||
| } | ||
| // {1: 'M'} | ||
| export const PathNumberCommandLengthMap: INumberMap = {} | ||
| for (let key in P) { | ||
| PathNumberCommandLengthMap[P[key]] = PathCommandLengthMap[key] | ||
| } | ||
| // {1: 3} |
| import { IPathCommandData, IPointData } from '@leafer/interface' | ||
| import { StringNumberMap } from '@leafer/math' | ||
| import { Debug } from '@leafer/debug' | ||
| import { PathCommandMap as Command, NeedConvertToCanvasCommandMap, NeedConvertToCurveCommandMap, PathCommandLengthMap, PathNumberCommandMap, PathNumberCommandLengthMap } from './PathCommandMap' | ||
| import { BezierHelper } from './BezierHelper' | ||
| import { EllipseHelper } from './EllipseHelper' | ||
| interface ICurrentCommand { | ||
| name?: number | ||
| length?: number | ||
| index?: number | ||
| dot?: number | ||
| } | ||
| 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 | ||
| const { rect, roundRect, arcTo, arc, ellipse, quadraticCurveTo } = BezierHelper | ||
| const { ellipticalArc } = EllipseHelper | ||
| const debug = Debug.get('PathConvert') | ||
| const setEndPoint = {} as IPointData | ||
| export const PathConvert = { | ||
| current: { dot: 0 } as ICurrentCommand, | ||
| stringify(data: IPathCommandData): string { | ||
| let i = 0, len = data.length, count: number, str: string = '', command: number, lastCommand: number | ||
| while (i < len) { | ||
| command = data[i] | ||
| count = PathNumberCommandLengthMap[command] | ||
| if (command === lastCommand) { | ||
| str += ' ' // 重复的命令可以省略 | ||
| } else { | ||
| str += PathNumberCommandMap[command] | ||
| } | ||
| for (let j = 1; j < count; j++) { | ||
| str += data[i + j]; | ||
| (j === count - 1) || (str += ' ') | ||
| } | ||
| lastCommand = command | ||
| i += count | ||
| } | ||
| return str | ||
| }, | ||
| parse(pathString: string, curveMode?: boolean): IPathCommandData { | ||
| let needConvert: boolean, char: string, lastChar: string, num = '' | ||
| const data: IPathCommandData = [] | ||
| const convertCommand = curveMode ? NeedConvertToCurveCommandMap : NeedConvertToCanvasCommandMap | ||
| for (let i = 0, len = pathString.length; i < len; i++) { | ||
| char = pathString[i] | ||
| if (StringNumberMap[char]) { | ||
| if (char === '.') { | ||
| current.dot++ | ||
| if (current.dot > 1) { | ||
| pushData(data, num); num = '' // .375.375 | ||
| } | ||
| } | ||
| num += char | ||
| } else if (Command[char]) { | ||
| if (num) { pushData(data, num); num = '' } | ||
| current.name = Command[char] | ||
| current.length = PathCommandLengthMap[char] | ||
| current.index = 0 | ||
| pushData(data, current.name) | ||
| if (!needConvert && convertCommand[char]) needConvert = true | ||
| } else { | ||
| if (char === '-' || char === '+') { | ||
| if (lastChar === 'e' || lastChar === 'E') { // L45e-12 21e+22 | ||
| num += char | ||
| } else { | ||
| if (num) pushData(data, num) // L-34-35 L+12+28 | ||
| num = char | ||
| } | ||
| } else { | ||
| if (num) { pushData(data, num); num = '' } | ||
| } | ||
| } | ||
| lastChar = char | ||
| } | ||
| if (num) pushData(data, num) | ||
| return needConvert ? PathConvert.toCanvasData(data, curveMode) : data | ||
| }, | ||
| toCanvasData(old: IPathCommandData, curveMode?: boolean): IPathCommandData { | ||
| let x = 0, y = 0, x1 = 0, y1 = 0, i = 0, len = old.length, controlX: number, controlY: number, command: number, lastCommand: number, smooth: boolean | ||
| const data: IPathCommandData = [] | ||
| while (i < len) { | ||
| command = old[i] | ||
| switch (command) { | ||
| //moveto(x, y) | ||
| case m: | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| case M: | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| data.push(M, x, y) | ||
| i += 3 | ||
| break | ||
| //horizontal lineto(x) | ||
| case h: | ||
| old[i + 1] += x | ||
| case H: | ||
| x = old[i + 1] | ||
| data.push(L, x, y) | ||
| i += 2 | ||
| break | ||
| //vertical lineto(y) | ||
| case v: | ||
| old[i + 1] += y | ||
| case V: | ||
| y = old[i + 1] | ||
| data.push(L, x, y) | ||
| i += 2 | ||
| break | ||
| //lineto(x,y) | ||
| case l: | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| case L: | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| data.push(L, x, y) | ||
| i += 3 | ||
| break | ||
| //smooth bezierCurveTo(x2, y2, x, y) | ||
| case s: //smooth | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| old[i + 3] += x | ||
| old[i + 4] += y | ||
| command = S | ||
| case S: | ||
| smooth = (lastCommand === C) || (lastCommand === S) | ||
| x1 = smooth ? (x * 2 - controlX) : old[i + 1] | ||
| y1 = smooth ? (y * 2 - controlY) : old[i + 2] | ||
| controlX = old[i + 1] | ||
| controlY = old[i + 2] | ||
| x = old[i + 3] | ||
| y = old[i + 4] | ||
| data.push(C, x1, y1, controlX, controlY, x, y) | ||
| i += 5 | ||
| break | ||
| //bezierCurveTo(x1, y1, x2, y2, x, y) | ||
| case c: | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| old[i + 3] += x | ||
| old[i + 4] += y | ||
| old[i + 5] += x | ||
| old[i + 6] += y | ||
| command = C | ||
| case C: | ||
| controlX = old[i + 3] | ||
| controlY = old[i + 4] | ||
| x = old[i + 5] | ||
| y = old[i + 6] | ||
| data.push(C, old[i + 1], old[i + 2], controlX, controlY, x, y) | ||
| i += 7 | ||
| break | ||
| //smooth quadraticCurveTo(x, y) | ||
| case t: | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| command = T | ||
| case T: //smooth | ||
| smooth = (lastCommand === Q) || (lastCommand === T) | ||
| controlX = smooth ? (x * 2 - controlX) : old[i + 1] | ||
| controlY = smooth ? (y * 2 - controlY) : old[i + 2] | ||
| curveMode ? quadraticCurveTo(data, x, y, controlX, controlY, old[i + 1], old[i + 2]) : data.push(Q, controlX, controlY, old[i + 1], old[i + 2]) | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| i += 3 | ||
| break | ||
| //quadraticCurveTo(x1, y1, x, y) | ||
| case q: | ||
| old[i + 1] += x | ||
| old[i + 2] += y | ||
| old[i + 3] += x | ||
| old[i + 4] += y | ||
| command = Q | ||
| case Q: | ||
| controlX = old[i + 1] | ||
| controlY = old[i + 2] | ||
| curveMode ? quadraticCurveTo(data, x, y, controlX, controlY, old[i + 3], old[i + 4]) : data.push(Q, controlX, controlY, old[i + 3], old[i + 4]) | ||
| x = old[i + 3] | ||
| y = old[i + 4] | ||
| i += 5 | ||
| break | ||
| //ellipticalArc(rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y) | ||
| case a: | ||
| old[i + 6] += x | ||
| old[i + 7] += y | ||
| case A: | ||
| 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 | ||
| x = old[i + 6] | ||
| y = old[i + 7] | ||
| i += 8 | ||
| break | ||
| case z: | ||
| case Z: | ||
| data.push(Z) | ||
| i++ | ||
| break | ||
| // canvas command | ||
| case N: // rect(x, y, width, height) | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| curveMode ? rect(data, x, y, old[i + 3], old[i + 4]) : copyData(data, old, i, 5) | ||
| i += 5 | ||
| break | ||
| case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4) | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| 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) | ||
| i += 9 | ||
| break | ||
| case X: // simple roundRect(x, y, width, height, radius) | ||
| x = old[i + 1] | ||
| y = old[i + 2] | ||
| curveMode ? roundRect(data, x, y, old[i + 3], old[i + 4], old[i + 5]) : copyData(data, old, i, 6) | ||
| i += 6 | ||
| break | ||
| case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 9 | ||
| break | ||
| case F: // simple ellipse(x, y, radiusX, radiusY) | ||
| curveMode ? ellipse(data, old[i + 1], old[i + 2], old[i + 3], old[i + 4], 0, 0, 360, false) : copyData(data, old, i, 5) | ||
| x = old[i + 1] + old[i + 3] | ||
| y = old[i + 2] | ||
| i += 5 | ||
| break | ||
| case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 7 | ||
| break | ||
| case P: // simple arc(x, y, radius) | ||
| curveMode ? arc(data, old[i + 1], old[i + 2], old[i + 3], 0, 360, false) : copyData(data, old, i, 4) | ||
| x = old[i + 1] + old[i + 3] | ||
| y = old[i + 2] | ||
| i += 4 | ||
| break | ||
| case U: // arcTo(x1, y1, x2, y2, radius) | ||
| 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) | ||
| x = setEndPoint.x | ||
| y = setEndPoint.y | ||
| i += 6 | ||
| break | ||
| default: | ||
| debug.error(`command: ${command} [index:${i}]`, old) | ||
| return data | ||
| } | ||
| lastCommand = command | ||
| } | ||
| return data | ||
| }, | ||
| copyData(data: IPathCommandData, old: IPathCommandData, index: number, count: number): void { | ||
| for (let i = index, end = index + count; i < end; i++) { | ||
| data.push(old[i]) | ||
| } | ||
| }, | ||
| pushData(data: IPathCommandData, strNum: string | number) { | ||
| if (current.index === current.length) { // 单个命令,多个数据的情况 | ||
| current.index = 1 | ||
| data.push(current.name) | ||
| } | ||
| data.push(Number(strNum)) | ||
| current.index++ | ||
| current.dot = 0 | ||
| } | ||
| } | ||
| const { current, pushData, copyData } = PathConvert |
| import { IPathCommandData } from '@leafer/interface' | ||
| export const PathCorner = { | ||
| smooth(data: IPathCommandData, _cornerRadius: number, _cornerSmoothing?: number): IPathCommandData { | ||
| return data | ||
| } | ||
| } |
| import { IPathCommandData, IPathDrawer, IPathString } from '@leafer/interface' | ||
| import { PathCommandDataHelper } from './PathCommandDataHelper' | ||
| import { PathHelper } from './PathHelper' | ||
| const { moveTo, lineTo, quadraticCurveTo, bezierCurveTo, closePath, beginPath, rect, roundRect, ellipse, arc, arcTo, moveToEllipse, moveToArc } = PathCommandDataHelper | ||
| export class PathCreator implements IPathDrawer { | ||
| public path: IPathCommandData | ||
| constructor(path?: IPathCommandData | IPathString) { | ||
| if (path) { | ||
| this.path = typeof path === 'string' ? PathHelper.parse(path) : path | ||
| } else { | ||
| this.path = [] | ||
| } | ||
| } | ||
| public beginPath(): PathCreator { | ||
| beginPath(this.path) | ||
| return this | ||
| } | ||
| // svg and canvas | ||
| public moveTo(x: number, y: number): PathCreator { | ||
| moveTo(this.path, x, y) | ||
| return this | ||
| } | ||
| public lineTo(x: number, y: number): PathCreator { | ||
| lineTo(this.path, x, y) | ||
| return this | ||
| } | ||
| public bezierCurveTo(x1: number, y1: number, x2: number, y2: number, x: number, y: number): PathCreator { | ||
| bezierCurveTo(this.path, x1, y1, x2, y2, x, y) | ||
| return this | ||
| } | ||
| public quadraticCurveTo(x1: number, y1: number, x: number, y: number): PathCreator { | ||
| quadraticCurveTo(this.path, x1, y1, x, y) | ||
| return this | ||
| } | ||
| public closePath(): PathCreator { | ||
| closePath(this.path) | ||
| return this | ||
| } | ||
| // canvas | ||
| public rect(x: number, y: number, width: number, height: number): PathCreator { | ||
| rect(this.path, x, y, width, height) | ||
| return this | ||
| } | ||
| public roundRect(x: number, y: number, width: number, height: number, cornerRadius: number | number[]): PathCreator { | ||
| roundRect(this.path, x, y, width, height, cornerRadius) | ||
| return this | ||
| } | ||
| public ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator { | ||
| ellipse(this.path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| return this | ||
| } | ||
| public arc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator { | ||
| arc(this.path, x, y, radius, startAngle, endAngle, anticlockwise) | ||
| return this | ||
| } | ||
| public arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): PathCreator { | ||
| arcTo(this.path, x1, y1, x2, y2, radius) | ||
| return this | ||
| } | ||
| // moveTo, then draw | ||
| public moveToEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation?: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator { | ||
| moveToEllipse(this.path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| return this | ||
| } | ||
| public moveToArc(x: number, y: number, radius: number, startAngle?: number, endAngle?: number, anticlockwise?: boolean): PathCreator { | ||
| moveToArc(this.path, x, y, radius, startAngle, endAngle, anticlockwise) | ||
| return this | ||
| } | ||
| } |
| import { IPathDrawer, IPathCommandData } from '@leafer/interface' | ||
| import { OneRadian, PI2 } from '@leafer/math' | ||
| import { Debug } from '@leafer/debug' | ||
| import { PathCommandMap as Command } from './PathCommandMap' | ||
| const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = Command | ||
| const debug = Debug.get('PathDrawer') | ||
| export const PathDrawer = { | ||
| drawPathByData(drawer: IPathDrawer, data: IPathCommandData): void { | ||
| if (!data) return | ||
| let command: number | ||
| let i = 0, len = data.length | ||
| while (i < len) { | ||
| command = data[i] | ||
| switch (command) { | ||
| case M: //moveto(x, y) | ||
| drawer.moveTo(data[i + 1], data[i + 2]) | ||
| i += 3 | ||
| break | ||
| case L: //lineto(x, y) | ||
| drawer.lineTo(data[i + 1], data[i + 2]) | ||
| i += 3 | ||
| break | ||
| case C: //bezierCurveTo(x1, y1, x2, y2, x, y) | ||
| drawer.bezierCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5], data[i + 6]) | ||
| i += 7 | ||
| break | ||
| case Q: //quadraticCurveTo(x1, y1, x, y) | ||
| drawer.quadraticCurveTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4]) | ||
| i += 5 | ||
| break | ||
| case Z: //closepath() | ||
| drawer.closePath() | ||
| i += 1 | ||
| break | ||
| // canvas command | ||
| case N: // rect(x, y, width, height) | ||
| drawer.rect(data[i + 1], data[i + 2], data[i + 3], data[i + 4]) | ||
| i += 5 | ||
| break | ||
| case D: // roundRect(x, y, width, height, radius1, radius2, radius3, radius4) | ||
| 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]]) | ||
| i += 9 | ||
| break | ||
| case X: // simple roundRect(x, y, width, height, radius) | ||
| drawer.roundRect(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]) | ||
| i += 6 | ||
| break | ||
| case G: // ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| i += 9 | ||
| break | ||
| case F: // simple ellipse(x, y, radiusX, radiusY) | ||
| drawer.ellipse(data[i + 1], data[i + 2], data[i + 3], data[i + 4], 0, 0, PI2, false) | ||
| i += 5 | ||
| break | ||
| case O: // arc(x, y, radius, startAngle, endAngle, anticlockwise) | ||
| 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) | ||
| i += 7 | ||
| break | ||
| case P: // simple arc(x, y, radius) | ||
| drawer.arc(data[i + 1], data[i + 2], data[i + 3], 0, PI2, false) | ||
| i += 4 | ||
| break | ||
| case U: // arcTo(x1, y1, x2, y2, radius) | ||
| drawer.arcTo(data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]) | ||
| i += 6 | ||
| break | ||
| default: | ||
| debug.error(`command: ${command} [index:${i}]`, data) | ||
| return | ||
| } | ||
| } | ||
| } | ||
| } |
| import { IPathCommandData, IPathCreator } from '@leafer/interface' | ||
| export const PathHelper = { | ||
| // index.ts rewrite | ||
| creator: {} as IPathCreator, | ||
| parse(_pathString: string, _curveMode?: boolean): IPathCommandData { return undefined }, | ||
| convertToCanvasData(_old: IPathCommandData, _curveMode?: boolean): IPathCommandData { return undefined } | ||
| } |
| import { IPathDrawer } from '@leafer/interface' | ||
| import { MathHelper } from '@leafer/math' | ||
| export const RectHelper = { | ||
| drawRoundRect(drawer: IPathDrawer, x: number, y: number, width: number, height: number, cornerRadius: number | number[]): void { | ||
| let [topLeft, topRight, bottomRight, bottomLeft] = MathHelper.fourNumber(cornerRadius) | ||
| const max = Math.min(width / 2, height / 2) | ||
| if (topLeft > max) topLeft = max | ||
| if (topRight > max) topRight = max | ||
| if (bottomRight > max) bottomRight = max | ||
| if (bottomLeft > max) bottomLeft = max | ||
| topLeft ? drawer.moveTo(x + topLeft, y) : drawer.moveTo(x, y) | ||
| topRight ? drawer.arcTo(x + width, y, x + width, y + height, topRight) : drawer.lineTo(x + width, y) | ||
| bottomRight ? drawer.arcTo(x + width, y + height, x, y + height, bottomRight) : drawer.lineTo(x + width, y + height) | ||
| bottomLeft ? drawer.arcTo(x, y + height, x, y, bottomLeft) : drawer.lineTo(x, y + height) | ||
| topLeft ? drawer.arcTo(x, y, x + width, y, topLeft) : drawer.lineTo(x, y) | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
9833
-78.9%5
-66.67%106
-88.95%1
Infinity%+ Added
+ Added
- Removed
- Removed
Updated
Updated