]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/uv_import_svg.py
62ee9f147680f92df2b37e44b6534cdd7fea48d5
[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 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 handler, make_parser
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                 return u * self.a + v * self.c + self.e, u * self.b + v * self.d + self.f
77
78         def translate(self, dx, dy):
79                 self.multiply(Matrix(1, 0, 0, 1, dx, dy))
80
81         def scale(self, sx, sy):
82                 self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
83
84         def rotate(self, a):
85                 a *= math.pi / 180
86                 self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
87
88         def skewX(self, a):
89                 a *= math.pi / 180
90                 self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
91
92         def skewY(self, a):
93                 a *= math.pi / 180
94                 self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
95
96
97 def parse_transform(s):
98         matrix = Matrix()
99         while True:
100                 match = istrans.match(s)
101                 if not match:
102                         break
103                 cmd = match.group(1)
104                 values = commawsp.split(match.group(2).strip())
105                 s = s[len(match.group(0)):]
106                 arg = []
107
108                 for value in values:
109                         match = isnumber.match(value)
110                         if not match:
111                                 raise Abort("bad transform value")
112
113                         arg.append(float(match.group(0)))
114
115                 num = len(arg)
116                 if cmd == "skewX":
117                         if num == 1:
118                                 matrix.skewX(arg[0])
119                                 continue
120
121                 elif cmd == "skewY":
122                         if num == 1:
123                                 matrix.skewY(arg[0])
124                                 continue
125
126                 elif cmd == "scale":
127                         if num == 1:
128                                 matrix.scale(arg[0], arg[0])
129                                 continue
130                         if num == 2:
131                                 matrix.scale(arg[0], arg[1])
132                                 continue
133
134                 elif cmd == "translate":
135                         if num == 1:
136                                 matrix.translate(arg[0], 0)
137                                 continue
138                         if num == 2:
139                                 matrix.translate(arg[0], arg[1])
140                                 continue
141
142                 elif cmd == "rotate":
143                         if num == 1:
144                                 matrix.rotate(arg[0])
145                                 continue
146                         if num == 3:
147                                 matrix.translate(-arg[1], -arg[2])
148                                 matrix.rotate(arg[0])
149                                 matrix.translate(arg[1], arg[2])
150                                 continue
151
152                 elif cmd == "matrix":
153                         if num == 6:
154                                 matrix.multiply(Matrix(*arg))
155                                 continue
156
157                 else:
158                         print "ERROR: unknown transform", cmd
159                         continue
160
161                 print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
162
163         if len(s):
164                 print "ERROR: transform with trailing garbage (%s)" % s
165         return matrix
166
167
168 class import_svg(handler.ContentHandler):
169         # err_handler
170         def error(self, exception):
171                 raise Abort(str(exception))
172
173         def fatalError(self, exception):
174                 raise Abort(str(exception))
175
176         def warning(self, exception):
177                 print "WARNING: " + str(exception)
178
179         # doc_handler
180         def setDocumentLocator(self, whatever):
181                 pass
182
183         def startDocument(self):
184                 self.verified = False
185                 self.scandesc = False
186                 self.matrices = [None]
187                 self.meshes = {}
188                 for o in Blender.Scene.GetCurrent().objects:
189                         if o.type != "Mesh":
190                                 continue
191                         mesh = o.getData(mesh = 1)
192                         if not mesh.faceUV:
193                                 continue
194                         if mesh.name in self.meshes:
195                                 continue
196                         self.meshes[mesh.name] = mesh
197
198         def endDocument(self):
199                 pass
200
201         def characters(self, data):
202                 if not self.scandesc:
203                         return
204                 if data.startswith("uv_export_svg.py"):
205                         self.verified = True
206
207         def ignorableWhitespace(self, data, start, length):
208                 pass
209
210         def processingInstruction(self, target, data):
211                 pass
212
213         def startElement(self, name, attrs):
214                 currmat = self.matrices[-1]
215                 try:
216                         m = parse_transform(attrs["transform"])
217                         if currmat != None:
218                                 m.multiply(currmat)
219                         self.matrices.append(m)
220                 except:
221                         self.matrices.append(currmat)
222
223                 if name == "polygon":
224                         self.handlePolygon(attrs)
225                 elif name == "svg":
226                         try:
227                                 x, y, w, h = commawsp.split(attrs["viewBox"], 4)
228                                 if int(x) or int(y):
229                                         raise Abort("bad viewBox")
230                                 self.width = int(w)
231                                 self.height = int(h)
232                                 if self.width != self.height:
233                                         raise Abort("viewBox isn't a square")
234                         except:
235                                 raise Abort("no viewBox")
236                 elif name == "desc" and not self.verified:
237                         self.scandesc = True
238
239         def endElement(self, name):
240                 self.scandesc = False
241                 self.matrices.pop()
242
243         def handlePolygon(self, attrs):
244                 if not self.verified:
245                         raise Abort("this file wasn't written by uv_export_svg.py")
246                 ident = attrs.get("id", None)
247                 points = attrs.get("points", None)
248
249                 if not ident or not points:
250                         print('bad polygon "%s"' % ident)
251                         return
252
253                 try:
254                         meshname, num = ident.strip().split(ID_SEPARATOR, 2)
255                 except:
256                         print('broken id "%s"' % ident)
257                         return
258
259                 if not meshname in self.meshes:
260                         print('unknown mesh "%s"' % meshname)
261                         return
262
263                 #print 'mesh %s face %d: ' % (meshname, num)
264                 matrix = self.matrices[-1]
265                 transuv = []
266                 for p in numwsp.split(points.strip()):
267                         u, v = commawsp.split(p.strip(), 2)
268                         u = float(u)
269                         v = float(v)
270                         if matrix:
271                                 u, v = matrix.transform(u, v)
272                         transuv.append((u / self.width, 1 - v / self.height))
273
274                 for i, uv in enumerate(self.meshes[meshname].faces[int(num)].uv):
275                         uv[0] = transuv[i][0]
276                         uv[1] = transuv[i][1]
277
278
279 def run_parser(path):
280         if BPyMessages.Error_NoFile(path):
281                 return
282
283         editmode = Blender.Window.EditMode()
284         if editmode:
285                 Blender.Window.EditMode(0)
286         Blender.Window.WaitCursor(1)
287
288         try:
289                 svg = make_parser()
290                 svg.setContentHandler(import_svg())
291                 svg.setErrorHandler(import_svg())
292                 svg.parse(path)
293                 Blender.Registry.SetKey("UVImportExportSVG", { "path" : path }, False)
294
295         except Abort, e:
296                 print "Error:", e.msg, "  -> aborting ...\n"
297                 Blender.Draw.PupMenu("Error%t|" + e.msg)
298
299         Blender.Window.RedrawAll()
300         Blender.Window.WaitCursor(0)
301         if editmode:
302                 Blender.Window.EditMode(1)
303
304
305 registry = Blender.Registry.GetKey("UVImportExportSVG", False)
306 if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
307         path = registry["path"]
308 else:
309         path = ""
310
311 Blender.Window.FileSelector(run_parser, "Import SVG", path)
312