Skip to content

Commit

Permalink
Changes:
Browse files Browse the repository at this point in the history
* removed 'auto' option for options.round, the reason is the high duration of getting the path bounding box at initialization;
* the default `origin` option value is no longer 'auto' but [0, 0, 0];
* conversion tools no longer make use of checks (EG: isPathArray) to remove bottlenecks at constructor initialization;
* fixed issue with `pathToRelative` where additional MoveTo segments didn't get converted to relative #40 ;
* fixed a small issue with `pathToAbsolute` with additional  relative `m` segments not properly processed;
* updated splitPath to work with relative arrays
* removed `pathFactory` (an elegant solution but not very fast) in favor of the below mentioned;
* removed `replaceArc` it's no longer needed;
* added new tools for each segment type for calculating length, and bbox as well as finding a point at length;
* fixed a small issue with Arc segments where getting point at given length was also finding points inside the shape and not in stroke;
* further performance improvements #44;
* updated tests;
* updated docs / demo: added path total length, point at length and bbox;
* updated dependencies.
  • Loading branch information
thednp committed Oct 13, 2024
1 parent a7d6372 commit 5e7a3ee
Show file tree
Hide file tree
Showing 48 changed files with 2,501 additions and 1,849 deletions.
62 changes: 59 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![NPM Version](https://img.shields.io/npm/v/svg-path-commander.svg)](https://www.npmjs.com/package/svg-path-commander)
[![NPM Downloads](https://img.shields.io/npm/dm/svg-path-commander.svg)](http://npm-stat.com/charts.html?svg-path-commander)
[![jsDeliver](https://img.shields.io/jsdelivr/npm/hw/svg-path-commander)](https://www.jsdelivr.com/package/npm/svg-path-commander)
[![typescript version](https://img.shields.io/badge/typescript-5.6.2-brightgreen)](https://www.typescriptlang.org/)
[![typescript version](https://img.shields.io/badge/typescript-5.6.3-brightgreen)](https://www.typescriptlang.org/)
[![prettier version](https://img.shields.io/badge/prettier-2.8.8-brightgreen)](https://prettier.io/)
[![eslint version](https://img.shields.io/badge/eslint-8.57.1-brightgreen)](https://github.com/eslint)
[![vitest version](https://img.shields.io/badge/vitest-2.1.2-brightgreen)](https://vitest.dev/)
Expand Down Expand Up @@ -75,7 +75,7 @@ const transform = {
rotate: 15, // Z axis rotation
scale: 0.75, // uniform scale on X, Y, Z axis
skew: 15, // skew 15deg on the X axis
origin: [15, 0] // if not specified, it will calculate a bounding box to determine a proper `transform-origin`
origin: [15, 0] // if not specified, it will use the default origin value [0, 0]
}
const transformed2DPathString = new SVGPathCommander(path).transform(transform).toString();
Expand All @@ -90,6 +90,21 @@ const transform = {
const transformed3DPathString = new SVGPathCommander(path).transform(transform).toString();
```
Access the `bbox` instance property to apply a consistent `transform-origin`:
```js
// apply a 3D transformation with a consistent origin
const transformed3DPath = new SVGPathCommander(path);
const { cx, cy, cz } = transformed3DPath.bbox;
const transform = {
translate: [15, 15, 15], // `[15, 15]` would apply a 2D translation, and only `15` for X axis translation
rotate: [15, 15, 15], // or only "15" for 2D rotation on Z axis
scale: [0.7, 0.75, 0.8], // or only "0.7" for 2D scale on all X, Y, Z axis
skew: [15, 15], // or only "15" for the X axis
origin: [cx, cy, cz] // the origin
}
const transformed3DPathString = transformed3DPath.transform(transform).toString();
```
SVGPathCommander comes with a full range of additional static methods, here's how to normalize a path:
```js
const path = 'M0 0 H50';
Expand All @@ -112,13 +127,53 @@ const myPathString = SVGPathCommander.pathToString([['M', 0, 0], ['L', 50, 0]]);
// result => 'M0 0 L50 0'
```
Check a path string validity:
```js
SVGPathCommander.isValidPath(path);
// result => boolean
```
Check if path is a certain kind of `PathArray`:
```js
SVGPathCommander.isAbsoluteArray([['M', 0, 0], ['L', 50, 0]]);
// result => true
```
Create a custom function to apply a 3D transformation using static methods:
```ts
import { parsePathString, getPathBBox, transformPath, pathToString } from 'svg-path-commander';
function myTransformFn(pathInput: string | PathArray, transformObject: TransformObject) {
const path = parsePathString(pathInput);
const { cx, cy, cz } = getPathBBox(path);
return pathToString(
transformPath(path, {
...transformObject, origin: [cx, cy, cz]
})
)
}
```
In extreme cases where performance is paramount, you can consider the parent SVG `viewBox` attribute to extract a bounding box required for a consistent transform origin.
```ts
// const svgViewBox = document.getElementById('my-svg').getAttribute('viewBox');
const viewBox = '0 0 24 24';
const [x, y, width, height] = viewBox.split(/\s/).map(Number);
const origin = [
x + width / 2, // CX
y + height / 2, // CY
Math.max(width, height) + Math.min(width, height) / 2, // CZ
];
// use this origin for your shape transformation
const myNewString = new SVGPathCommander('M0 0 H50')
.transform({ rotate: [35, 0, 0], origin })
.toString();
```
Convert a shape to `<path>` and transfer all non-specific attributes
```js
const myCircle = document.getElementById('myCircle');
Expand Down Expand Up @@ -213,6 +268,7 @@ For developer guidelines, and a complete list of static methods, head over to th
* James Halliday for his excelent [point-at-length](https://github.com/substack/point-at-length)
* Eric Eastwood for his excelent [svg-curve-lib](https://github.com/MadLittleMods/svg-curve-lib)
* PhET Interactive Simulations for their [kite](https://github.com/phetsims/kite)
* [herrstrietzel](https://github.com/herrstrietzel) for his awesome [svg-pathdata-getbbox](https://github.com/herrstrietzel/svg-pathdata-getbbox)
# License
**SVGPathCommander** is released under [MIT Licence](https://github.com/thednp/svg-path-commander/blob/master/LICENSE).
2 changes: 1 addition & 1 deletion dist/svg-path-commander.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/svg-path-commander.cjs.map

Large diffs are not rendered by default.

138 changes: 113 additions & 25 deletions dist/svg-path-commander.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export type LengthFactory = {
};
};
export type Options = {
round: "auto" | "off" | number;
round: "off" | number;
origin: number[];
};
export type PathTransform = {
Expand Down Expand Up @@ -368,6 +368,82 @@ export type PointTuple = [
number,
number
];
export type DerivedPoint = Point & {
t: number;
};
export type QuadPoints = [
Point,
Point,
Point,
Point,
Point,
Point
];
export type CubicPoints = [
Point,
Point,
Point,
Point,
Point,
Point,
Point,
Point
];
export type DerivedQuadPoints = [
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint
];
export type DerivedCubicPoints = [
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint,
DerivedPoint
];
export type QuadCoordinates = [
number,
number,
number,
number,
number,
number
];
export type CubicCoordinates = [
number,
number,
number,
number,
number,
number,
number,
number
];
export type ArcCoordinates = [
number,
number,
number,
number,
number,
number,
number,
number,
number
];
export type LineCoordinates = [
number,
number,
number,
number
];
export type DeriveCallback = (t: number) => Point;
export type IteratorCallback = (segment: PathSegment, params: ParserParams, index: number) => PathSegment;
/**
* Creates a new SVGPathCommander instance with the following properties:
* * segments: `pathArray`
Expand All @@ -381,31 +457,24 @@ export type PointTuple = [
declare class SVGPathCommander {
static CSSMatrix: typeof CSSMatrix$1;
static getSVGMatrix: (transform: TransformObjectValues) => CSSMatrix$1;
static getPathBBox: (path: PathArray | string) => PathBBox;
static getPathBBox: (pathInput: PathArray | string) => {
x: number;
y: number;
width: number;
height: number;
x2: number;
y2: number;
cx: number;
cy: number;
cz: number;
};
static getPathArea: (path: PathArray) => number;
static getTotalLength: (pathInput: string | PathArray) => number;
static getDrawDirection: (path: string | PathArray) => boolean;
static getPointAtLength: (pathInput: string | PathArray, distance: number) => {
static getPointAtLength: (pathInput: string | PathArray, distance?: number) => {
x: number;
y: number;
};
static pathFactory: (pathInput: string | PathArray, distance?: number) => {
point: {
x: number;
y: number;
};
length: number;
readonly bbox: {
min: {
x: number;
y: number;
};
max: {
x: number;
y: number;
};
};
};
static getPropertiesAtLength: (pathInput: string | PathArray, distance?: number) => SegmentProperties;
static getPropertiesAtPoint: (pathInput: string | PathArray, point: {
x: number;
Expand Down Expand Up @@ -440,16 +509,15 @@ declare class SVGPathCommander {
static parsePathString: (pathInput: string | PathArray) => PathArray;
static roundPath: (path: PathArray, roundOption?: number | "off") => PathArray;
static splitPath: (pathInput: PathArray) => PathArray[];
static splitCubic: (pts: number[]) => [
static splitCubic: (pts: number[], ratio?: number) => [
CubicSegment,
CubicSegment
];
static replaceArc: (pathInput: PathArray | string) => PathArray;
static optimizePath: (pathInput: PathArray, round: "off" | number) => PathArray;
static reverseCurve: (path: CurveArray) => CurveArray;
static reversePath: (pathInput: PathArray) => PathArray;
static normalizePath: (pathInput: string | PathArray) => NormalArray;
static transformPath: (path: string | PathArray, transform?: Partial<TransformObject>) => PathArray;
static transformPath: (pathInput: PathArray | string, transform?: Partial<TransformObject>) => PathArray;
static pathToAbsolute: (pathInput: string | PathArray) => AbsoluteArray;
static pathToRelative: (pathInput: string | PathArray) => RelativeArray;
static pathToCurve: (pathInput: string | PathArray) => CurveArray;
Expand All @@ -467,15 +535,35 @@ declare class SVGPathCommander {
* @param config instance options
*/
constructor(pathValue: string, config?: Partial<Options>);
get bbox(): PathBBox;
get bbox(): {
x: number;
y: number;
width: number;
height: number;
x2: number;
y2: number;
cx: number;
cy: number;
cz: number;
};
get length(): number;
/**
* Returns the path bounding box, equivalent to native `path.getBBox()`.
*
* @public
* @returns the pathBBox
*/
getBBox(): PathBBox;
getBBox(): {
x: number;
y: number;
width: number;
height: number;
x2: number;
y2: number;
cx: number;
cy: number;
cz: number;
};
/**
* Returns the total path length, equivalent to native `path.getTotalLength()`.
*
Expand Down
2 changes: 1 addition & 1 deletion dist/svg-path-commander.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/svg-path-commander.js.map

Large diffs are not rendered by default.

Loading

0 comments on commit 5e7a3ee

Please sign in to comment.