thednp / svg-path-commander Goto Github PK
View Code? Open in Web Editor NEWTypescript tools for advanced processing of SVG path data.
Home Page: https://thednp.github.io/svg-path-commander/
License: MIT License
Typescript tools for advanced processing of SVG path data.
Home Page: https://thednp.github.io/svg-path-commander/
License: MIT License
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);
Remove:
svg-path-commander/src/convert/pathToRelative.js
Lines 62 to 67 in fd72105
Move Line 79 to 94:
svg-path-commander/src/convert/pathToRelative.js
Lines 79 to 97 in fd72105
Remove:
svg-path-commander/src/parser/parsePathString.js
Lines 30 to 35 in fd72105
Remove:
Remove:
Remove:
svg-path-commander/src/util/getSegmentAtLength.js
Lines 10 to 12 in fd72105
Remove:
svg-path-commander/src/util/getSegmentOfPoint.js
Lines 11 to 13 in fd72105
I will do some tests later
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.
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"
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.
https://github.com/thednp/svg-path-commander/pull/11/files
svg-path-commander/types/svg-path-commander.d.ts
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?
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
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
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.
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.
I compared SVGPathCommander.getPointAtLength() with the native svg path getPointAtLength().
The results are very different. More so, the results for the points at 0 length, half the length and full length are the same.
here is a codepen showing the results:
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.
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?
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?
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).
<!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
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?
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.
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?
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.
The Node.js example from the readme does not seem to work. Both locally and on RunKit I get the error that DOMMatrix
is not defined. I've added a link to the RunKit example below:
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 :)
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?
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
.
Using the pathToCurve
function to convert 2 consecutive arcs produces a different shape.
Here you can see the result of 2 arcs that form an ellipse converted to cubic beziers:
https://codepen.io/danohlsen/pen/GREgoMo
PS very useful lib BTW! 💙
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"?
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.
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!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.