Make multiple copies with transformations

General discussions about Inkscape.
patrickthebold
Posts: 2
Joined: Sun Jan 02, 2011 3:25 pm

Make multiple copies with transformations

Postby patrickthebold » Sun Jan 02, 2011 3:50 pm

I read about how to make your own extensions here: http://www.hoboes.com/Mimsy/hacks/write-inkscape-extension-create-multiple-duplicates/ and I wanted/needed something similar but with a bit more functionality. I'm not sure if there is a central place to put extensions so I thought I would post what I did here:

Code: Select all

#!/usr/bin/env python

import sys, copy

sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
import inkex, simpletransform

from math import sin, cos, pi, pow

class DuplicateMultiple(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option('-b', '--begin',
                action='store', type='int',
                dest='begin', default=0,
                help='n starts here')
        self.OptionParser.add_option('-e', '--end',
                action='store', type='int',
                dest='end', default=10,
                help='n stops here')
        self.OptionParser.add_option('-x', '--horizontal',
                action='store', type='string',
                dest='horizontal', default='n',
                help='Horizontal distance?')
        self.OptionParser.add_option('-y', '--vertical',
                action='store', type='string',
                dest='vertical', default='n^2',
                help='Vertical distance?')
        self.OptionParser.add_option('-c', '--centerx',
                action='store', type='string',
                dest='cx', default='0',
                help='x coordinate of center')
        self.OptionParser.add_option('-d', '--centery',
                action='store', type='string',
                dest='cy', default='0',
                help='y coordinate of center')
        self.OptionParser.add_option("-u", "--unit",
                action="store", type="string",
                dest="unit", default="px",
                help="The unit of the measurement")
        self.OptionParser.add_option('-s', '--scale',
                action='store', type='string',
                dest='scale', default="n/2",
                help='Scale Factor')
        self.OptionParser.add_option('-r', '--rotate',
                action='store', type='string',
                dest='rotate', default='-90',
                help='Degrees rotated CW')

    def effect(self):
        cx=eval(self.options.cx)
        cy=eval(self.options.cy)
        if self.selected:
            for id, node in self.selected.iteritems():
                for n in range(self.options.begin, self.options.end+1):
                    horiz=eval(self.options.horizontal)
                    vert=eval(self.options.vertical)
                    strtranslation = 'translate(' + str(inkex.unittouu(str(horiz) + self.options.unit)) + ', ' + str(inkex.unittouu(str(vert) + self.options.unit))  + ')'
                    translation = simpletransform.parseTransform(strtranslation)

                    strtranslation2= 'translate(' + str(inkex.unittouu(str(-1*cx) + self.options.unit)) + ', ' + str(inkex.unittouu(str(-1*cy) + self.options.unit))  + ')'

                    translation2 = simpletransform.parseTransform(strtranslation2)

                    strtranslation3= 'translate(' + str(inkex.unittouu(str(cx) + self.options.unit)) + ', ' + str(inkex.unittouu(str(cy) + self.options.unit))  + ')'

                    translation3 = simpletransform.parseTransform(strtranslation3)

                    strrotation = 'rotate(' + str(eval(self.options.rotate))  +  ')'
                    rotation = simpletransform.parseTransform(strrotation)
                    strscaleing = 'scale(' + str(eval(self.options.scale))  +  ')'
                    scaleing = simpletransform.parseTransform(strscaleing)

                    newNode = copy.deepcopy(node)
                    simpletransform.applyTransformToNode(translation2, newNode)
                    simpletransform.applyTransformToNode(rotation, newNode)
                    simpletransform.applyTransformToNode(scaleing, newNode)
                    simpletransform.applyTransformToNode(translation3, newNode)
                    simpletransform.applyTransformToNode(translation, newNode)

                    self.current_layer.append(newNode)
                    node = newNode
                    cx=cx+horiz
                    cy=cy+vert

effect = DuplicateMultiple()
effect.affect()

is the code I call duplicates.py and

Code: Select all

<inkscape-extension>
    <_name>Multiple Copies</_name>
    <id>com.curran.filter.duplicates</id>
    <dependency type="executable" location="extensions">inkex.py</dependency>
    <dependency type="executable" location="extensions">simpletransform.py</dependency>
    <param name="begin" type="int" min="-1000" max="1000" _gui-text="For n=">0</param>
    <param name="end" type="int" min="-1000" max="1000" _gui-text="to">10</param>
    <param name="horizontal" type="string" _gui-text="X translation">n</param>
    <param name="vertical" type="string" _gui-text="Y translation">n^2</param>
    <param name="centerx" type="string" _gui-text="X coordinate of center">0</param>
    <param name="centery" type="string" _gui-text="Y coordinate of center">0</param>
    <param name="unit" type="enum" _gui-text="Length Unit: ">
        <item value="px">px</item>
        <item value="pt">pt</item>
        <item value="in">in</item>
        <item value="ft">ft</item>
        <item value="yd">yd</item>
        <item value="mm">mm</item>
        <item value="cm">cm</item>
        <item value="m">m</item>
        <item value="km">km</item>
    </param>
    <param name="scale" type="string" _gui-text="Scale Factor">n/2</param>
    <param name="rot" type="string" _gui-text="Degrees Rotated CW">-90</param>
    <effect>
          <object-type>all</object-type>
          <effects-menu>
                <submenu _name="Mine" />
          </effects-menu>
    </effect>
    <script>
         <command reldir="extensions" interpreter="python">duplicates.py</command>
    </script>
</inkscape-extension>

is what I call duplicates.inx.

To use you can put in ~/.config/inkscape/extensions/

It just does a for loop in python, and the parameters to the geometric transformation done to each copy are given by a bit of python code. Note that it is affected by Bug #170049, because it uses the coordinate system that inkscape doesn't show you.

As an example of use you can start with a 3.4 inch square in the lower left corner of the page (A4 size?) and use the parameters:

Code: Select all

X translation: 5.5/2*sin(n*pi/2)*pow(.62,(n-1))+(0.5*5.5-3.4)*cos(n*pi/2)*pow(.62,(n-1))

Y translation: -5.5/2*cos(n*pi/2)*pow(.62,(n-1))+(0.5*5.5-3.4)*sin(n*pi/2)*pow(.62,(n-1))

x coordinate of center: 1.7

y cooridnate of center: 11.69-1.7

inches

scale factor: .62

rotation: 90

To make a golden rectangle. You can see the 11.69 because that is the height of A4 paper I think, so you can see how the aformentioned bug screws things up.

~suv
Posts: 2272
Joined: Sun May 10, 2009 2:07 am

Re: Make multiple copies with transformations

Postby ~suv » Sun Jan 02, 2011 8:02 pm

patrickthebold wrote:I'm not sure if there is a central place to put extensions so I thought I would post what I did here
There is not yet a central repository for Inkscape extensions besides the list in the Inkscape Wiki:
It might be best to host your extension at a site like Google Code or Launchpad (which allow versioning as well as issue tracking), and adding a link to it (with a short description) to the list in the Wiki.

~suv
Posts: 2272
Joined: Sun May 10, 2009 2:07 am

Re: Make multiple copies with transformations

Postby ~suv » Sun Jan 02, 2011 8:48 pm

Some early comments (without having tested your extension in-depth yet - thank you for sharing it!):
patrickthebold wrote:

Code: Select all

sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
No longer needed since Inkscape 0.47 (the path to the shared extensions directory is known to all python extensions now; this Mac-specific path would not work in older versions (0.46) if the user installed the app in a different location, and not at all with Inkscape 0.46 on other platforms).

duplicates.inx:
patrickthebold wrote:

Code: Select all

<inkscape-extension>
Look at the examples that ship with current Inkscape 0.48: since 0.47, the INX extension definition files are now properly declared as XML files:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">


patrickthebold wrote:Note that it is affected by Bug #170049, because it uses the coordinate system that inkscape doesn't show you.
Not sure, but aren't there functions available in one of the python modules that ship with inkscape for the transforms between the SVG and the GUI coordinate system?

Off topic:
patrickthebold wrote:(…) so you can see how the aformentioned bug screws things up.
You seem annoyed ;)
IMHO that bug isn't a bug per se - the concept to separate between the coordinate system as stored in the source and (configurable) coordinate system(s) used for displaying the content in the application GUI does make sense and can offer (if enhanced) a lot of additional functionality (e.g. configurable origin; user, view-related & object coordinate systems - best combined with the ability to rotate or flip the canvas, e.g. to the current active coordinate system).
Using the first quadrant of a normal oriented (right-handed) Cartesian coordinate system in the GUI does make sense for many use cases of a vector drawing application (admittedly less so when creating mockups or graphics for web design).

patrickthebold
Posts: 2
Joined: Sun Jan 02, 2011 3:25 pm

Re: Make multiple copies with transformations

Postby patrickthebold » Mon Jan 03, 2011 2:23 pm

~suv wrote:
patrickthebold wrote:Note that it is affected by Bug #170049, because it uses the coordinate system that inkscape doesn't show you.
Not sure, but aren't there functions available in one of the python modules that ship with inkscape for the transforms between the SVG and the GUI coordinate system?

Off topic:
patrickthebold wrote:(…) so you can see how the aformentioned bug screws things up.
You seem annoyed ;)
IMHO that bug isn't a bug per se - the concept to separate between the coordinate system as stored in the source and (configurable) coordinate system(s) used for displaying the content in the application GUI does make sense and can offer (if enhanced) a lot of additional functionality (e.g. configurable origin; user, view-related & object coordinate systems - best combined with the ability to rotate or flip the canvas, e.g. to the current active coordinate system).
Using the first quadrant of a normal oriented (right-handed) Cartesian coordinate system in the GUI does make sense for many use cases of a vector drawing application (admittedly less so when creating mockups or graphics for web design).


Off topic:
First let me say thanks for the tips. I will make the suggested changes and maybe host them somewhere. As for sounding annoyed it is mainly because I don't know enough programming to fix it for this extension. I will look for the python modules you mentioned. But right now the extension is hard to use because the user has to switch coordinate systems themselves, the only way I can fix it (at this point in my understanding) is to hard code the height of A4 paper into the code, which is ugly. What is strange is that even if I change the paper size the difference between the coordinates is always the height of A4 paper.

I might actually be annoyed enough to look at the code base and implement what you suggested. Now I think I need to google C++... :)


Return to “General Discussions”