mirror of https://github.com/kivy/kivy.git
Add svg rotation transform support (#8170)
* Add svg rotation transform support Fixes #8166 - ChatGPT wrote the matrix transformations. I merely fixed type and import errors and verified the code works. * Adding some simple screenshot-based Svg tests * Simplify rotated example svg * Clean up unnecessary lines
This commit is contained in:
parent
e37e3b4dc0
commit
7f23e32441
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" standalone="no"?><svg width="256" height="256"
|
||||
viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M102,42 Q 165,22 59 48 T 197 164" stroke="green" stroke-width="5.0"
|
||||
fill="none" stroke-opacity="0.375" transform="" />
|
||||
<path d="M102,42 Q 165,22 59 48 T 197 164" stroke="green" stroke-width="5.0"
|
||||
fill="none" stroke-opacity="0.375" transform=" rotate(120.0 128 128)" />
|
||||
<path d="M102,42 Q 165,22 59 48 T 197 164" stroke="green" stroke-width="5.0"
|
||||
fill="none" stroke-opacity="0.375" transform=" rotate(240.0 128 128)" />
|
||||
|
||||
<rect stroke="blue" stroke-width="3.75" x="24" y="98" width="59" height="132"
|
||||
fill="none" stroke-opacity="0.75" transform="" />
|
||||
<rect stroke="blue" stroke-width="3.75" x="24" y="98" width="59" height="132"
|
||||
fill="none" stroke-opacity="0.75" transform=" rotate(-120.0 128 128)" />
|
||||
<rect stroke="blue" stroke-width="3.75" x="24" y="98" width="59" height="132"
|
||||
fill="none" stroke-opacity="0.75" transform=" rotate(-240.0 128 128)" />
|
||||
|
||||
<path d="M6,39 Q 230,177 256 149 T 37 164" stroke="red" stroke-width="1.75"
|
||||
fill="none" stroke-opacity="0.625" transform="" />
|
||||
<path d="M6,39 Q 230,177 256 149 T 37 164" stroke="red" stroke-width="1.75"
|
||||
fill="none" stroke-opacity="0.625" transform=" rotate(120.0 128 128)" />
|
||||
<path d="M6,39 Q 230,177 256 149 T 37 164" stroke="red" stroke-width="1.75"
|
||||
fill="none" stroke-opacity="0.625" transform=" rotate(240.0 128 128)" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -24,6 +24,7 @@ __all__ = ("Svg", )
|
|||
|
||||
include "common.pxi"
|
||||
|
||||
import math
|
||||
import re
|
||||
cimport cython
|
||||
from xml.etree.cElementTree import parse
|
||||
|
@ -229,6 +230,21 @@ cdef class Matrix(object):
|
|||
print("SVG: unknown how to parse: {!r}".format(value))
|
||||
self.mat[0] = <float>float(a)
|
||||
self.mat[3] = <float>float(b)
|
||||
elif string.startswith('rotate('):
|
||||
value = parse_list(string[7:-1])
|
||||
angle = <float>float(value[0])
|
||||
if len(value) == 3:
|
||||
cx, cy = map(float, value[1:])
|
||||
else:
|
||||
cx = cy = 0
|
||||
cos_a = math.cos(math.radians(angle))
|
||||
sin_a = math.sin(math.radians(angle))
|
||||
self.mat[0] = cos_a
|
||||
self.mat[1] = sin_a
|
||||
self.mat[2] = -sin_a
|
||||
self.mat[3] = cos_a
|
||||
self.mat[4] = -cx * cos_a + cy * sin_a + cx
|
||||
self.mat[5] = -cx * sin_a - cy * cos_a + cy
|
||||
elif string is not None:
|
||||
i = 0
|
||||
for f in string:
|
||||
|
@ -386,7 +402,7 @@ cdef class Svg(RenderContext):
|
|||
:param color the default color to use for Svg elements that specify "currentColor"
|
||||
|
||||
.. note:: if you want to use SVGs from string, you can parse the source yourself
|
||||
using `from xml.etree.cElementTree import fromstring` and pass the result to
|
||||
using `from xml.etree.cElementTree import fromstring` and pass the result to
|
||||
Svg().set_tree(). This will trigger the rendering of the Svg - as an alternative
|
||||
to assigning a filepath to Svg.source. This is also viable to trigger reloading.
|
||||
|
||||
|
@ -526,7 +542,7 @@ cdef class Svg(RenderContext):
|
|||
def set_tree(self, tree):
|
||||
'''
|
||||
sets the tree used to render the Svg and triggers reloading.
|
||||
|
||||
|
||||
:param xml.etree.cElementTree tree: the tree parsed from the SVG source
|
||||
|
||||
.. versionadded:: 2.0.0
|
||||
|
@ -937,7 +953,7 @@ cdef class Svg(RenderContext):
|
|||
x_ = cp * dx + sp * dy
|
||||
y_ = -sp * dx + cp * dy
|
||||
r2 = (((rx * ry)**2 - (rx * y_)**2 - (ry * x_)**2)/
|
||||
((rx * y_)**2 + (ry * x_)**2))
|
||||
((rx * y_)**2 + (ry * x_)**2))
|
||||
if r2 < 0: r2 = 0
|
||||
r = sqrt(r2)
|
||||
if large_arc == sweep:
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
'''
|
||||
Svg tests
|
||||
==============
|
||||
|
||||
Testing Svg rendering.
|
||||
'''
|
||||
|
||||
from kivy.tests.common import GraphicUnitTest
|
||||
|
||||
|
||||
SIMPLE_SVG = """<?xml version="1.0" standalone="no"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<rect stroke="blue" stroke-width="4" x="24" y="30" width="92" height="166"
|
||||
fill="none" stroke-opacity="0.5" />
|
||||
</svg>
|
||||
"""
|
||||
|
||||
SCALE_SVG = """<?xml version="1.0" standalone="no"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<rect stroke="red" stroke-width="4" x="24" y="30" width="10" height="10"
|
||||
fill="none" stroke-opacity="0.5" transform="scale(2, 3)"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
ROTATE_SVG = """<?xml version="1.0" standalone="no"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<rect stroke="green" stroke-width="4" x="24" y="30" width="50" height="100"
|
||||
stroke-opacity="0.75" transform="rotate(60 128 128)" />
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
class SvgTest(GraphicUnitTest):
|
||||
|
||||
def test_simple(self):
|
||||
import xml.etree.ElementTree as ET
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.graphics.svg import Svg
|
||||
|
||||
# create a root widget
|
||||
wid = Widget()
|
||||
|
||||
# put some graphics instruction on it
|
||||
with wid.canvas:
|
||||
svg = Svg()
|
||||
svg.set_tree(ET.ElementTree(ET.fromstring(SIMPLE_SVG)))
|
||||
|
||||
# render, and capture it directly
|
||||
self.render(wid)
|
||||
|
||||
def test_scale(self):
|
||||
import xml.etree.ElementTree as ET
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.graphics.svg import Svg
|
||||
|
||||
# create a root widget
|
||||
wid = Widget()
|
||||
|
||||
# put some graphics instruction on it
|
||||
with wid.canvas:
|
||||
svg = Svg()
|
||||
svg.set_tree(ET.ElementTree(ET.fromstring(SCALE_SVG)))
|
||||
|
||||
# render, and capture it directly
|
||||
self.render(wid)
|
||||
|
||||
def test_rotate(self):
|
||||
import xml.etree.ElementTree as ET
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.graphics.svg import Svg
|
||||
|
||||
# create a root widget
|
||||
wid = Widget()
|
||||
|
||||
# put some graphics instruction on it
|
||||
with wid.canvas:
|
||||
svg = Svg()
|
||||
svg.set_tree(ET.ElementTree(ET.fromstring(ROTATE_SVG)))
|
||||
|
||||
# render, and capture it directly
|
||||
self.render(wid)
|
Loading…
Reference in New Issue