add copy+increment option to scale to size extension

This commit is contained in:
Mario Voigt 2022-01-09 22:35:09 +01:00
parent e4fc012a0b
commit fdeb67aa82
3 changed files with 82 additions and 64 deletions

View File

@ -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",

View File

@ -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>

View File

@ -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": i = 1
if self.options.keep_aspect is False: for element in allCopies:
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, 1.0, 0.0]] i += 1
if isinstance (element, inkex.Rectangle) or \
isinstance (element, inkex.Circle) or \
isinstance (element, inkex.Ellipse):
bbox = element.bounding_box() * unit_factor
new_horiz_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.width / scale_factor
new_vert_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.height / scale_factor
else: else:
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_horiz_scale, 0.0]] bbox = element.bounding_box()
elif self.options.scale_type == "Vertical": new_horiz_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.width
if self.options.keep_aspect is False: new_vert_scale = (so.expected_size + so.scale_increment * i) * unit_factor / bbox.height
translation_matrix = [[1.0, 0.0, 0.0], [0.0, new_vert_scale, 0.0]] 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)
#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: else:
translation_matrix = [[new_vert_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]] translation_matrix = [
else: #Uniform [1.0, 0.0, bbox.left - bbox_new.left + (bbox.width - bbox_new.width)/2],
translation_matrix = [[new_horiz_scale, 0.0, 0.0], [0.0, new_vert_scale, 0.0]] [0.0, 1.0, bbox.top - bbox_new.top + (bbox.height - bbox_new.height)/2]
element.transform = Transform(translation_matrix) * element.composed_transform() ]
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)
#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()