]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/uv_import_svg.py
initial commit for a python based terrasync client
[flightgear.git] / utils / Modeller / uv_import_svg.py
1 #!BPY
2
3 # """
4 # Name: 'UV: (Re)Import UV from SVG'
5 # Blender: 245
6 # Group: 'Image'
7 # Tooltip: 'Re-import UV layout from SVG file'
8 # """
9
10 __author__ = "Melchior FRANZ < mfranz # aon : at >"
11 __url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/uv_import_svg.py"]
12 __version__ = "0.2"
13 __bpydoc__ = """\
14 Imports an SVG file containing UV maps, which has been saved by the
15 uv_export.svg script. This allows to move, scale, and rotate object
16 mappings in SVG editors like Inkscape. Note that all contained UV maps
17 will be set, no matter which objects are actually selected at the moment.
18 The choice has been made when the file was saved!
19 """
20
21
22 #--------------------------------------------------------------------------------
23 # Copyright (C) 2008  Melchior FRANZ  < mfranz # aon : at >
24 #
25 # This program is free software; you can redistribute it and/or
26 # modify it under the terms of the GNU General Public License as
27 # published by the Free Software Foundation; either version 2 of the
28 # License, or (at your option) any later version.
29 #
30 # This program is distributed in the hope that it will be useful, but
31 # WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33 # General Public License for more details.
34 #
35 # You should have received a copy of the GNU General Public License
36 # along with this program; if not, write to the Free Software
37 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
38 #--------------------------------------------------------------------------------
39
40
41 ID_SEPARATOR = '_.:._'
42
43
44 import Blender, BPyMessages, sys, math, re, xml.sax
45
46
47 numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
48 commawsp = re.compile('\s+|\s*,\s*')
49 istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
50 isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
51
52
53 class Abort(Exception):
54         def __init__(self, msg):
55                 self.msg = msg
56
57
58 class Matrix:
59         def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
60                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
61
62         def __str__(self):
63                 return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
64
65         def multiply(self, mat):
66                 a = mat.a * self.a + mat.c * self.b
67                 b = mat.b * self.a + mat.d * self.b
68                 c = mat.a * self.c + mat.c * self.d
69                 d = mat.b * self.c + mat.d * self.d
70                 e = mat.a * self.e + mat.c * self.f + mat.e
71                 f = mat.b * self.e + mat.d * self.f + mat.f
72                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
73
74         def transform(self, u, v):
75                 return u * self.a + v * self.c + self.e, u * self.b + v * self.d + self.f
76
77         def translate(self, dx, dy):
78                 self.multiply(Matrix(1, 0, 0, 1, dx, dy))
79
80         def scale(self, sx, sy):
81                 self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
82
83         def rotate(self, a):
84                 a *= math.pi / 180
85                 self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
86
87         def skewX(self, a):
88                 a *= math.pi / 180
89                 self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
90
91         def skewY(self, a):
92                 a *= math.pi / 180
93                 self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
94
95
96 def parse_transform(s):
97         matrix = Matrix()
98         while True:
99                 match = istrans.match(s)
100                 if not match:
101                         break
102                 cmd = match.group(1)
103                 values = commawsp.split(match.group(2).strip())
104                 s = s[len(match.group(0)):]
105                 arg = []
106
107                 for value in values:
108                         match = isnumber.match(value)
109                         if not match:
110                                 raise Abort("bad transform value")
111
112                         arg.append(float(match.group(0)))
113
114                 num = len(arg)
115                 if cmd == "skewX":
116                         if num == 1:
117                                 matrix.skewX(arg[0])
118                                 continue
119
120                 elif cmd == "skewY":
121                         if num == 1:
122                                 matrix.skewY(arg[0])
123                                 continue
124
125                 elif cmd == "scale":
126                         if num == 1:
127                                 matrix.scale(arg[0], arg[0])
128                                 continue
129                         if num == 2:
130                                 matrix.scale(arg[0], arg[1])
131                                 continue
132
133                 elif cmd == "translate":
134                         if num == 1:
135                                 matrix.translate(arg[0], 0)
136                                 continue
137                         if num == 2:
138                                 matrix.translate(arg[0], arg[1])
139                                 continue
140
141                 elif cmd == "rotate":
142                         if num == 1:
143                                 matrix.rotate(arg[0])
144                                 continue
145                         if num == 3:
146                                 matrix.translate(-arg[1], -arg[2])
147                                 matrix.rotate(arg[0])
148                                 matrix.translate(arg[1], arg[2])
149                                 continue
150
151                 elif cmd == "matrix":
152                         if num == 6:
153                                 matrix.multiply(Matrix(*arg))
154                                 continue
155
156                 else:
157                         print "ERROR: unknown transform", cmd
158                         continue
159
160                 print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
161
162         if len(s):
163                 print "ERROR: transform with trailing garbage (%s)" % s
164         return matrix
165
166
167 class import_svg(xml.sax.handler.ContentHandler):
168         # err_handler
169         def error(self, exception):
170                 raise Abort(str(exception))
171
172         def fatalError(self, exception):
173                 raise Abort(str(exception))
174
175         def warning(self, exception):
176                 print "WARNING: " + str(exception)
177
178         # doc_handler
179         def setDocumentLocator(self, whatever):
180                 pass
181
182         def startDocument(self):
183                 self.verified = False
184                 self.scandesc = False
185                 self.matrices = [None]
186                 self.meshes = {}
187                 for o in Blender.Scene.GetCurrent().objects:
188                         if o.type != "Mesh":
189                                 continue
190                         mesh = o.getData(mesh = 1)
191                         if not mesh.faceUV:
192                                 continue
193                         if mesh.name in self.meshes:
194                                 continue
195                         self.meshes[mesh.name] = mesh
196
197         def endDocument(self):
198                 pass
199
200         def characters(self, data):
201                 if not self.scandesc:
202                         return
203                 if data.startswith("uv_export_svg.py"):
204                         self.verified = True
205
206         def ignorableWhitespace(self, data, start, length):
207                 pass
208
209         def processingInstruction(self, target, data):
210                 pass
211
212         def startElement(self, name, attrs):
213                 currmat = self.matrices[-1]
214                 if "transform" in attrs:
215                         m = parse_transform(attrs["transform"])
216                         if currmat != None:
217                                 m.multiply(currmat)
218                         self.matrices.append(m)
219                 else:
220                         self.matrices.append(currmat)
221
222                 if name == "polygon":
223                         self.handlePolygon(attrs)
224                 elif name == "svg":
225                         if "viewBox" in attrs:
226                                 x, y, w, h = commawsp.split(attrs["viewBox"], 4)
227                                 if int(x) or int(y):
228                                         raise Abort("bad viewBox")
229                                 self.width = int(w)
230                                 self.height = int(h)
231                                 if self.width != self.height:
232                                         raise Abort("viewBox isn't a square")
233                         else:
234                                 raise Abort("no viewBox")
235                 elif name == "desc" and not self.verified:
236                         self.scandesc = True
237
238         def endElement(self, name):
239                 self.scandesc = False
240                 self.matrices.pop()
241
242         def handlePolygon(self, attrs):
243                 if not self.verified:
244                         raise Abort("this file wasn't written by uv_export_svg.py")
245                 ident = attrs.get("id", None)
246                 points = attrs.get("points", None)
247
248                 if not ident or not points:
249                         print('bad polygon "%s"' % ident)
250                         return
251
252                 try:
253                         meshname, num = ident.strip().split(ID_SEPARATOR, 2)
254                 except:
255                         print('broken id "%s"' % ident)
256                         return
257
258                 if not meshname in self.meshes:
259                         print('unknown mesh "%s"' % meshname)
260                         return
261
262                 #print 'mesh %s face %d: ' % (meshname, num)
263                 matrix = self.matrices[-1]
264                 transuv = []
265                 for p in numwsp.split(points.strip()):
266                         u, v = commawsp.split(p.strip(), 2)
267                         u = float(u)
268                         v = float(v)
269                         if matrix:
270                                 u, v = matrix.transform(u, v)
271                         transuv.append((u / self.width, 1 - v / self.height))
272
273                 for i, uv in enumerate(self.meshes[meshname].faces[int(num)].uv):
274                         uv[0] = transuv[i][0]
275                         uv[1] = transuv[i][1]
276
277
278 def run_parser(path):
279         if BPyMessages.Error_NoFile(path):
280                 return
281
282         editmode = Blender.Window.EditMode()
283         if editmode:
284                 Blender.Window.EditMode(0)
285         Blender.Window.WaitCursor(1)
286
287         try:
288                 xml.sax.parse(path, import_svg(), import_svg())
289                 Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
290
291         except Abort, e:
292                 print "Error:", e.msg, "  -> aborting ...\n"
293                 Blender.Draw.PupMenu("Error%t|" + e.msg)
294
295         Blender.Window.RedrawAll()
296         Blender.Window.WaitCursor(0)
297         if editmode:
298                 Blender.Window.EditMode(1)
299
300
301 registry = Blender.Registry.GetKey("UVImportExportSVG", False)
302 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
303         path = registry["path"]
304 else:
305         path = ""
306
307 Blender.Window.FileSelector(run_parser, "Import SVG", path)
308