]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/uv_import_svg.py
Win32 fix
[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/"
12 __version__ = "0.1"
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 mapping 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
45 from xml.sax import saxexts
46
47
48 numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
49 commawsp = re.compile('\s+|\s*,\s*')
50 istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
51 isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
52
53
54 class Abort(Exception):
55         def __init__(self, msg):
56                 self.msg = msg
57
58
59 class Matrix:
60         def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
61                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
62
63         def __str__(self):
64                 return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
65
66         def multiply(self, mat):
67                 a = mat.a * self.a + mat.c * self.b
68                 b = mat.b * self.a + mat.d * self.b
69                 c = mat.a * self.c + mat.c * self.d
70                 d = mat.b * self.c + mat.d * self.d
71                 e = mat.a * self.e + mat.c * self.f + mat.e
72                 f = mat.b * self.e + mat.d * self.f + mat.f
73                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
74
75         def transform(self, u, v):
76                 x = u * self.a + v * self.c + self.e
77                 y = u * self.b + v * self.d + self.f
78                 return (x, y)
79
80         def translate(self, dx, dy):
81                 self.multiply(Matrix(1, 0, 0, 1, dx, dy))
82
83         def scale(self, sx, sy):
84                 self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
85
86         def rotate(self, a):
87                 a *= math.pi / 180
88                 self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
89
90         def skewX(self, a):
91                 a *= math.pi / 180
92                 self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
93
94         def skewY(self, a):
95                 a *= math.pi / 180
96                 self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
97
98
99 def parse_transform(s):
100         matrix = Matrix()
101         while True:
102                 match = istrans.match(s)
103                 if not match:
104                         break
105                 cmd = match.group(1)
106                 values = commawsp.split(match.group(2).strip())
107                 s = s[len(match.group(0)):]
108                 arg = []
109
110                 for value in values:
111                         match = isnumber.match(value)
112                         if not match:
113                                 raise Abort("bad transform value")
114
115                         arg.append(float(match.group(0)))
116
117                 num = len(arg)
118                 if cmd == "skewX":
119                         if num == 1:
120                                 matrix.skewX(arg[0])
121                                 continue
122
123                 elif cmd == "skewY":
124                         if num == 1:
125                                 matrix.skewY(arg[0])
126                                 continue
127
128                 elif cmd == "scale":
129                         if num == 1:
130                                 matrix.scale(arg[0], arg[0])
131                                 continue
132                         if num == 2:
133                                 matrix.scale(arg[0], arg[1])
134                                 continue
135
136                 elif cmd == "translate":
137                         if num == 1:
138                                 matrix.translate(arg[0], 0)
139                                 continue
140                         if num == 2:
141                                 matrix.translate(arg[0], arg[1])
142                                 continue
143
144                 elif cmd == "rotate":
145                         if num == 1:
146                                 matrix.rotate(arg[0])
147                                 continue
148                         if num == 3:
149                                 matrix.translate(-arg[1], -arg[2])
150                                 matrix.rotate(arg[0])
151                                 matrix.translate(arg[1], arg[2])
152                                 continue
153
154                 elif cmd == "matrix":
155                         if num == 6:
156                                 matrix.multiply(Matrix(*arg))
157                                 continue
158
159                 else:
160                         print "ERROR: unknown transform", cmd
161                         continue
162
163                 print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
164
165         if len(s):
166                 print "ERROR: transform with trailing garbage (%s)" % s
167         return matrix
168
169
170 class import_svg:
171         # err_handler
172         def error(self, exception):
173                 raise Abort(str(exception))
174
175         def fatalError(self, exception):
176                 raise Abort(str(exception))
177
178         def warning(self, exception):
179                 print "WARNING: " + str(exception)
180
181         # doc_handler
182         def setDocumentLocator(self, whatever):
183                 pass
184
185         def startDocument(self):
186                 self.verified = False
187                 self.scandesc = False
188                 self.matrices = [None]
189                 self.meshes = {}
190                 for o in Blender.Scene.GetCurrent().objects:
191                         if o.type != "Mesh":
192                                 continue
193                         mesh = o.getData(mesh = 1)
194                         if not mesh.faceUV:
195                                 continue
196                         if mesh.name in self.meshes:
197                                 continue
198                         self.meshes[mesh.name] = mesh
199
200         def endDocument(self):
201                 pass
202
203         def characters(self, data, start, length):
204                 if not self.scandesc:
205                         return
206                 if data[start:start + length].startswith("uv_export_svg.py"):
207                         self.verified = True
208
209         def ignorableWhitespace(self, data, start, length):
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                 svg = saxexts.ParserFactory().make_parser("xml.sax.drivers.drv_xmlproc")
289                 svg.setDocumentHandler(import_svg())
290                 svg.setErrorHandler(import_svg())
291                 svg.parse(path)
292                 Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
293
294         except Abort, e:
295                 print "Error:", e.msg, "  -> aborting ...\n"
296                 Blender.Draw.PupMenu("Error%t|" + e.msg)
297
298         Blender.Window.RedrawAll()
299         Blender.Window.WaitCursor(0)
300         if editmode:
301                 Blender.Window.EditMode(1)
302
303
304 registry = Blender.Registry.GetKey("UVImportExportSVG", False)
305 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
306         path = registry["path"]
307 else:
308         path = ""
309
310 Blender.Window.FileSelector(run_parser, "Import SVG", path)
311