]> git.mxchange.org Git - flightgear.git/blob - utils/Modeller/uv_import_svg.py
e934be5eeeb227e90b0e446dbc006d51a0ae44f8
[flightgear.git] / utils / Modeller / uv_import_svg.py
1 #!BPY
2
3 # """
4 # Name: 'SVG: Re-Import UV layout from SVG file'
5 # Blender: 245
6 # Group: 'UV'
7 # Tooltip: 'Re-import UV layout from SVG file'
8 # """
9
10 __author__ = "Melchior FRANZ < mfranz # aon : at >"
11 __url__ = "http://members.aon.at/mfranz/flightgear/"
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 ID_SEPARATOR = '_____'
23
24
25 import Blender, sys, math, re
26 from xml.sax import saxexts
27
28
29 registry = {}
30 numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
31 commawsp = re.compile('\s+|\s*,\s*')
32 istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
33 isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
34
35
36 class Abort(Exception):
37         def __init__(self, msg):
38                 self.msg = msg
39
40
41 class Matrix:
42         def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
43                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
44
45         def __str__(self):
46                 return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
47
48         def multiply(self, mat):
49                 a = self.a * mat.a + self.c * mat.b
50                 b = self.b * mat.a + self.d * mat.b
51                 c = self.a * mat.c + self.c * mat.d
52                 d = self.b * mat.c + self.d * mat.d
53                 e = self.a * mat.e + self.c * mat.f + self.e
54                 f = self.b * mat.e + self.d * mat.f + self.f
55                 self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
56
57         def transform(self, u, v):
58                 x = u * self.a + v * self.c + self.e
59                 y = u * self.b + v * self.d + self.f
60                 return (x, y)
61
62         def translate(self, dx, dy):
63                 self.multiply(Matrix(1, 0, 0, 1, dx, dy))
64
65         def scale(self, sx, sy):
66                 self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
67
68         def rotate(self, a):
69                 a *= math.pi / 180
70                 self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
71
72         def skewX(self, a):
73                 a *= math.pi / 180
74                 self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
75
76         def skewY(self, a):
77                 a *= math.pi / 180
78                 self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
79
80
81 def parse_transform(s):
82         matrix = Matrix()
83         while True:
84                 match = istrans.match(s)
85                 if not match:
86                         break
87                 cmd = match.group(1)
88                 values = commawsp.split(match.group(2).strip())
89                 s = s[len(match.group(0)):]
90                 arg = []
91
92                 for value in values:
93                         match = isnumber.match(value)
94                         if not match:
95                                 raise Abort("bad transform value")
96
97                         arg.append(float(match.group(0)))
98
99                 num = len(arg)
100                 if cmd == "skewX":
101                         if num == 1:
102                                 matrix.skewX(arg[0])
103                                 continue
104
105                 elif cmd == "skewY":
106                         if num == 1:
107                                 matrix.skewY(arg[0])
108                                 continue
109
110                 elif cmd == "scale":
111                         if num == 1:
112                                 matrix.scale(arg[0], arg[0])
113                                 continue
114                         if num == 2:
115                                 matrix.scale(arg[0], arg[1])
116                                 continue
117
118                 elif cmd == "translate":
119                         if num == 1:
120                                 matrix.translate(arg[0], 0)
121                                 continue
122                         if num == 2:
123                                 matrix.translate(arg[0], arg[1])
124                                 continue
125
126                 elif cmd == "rotate":
127                         if num == 1:
128                                 matrix.rotate(arg[0])
129                                 continue
130                         if num == 3:
131                                 matrix.translate(-arg[1], -arg[2])
132                                 matrix.rotate(arg[0])
133                                 matrix.translate(arg[1], arg[2])
134                                 continue
135
136                 elif cmd == "matrix":
137                         if num == 6:
138                                 matrix.multiply(Matrix(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]))
139                                 continue
140
141                 else:
142                         print "ERROR: unknown transform", cmd
143                         continue
144
145                 print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
146
147         if len(s):
148                 print "ERROR: transform with trailing garbage (%s)" % s
149         return matrix
150
151
152 class import_svg:
153         # err_handler
154         def error(self, exception):
155                 raise Abort(str(exception))
156
157         def fatalError(self, exception):
158                 raise Abort(str(exception))
159
160         def warning(self, exception):
161                 print "WARNING: " + str(exception)
162
163         # doc_handler
164         def setDocumentLocator(self, whatever):
165                 pass
166
167         def startDocument(self):
168                 self.verified = False
169                 self.scandesc = False
170                 self.matrices = [None]
171                 self.meshes = {}
172                 for o in Blender.Scene.GetCurrent().objects:
173                         if o.type != "Mesh":
174                                 continue
175                         mesh = o.getData(mesh = 1)
176                         if not mesh.faceUV:
177                                 continue
178                         if mesh.name in self.meshes:
179                                 continue
180                         self.meshes[mesh.name] = mesh
181
182         def endDocument(self):
183                 pass
184
185         def characters(self, data, start, length):
186                 if not self.scandesc:
187                         return
188                 if data[start:start + length].startswith("uv_export_svg.py"):
189                         self.verified = True
190
191         def ignorableWhitespace(self, data, start, length):
192                 pass
193
194         def startElement(self, name, attrs):
195                 currmat = self.matrices[-1]
196                 if "transform" in attrs:
197                         m = parse_transform(attrs["transform"])
198                         if currmat != None:
199                                 m.multiply(currmat)
200                         self.matrices.append(m)
201                 else:
202                         self.matrices.append(currmat)
203
204                 if name == "polygon":
205                         self.handlePolygon(attrs)
206                 elif name == "svg":
207                         if "viewBox" in attrs:
208                                 x, y, w, h = commawsp.split(attrs["viewBox"], 4)
209                                 if int(x) or int(y):
210                                         raise Abort("bad viewBox")
211                                 self.width = int(w)
212                                 self.height = int(h)
213                                 if self.width != self.height:
214                                         raise Abort("viewBox isn't a square")
215                         else:
216                                 raise Abort("no viewBox")
217                 elif name == "desc" and not self.verified:
218                         self.scandesc = True
219
220         def endElement(self, name):
221                 self.scandesc = False
222                 self.matrices = self.matrices[:-1]
223
224         def handlePolygon(self, attrs):
225                 if not self.verified:
226                         raise Abort("this file wasn't written by uv_export_svg.py")
227                 ident = attrs.get("id", None)
228                 points = attrs.get("points", None)
229
230                 if not ident or not points:
231                         print('bad polygon "%s"' % ident)
232                         return
233
234                 sep = ident.find(ID_SEPARATOR)
235                 if sep < 0:
236                         print('broken id "%s"' % ident)
237                         return
238
239                 meshname = str(ident[:sep])
240                 num = int(ident[sep + len(ID_SEPARATOR):])
241
242                 if not meshname in self.meshes:
243                         print('unknown mesh "%s"' % meshname)
244                         return
245
246                 #print 'mesh %s face %d: ' % (meshname, num)
247                 matrix = self.matrices[-1]
248                 transuv = []
249                 for p in numwsp.split(points.strip()):
250                         u, v = commawsp.split(p.strip(), 2)
251                         u = float(u)
252                         v = float(v)
253                         if matrix:
254                                 u, v = matrix.transform(u, v)
255                         transuv.append((u / self.width, 1 - v / self.height))
256
257                 for i, uv in enumerate(self.meshes[meshname].faces[num].uv):
258                         uv[0] = transuv[i][0]
259                         uv[1] = transuv[i][1]
260
261
262 def run_parser(filename):
263         editmode = Blender.Window.EditMode()
264         if editmode:
265                 Blender.Window.EditMode(0)
266         Blender.Window.WaitCursor(1)
267
268         try:
269                 svg = saxexts.ParserFactory().make_parser("xml.sax.drivers.drv_xmlproc")
270                 svg.setDocumentHandler(import_svg())
271                 svg.setErrorHandler(import_svg())
272                 svg.parse(filename)
273
274         except Abort, e:
275                 print "Error:", e.msg, "  -> aborting ...\n"
276                 Blender.Draw.PupMenu("Error%t|" + e.msg)
277
278         Blender.Window.RedrawAll()
279         Blender.Window.WaitCursor(0)
280         if editmode:
281                 Blender.Window.EditMode(1)
282
283
284 active = Blender.Scene.GetCurrent().objects.active
285 (basename, extname) = Blender.sys.splitext(Blender.Get("filename"))
286 filename = Blender.sys.basename(basename) + "-" + active.name + ".svg"
287
288 registry = Blender.Registry.GetKey("UVImportExportSVG", False)
289 if registry and basename in registry:
290         filename = registry[basename]
291
292 Blender.Window.FileSelector(run_parser, "Import SVG", filename)
293