add copy+increment option to scale to size extension
This commit is contained in:
parent
e4fc012a0b
commit
fdeb67aa82
@ -1,10 +1,10 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Scale To Size (Replaced by default transform scale)",
|
"name": "Scale To Size",
|
||||||
"id": "fablabchemnitz.de.scale_to_size",
|
"id": "fablabchemnitz.de.scale_to_size",
|
||||||
"path": "scale_to_size",
|
"path": "scale_to_size",
|
||||||
"dependent_extensions": null,
|
"dependent_extensions": null,
|
||||||
"original_name": "Scale To Size (Replaced by default transform scale)",
|
"original_name": "Scale To Size",
|
||||||
"original_id": "fablabchemnitz.de.scale_to_size",
|
"original_id": "fablabchemnitz.de.scale_to_size",
|
||||||
"license": "GNU GPL v3",
|
"license": "GNU GPL v3",
|
||||||
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE",
|
"license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
<name>Scale To Size (Replaced by default transform scale)</name>
|
<name>Scale To Size</name>
|
||||||
<id>fablabchemnitz.de.scale_to_size</id>
|
<id>fablabchemnitz.de.scale_to_size</id>
|
||||||
<param name="unit" gui-text="Unit" type="optiongroup" appearance="combo">
|
<param name="unit" gui-text="Unit" type="optiongroup" appearance="combo">
|
||||||
<option value="mm">mm</option>
|
<option value="mm">mm</option>
|
||||||
@ -9,7 +9,9 @@
|
|||||||
<option value="pt">pt</option>
|
<option value="pt">pt</option>
|
||||||
<option value="px">px</option>
|
<option value="px">px</option>
|
||||||
</param>
|
</param>
|
||||||
<param name="expected_size" type="float" min="0.001" max="10000.0" gui-text="Expected size: ">10.0</param>
|
<param name="expected_size" type="float" precision="3" min="0.001" max="10000.000" gui-text="Expected size: ">100.000</param>
|
||||||
|
<param name="copies" type="int" min="0" max="99999" gui-text="Count of copies" gui-description="0 means just the original element is transformed.">0</param>
|
||||||
|
<param name="scale_increment" type="float" precision="3" min="-10000.000" max="10000.000" gui-text="Scale increment (in selected units): ">0.000</param>
|
||||||
<param name="scale_type" type="optiongroup" appearance="combo" gui-text="Scaling type:">
|
<param name="scale_type" type="optiongroup" appearance="combo" gui-text="Scaling type:">
|
||||||
<option value="Horizontal">Horizontal</option>
|
<option value="Horizontal">Horizontal</option>
|
||||||
<option value="Vertical">Vertical</option>
|
<option value="Vertical">Vertical</option>
|
||||||
|
@ -10,6 +10,7 @@ from inkex.paths import CubicSuperPath, Path
|
|||||||
from inkex import Transform
|
from inkex import Transform
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from inkex.styles import Style
|
from inkex.styles import Style
|
||||||
|
import copy
|
||||||
|
|
||||||
# This extension can scale any object or path on X, Y or both axes. This addon is kind of obsolete because you can do the same from transforms menu
|
# This extension can scale any object or path on X, Y or both axes. This addon is kind of obsolete because you can do the same from transforms menu
|
||||||
|
|
||||||
@ -18,75 +19,90 @@ class ScaleToSize(inkex.EffectExtension):
|
|||||||
def add_arguments(self, pars):
|
def add_arguments(self, pars):
|
||||||
pars.add_argument('--unit')
|
pars.add_argument('--unit')
|
||||||
pars.add_argument("--keep_aspect", type=inkex.Boolean, default=True, help="Does not apply for uniform scaling")
|
pars.add_argument("--keep_aspect", type=inkex.Boolean, default=True, help="Does not apply for uniform scaling")
|
||||||
pars.add_argument("--expected_size", type=float, default=1.0, help="The expected size of the object")
|
pars.add_argument("--expected_size", type=float, default=100.000, help="The expected size of the object")
|
||||||
|
pars.add_argument("--copies", type=int, default=1, help="Count of copies")
|
||||||
|
pars.add_argument("--scale_increment", type=float, default=0.000, help="Scale increment")
|
||||||
pars.add_argument("--scale_type", default="Horizontal", help="Scale type (Uniform, Horizontal, Vertical)")
|
pars.add_argument("--scale_type", default="Horizontal", help="Scale type (Uniform, Horizontal, Vertical)")
|
||||||
pars.add_argument("--description")
|
pars.add_argument("--description")
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
unit_factor = 1.0 / self.svg.uutounit(1.0,self.options.unit)
|
unit_factor = 1.0 / self.svg.uutounit(1.0,self.options.unit)
|
||||||
|
so = self.options
|
||||||
scale_factor = self.svg.unittouu("1px")
|
scale_factor = self.svg.unittouu("1px")
|
||||||
|
|
||||||
#get recent XY coordinates (top left corner of the bounding box)
|
#get recent XY coordinates (top left corner of the bounding box)
|
||||||
for element in self.svg.selected.values():
|
for element in self.svg.selected.values():
|
||||||
if isinstance (element, inkex.Rectangle) or \
|
allCopies = []
|
||||||
isinstance (element, inkex.Circle) or \
|
allCopies.append(element)
|
||||||
isinstance (element, inkex.Ellipse):
|
for aCopy in range(so.copies):
|
||||||
bbox = element.bounding_box() * unit_factor
|
newElem = copy.copy(element)
|
||||||
new_horiz_scale = self.options.expected_size * unit_factor / bbox.width / scale_factor
|
oldId = element.get('id')
|
||||||
new_vert_scale = self.options.expected_size * unit_factor / bbox.height / scale_factor
|
newElem.set('id', "{}_scalecopy_{}".format(oldId, aCopy))
|
||||||
else:
|
parent = element.getparent()
|
||||||
bbox = element.bounding_box()
|
parent.insert(parent.index(element) + 1 + aCopy, newElem)
|
||||||
new_horiz_scale = self.options.expected_size * unit_factor / bbox.width
|
allCopies.append(newElem)
|
||||||
new_vert_scale = self.options.expected_size * unit_factor / bbox.height
|
|
||||||
if self.options.scale_type == "Horizontal":
|
|
||||||
if self.options.keep_aspect is False:
|
|
||||||
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, 1.0, 0.0]]
|
|
||||||
else:
|
|
||||||
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_horiz_scale, 0.0]]
|
|
||||||
elif self.options.scale_type == "Vertical":
|
|
||||||
if self.options.keep_aspect is False:
|
|
||||||
translation_matrix = [[1.0, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
|
||||||
else:
|
|
||||||
translation_matrix = [[new_vert_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
|
||||||
else: #Uniform
|
|
||||||
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
|
||||||
element.transform = Transform(translation_matrix) * element.composed_transform()
|
|
||||||
|
|
||||||
# now that the element moved we need to get the elements XY coordinates again to fix them
|
|
||||||
bbox_new = element.bounding_box()
|
|
||||||
|
|
||||||
#inkex.utils.debug(cx)
|
i = 1
|
||||||
#inkex.utils.debug(cy)
|
for element in allCopies:
|
||||||
#inkex.utils.debug(cx_new)
|
i += 1
|
||||||
#inkex.utils.debug(cy_new)
|
if isinstance (element, inkex.Rectangle) or \
|
||||||
|
isinstance (element, inkex.Circle) or \
|
||||||
# We remove the transformation attribute from SVG XML tree in case it's regular path. In case the element is an object like arc,circle, ellipse or star we have the attribute "sodipodi:type". Then we do not play with it's path because the size transformation will not apply (this code block is ugly)
|
isinstance (element, inkex.Ellipse):
|
||||||
if element.get ('sodipodi:type') is None and 'd' in element.attrib:
|
bbox = element.bounding_box() * unit_factor
|
||||||
#inkex.utils.debug("it's a path!")
|
new_horiz_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.width / scale_factor
|
||||||
d = element.get('d')
|
new_vert_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.height / scale_factor
|
||||||
p = CubicSuperPath(d)
|
else:
|
||||||
transf = Transform(element.get("transform", None))
|
bbox = element.bounding_box()
|
||||||
if 'transform' in element.attrib:
|
new_horiz_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.width
|
||||||
del element.attrib['transform']
|
new_vert_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.height
|
||||||
p = Path(p).to_absolute().transform(transf, True)
|
if self.options.scale_type == "Horizontal":
|
||||||
element.set('d', Path(CubicSuperPath(p).to_path()))
|
if self.options.keep_aspect is False:
|
||||||
#else:
|
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, 1.0, 0.0]]
|
||||||
#inkex.utils.debug("it's an object!")
|
else:
|
||||||
|
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_horiz_scale, 0.0]]
|
||||||
#we perform second translation to reset the center of the path
|
elif self.options.scale_type == "Vertical":
|
||||||
if isinstance (element, inkex.Rectangle) or \
|
if self.options.keep_aspect is False:
|
||||||
isinstance (element, inkex.Circle) or \
|
translation_matrix = [[1.0, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
||||||
isinstance (element, inkex.Ellipse):
|
else:
|
||||||
translation_matrix = [
|
translation_matrix = [[new_vert_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
||||||
[1.0, 0.0, scale_factor * (bbox.left - bbox_new.left + (bbox.width - bbox_new.width)/2)],
|
else: #Uniform
|
||||||
[0.0, 1.0, scale_factor * (bbox.top - bbox_new.top + (bbox.height - bbox_new.height)/2)]
|
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]]
|
||||||
]
|
element.transform = Transform(translation_matrix) * element.composed_transform()
|
||||||
else:
|
|
||||||
translation_matrix = [
|
# now that the element moved we need to get the elements XY coordinates again to fix them
|
||||||
[1.0, 0.0, bbox.left - bbox_new.left + (bbox.width - bbox_new.width)/2],
|
bbox_new = element.bounding_box()
|
||||||
[0.0, 1.0, bbox.top - bbox_new.top + (bbox.height - bbox_new.height)/2]
|
|
||||||
]
|
#inkex.utils.debug(cx)
|
||||||
element.transform = Transform(translation_matrix) * element.composed_transform()
|
#inkex.utils.debug(cy)
|
||||||
|
#inkex.utils.debug(cx_new)
|
||||||
|
#inkex.utils.debug(cy_new)
|
||||||
|
|
||||||
|
# We remove the transformation attribute from SVG XML tree in case it's regular path. In case the element is an object like arc,circle, ellipse or star we have the attribute "sodipodi:type". Then we do not play with it's path because the size transformation will not apply (this code block is ugly)
|
||||||
|
if element.get ('sodipodi:type') is None and 'd' in element.attrib:
|
||||||
|
#inkex.utils.debug("it's a path!")
|
||||||
|
d = element.get('d')
|
||||||
|
p = CubicSuperPath(d)
|
||||||
|
transf = Transform(element.get("transform", None))
|
||||||
|
if 'transform' in element.attrib:
|
||||||
|
del element.attrib['transform']
|
||||||
|
p = Path(p).to_absolute().transform(transf, True)
|
||||||
|
element.set('d', Path(CubicSuperPath(p).to_path()))
|
||||||
|
#else:
|
||||||
|
#inkex.utils.debug("it's an object!")
|
||||||
|
|
||||||
|
#we perform second translation to reset the center of the path
|
||||||
|
if isinstance (element, inkex.Rectangle) or \
|
||||||
|
isinstance (element, inkex.Circle) or \
|
||||||
|
isinstance (element, inkex.Ellipse):
|
||||||
|
translation_matrix = [
|
||||||
|
[1.0, 0.0, scale_factor * (bbox.left - bbox_new.left + (bbox.width - bbox_new.width)/2)],
|
||||||
|
[0.0, 1.0, scale_factor * (bbox.top - bbox_new.top + (bbox.height - bbox_new.height)/2)]
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
translation_matrix = [
|
||||||
|
[1.0, 0.0, bbox.left - bbox_new.left + (bbox.width - bbox_new.width)/2],
|
||||||
|
[0.0, 1.0, bbox.top - bbox_new.top + (bbox.height - bbox_new.height)/2]
|
||||||
|
]
|
||||||
|
element.transform = Transform(translation_matrix) * element.composed_transform()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ScaleToSize().run()
|
ScaleToSize().run()
|
Reference in New Issue
Block a user