-
-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Write to svg #102
Comments
Not strictly with the library as is, you could certainly use something like |
Thanks for your reply! Is it possible to apply transformations to objects at object init-time? Like applying rotations to a Line object, so that calling the bbox method returns the correct box of the rotated object? |
Yeah. If you have a SimpleLine you can multiply it by a Matrix or another transformation. >> from svgelements import SimpleLine
>> (SimpleLine((0,0), (100,100)) * "rotate(45deg)").bbox()
(-70.71067811865474, 0.0, 0.0, 70.71067811865476) |
>> SimpleLine((0,0), (100,100), transform="scale(0.1)").bbox()
(0.0, 0.0, 0.0, 10.0) And if you wanted to do something like that during init, it's pretty easy to do there too. They will apply according to the svg spec. >>SimpleLine((0,0), (100,100), transform="scale(0.1) translate(100,200)").bbox()
(10.0, 20.0, 10.0, 30.0) So the translated location is scaled before bbox() is called. That's true for most things, if SVG said how to do that work, that's how it's done. As part of #87 I might eventually get around to writing a full reading, writing, and geometric rendering library. But, depending on your uses this one is likely one of the most complete for parsing and remixing of svg geometries. |
Excellent! Thanks for your help! |
By the way, the stroke_width or stroke_linecap seems to have no effect on the bounding box: In [91]: se.SimpleLine((0,0),(10,10),stroke_linecap="round",stroke_width=100).bbox()
Out[91]: (0.0, 0.0, 0.0, 10.0) which could be added manually to the bbox for simple shapes like Line, but not for a complex Path! |
<svg width="285" height="350" viewBox="0 0 285 350" fill="none" xmlns="http://www.w3.org/2000/svg">
<line id="test-line" x0="0" x1="100" y0="0" y1="100" stroke-width="50" stroke="red"/>
</svg> Then: document.getElementById('test-line').getBBox()
> SVGRect {x: 0, y: 0, width: 100, height: 100} The stroke-width is a paint attribute on the geometry, the getBBox() gets the bounding box of the actual object. If you want your object to actually be a filled fat line within geometry you'd want to convert it to a filled closed shape of the outerpath. In the above test on Chrome you'll see that it had a stroke-width of 50 and thus was well outside the SVGRect() returned but only gave the size of the geometry, which is the correct answer and method. How things paint and things like their line-cap don't actually define the bounding box. If their actual area is within the bbox() not the total area of the paint. |
Also, if you wanted to do this for a path, it wouldn't be that hard. You'd need to do some sampling along the path, finding the normal vector, moving a particular distance from the line on both sides and then use that to define the shape. But, it would be clearly much easier to just add stroke-width to the edges since your value isn't going to exceed that except for perhaps with an line end-cap. |
Could you maybegive me an example how could I convert a SimpleLine to a filled-closed path so that asking the bbox would consider the stroke_width of the line too? |
If you wanted to do it with just a line, you could easily get the normal vector and expand the line outwards. Basically you find the direction that is exactly 90° off the direction of the line. You would then expand that shape outwards in both directions by stroke_width/2 and in theory you'd have geometry that would be expected be equal to that line with a stroke width. It wouldn't, however, take into account, endcap with is another property of drawing things where the line gets a end bit, either none or butt or miter etc. But, you would have accounted for the width there. I don't, however have code to calculate the normal or tangent vectors of a shape. This code does exist within svgpathtools but I didn't manage to port it over or see a reason to do so at the time. This code base is much more concerned with correctly parsing svg and pathtools is more about math. And you'd mostly need to sample the normal vector at certainly positions in order to do this operation. Along a line the correct answer is usually pretty easy. arctan2(y2-y1,x2-x1) + 90° would give you the angle then you could simply use polar coords It gets progressively harder in other shapes but svgpathtools has a demonstration of the offset code there. And while it might be a shame to take the svgelements path and take the .d() value and then put that in an svgpathtools path just to take find a sampling of the normal vector as you need to make an offset curve, it's the only way I currently know how to do right away. |
Many thanks! Still one last question on this: how could I expand the lines pathd outwards? |
I ended up rotating a rect to the angle of the line instead of expanding the shape outwards: from math import atan2, hypot
import svgwrite as s
import svgelements as se
D = s.drawing.Drawing(filename="/tmp/asd.svg", size=(1000, 1000), debug=True)
x1,y1,x2,y2=10,35,30,20
a=atan2(y2-y1,x2-x1)
thickness =10
ry=y1 - thickness*.5
r=se.Rect(x1,ry,hypot(x2-x1, y2-y1),thickness)*f"rotate({a}rad {x1} {y1})"
# A simpleline to test
D.add(s.path.Path(d=se.SimpleLine(x1,y1,x2,y2).d(), stroke=s.utils.rgb( 0, 0, 205)))
D.add(s.path.Path(d=r.d(),fill=s.utils.rgb(100,0,0,"%")))
D.save(pretty=True) This seems to give me what I was looking for. Thanks for your help again! |
That's some nifty math. Line is a bit easy to do since your normal vector stays the same, though you get some heftier math with curves. I think a order 3 bezier curve needs to be properly offset with an order 10 curve. Though there's some very simple sampling techniques you can do simply that. Clever trick replacing the line with a rotated rect of a given thickness. In fact, you could set your |
That's exactly why I wanted the Rect; it looks like a line plus, I have all nice bbox information even by simulating the line-cap with rx/ry. |
is it also possible to write elements to a svg file?
The text was updated successfully, but these errors were encountered: