Merge from lp:~ecmanaut/scour/transform. Optimise negative 3-digit rotation angles to the positive form, and angles over positive 270 to the negative form. Also cath more angles outside of the range [-360, 360].

This commit is contained in:
Louis Simard 2011-03-14 16:56:56 -04:00
commit cd42752cad
5 changed files with 52 additions and 14 deletions

View file

@ -1482,11 +1482,11 @@ def mayContainTextNodes(node):
result = True result = True
# Blacklisted elements. Those are guaranteed not to be text elements. # Blacklisted elements. Those are guaranteed not to be text elements.
elif node.nodeName in ['rect', 'circle', 'ellipse', 'line', 'polygon', elif node.nodeName in ['rect', 'circle', 'ellipse', 'line', 'polygon',
'polyline', 'path', 'image', 'stop']: 'polyline', 'path', 'image', 'stop']:
result = False result = False
# Group elements. If we're missing any here, the default of True is used. # Group elements. If we're missing any here, the default of True is used.
elif node.nodeName in ['g', 'clipPath', 'marker', 'mask', 'pattern', elif node.nodeName in ['g', 'clipPath', 'marker', 'mask', 'pattern',
'linearGradient', 'radialGradient', 'symbol']: 'linearGradient', 'radialGradient', 'symbol']:
result = False result = False
for child in node.childNodes: for child in node.childNodes:
if mayContainTextNodes(child): if mayContainTextNodes(child):
@ -2110,7 +2110,7 @@ def parseListOfPoints(s):
nums = [] nums = []
# also, if 100-100 is found, split it into two also # also, if 100-100 is found, split it into two also
# <polygon points="100,-100,100-100,100-100-100,-100-100" /> # <polygon points="100,-100,100-100,100-100-100,-100-100" />
for i in xrange(len(ws_nums)): for i in xrange(len(ws_nums)):
negcoords = ws_nums[i].split("-") negcoords = ws_nums[i].split("-")
@ -2328,6 +2328,28 @@ def reducePrecision(element) :
return num return num
def optimizeAngle(angle):
"""
Because any rotation can be expressed within 360 degrees
of any given number, and since negative angles sometimes
are one character longer than corresponding positive angle,
we shorten the number to one in the range to [-90, 270[.
"""
# First, we put the new angle in the range ]-360, 360[.
# The modulo operator yields results with the sign of the
# divisor, so for negative dividends, we preserve the sign
# of the angle.
if angle < 0: angle %= -360
else: angle %= 360
# 720 degrees is unneccessary, as 360 covers all angles.
# As "-x" is shorter than "35x" and "-xxx" one character
# longer than positive angles <= 260, we constrain angle
# range to [-90, 270[ (or, equally valid: ]-100, 260]).
if angle >= 270: angle -= 360
elif angle < -90: angle += 360
return angle
def optimizeTransform(transform): def optimizeTransform(transform):
""" """
Optimises a series of transformations parsed from a single Optimises a series of transformations parsed from a single
@ -2401,6 +2423,7 @@ def optimizeTransform(transform):
if len(args) == 2 and args[1] == 0: if len(args) == 2 and args[1] == 0:
del args[1] del args[1]
elif type == 'rotate': elif type == 'rotate':
args[0] = optimizeAngle(args[0]) # angle
# Only the angle is required for rotations. # Only the angle is required for rotations.
# If the coordinates are unspecified, it's the origin (0, 0). # If the coordinates are unspecified, it's the origin (0, 0).
if len(args) == 3 and args[1] == args[2] == 0: if len(args) == 3 and args[1] == args[2] == 0:
@ -2447,14 +2470,7 @@ def optimizeTransform(transform):
elif (currType == prevType == 'rotate' elif (currType == prevType == 'rotate'
and len(prevArgs) == len(currArgs) == 1): and len(prevArgs) == len(currArgs) == 1):
# Only coalesce if both rotations are from the origin. # Only coalesce if both rotations are from the origin.
prevArgs[0] += currArgs[0] # angle prevArgs[0] = optimizeAngle(prevArgs[0] + currArgs[0])
# Put the new angle in the range ]-360, 360[.
# The modulo operator yields results with the sign of the
# divisor, so for negative dividends, we preserve the sign
# of the angle.
if prevArgs[0] < 0: prevArgs[0] = prevArgs[0] % -360
else: prevArgs[0] = prevArgs[0] % 360
del transform[i] del transform[i]
elif currType == prevType == 'scale': elif currType == prevType == 'scale':
prevArgs[0] *= currArgs[0] # x prevArgs[0] *= currArgs[0] # x

View file

@ -1243,9 +1243,9 @@ class TransformRotate90(unittest.TestCase):
class TransformRotateCCW135(unittest.TestCase): class TransformRotateCCW135(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/transform-matrix-is-rotate-neg-135.svg') doc = scour.scourXmlFile('unittests/transform-matrix-is-rotate-225.svg')
self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(-135)', self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(225)',
'Counter-clockwise rotation matrix not converted to rotate(-135)') 'Counter-clockwise rotation matrix not converted to rotate(225)')
class TransformRotateCCW45(unittest.TestCase): class TransformRotateCCW45(unittest.TestCase):
def runTest(self): def runTest(self):
@ -1277,6 +1277,18 @@ class TransformTranslate(unittest.TestCase):
self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'translate(2 3)', self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'translate(2 3)',
'Translation matrix not converted to translate(2 3)') 'Translation matrix not converted to translate(2 3)')
class TransformRotationRange719_5(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/transform-rotate-trim-range-719.5.svg')
self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(-.5)',
'Transform containing rotate(719.5) not shortened to rotate(-.5)')
class TransformRotationRangeCCW540_0(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/transform-rotate-trim-range-neg-540.0.svg')
self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(180)',
'Transform containing rotate(-540.0) not shortened to rotate(180)')
class TransformRotation3Args(unittest.TestCase): class TransformRotation3Args(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/transform-rotate-fold-3args.svg') doc = scour.scourXmlFile('unittests/transform-rotate-fold-3args.svg')

View file

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 293 B

Before After
Before After

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-9 0 9 9">
<line stroke="rgba(255,0,0,0.5)" y1="9" x1="9" transform="rotate(719.5)"/>
<!-- all rotation angles can be mapped to the shorter range [-90, 270[ -->
</svg>

After

Width:  |  Height:  |  Size: 289 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-9 0 9 9">
<line stroke="rgba(255,0,0,0.5)" y1="9" x1="9" transform="rotate(-540.0) rotate(0)"/>
<!-- all rotation angles can be mapped to the shorter range [-90, 270[ -->
</svg>

After

Width:  |  Height:  |  Size: 300 B