drawsvg
drawsvg copied to clipboard
reversed Y coordinate interacts badly with translate
drawSvg, as mentioned in https://github.com/cduck/drawSvg/issues/11, reverses the direction of the Y coordinate so that increasing values are upwards (like Cartesian coordinates), rather than downwards (like screen position.)
Unfortunately, this means that translate() operations have to explicitly take this into account. Here's an example:
import drawSvg as draw
d = draw.Drawing( 200, 200, (0,0) )
d.append( draw.Circle( 100, 100, 50, fill="none", stroke="black" ) )
g = draw.Group( transform="translate(100,100)" )
g.append( draw.Circle( 0, 0, 40, fill="none", stroke="black" ) )
d.append( g )
d.saveSvg( "example-transform.svg" )
If everything was using the same coordinate system, this should result in two concentric circles, but instead the output is:
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="200" height="200" viewBox="0 -200 200 200">
<defs>
</defs>
<circle cx="100" cy="-100" r="50" fill="none" stroke="black" />
<g transform="translate(100,100)">
<circle cx="0" cy="0" r="40" fill="none" stroke="black" />
</g>
</svg>

Is there a preferred way of specifying translations and other transforms? I didn't find one in the code or examples.
Could the library parse transform attributes and rewrite them to the preferred coordinate system?
Failing that, the documentation should be more clear about the flipped coordinate system and what will and won't work.
Thanks for your input. There currently isn't a preferred way to specify transforms. I like your idea of changing each transform to the coordinate system but I haven't done this because it's a backwards compatibility issue at this point.
Adding scale(1,-1) before and after the transform is probably the best way to manually do this: "scale(1,-1) translate(100,100) scale(1,-1)"
At a temporary solution, here is a monkey patch you can use to get the right behavior:
import drawSvg as draw
# Monkey patch drawSvg so the transform argument uses the same coordinate system
# as the rest of the library
old_init = draw.DrawingBasicElement.__init__
def fix_init(self, **kwargs):
old_init(self, **kwargs)
transform = self.args.get('transform')
if transform is not None:
self.args['transform'] = 'scale(1,-1) {} scale(1,-1)'.format(transform)
draw.DrawingBasicElement.__init__ = fix_init
d = draw.Drawing(...
Hello. Could you tell me the reason for the try: y = -y-height except ... bellow ? There are others on Circle and Ellipse elements regarding y arg.
class Rectangle(DrawingBasicElement):
''' A rectangle
Additional keyword arguments are output as additional arguments to the
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
TAG_NAME = 'rect'
def __init__(self, x, y, width, height, **kwargs):
try:
y = -y-height
except TypeError:
pass
super().__init__(x=x, y=y, width=width, height=height,
**kwargs)
With this try except, to draw properly I must pass y as string, like:
draw.Rectangle( x = 10, y = '0', width = 30, height = 200, fill = '#ff0000' )
Is this correct ?
Best regards, Mário.
That's not the intended way to flip the y-coordinate. See #11 for a better way.
The reason for the try/except was to allow values with units like "50%" to be used.
Hitting this myself - drawSvg has such great potential, there is no equivalent for it - I am doing web tools / web dev and screen coordinates with top, left at 0,0 are natural to think in.
#11 did not work as it flips text
I am using shapely https://shapely.readthedocs.io/en/stable/manual.html to perform the transformations
from shapely import Point, affinity, get_x, get_y
def to_pt(d, x, y):
p = Point(x, y)
pt = affinity.translate(p, -d.width / 2, -d.height / 2)
pt = affinity.scale(pt, 1, -1, 1, (0, 0))
return (get_x(pt), get_y(pt))
example
screen_x = 123
screen_y = 123
(d_x, d_y) = to_pt(d, screen_x, screen_y)
# where d_x and d_y are the screen_x/screen_y in drawSvg's own coordinate system - center of screen with flipped `y`
I leave this here for the next soul to find help. Do still note that the height must still be specified as negative though to match.
Maybe next version of awesome drawSvg would have a configurable coordinate system. It is a great lib, keep-it up!