svg-path-commander 1.0.5 → 2.0.0-alpha2
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/README.md +26 -4
- package/dist/svg-path-commander.cjs +2 -0
- package/dist/svg-path-commander.cjs.map +1 -0
- package/dist/svg-path-commander.d.ts +454 -0
- package/dist/svg-path-commander.js +2 -3762
- package/dist/svg-path-commander.js.map +1 -0
- package/dist/svg-path-commander.mjs +1194 -0
- package/dist/svg-path-commander.mjs.map +1 -0
- package/package.json +39 -42
- package/src/convert/pathToAbsolute.ts +101 -0
- package/src/convert/{pathToCurve.js → pathToCurve.ts} +12 -14
- package/src/convert/pathToRelative.ts +92 -0
- package/src/convert/pathToString.ts +17 -0
- package/src/{svg-path-commander.js → index.ts} +143 -66
- package/src/interface.ts +129 -0
- package/src/math/distanceSquareRoot.ts +13 -0
- package/src/math/midPoint.ts +16 -0
- package/src/math/{polygonArea.js → polygonArea.ts} +6 -4
- package/src/math/{polygonLength.js → polygonLength.ts} +5 -3
- package/src/math/rotateVector.ts +16 -0
- package/src/options/options.ts +9 -0
- package/src/parser/error.ts +2 -0
- package/src/parser/finalizeSegment.ts +31 -0
- package/src/parser/{invalidPathValue.js → invalidPathValue.ts} +0 -0
- package/src/parser/{isArcCommand.js → isArcCommand.ts} +5 -4
- package/src/parser/isDigit.ts +12 -0
- package/src/parser/isDigitStart.ts +14 -0
- package/src/parser/isPathCommand.ts +28 -0
- package/src/parser/isSpace.ts +23 -0
- package/src/parser/paramsCount.ts +16 -0
- package/src/parser/paramsParser.ts +14 -0
- package/src/parser/{parsePathString.js → parsePathString.ts} +13 -7
- package/src/parser/pathParser.ts +29 -0
- package/src/parser/{scanFlag.js → scanFlag.ts} +8 -5
- package/src/parser/{scanParam.js → scanParam.ts} +12 -11
- package/src/parser/{scanSegment.js → scanSegment.ts} +9 -5
- package/src/parser/{skipSpaces.js → skipSpaces.ts} +5 -3
- package/src/process/{arcToCubic.js → arcToCubic.ts} +42 -28
- package/src/process/fixArc.ts +23 -0
- package/src/process/getSVGMatrix.ts +70 -0
- package/src/process/lineToCubic.ts +17 -0
- package/src/process/{normalizePath.js → normalizePath.ts} +10 -10
- package/src/process/normalizeSegment.ts +47 -0
- package/src/process/{optimizePath.js → optimizePath.ts} +18 -21
- package/src/process/{projection2d.js → projection2d.ts} +16 -15
- package/src/process/quadToCubic.ts +31 -0
- package/src/process/reverseCurve.ts +21 -0
- package/src/process/reversePath.ts +101 -0
- package/src/process/roundPath.ts +29 -0
- package/src/process/segmentToCubic.ts +46 -0
- package/src/process/shortenSegment.ts +79 -0
- package/src/process/splitCubic.ts +28 -0
- package/src/process/{splitPath.js → splitPath.ts} +9 -8
- package/src/process/{transformPath.js → transformPath.ts} +55 -48
- package/src/types.ts +193 -0
- package/src/util/getClosestPoint.ts +15 -0
- package/src/util/{getDrawDirection.js → getDrawDirection.ts} +7 -4
- package/src/util/getPathArea.ts +70 -0
- package/src/util/{getPathBBox.js → getPathBBox.ts} +16 -5
- package/src/util/getPointAtLength.ts +14 -0
- package/src/util/{getPropertiesAtLength.js → getPropertiesAtLength.ts} +28 -19
- package/src/util/{getPropertiesAtPoint.js → getPropertiesAtPoint.ts} +16 -12
- package/src/util/getSegmentAtLength.ts +15 -0
- package/src/util/getSegmentOfPoint.ts +18 -0
- package/src/util/{getTotalLength.js → getTotalLength.ts} +6 -4
- package/src/util/isAbsoluteArray.ts +18 -0
- package/src/util/{isCurveArray.js → isCurveArray.ts} +6 -4
- package/src/util/{isNormalizedArray.js → isNormalizedArray.ts} +4 -2
- package/src/util/isPathArray.ts +19 -0
- package/src/util/isPointInStroke.ts +15 -0
- package/src/util/isRelativeArray.ts +18 -0
- package/src/util/{isValidPath.js → isValidPath.ts} +5 -4
- package/src/util/{pathLengthFactory.js → pathLengthFactory.ts} +38 -31
- package/src/util/{segmentArcFactory.js → segmentArcFactory.ts} +71 -55
- package/src/util/segmentCubicFactory.ts +114 -0
- package/src/util/{segmentLineFactory.js → segmentLineFactory.ts} +10 -8
- package/src/util/{segmentQuadFactory.js → segmentQuadFactory.ts} +47 -32
- package/src/util/shapeToPath.ts +214 -0
- package/dist/svg-path-commander.es5.js +0 -3875
- package/dist/svg-path-commander.es5.min.js +0 -2
- package/dist/svg-path-commander.esm.js +0 -3754
- package/dist/svg-path-commander.esm.min.js +0 -2
- package/dist/svg-path-commander.min.js +0 -2
- package/src/convert/pathToAbsolute.js +0 -86
- package/src/convert/pathToRelative.js +0 -84
- package/src/convert/pathToString.js +0 -14
- package/src/index.js +0 -10
- package/src/math/distanceSquareRoot.js +0 -14
- package/src/math/epsilon.js +0 -8
- package/src/math/midPoint.js +0 -13
- package/src/math/rotateVector.js +0 -14
- package/src/options/options.js +0 -10
- package/src/parser/error.js +0 -2
- package/src/parser/finalizeSegment.js +0 -28
- package/src/parser/isDigit.js +0 -9
- package/src/parser/isDigitStart.js +0 -13
- package/src/parser/isPathCommand.js +0 -25
- package/src/parser/isSpace.js +0 -16
- package/src/parser/paramsCount.js +0 -9
- package/src/parser/paramsParser.js +0 -8
- package/src/parser/pathParser.js +0 -24
- package/src/process/clonePath.js +0 -9
- package/src/process/fixArc.js +0 -21
- package/src/process/fixPath.js +0 -31
- package/src/process/getSVGMatrix.js +0 -61
- package/src/process/lineToCubic.js +0 -30
- package/src/process/normalizeSegment.js +0 -45
- package/src/process/quadToCubic.js +0 -22
- package/src/process/reverseCurve.js +0 -18
- package/src/process/reversePath.js +0 -89
- package/src/process/roundPath.js +0 -26
- package/src/process/segmentToCubic.js +0 -46
- package/src/process/shortenSegment.js +0 -58
- package/src/process/splitCubic.js +0 -26
- package/src/util/getClosestPoint.js +0 -12
- package/src/util/getPathArea.js +0 -47
- package/src/util/getPointAtLength.js +0 -12
- package/src/util/getSegmentAtLength.js +0 -11
- package/src/util/getSegmentOfPoint.js +0 -12
- package/src/util/isAbsoluteArray.js +0 -14
- package/src/util/isPathArray.js +0 -14
- package/src/util/isPointInStroke.js +0 -13
- package/src/util/isRelativeArray.js +0 -14
- package/src/util/segmentCubicFactory.js +0 -97
- package/src/util/shapeToPath.js +0 -204
- package/src/util/util.js +0 -82
- package/src/version.js +0 -8
- package/types/index.d.ts +0 -120
- package/types/more/modules.ts +0 -82
- package/types/more/svg.d.ts +0 -211
- package/types/svg-path-commander.d.ts +0 -1089
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import arcToCubic from './arcToCubic';
|
|
2
|
+
import quadToCubic from './quadToCubic';
|
|
3
|
+
import lineToCubic from './lineToCubic';
|
|
4
|
+
import type { CSegment, MSegment, PathSegment } from '../types';
|
|
5
|
+
import type { ParserParams } from '../interface';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts any segment to C (cubic-bezier).
|
|
9
|
+
*
|
|
10
|
+
* @param segment the source segment
|
|
11
|
+
* @param params the source segment parameters
|
|
12
|
+
* @returns the cubic-bezier segment
|
|
13
|
+
*/
|
|
14
|
+
const segmentToCubic = (segment: PathSegment, params: ParserParams): MSegment | CSegment => {
|
|
15
|
+
const [pathCommand] = segment;
|
|
16
|
+
const values = segment.slice(1).map(Number);
|
|
17
|
+
const [x, y] = values;
|
|
18
|
+
let args;
|
|
19
|
+
const { x1: px1, y1: py1, x: px, y: py } = params;
|
|
20
|
+
|
|
21
|
+
if (!'TQ'.includes(pathCommand)) {
|
|
22
|
+
params.qx = null;
|
|
23
|
+
params.qy = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (pathCommand === 'M') {
|
|
27
|
+
params.x = x;
|
|
28
|
+
params.y = y;
|
|
29
|
+
return segment;
|
|
30
|
+
} else if (pathCommand === 'A') {
|
|
31
|
+
args = [px1, py1, ...values] as [number, number, number, number, number, number, number, number, number];
|
|
32
|
+
return ['C', ...arcToCubic(...args)] as CSegment;
|
|
33
|
+
} else if (pathCommand === 'Q') {
|
|
34
|
+
params.qx = x;
|
|
35
|
+
params.qy = y;
|
|
36
|
+
args = [px1, py1, ...values] as [number, number, number, number, number, number];
|
|
37
|
+
return ['C', ...quadToCubic(...args)] as CSegment;
|
|
38
|
+
} else if (pathCommand === 'L') {
|
|
39
|
+
return ['C', ...lineToCubic(px1, py1, x, y)] as CSegment;
|
|
40
|
+
} else if (pathCommand === 'Z') {
|
|
41
|
+
return ['C', ...lineToCubic(px1, py1, px, py)] as CSegment;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return segment as MSegment | CSegment;
|
|
45
|
+
};
|
|
46
|
+
export default segmentToCubic;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ParserParams } from 'src/interface';
|
|
2
|
+
import type {
|
|
3
|
+
AbsoluteSegment,
|
|
4
|
+
HSegment,
|
|
5
|
+
NormalSegment,
|
|
6
|
+
PathCommand,
|
|
7
|
+
ShortSegment,
|
|
8
|
+
SSegment,
|
|
9
|
+
TSegment,
|
|
10
|
+
VSegment,
|
|
11
|
+
ZSegment,
|
|
12
|
+
} from '../types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shorten a single segment of a `pathArray` object.
|
|
16
|
+
*
|
|
17
|
+
* @param segment the `absoluteSegment` object
|
|
18
|
+
* @param normalSegment the `normalSegment` object
|
|
19
|
+
* @param params the coordinates of the previous segment
|
|
20
|
+
* @param prevCommand the path command of the previous segment
|
|
21
|
+
* @returns the shortened segment
|
|
22
|
+
*/
|
|
23
|
+
const shortenSegment = (
|
|
24
|
+
segment: AbsoluteSegment,
|
|
25
|
+
normalSegment: NormalSegment,
|
|
26
|
+
params: ParserParams,
|
|
27
|
+
prevCommand: PathCommand,
|
|
28
|
+
): ShortSegment => {
|
|
29
|
+
const [pathCommand] = segment;
|
|
30
|
+
const round4 = (n: number) => Math.round(n * 10 ** 4) / 10 ** 4;
|
|
31
|
+
const segmentValues = segment.slice(1).map(n => +n);
|
|
32
|
+
const normalValues = normalSegment.slice(1).map(n => +n);
|
|
33
|
+
const { x1: px1, y1: py1, x2: px2, y2: py2, x: px, y: py } = params;
|
|
34
|
+
let result = segment;
|
|
35
|
+
const [x, y] = normalValues.slice(-2);
|
|
36
|
+
|
|
37
|
+
if (!'TQ'.includes(pathCommand)) {
|
|
38
|
+
// optional but good to be cautious
|
|
39
|
+
params.qx = null;
|
|
40
|
+
params.qy = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (['V', 'H', 'S', 'T', 'Z'].includes(pathCommand)) {
|
|
44
|
+
result = [pathCommand, ...segmentValues] as VSegment | HSegment | SSegment | TSegment | ZSegment;
|
|
45
|
+
} else if (pathCommand === 'L') {
|
|
46
|
+
if (round4(px) === round4(x)) {
|
|
47
|
+
result = ['V', y];
|
|
48
|
+
} else if (round4(py) === round4(y)) {
|
|
49
|
+
result = ['H', x];
|
|
50
|
+
}
|
|
51
|
+
} else if (pathCommand === 'C') {
|
|
52
|
+
const [x1, y1] = normalValues;
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
'CS'.includes(prevCommand) &&
|
|
56
|
+
((round4(x1) === round4(px1 * 2 - px2) && round4(y1) === round4(py1 * 2 - py2)) ||
|
|
57
|
+
(round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))
|
|
58
|
+
) {
|
|
59
|
+
result = ['S', ...normalValues.slice(-4)] as SSegment;
|
|
60
|
+
}
|
|
61
|
+
params.x1 = x1;
|
|
62
|
+
params.y1 = y1;
|
|
63
|
+
} else if (pathCommand === 'Q') {
|
|
64
|
+
const [qx, qy] = normalValues;
|
|
65
|
+
params.qx = qx;
|
|
66
|
+
params.qy = qy;
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
'QT'.includes(prevCommand) &&
|
|
70
|
+
((round4(qx) === round4(px1 * 2 - px2) && round4(qy) === round4(py1 * 2 - py2)) ||
|
|
71
|
+
(round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))
|
|
72
|
+
) {
|
|
73
|
+
result = ['T', ...normalValues.slice(-2)] as TSegment;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result as ShortSegment;
|
|
78
|
+
};
|
|
79
|
+
export default shortenSegment;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import midPoint from '../math/midPoint';
|
|
2
|
+
import type { CubicSegment } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Split a cubic-bezier segment into two.
|
|
6
|
+
*
|
|
7
|
+
* @param pts the cubic-bezier parameters
|
|
8
|
+
* @return two new cubic-bezier segments
|
|
9
|
+
*/
|
|
10
|
+
const splitCubic = (pts: number[] /* , ratio */): [CubicSegment, CubicSegment] => {
|
|
11
|
+
const t = /* ratio || */ 0.5;
|
|
12
|
+
const p0 = pts.slice(0, 2) as [number, number];
|
|
13
|
+
const p1 = pts.slice(2, 4) as [number, number];
|
|
14
|
+
const p2 = pts.slice(4, 6) as [number, number];
|
|
15
|
+
const p3 = pts.slice(6, 8) as [number, number];
|
|
16
|
+
const p4 = midPoint(p0, p1, t);
|
|
17
|
+
const p5 = midPoint(p1, p2, t);
|
|
18
|
+
const p6 = midPoint(p2, p3, t);
|
|
19
|
+
const p7 = midPoint(p4, p5, t);
|
|
20
|
+
const p8 = midPoint(p5, p6, t);
|
|
21
|
+
const p9 = midPoint(p7, p8, t);
|
|
22
|
+
|
|
23
|
+
return [
|
|
24
|
+
['C', ...p4, ...p7, ...p9],
|
|
25
|
+
['C', ...p8, ...p6, ...p3],
|
|
26
|
+
];
|
|
27
|
+
};
|
|
28
|
+
export default splitCubic;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
import type { PathArray } from '../types';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Split a path into an `Array` of sub-path strings.
|
|
3
5
|
*
|
|
4
6
|
* In the process, values are converted to absolute
|
|
5
7
|
* for visual consistency.
|
|
6
8
|
*
|
|
7
|
-
* @param
|
|
9
|
+
* @param pathInput the source `pathArray`
|
|
8
10
|
* @return {SVGPath.pathArray[]} an array with all sub-path strings
|
|
9
11
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/** @type {SVGPath.pathArray} */
|
|
14
|
-
let path;
|
|
12
|
+
const splitPath = (pathInput: PathArray): PathArray[] => {
|
|
13
|
+
const composite = [] as PathArray[];
|
|
14
|
+
let path: PathArray;
|
|
15
15
|
let pi = -1;
|
|
16
16
|
|
|
17
|
-
pathInput.forEach(
|
|
17
|
+
pathInput.forEach(seg => {
|
|
18
18
|
if (seg[0] === 'M') {
|
|
19
19
|
path = [seg];
|
|
20
20
|
pi += 1;
|
|
@@ -25,4 +25,5 @@ export default function splitPath(pathInput) {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
return composite;
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
|
+
export default splitPath;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import normalizePath from './normalizePath';
|
|
2
|
-
import clonePath from './clonePath';
|
|
3
2
|
import pathToAbsolute from '../convert/pathToAbsolute';
|
|
4
3
|
import segmentToCubic from './segmentToCubic';
|
|
5
4
|
import fixArc from './fixArc';
|
|
@@ -7,6 +6,8 @@ import getSVGMatrix from './getSVGMatrix';
|
|
|
7
6
|
import projection2d from './projection2d';
|
|
8
7
|
import paramsParser from '../parser/paramsParser';
|
|
9
8
|
import defaultOptions from '../options/options';
|
|
9
|
+
import type { PathArray, PathCommand, TransformObjectValues } from '../types';
|
|
10
|
+
import type { PathTransform, TransformObject } from '../interface';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Apply a 2D / 3D transformation to a `pathArray` instance.
|
|
@@ -14,17 +15,24 @@ import defaultOptions from '../options/options';
|
|
|
14
15
|
* Since *SVGElement* doesn't support 3D transformation, this function
|
|
15
16
|
* creates a 2D projection of the <path> element.
|
|
16
17
|
*
|
|
17
|
-
* @param
|
|
18
|
-
* @param
|
|
19
|
-
* @returns
|
|
18
|
+
* @param path the `pathArray` to apply transformation
|
|
19
|
+
* @param transform the transform functions `Object`
|
|
20
|
+
* @returns the resulted `pathArray`
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
let x = 0;
|
|
22
|
+
const transformPath = (path: string | PathArray, transform?: Partial<TransformObject>): PathArray => {
|
|
23
|
+
let x = 0;
|
|
24
|
+
let y = 0;
|
|
25
|
+
let i;
|
|
26
|
+
let j;
|
|
27
|
+
let ii;
|
|
28
|
+
let jj;
|
|
29
|
+
let lx;
|
|
30
|
+
let ly;
|
|
23
31
|
const absolutePath = pathToAbsolute(path);
|
|
24
32
|
const transformProps = transform && Object.keys(transform);
|
|
25
33
|
|
|
26
34
|
// when used as a static method, invalidate somehow
|
|
27
|
-
if (!transform || !transformProps.length) return
|
|
35
|
+
if (!transform || (transformProps && !transformProps.length)) return [...absolutePath];
|
|
28
36
|
|
|
29
37
|
const normalizedPath = normalizePath(absolutePath);
|
|
30
38
|
// transform origin is extremely important
|
|
@@ -32,16 +40,14 @@ export default function transformPath(path, transform) {
|
|
|
32
40
|
const { origin: defaultOrigin } = defaultOptions;
|
|
33
41
|
Object.assign(transform, { origin: defaultOrigin });
|
|
34
42
|
}
|
|
35
|
-
const matrixInstance = getSVGMatrix(transform);
|
|
43
|
+
const matrixInstance = getSVGMatrix(transform as TransformObjectValues);
|
|
36
44
|
const { origin } = transform;
|
|
37
45
|
const params = { ...paramsParser };
|
|
38
|
-
/** @type {SVGPath.pathSegment} */
|
|
39
46
|
let segment = [];
|
|
40
47
|
let seglen = 0;
|
|
41
48
|
let pathCommand = '';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const allPathCommands = []; // needed for arc to curve transformation
|
|
49
|
+
let transformedPath = [] as PathTransform[];
|
|
50
|
+
const allPathCommands = [] as PathCommand[]; // needed for arc to curve transformation
|
|
45
51
|
|
|
46
52
|
if (!matrixInstance.isIdentity) {
|
|
47
53
|
for (i = 0, ii = absolutePath.length; i < ii; i += 1) {
|
|
@@ -54,7 +60,7 @@ export default function transformPath(path, transform) {
|
|
|
54
60
|
// we don't have any scripting know-how on 3d ellipse transformation
|
|
55
61
|
// Arc segments don't work 3D transformations or skews
|
|
56
62
|
/// ////////////////////////////////////////
|
|
57
|
-
allPathCommands[i] = pathCommand;
|
|
63
|
+
allPathCommands[i] = pathCommand as PathCommand;
|
|
58
64
|
|
|
59
65
|
if (pathCommand === 'A') {
|
|
60
66
|
segment = segmentToCubic(normalizedPath[i], params);
|
|
@@ -73,49 +79,50 @@ export default function transformPath(path, transform) {
|
|
|
73
79
|
|
|
74
80
|
params.x1 = +segment[seglen - 2];
|
|
75
81
|
params.y1 = +segment[seglen - 1];
|
|
76
|
-
params.x2 = +
|
|
77
|
-
params.y2 = +
|
|
82
|
+
params.x2 = +segment[seglen - 4] || params.x1;
|
|
83
|
+
params.y2 = +segment[seglen - 3] || params.y1;
|
|
78
84
|
|
|
79
|
-
/** @type {SVGPath.pathTransformList} */
|
|
80
85
|
const result = {
|
|
81
|
-
s: absolutePath[i],
|
|
86
|
+
s: absolutePath[i],
|
|
87
|
+
c: absolutePath[i][0],
|
|
88
|
+
x: params.x1,
|
|
89
|
+
y: params.y1,
|
|
82
90
|
};
|
|
83
91
|
|
|
84
92
|
transformedPath = [...transformedPath, ...[result]];
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
return transformedPath.map(
|
|
95
|
+
return transformedPath.map(seg => {
|
|
88
96
|
pathCommand = seg.c;
|
|
89
97
|
segment = seg.s;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return segment;
|
|
98
|
+
if (pathCommand === 'L' || pathCommand === 'H' || pathCommand === 'V') {
|
|
99
|
+
[lx, ly] = projection2d(matrixInstance, [seg.x, seg.y], origin as [number, number, number]);
|
|
100
|
+
|
|
101
|
+
/* istanbul ignore else */
|
|
102
|
+
if (x !== lx && y !== ly) {
|
|
103
|
+
segment = ['L', lx, ly];
|
|
104
|
+
} else if (y === ly) {
|
|
105
|
+
segment = ['H', lx];
|
|
106
|
+
} else if (x === lx) {
|
|
107
|
+
segment = ['V', ly];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// now update x and y
|
|
111
|
+
x = lx;
|
|
112
|
+
y = ly;
|
|
113
|
+
|
|
114
|
+
return segment;
|
|
115
|
+
} else {
|
|
116
|
+
for (j = 1, jj = segment.length; j < jj; j += 2) {
|
|
117
|
+
[x, y] = projection2d(matrixInstance, [+segment[j], +segment[j + 1]], origin as [number, number, number]);
|
|
118
|
+
segment[j] = x;
|
|
119
|
+
segment[j + 1] = y;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return segment;
|
|
117
123
|
}
|
|
118
|
-
});
|
|
124
|
+
}) as PathArray;
|
|
119
125
|
}
|
|
120
|
-
return
|
|
121
|
-
}
|
|
126
|
+
return [...absolutePath];
|
|
127
|
+
};
|
|
128
|
+
export default transformPath;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { LineAttr, CircleAttr, PolyAttr, RectAttr, EllipseAttr, GlyphAttr, TransformObject } from './interface';
|
|
2
|
+
|
|
3
|
+
export type SpaceNumber =
|
|
4
|
+
| 0x1680
|
|
5
|
+
| 0x180e
|
|
6
|
+
| 0x2000
|
|
7
|
+
| 0x2001
|
|
8
|
+
| 0x2002
|
|
9
|
+
| 0x2003
|
|
10
|
+
| 0x2004
|
|
11
|
+
| 0x2005
|
|
12
|
+
| 0x2006
|
|
13
|
+
| 0x2007
|
|
14
|
+
| 0x2008
|
|
15
|
+
| 0x2009
|
|
16
|
+
| 0x200a
|
|
17
|
+
| 0x202f
|
|
18
|
+
| 0x205f
|
|
19
|
+
| 0x3000
|
|
20
|
+
| 0xfeff
|
|
21
|
+
| 0x0a
|
|
22
|
+
| 0x0d
|
|
23
|
+
| 0x2028
|
|
24
|
+
| 0x2029
|
|
25
|
+
| 0x20
|
|
26
|
+
| 0x09
|
|
27
|
+
| 0x0b
|
|
28
|
+
| 0x0c
|
|
29
|
+
| 0xa0
|
|
30
|
+
| 0x1680;
|
|
31
|
+
|
|
32
|
+
export type PathCommandNumber = 0x6d | 0x7a | 0x6c | 0x68 | 0x76 | 0x63 | 0x73 | 0x71 | 0x74 | 0x61;
|
|
33
|
+
|
|
34
|
+
export type DigitNumber = 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x36 | 0x37 | 0x38 | 0x39;
|
|
35
|
+
|
|
36
|
+
// custom types
|
|
37
|
+
export type MCommand = 'M';
|
|
38
|
+
export type mCommand = 'm';
|
|
39
|
+
|
|
40
|
+
export type LCommand = 'L';
|
|
41
|
+
export type lCommand = 'l';
|
|
42
|
+
|
|
43
|
+
export type VCommand = 'V';
|
|
44
|
+
export type vCommand = 'v';
|
|
45
|
+
|
|
46
|
+
export type HCommand = 'H';
|
|
47
|
+
export type hCommand = 'h';
|
|
48
|
+
|
|
49
|
+
export type ZCommand = 'Z';
|
|
50
|
+
export type zCommand = 'z';
|
|
51
|
+
|
|
52
|
+
export type CCommand = 'C';
|
|
53
|
+
export type cCommand = 'c';
|
|
54
|
+
|
|
55
|
+
export type SCommand = 'S';
|
|
56
|
+
export type sCommand = 's';
|
|
57
|
+
|
|
58
|
+
export type QCommand = 'Q';
|
|
59
|
+
export type qCommand = 'q';
|
|
60
|
+
|
|
61
|
+
export type TCommand = 'T';
|
|
62
|
+
export type tCommand = 't';
|
|
63
|
+
|
|
64
|
+
export type ACommand = 'A';
|
|
65
|
+
export type aCommand = 'a';
|
|
66
|
+
|
|
67
|
+
export type AbsoluteCommand =
|
|
68
|
+
| MCommand
|
|
69
|
+
| LCommand
|
|
70
|
+
| VCommand
|
|
71
|
+
| HCommand
|
|
72
|
+
| ZCommand
|
|
73
|
+
| CCommand
|
|
74
|
+
| SCommand
|
|
75
|
+
| QCommand
|
|
76
|
+
| TCommand
|
|
77
|
+
| ACommand;
|
|
78
|
+
|
|
79
|
+
export type RelativeCommand =
|
|
80
|
+
| mCommand
|
|
81
|
+
| lCommand
|
|
82
|
+
| vCommand
|
|
83
|
+
| hCommand
|
|
84
|
+
| zCommand
|
|
85
|
+
| cCommand
|
|
86
|
+
| sCommand
|
|
87
|
+
| qCommand
|
|
88
|
+
| tCommand
|
|
89
|
+
| aCommand;
|
|
90
|
+
|
|
91
|
+
export type PathCommand = AbsoluteCommand | RelativeCommand;
|
|
92
|
+
|
|
93
|
+
export type MSegment = [MCommand, number, number];
|
|
94
|
+
export type mSegment = [mCommand, number, number];
|
|
95
|
+
export type MoveSegment = MSegment | mSegment;
|
|
96
|
+
|
|
97
|
+
export type LSegment = [LCommand, number, number];
|
|
98
|
+
export type lSegment = [lCommand, number, number];
|
|
99
|
+
export type LineSegment = LSegment | lSegment;
|
|
100
|
+
|
|
101
|
+
export type VSegment = [VCommand, number];
|
|
102
|
+
export type vSegment = [vCommand, number];
|
|
103
|
+
export type VertLineSegment = vSegment | VSegment;
|
|
104
|
+
|
|
105
|
+
export type HSegment = [HCommand, number];
|
|
106
|
+
export type hSegment = [hCommand, number];
|
|
107
|
+
export type HorLineSegment = HSegment | hSegment;
|
|
108
|
+
|
|
109
|
+
export type ZSegment = [ZCommand];
|
|
110
|
+
export type zSegment = [zCommand];
|
|
111
|
+
export type CloseSegment = ZSegment | zSegment;
|
|
112
|
+
|
|
113
|
+
export type CSegment = [CCommand, number, number, number, number, number, number];
|
|
114
|
+
export type cSegment = [cCommand, number, number, number, number, number, number];
|
|
115
|
+
export type CubicSegment = CSegment | cSegment;
|
|
116
|
+
|
|
117
|
+
export type SSegment = [SCommand, number, number, number, number];
|
|
118
|
+
export type sSegment = [sCommand, number, number, number, number];
|
|
119
|
+
export type ShortCubicSegment = SSegment | sSegment;
|
|
120
|
+
|
|
121
|
+
export type QSegment = [QCommand, number, number, number, number];
|
|
122
|
+
export type qSegment = [qCommand, number, number, number, number];
|
|
123
|
+
export type QuadSegment = QSegment | qSegment;
|
|
124
|
+
|
|
125
|
+
export type TSegment = [TCommand, number, number];
|
|
126
|
+
export type tSegment = [tCommand, number, number];
|
|
127
|
+
export type ShortQuadSegment = TSegment | tSegment;
|
|
128
|
+
|
|
129
|
+
export type ASegment = [ACommand, number, number, number, number, number, number, number];
|
|
130
|
+
export type aSegment = [aCommand, number, number, number, number, number, number, number];
|
|
131
|
+
export type ArcSegment = ASegment | aSegment;
|
|
132
|
+
|
|
133
|
+
export type PathSegment =
|
|
134
|
+
| MoveSegment
|
|
135
|
+
| LineSegment
|
|
136
|
+
| VertLineSegment
|
|
137
|
+
| HorLineSegment
|
|
138
|
+
| CloseSegment
|
|
139
|
+
| CubicSegment
|
|
140
|
+
| ShortCubicSegment
|
|
141
|
+
| QuadSegment
|
|
142
|
+
| ShortQuadSegment
|
|
143
|
+
| ArcSegment;
|
|
144
|
+
|
|
145
|
+
export type ShortSegment = VertLineSegment | HorLineSegment | ShortCubicSegment | ShortQuadSegment | CloseSegment;
|
|
146
|
+
|
|
147
|
+
export type AbsoluteSegment =
|
|
148
|
+
| MSegment
|
|
149
|
+
| LSegment
|
|
150
|
+
| VSegment
|
|
151
|
+
| HSegment
|
|
152
|
+
| CSegment
|
|
153
|
+
| SSegment
|
|
154
|
+
| QSegment
|
|
155
|
+
| TSegment
|
|
156
|
+
| ASegment
|
|
157
|
+
| ZSegment;
|
|
158
|
+
|
|
159
|
+
export type RelativeSegment =
|
|
160
|
+
| mSegment
|
|
161
|
+
| lSegment
|
|
162
|
+
| vSegment
|
|
163
|
+
| hSegment
|
|
164
|
+
| cSegment
|
|
165
|
+
| sSegment
|
|
166
|
+
| qSegment
|
|
167
|
+
| tSegment
|
|
168
|
+
| aSegment
|
|
169
|
+
| zSegment;
|
|
170
|
+
|
|
171
|
+
export type NormalSegment = MSegment | LSegment | CSegment | QSegment | ASegment | ZSegment;
|
|
172
|
+
|
|
173
|
+
export type PathArray = [MSegment | mSegment, ...PathSegment[]];
|
|
174
|
+
export type AbsoluteArray = [MSegment, ...AbsoluteSegment[]];
|
|
175
|
+
export type RelativeArray = [MSegment, ...RelativeSegment[]];
|
|
176
|
+
export type NormalArray = [MSegment, ...NormalSegment[]];
|
|
177
|
+
export type CurveArray = [MSegment, ...CSegment[]];
|
|
178
|
+
export type PolygonArray = [MSegment, ...LSegment[], ZSegment];
|
|
179
|
+
export type PolylineArray = [MSegment, ...LSegment[]];
|
|
180
|
+
|
|
181
|
+
export type ShapeTypes =
|
|
182
|
+
| SVGPolylineElement
|
|
183
|
+
| SVGPolygonElement
|
|
184
|
+
| SVGLineElement
|
|
185
|
+
| SVGEllipseElement
|
|
186
|
+
| SVGCircleElement
|
|
187
|
+
| SVGRectElement;
|
|
188
|
+
|
|
189
|
+
export type ShapeTags = 'line' | 'polyline' | 'polygon' | 'ellipse' | 'circle' | 'rect' | 'glyph';
|
|
190
|
+
|
|
191
|
+
export type ShapeOps = LineAttr | PolyAttr | PolyAttr | EllipseAttr | CircleAttr | RectAttr | GlyphAttr;
|
|
192
|
+
|
|
193
|
+
export type TransformObjectValues = Partial<TransformObject> & { origin: [number, number, number] };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PathArray } from '../types';
|
|
2
|
+
import getPropertiesAtPoint from './getPropertiesAtPoint';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the point in path closest to a given point.
|
|
6
|
+
*
|
|
7
|
+
* @param pathInput target `pathArray`
|
|
8
|
+
* @param point the given point
|
|
9
|
+
* @returns the best match
|
|
10
|
+
*/
|
|
11
|
+
const getClosestPoint = (pathInput: string | PathArray, point: { x: number; y: number }): { x: number; y: number } => {
|
|
12
|
+
return getPropertiesAtPoint(pathInput, point).closest;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default getClosestPoint;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import getPathArea from './getPathArea';
|
|
2
2
|
import pathToCurve from '../convert/pathToCurve';
|
|
3
|
+
import type { PathArray } from '../types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Check if a path is drawn clockwise and returns true if so,
|
|
6
7
|
* false otherwise.
|
|
7
8
|
*
|
|
8
|
-
* @param
|
|
9
|
-
* @returns
|
|
9
|
+
* @param path the path string or `pathArray`
|
|
10
|
+
* @returns true when clockwise or false if not
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
const getDrawDirection = (path: string | PathArray) => {
|
|
12
13
|
return getPathArea(pathToCurve(path)) >= 0;
|
|
13
|
-
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default getDrawDirection;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import pathToCurve from '../convert/pathToCurve';
|
|
2
|
+
import type { PathArray } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the area of a single cubic-bezier segment.
|
|
6
|
+
*
|
|
7
|
+
* http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
|
|
8
|
+
*
|
|
9
|
+
* @param x1 the starting point X
|
|
10
|
+
* @param y1 the starting point Y
|
|
11
|
+
* @param c1x the first control point X
|
|
12
|
+
* @param c1y the first control point Y
|
|
13
|
+
* @param c2x the second control point X
|
|
14
|
+
* @param c2y the second control point Y
|
|
15
|
+
* @param x2 the ending point X
|
|
16
|
+
* @param y2 the ending point Y
|
|
17
|
+
* @returns the area of the cubic-bezier segment
|
|
18
|
+
*/
|
|
19
|
+
const getCubicSegArea = (
|
|
20
|
+
x1: number,
|
|
21
|
+
y1: number,
|
|
22
|
+
c1x: number,
|
|
23
|
+
c1y: number,
|
|
24
|
+
c2x: number,
|
|
25
|
+
c2y: number,
|
|
26
|
+
x2: number,
|
|
27
|
+
y2: number,
|
|
28
|
+
) => {
|
|
29
|
+
return (
|
|
30
|
+
(3 *
|
|
31
|
+
((y2 - y1) * (c1x + c2x) -
|
|
32
|
+
(x2 - x1) * (c1y + c2y) +
|
|
33
|
+
c1y * (x1 - c2x) -
|
|
34
|
+
c1x * (y1 - c2y) +
|
|
35
|
+
y2 * (c2x + x1 / 3) -
|
|
36
|
+
x2 * (c2y + y1 / 3))) /
|
|
37
|
+
20
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the area of a shape.
|
|
43
|
+
*
|
|
44
|
+
* @author Jürg Lehni & Jonathan Puckey
|
|
45
|
+
*
|
|
46
|
+
* @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
|
|
47
|
+
*
|
|
48
|
+
* @param path the shape `pathArray`
|
|
49
|
+
* @returns the length of the cubic-bezier segment
|
|
50
|
+
*/
|
|
51
|
+
const getPathArea = (path: PathArray) => {
|
|
52
|
+
let x = 0;
|
|
53
|
+
let y = 0;
|
|
54
|
+
let len = 0;
|
|
55
|
+
|
|
56
|
+
return pathToCurve(path)
|
|
57
|
+
.map(seg => {
|
|
58
|
+
switch (seg[0]) {
|
|
59
|
+
case 'M':
|
|
60
|
+
[, x, y] = seg;
|
|
61
|
+
return 0;
|
|
62
|
+
default:
|
|
63
|
+
len = getCubicSegArea(x, y, ...(seg.slice(1) as [number, number, number, number, number, number]));
|
|
64
|
+
[x, y] = seg.slice(-2) as [number, number];
|
|
65
|
+
return len;
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.reduce((a, b) => a + b, 0);
|
|
69
|
+
};
|
|
70
|
+
export default getPathArea;
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
import { PathBBox } from 'src/interface';
|
|
2
|
+
import { PathArray } from 'src/types';
|
|
1
3
|
import pathLengthFactory from './pathLengthFactory';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Returns the bounding box of a shape.
|
|
5
7
|
*
|
|
6
|
-
* @param
|
|
7
|
-
* @returns
|
|
8
|
+
* @param path the shape `pathArray`
|
|
9
|
+
* @returns the length of the cubic-bezier segment
|
|
8
10
|
*/
|
|
9
|
-
|
|
11
|
+
const getPathBBox = (path?: PathArray | string): PathBBox => {
|
|
10
12
|
if (!path) {
|
|
11
13
|
return {
|
|
12
|
-
x: 0,
|
|
14
|
+
x: 0,
|
|
15
|
+
y: 0,
|
|
16
|
+
width: 0,
|
|
17
|
+
height: 0,
|
|
18
|
+
x2: 0,
|
|
19
|
+
y2: 0,
|
|
20
|
+
cx: 0,
|
|
21
|
+
cy: 0,
|
|
22
|
+
cz: 0,
|
|
13
23
|
};
|
|
14
24
|
}
|
|
15
25
|
|
|
@@ -33,4 +43,5 @@ export default function getPathBBox(path) {
|
|
|
33
43
|
// an estimted guess
|
|
34
44
|
cz: Math.max(width, height) + Math.min(width, height) / 2,
|
|
35
45
|
};
|
|
36
|
-
}
|
|
46
|
+
};
|
|
47
|
+
export default getPathBBox;
|