Git Product home page Git Product logo

thednp / svg-path-commander Goto Github PK

View Code? Open in Web Editor NEW
207.0 207.0 18.0 3.67 MB

Typescript tools for advanced processing of SVG path data.

Home Page: https://thednp.github.io/svg-path-commander/

License: MIT License

JavaScript 6.14% TypeScript 83.13% HTML 10.73%
absolute-values convert-to-path javascript normalize-path optimize-path path-reverse path-segments relative-values shape-draw-direction shorthand-commands svg transform-functions typescript typescript-library

svg-path-commander's People

Contributors

alecjacobson avatar anasrar avatar calvin-ll avatar gingerchris avatar thednp avatar tombigel avatar visualfox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

svg-path-commander's Issues

Support custom `document` on `shapeToPath`

This is allow you to use shapeToPath on jsdom

const { document } = new JSDOM(
  `<html>
  <head></head>
  <body>
    <svg id="mySVG" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
      <rect id="myRect" x="0" width="100" height="100" rx="15" />
    </svg>
  </body>
</html>`,
  {
    pretendToBeVisual: true,
  }
).window;

const myRect = document.getElementById('myRect');
SVGPathCommander.shapeToPath(myRect, true, document);

Code Review

Remove:

// this branch makes no sense anymore
// if (relativeCommand === 'm') {
// [x, y] = values;
// mx = x;
// my = y;
// }

Move Line 79 to 94:

const segLength = relativeSegment.length;
switch (relativeCommand) {
case 'z':
x = mx;
y = my;
break;
case 'h':
// @ts-ignore
x += relativeSegment[1];
break;
case 'v':
// @ts-ignore
y += relativeSegment[1];
break;
default:
// @ts-ignore
x += relativeSegment[segLength - 2];
// @ts-ignore
y += relativeSegment[segLength - 1];

Remove:

// if (!path.err.length) {
// if (!'mM'.includes(path.segments[0][0])) {
// path.err = `${error}: missing M/m`;
// } else {
// path.segments[0][0] = 'M';
// }

Remove:

// import fixPath from '../process/fixPath';

Remove:

// const path = fixPath(parsePathString(pathInput));

Remove:

// const props = getPropertiesAtLength(pathInput, distance);
// const { segment } = typeof props !== 'undefined' ? props : { segment: null };
// return segment;

Remove:

// const props = getPropertiesAtPoint(path, point);
// const { segment } = props;
// return typeof segment !== 'undefined' ? segment.segment : null;

I will do some tests later

getSVGMatrix not exposed

Previously you had stated this function could be imported as such

import SVGPathCommander from 'svg-path-commander';
const { getSVGMatrix } = SVGPathCommander;

but that didn't work and, looking at the code, it appears as though you have not exported it in any way.

Trim string before converting shape to path

If there are two spaces at the end of the points-string, it convertes it to 0 0z which results in a wrong path.

Current

<polygon id='poly' fill="#F7CD48" points="15,29.5 32,32 32,17 15,17  "/>
to:
<path id='polynew' fill="#F7CD48" d="M15 29.5L32 32L32 17L15 17L0 0z"/>

Expected

<polygon id='poly' fill="#F7CD48" points="15,29.5 32,32 32,17 15,17  "/>
to:
<path id='polynew' fill="#F7CD48" d="M15 29.5L32 32L32 17L15 17Lz"/>

If the spaces are removed it works:
from points="15,29.5 32,32 32,17 15,17 " to points="15,29.5 32,32 32,17 15,17"

Demo: https://jsfiddle.net/8rfwm5yv/

Inverted "flipX()" and "flipY()"

svg-path-commander.js#L252

flipX() and flipY() should be inverted, flipX() is flipping the y coordinates and flipY() the x coordinates ... Not a big deal, but worth mentioning.

Great library by the way, I'm using it to create a small SVG path editor so big thanks.

Transformation of the path containing the arch

The error in the size of the element after the transformation of the path containing the arch reaches 0.2-0.04.

for path d="M297.334,355.734 A2,2 0 0 1 294.667,353.848 A15,15 0 0 1 324.667,353.848 A15,15 0 0 1 294.667,353.848 "
heigth 30.
After transform:
const transform = {
origin:[0,0],
scale: [1.004529989207172, 1] ,
translate:[ -11.871495599548004, 0]
}
SVGPathCommander(path, {round:'false'}).optimize().transform(transform).toString();
heigth is 30.044 ??
Transformation along the y axis does not lead to such an effect. The size remains 30.
Is this within the margin of error? Or are there any ways to avoid this?

shapeToPath can't infer RectAttr type as param

Using this example from the readme:

const myRectAttr = {
	type: 'rect',
	x: 25,
	y: 25,
	width: 50,
	height: 50,
	rx: 5
};

const myRectPath = SVGPathCommander.shapeToPath(myRectAttr);

Results in this type error:

Argument of type '{ type: string; x: number; y: number; width: number; height: number; rx: number; }' is not assignable to parameter of type 'ShapeTypes | ShapeOps'.
  Property 'd' is missing in type '{ type: string; x: number; y: number; width: number; height: number; rx: number; }' but required in type 'GlyphAttr'.ts(2345)

Annotating myRectAttr with const myRectAttr: RectAttr solves the issue, but it seems like an unfortunate requirement.

I tried adding ry to the keys of myRectAttr, but this resulted in the same type error.

const myRectAttr = {
	type: 'rect',
	x: 25,
	y: 25,
	width: 50,
	height: 50,
	rx: 5,
	ry: 5
};

const myRectPath = SVGPathCommander.shapeToPath(myRectAttr);

(same error)

Edit to add:
typescript: 4.9.5
svg-path-commander: 2.0.4

SVGPathCommander.transform() return the path that has 'NaN'

Hello,
I try to transfrom SVG path. my code is like this,

const path = new SVGPathCommander(svgPath, {});
const pathMatrix: SVGPathCommander.transformObject = {
  translate: [x, y],
  rotate: angle,
  origin: [0, 0],
};
path.transform(pathMatrix);

The path has NaN position value.
I found that the function projection2D(m, point2D, origin) divide number by 0 when originZ is 0. I think it cause NaN value.
When I use random number on z value of origin like this origin: [0, 0, {random number}], it doesn't make NaN

flipX - origin is not iterable: Related to using in Vue 3 component?

I'm simply trying to execute the example code:

import SVGPathCommander from 'svg-path-commander';

const path = 'M0 0 L50 100';

const flippedPathString = new SVGPathCommander(path).flipX().toString();

but receive console error "origin is not iterable." Is this related to executing inside a Vue component? On the demo page (https://thednp.github.io/svg-path-commander/index.html), I can paste my data in, perform transformations, and receive expected output.

default origin documentation

path command transformations are all consistent with the SVG coordinates system, where others compute transform origin only for rotation transformation.

I found it a bit confusing that the default origin is something other than [0, 0] as in the DOM. That being said, it's not clear where the default origin is in relation to the bounding box given this statement.

can shapeToPath return a PathArray or SVGPathCommander?

I'd like to be able to call shapeToPath and chain it with transform, like this:

SVGPathCommander.shapeToPath({
	type: 'rect',
	x: center[0],
	y: center[1],
	width,
	height,
	rx: radius,
	ry: radius
})
.transform({
	origin: [0, 0],
	translate: [x, y],
})

However, it appears shapeToPath returns a <path ... > element instead of a PathArray or SVGPathCommander, which means I cannot modify the result using all of the helpful transform functions, afaict.

path commands (ex. MCommand, LCommand ..) are not exported

Hello, I'm using this library with typescript.
I trying to edit segments with path commands.
I can edit width path command string (ex. 'M', 'L', 'C' ..). I think that using path commands is looks better.
However path commands are not exoprted to index.d.ts, only absoluteCommand and relativeCommand are exported.
Is it intended exporting?

toRelative does not convert absolute moveTo commands in additional segments

The following path string has two segments
M473.29 335.29C470.49 352.29 454.98 365.29 437.59 367.03S408.52 358.61 411.33 341.59S429.55 311.59 446.85 309.87S476 318.28 473.29 335.29Z M 452.24 337.43C454.3 324.92 454.3 313.08 446.24 313.9S434.34 326.95 432.29 339.46S430.29 363.79 438.29 362.98S450.18 349.94 452.24 337.43Z

The output of SVGPathCommander(pathString).toRelative() is:

M473.29 335.29c-2.8 17 -18.31 30 -35.7 31.74s-29.07 -8.42 -26.26 -25.44s18.22 -30 35.52 -31.72s29.15 8.41 26.44 25.42zM452.24 337.43c2.06 -12.51 2.06 -24.35 -6 -23.53s-11.9 13.05 -13.95 25.56s-2 24.33 6 23.52s11.89 -13.04 13.95 -25.55z

The second segment starts from an absolute M rather than a relative one.
Is this the intended behavior?

Unsupported implicit `lineTo` when using additional `moveTo` parameters

Here is the doc for the relative moveTo:

Move the current point by shifting the last known position of the path by dx along the x-axis and by dy along the y-axis.
Any subsequent coordinate pair(s) are interpreted as parameter(s) for implicit relative LineTo (l) command(s) (see below).

Replication

<!DOCTYPE html>
<html>
<head>
	<title>SVG recaling</title>
	<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/svg-path-commander.min.js"></script>
</head>
<body>
<p id="myPathString"></p>
</body>
<script type="text/javascript">
	const path = "m133.67 263.88-28.317-13.209-26.934 15.839 3.8116-31.013-23.387-20.722 30.673-5.9585 12.48-28.646 15.145 27.33 31.1 3.0176-21.313 22.85z";
	const transform = {
	  origin: [0,0],
	  scale: 1 // uniform scale on X, Y, Z axis
	};
	// This will output:
	// M133.67 263.88M105.353 250.671M78.419 266.51M82.2306 235.497M58.8436 214.775M89.5166 208.8165M101.9966 180.1705M117.1416 207.5005M148.2416 210.5181M126.9286 233.3681Z
	const transformed2DPathString = new SVGPathCommander(path).transform(transform).toString();
	document.getElementById('myPathString').innerText = transformed2DPathString;
</script>
</html>

The following path describes a star shape (one of Inkscape's primitives):

m133.67 263.88
  -28.317-13.209
  -26.934 15.839 
  3.8116-31.013
  -23.387-20.722 
  30.673-5.9585 
  12.48-28.646 
  15.145 27.33 
  31.1 3.0176
  -21.313 22.85
  z

However, it uses the implicit lineTo mentionned in the doc. Thus, it is equivalent to that:

m133.67 263.88
  l -28.317-13.209
  l -26.934 15.839 
  l 3.8116-31.013
  l -23.387-20.722 
  l 30.673-5.9585 
  l 12.48-28.646 
  l 15.145 27.33 
  l 31.1 3.0176
  l -21.313 22.85
  z

However, SVG Path Commander, when asked to rescale it, will convert every subsequent pair of moveTo parameter into new moveTo commands, which is not the behaviour described in the doc. Hence, the above, when scaled by scale: 1 will become:

M133.67 263.88
M105.353 250.671
M78.419 266.51
M82.2306 235.497
M58.8436 214.775
M89.5166 208.8165
M101.9966 180.1705
M117.1416 207.5005
M148.2416 210.5181
M126.9286 233.3681Z

Which doesn't draw anything (it just moves the starting point around). Instead, it should look like this:

M133.67 263.88
105.353 250.671
78.419 266.51
82.2306 235.497
58.8436 214.775
89.5166 208.8165
101.9966 180.1705
117.1416 207.5005
148.2416 210.5181
126.9286 233.3681Z

which is the same as

M133.67 263.88
L 105.353 250.671
L 78.419 266.51
L 82.2306 235.497
L 58.8436 214.775
L 89.5166 208.8165
L 101.9966 180.1705
L 117.1416 207.5005
L 148.2416 210.5181
L 126.9286 233.3681Z

Apply a matrix transform to a path

Reading the API all the ingredients for applying a matrix transform to a path should be there, but could an example for this be provided?

I've tried various things, but I get different results compared to what "svgpath" gives me, I'm not sure why.

Maybe a little helper for this should be exposed also, for convenience?

Subdividing paths/curves?

Hi, I'm trying to use this package to convert an SVG into a series of line segments for plotting. The only way I can think of is calling getPointAtLength at different intervals for each path. Would this be a decent way of doing that? I wasn't sure if it may be really inefficient or something.

I also thought about converting each path into a curve and then using something like bezier.js package which has a way to subdivide Bezier curves n times.

Scale transform doesn't take into consideration the origin property

Hi, I noticed these transformations take into consideration the origin:

if (flipY) pathCommander.transform({ rotate: [180, 0, 0], origin: groupCenter });
if (rotate) pathCommander.transform({ rotate, origin: groupCenter });

while this will still scale the path based on its own center instead of the given origin
if (scale) pathCommander.transform({ scale, origin: groupCenter });

Why is that?

create path from stroked shape/path

Is it possible to use this library (or any other that you know of) that can convert a shape like rect, circle, path with stroke properties to a new path that includes the stroke within the path without having stroke properties on the element?

For example, if the original shape is a 2x2 square with a stroke width of 1 the new path would draw a 3x3 square.

Transforms on g elements

So I'm really really loving this plugin but it's kinda limited.
Sometimes when doing different transforms on the same path points, it's better to apply them to a group instead. This way in the final svg there will be more repeated d attributes (same path) and the overall file will be compressed better (gzip will benefit from repetitions).
Is there any way to take advantage of the module for this?
In particular I love the fact that it's SSR and I can easily scale o flip paths around their own center without having to deal with transform-box and its compatibility issues. But still I need to apply the transforms in the group non in the path itself, do you think is possible to save some of the work done on the path transforming and bring it to the g element maybe using the matrix transform?
What's really annoying is in that case we can set the origin of the transform only for the rotate operation, but maybe with the Matrix transforms there's some difference? I'm not sure.

Any help is really appreaciated :)

How to apply 4x4 matrix using SVG path commander

I have a 4x4 matrix for use with CSS matrix3d but I'm not sure how this translates to the 3d transformation in SVG path commander:

matrix3d(
  1.2198581560283686, 0, 0, 0.00042118420695089805,
  0, 1, 0, 0,
  0, 0, 1, 0,
  0, 0, 0, 1
)

The documentation shows this:

let transformObject = {
  origin: [205,205,205], // transform origin assumes the shape bounding box is 410 wide and 410 tall
  scale: [0.3,0.3,0.3],  // all axes scale
  translate: [20,0,0],   // translateX
  rotate:[0,0,45],       // rotateZ
  skew:[20,0]            // skewX
}

let myPathString = new SVGPathCommander('M0 0L0 0')
                      .transform(transformObject)
                      .toString()

The final column of the matrix3d matrix appears to be perspective, is this input compatible with svg-path-commander?

Feature Request clostestPoint & segmentOfPoint

I searched through the internet but didn't found a library that supports clostestPoint and segmentOfPoint. I don't use your library (no need for this functions) but I saw that you are reagular committing and that you have already many nice features, so maybe you are interessed.

For clostestPoint I found that code (scroll down until function clostestPoint). It is working well.

I also would need the start and end point of the segment, so I would need segmentOfPoint but I haven't found solutions without the deprecated pathSegList.

Unable to import and use default export in vite/sveltekit

I'm trying to understand the cause of the following error when I import SVGPathCommander from "svg-path-commander" in a sveltekit project:

TypeError: __vite_ssr_import_1__.default is not a constructor
    at +page.svelte:4:8
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1973:22)
    at Object.default (root.svelte:42:40)
    at eval (/node_modules/@sveltejs/kit/src/runtime/components/layout.svelte:8:41)
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1973:22)
    at root.svelte:41:39
    at $$render (/node_modules/svelte/internal/index.mjs:1973:22)
    at Object.render (/node_modules/svelte/internal/index.mjs:1981:26)
    at Module.render_response (/node_modules/@sveltejs/kit/src/runtime/server/page/render.js:180:29)
    at async Module.render_page (/node_modules/@sveltejs/kit/src/runtime/server/page/index.js:285:10)

I've created a test project / minimal replication here, which is simply a skeleton sveltekit project with the above import added:

https://github.com/canadaduane/test-not-a-constructor

It's almost like sveltekit can't see the esnext exported module. Is there additional configuration needed, or should it "just work"?

support native DOMMatrix and/or CSSMatrix for transform

SVGPathCommander can use the DOMMatrix API for SVGPathElement path command transformation and implements a very fast and modernized DOMMatrix shim.

It would be helpful if the transform function would accept the same type as the parameter to CSSMatrix.fromMatrix so that native DOMMatrix objects or CSSMatrix could be used. They are useful for rendering transform strings when sharing transforms between SVGPathCommander and native SVG transform attributes.

The getSVGMatrix utility we developed will always compute the matrix by applying the transform functions in the following order: translate, rotate, skew and scale, which is the default composition/recomposition order specified in the W3C draft

My IDE attempted to import this function as such

import getSVGMatrix from 'svg-path-commander/src/process/getSVGMatrix';

but that crashed my build ("vite": "^3.2.6")

 [plugin vite:dep-scan] Missing "./src/process/getSVGMatrix" export in "svg-path-commander" package

    src/widgets/PyramidNet/models/PyramidNetWidgetStore.tsx:18:25:
      18 │ import getSVGMatrix from 'svg-path-commander/src/process/getSVGMatrix';
         ╵                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  This error came from the "onResolve" callback registered here:

    node_modules/esbuild/lib/main.js:1260:20:
      1260 │       let promise = setup({
           ╵                     ^

So I had to copy the file into my project and write a wrapper to do the transformation as such

export function convertTransformObjectToDOMMatrixReadOnly(trans: Partial<TransformObject>): DOMMatrixReadOnly {
  const cssMatrix = getSVGMatrix({
    ...trans,
    origin: [0, 0, 0],
  });
  const arr = CSSMatrix.toArray(cssMatrix, true) as Matrix;
  return new DOMMatrixReadOnly(arr);
}

P.S. Thanks for making this, one of my projects has a fluent interface for building path data and I decided to give this a try for parsing instead of svgpath. It's got some extra features that seem quite useful.

About the lineToCubic algorithm.

In the current lineToCubic algorithm, the cp1 is at 1/2 between start and end points, and cp2 is at end point: https://github.com/thednp/svg-path-commander/blob/master/src/process/lineToCubic.ts
If please I would like to ask why not cp1 at 1/3 and cp2 at 2/3 between stat and end points?

Besides, Could you please tell me where is the old lineToCubic algorithm (https://github.com/thednp/svg-path-commander/blob/8bf14ca186cc87f8a687f9737421d3bcbad91388/src/process/lineToCubic.js) comes from, it seems rather complicated, why you pick it and why you replaced it finally.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.