1 #!/usr/bin/env python
2 '''
3 Copyright (C) 2009 Aurelio A. Heckert, aurium (a) gmail dot com
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 '''
19 import inkex, simplestyle, math, re, string
21 class InterpAttG(inkex.Effect):
23 def __init__(self):
24 inkex.Effect.__init__(self)
25 self.OptionParser.add_option("-a", "--att",
26 action="store", type="string",
27 dest="att", default="fill",
28 help="Attribute to be interpolated.")
29 self.OptionParser.add_option("-o", "--att-other",
30 action="store", type="string",
31 dest="att_other",
32 help="Other atribute (for a limited UI).")
33 self.OptionParser.add_option("-t", "--att-other-type",
34 action="store", type="string",
35 dest="att_other_type",
36 help="The other attribute type.")
37 self.OptionParser.add_option("-w", "--att-other-where",
38 action="store", type="string",
39 dest="att_other_where",
40 help="That is a tag attribute or a style attribute?")
41 self.OptionParser.add_option("-s", "--start-val",
42 action="store", type="string",
43 dest="start_val", default="#F00",
44 help="Initial interpolation value.")
45 self.OptionParser.add_option("-e", "--end-val",
46 action="store", type="string",
47 dest="end_val", default="#00F",
48 help="End interpolation value.")
49 self.OptionParser.add_option("-u", "--unit",
50 action="store", type="string",
51 dest="unit", default="color",
52 help="Values unit.")
54 def getColorValues(self):
55 sv = string.replace( self.options.start_val, '#', '' )
56 ev = string.replace( self.options.end_val, '#', '' )
57 if re.search('\s|,', sv):
58 # There are separators. That must be a integer RGB color definition.
59 sv = re.split( '[\s,]+', sv )
60 ev = re.split( '[\s,]+', ev )
61 self.R_ini = int( sv[0] )
62 self.G_ini = int( sv[1] )
63 self.B_ini = int( sv[2] )
64 self.R_end = int( ev[0] )
65 self.G_end = int( ev[1] )
66 self.B_end = int( ev[2] )
67 else:
68 # There is no separator. That must be a Hex RGB color definition.
69 if len(sv) == 3:
70 self.R_ini = int( sv[0] + sv[0], 16 )
71 self.G_ini = int( sv[1] + sv[1], 16 )
72 self.B_ini = int( sv[2] + sv[2], 16 )
73 self.R_end = int( ev[0] + ev[0], 16 )
74 self.G_end = int( ev[1] + ev[1], 16 )
75 self.B_end = int( ev[2] + ev[2], 16 )
76 else: #the len must be 6
77 self.R_ini = int( sv[0] + sv[1], 16 )
78 self.G_ini = int( sv[2] + sv[3], 16 )
79 self.B_ini = int( sv[4] + sv[5], 16 )
80 self.R_end = int( ev[0] + ev[1], 16 )
81 self.G_end = int( ev[2] + ev[3], 16 )
82 self.B_end = int( ev[4] + ev[5], 16 )
83 self.R_inc = ( self.R_end - self.R_ini ) / float( self.tot_el - 1 )
84 self.G_inc = ( self.G_end - self.G_ini ) / float( self.tot_el - 1 )
85 self.B_inc = ( self.B_end - self.B_ini ) / float( self.tot_el - 1 )
86 self.R_cur = self.R_ini
87 self.G_cur = self.G_ini
88 self.B_cur = self.B_ini
90 def getNumberValues(self):
91 sv = self.options.start_val
92 ev = self.options.end_val
93 if self.inte_att_type and self.inte_att_type != 'none':
94 sv = inkex.unittouu( sv + self.inte_att_type )
95 ev = inkex.unittouu( ev + self.inte_att_type )
96 self.val_cur = self.val_ini = sv
97 self.val_end = ev
98 self.val_inc = ( ev - sv ) / float( self.tot_el - 1 )
100 def getTotElements(self):
101 self.tot_el = 0
102 self.collection = None
103 if len( self.selected ) == 0:
104 return False
105 if len( self.selected ) > 1:
106 # multiple selection
107 self.collection = self.options.ids
108 for i in self.options.ids:
109 path = '//*[@id="%s"]' % i
110 self.collection[self.tot_el] = self.document.xpath(path, namespaces=inkex.NSS)[0]
111 self.tot_el += 1
112 else:
113 # must be a group
114 self.collection = self.selected[ self.options.ids[0] ]
115 for i in self.collection:
116 self.tot_el += 1
118 def effect(self):
119 if self.options.att == 'other':
120 self.inte_att = self.options.att_other
121 self.inte_att_type = self.options.att_other_type
122 self.where = self.options.att_other_where
123 else:
124 self.inte_att = self.options.att
125 if self.inte_att == 'width':
126 self.inte_att_type = 'float'
127 self.where = 'tag'
128 elif self.inte_att == 'height':
129 self.inte_att_type = 'float'
130 self.where = 'tag'
131 elif self.inte_att == 'scale':
132 self.inte_att_type = 'float'
133 self.where = 'transform'
134 elif self.inte_att == 'trans-x':
135 self.inte_att_type = 'float'
136 self.where = 'transform'
137 elif self.inte_att == 'trans-y':
138 self.inte_att_type = 'float'
139 self.where = 'transform'
140 elif self.inte_att == 'fill':
141 self.inte_att_type = 'color'
142 self.where = 'style'
143 elif self.inte_att == 'opacity':
144 self.inte_att_type = 'float'
145 self.where = 'style'
147 self.getTotElements()
149 if self.inte_att_type == 'color':
150 self.getColorValues()
151 else:
152 self.getNumberValues()
154 if self.collection is None:
155 inkex.errormsg( 'There is no selection to interpolate' )
156 return False
158 for node in self.collection:
159 if self.inte_att_type == 'color':
160 val = 'rgb('+ \
161 str(int(round(self.R_cur))) +','+ \
162 str(int(round(self.G_cur))) +','+ \
163 str(int(round(self.B_cur))) +')'
164 else:
165 if self.inte_att_type == 'float':
166 val = self.val_cur
167 else: # inte_att_type == 'int'
168 val = round(self.val_cur)
170 if self.where == 'style':
171 s = node.get('style')
172 re_find = '(^|;)'+ self.inte_att +':[^;]*(;|$)'
173 if re.search( re_find, s ):
174 s = re.sub( re_find, '\\1'+ self.inte_att +':'+ str(val) +'\\2', s )
175 else:
176 s += ';'+ self.inte_att +':'+ str(val)
177 node.set( 'style', s )
178 elif self.where == 'transform':
179 t = node.get('transform')
180 if t == None: t = ""
181 if self.inte_att == 'trans-x':
182 val = "translate("+ str(val) +",0)"
183 elif self.inte_att == 'trans-y':
184 val = "translate(0,"+ str(val) +")"
185 else:
186 val = self.inte_att + "("+ str(val) +")"
187 node.set( 'transform', t +" "+ val )
188 else: # self.where == 'tag':
189 node.set( self.inte_att, str(val) )
191 if self.inte_att_type == 'color':
192 self.R_cur += self.R_inc
193 self.G_cur += self.G_inc
194 self.B_cur += self.B_inc
195 else:
196 self.val_cur += self.val_inc
198 return True
200 if __name__ == '__main__': #pragma: no cover
201 e = InterpAttG()
202 if e.affect():
203 exit(0)
204 else:
205 exit(1)