From: mfranz Date: Wed, 5 Mar 2008 12:30:01 +0000 (+0000) Subject: UV<->SVG exporter and importer: The importer loads an SVG file that was X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=2fcae160a136568557322905614e63ef028577ec;p=flightgear.git UV<->SVG exporter and importer: The importer loads an SVG file that was saved before by the exporter. SVG editors like Inkscape can be used to move the UV outlines around, to rotate and scale them. --- diff --git a/utils/Modeller/uv_export_svg.py b/utils/Modeller/uv_export_svg.py new file mode 100644 index 000000000..a270fe13a --- /dev/null +++ b/utils/Modeller/uv_export_svg.py @@ -0,0 +1,130 @@ +#!BPY + +# """ +# Name: 'SVG: Export UV layout to SVG file' +# Blender: 245 +# Group: 'UV' +# Tooltip: 'Export selected objects to SVG file' +# """ + +__author__ = "Melchior FRANZ < mfranz # aon : at >" +__url__ = "http://members.aon.at/mfranz/flightgear/" +__version__ = "0.1" +__bpydoc__ = """\ +Saves the UV mappings of all selected files to an SVG file. The uv_import_svg.py +script can be used to re-import such a file. Each object and each group of adjacent +faces therein will be put into an SVG group. +""" + +ID_SEPARATOR = '#' + + +import Blender, sys + + +class Abort(Exception): + def __init__(self, msg): + self.msg = msg + + +def get_adjacent(pool): + i, face = pool.popitem() + group = [face] + + uvcoords = {} + for c in face.uv: + uvcoords[(c[0], c[1])] = True + + while True: + found = [] + for face in pool.itervalues(): + for c in face.uv: + if (c[0], c[1]) in uvcoords: + for d in face.uv: + uvcoords[(d[0], d[1])] = True + found.append(face) + break + if not found: + break + for face in found: + group.append(face) + del pool[face.index] + + return group + + +def write_svg(filename): + size = Blender.Draw.PupMenu("Image size%t|128|256|512|1024|2048|4096|8192") + if size < 0: + raise Abort('no image size chosen') + size = 1 << (size + 6) + + print "exporting to '%s' (size %d) ... " % (filename, size), + svg = open(filename, "w") + svg.write('\n') + svg.write('\n\n') + svg.write('\n' + % (size, size, size, size)) + svg.write("\tuv_export_svg.py: %s\n" % filename); + svg.write('\t\n' + % (size, size, 1.0)) + + unique_meshes = {} + for o in Blender.Scene.GetCurrent().objects.selected: + if o.type != "Mesh": + continue + + mesh = o.getData(mesh = 1) + if not mesh.faceUV: + continue + if mesh.name in unique_meshes: + #print "dropping duplicate mesh", mesh.name, "of object", o.name + continue + unique_meshes[mesh.name] = True + + svg.write('\t\n' % (o.name, o.name)) + + pool = {} + for f in mesh.faces: + pool[f.index] = f + + while len(pool): + svg.write('\t\t\n') + for f in get_adjacent(pool): + svg.write('\t\t\t\n' % (mesh.name, ID_SEPARATOR, f.index)) + svg.write('\t\t\n') + + svg.write("\t\n") + + svg.write('\n') + svg.close() + print "done." + + +def export(filename): + registry = {} + registry[basename] = Blender.sys.basename(filename) + Blender.Registry.SetKey("UVImportExportSVG", registry, False) + + editmode = Blender.Window.EditMode() + if editmode: + Blender.Window.EditMode(0) + + try: + write_svg(filename) + except Abort, e: + print "Error:", e.msg, " -> aborting ...\n" + Blender.Draw.PupMenu("Error%t|" + e.msg) + + if editmode: + Blender.Window.EditMode(1) + + +active = Blender.Scene.GetCurrent().objects.active +(basename, extname) = Blender.sys.splitext(Blender.Get("filename")) +filename = Blender.sys.basename(basename) + "-" + active.name + ".svg" +Blender.Window.FileSelector(export, "Export to SVG", filename) diff --git a/utils/Modeller/uv_import_svg.py b/utils/Modeller/uv_import_svg.py new file mode 100755 index 000000000..4d6fd4176 --- /dev/null +++ b/utils/Modeller/uv_import_svg.py @@ -0,0 +1,293 @@ +#!BPY + +# """ +# Name: 'SVG: Re-Import UV layout from SVG file' +# Blender: 245 +# Group: 'UV' +# Tooltip: 'Re-import UV layout from SVG file' +# """ + +__author__ = "Melchior FRANZ < mfranz # aon : at >" +__url__ = "http://members.aon.at/mfranz/flightgear/" +__version__ = "0.1" +__bpydoc__ = """\ +Imports an SVG file containing UV maps, which has been saved by the +uv_export.svg script. This allows to move, scale, and rotate object +mapping in SVG editors like Inkscape. Note that all contained UV maps +will be set, no matter which objects are actually selected at the moment. +The choice has been made when the file was saved! +""" + + +ID_SEPARATOR = '#' + + +import Blender, sys, math, re +from xml.sax import saxexts + + +registry = {} +numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])') +commawsp = re.compile('\s+|\s*,\s*') +istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*') +isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$') + + +class Abort(Exception): + def __init__(self, msg): + self.msg = msg + + +class Matrix: + def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0): + self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f + + def __str__(self): + return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f) + + def multiply(self, mat): + a = self.a * mat.a + self.c * mat.b + b = self.b * mat.a + self.d * mat.b + c = self.a * mat.c + self.c * mat.d + d = self.b * mat.c + self.d * mat.d + e = self.a * mat.e + self.c * mat.f + self.e + f = self.b * mat.e + self.d * mat.f + self.f + self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f + + def transform(self, u, v): + x = u * self.a + v * self.c + self.e + y = u * self.b + v * self.d + self.f + return (x, y) + + def translate(self, dx, dy): + self.multiply(Matrix(1, 0, 0, 1, dx, dy)) + + def scale(self, sx, sy): + self.multiply(Matrix(sx, 0, 0, sy, 0, 0)) + + def rotate(self, a): + a *= math.pi / 180 + self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0)) + + def skewX(self, a): + a *= math.pi / 180 + self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0)) + + def skewY(self, a): + a *= math.pi / 180 + self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0)) + + +def parse_transform(s): + matrix = Matrix() + while True: + match = istrans.match(s) + if not match: + break + cmd = match.group(1) + values = commawsp.split(match.group(2).strip()) + s = s[len(match.group(0)):] + arg = [] + + for value in values: + match = isnumber.match(value) + if not match: + raise Abort("bad transform value") + + arg.append(float(match.group(0))) + + num = len(arg) + if cmd == "skewX": + if num == 1: + matrix.skewX(arg[0]) + continue + + elif cmd == "skewY": + if num == 1: + matrix.skewY(arg[0]) + continue + + elif cmd == "scale": + if num == 1: + matrix.scale(arg[0], arg[0]) + continue + if num == 2: + matrix.scale(arg[0], arg[1]) + continue + + elif cmd == "translate": + if num == 1: + matrix.translate(arg[0], 0) + continue + if num == 2: + matrix.translate(arg[0], arg[1]) + continue + + elif cmd == "rotate": + if num == 1: + matrix.rotate(arg[0]) + continue + if num == 3: + matrix.translate(-arg[1], -arg[2]) + matrix.rotate(arg[0]) + matrix.translate(arg[1], arg[2]) + continue + + elif cmd == "matrix": + if num == 6: + matrix.multiply(Matrix(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5])) + continue + + else: + print "ERROR: unknown transform", cmd + continue + + print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num) + + if len(s): + print "ERROR: transform with trailing garbage (%s)" % s + return matrix + + +class import_svg: + # err_handler + def error(self, exception): + raise Abort(str(exception)) + + def fatalError(self, exception): + raise Abort(str(exception)) + + def warning(self, exception): + print "WARNING: " + str(exception) + + # doc_handler + def setDocumentLocator(self, whatever): + pass + + def startDocument(self): + self.verified = False + self.scandesc = False + self.matrices = [None] + self.meshes = {} + for o in Blender.Scene.GetCurrent().objects: + if o.type != "Mesh": + continue + mesh = o.getData(mesh = 1) + if not mesh.faceUV: + continue + if mesh.name in self.meshes: + continue + self.meshes[mesh.name] = mesh + + def endDocument(self): + pass + + def characters(self, data, start, length): + if not self.scandesc: + return + if data[start:start + length].startswith("uv_export_svg.py"): + self.verified = True + + def ignorableWhitespace(self, data, start, length): + pass + + def startElement(self, name, attrs): + currmat = self.matrices[-1] + if "transform" in attrs: + m = parse_transform(attrs["transform"]) + if currmat != None: + m.multiply(currmat) + self.matrices.append(m) + else: + self.matrices.append(currmat) + + if name == "polygon": + self.handlePolygon(attrs) + elif name == "svg": + if "viewBox" in attrs: + x, y, w, h = commawsp.split(attrs["viewBox"], 4) + if int(x) or int(y): + raise Abort("bad viewBox") + self.width = int(w) + self.height = int(h) + if self.width != self.height: + raise Abort("viewBox isn't a square") + else: + raise Abort("no viewBox") + elif name == "desc" and not self.verified: + self.scandesc = True + + def endElement(self, name): + self.scandesc = False + self.matrices = self.matrices[:-1] + + def handlePolygon(self, attrs): + if not self.verified: + raise Abort("this file wasn't written by uv_export_svg.py") + ident = attrs.get("id", None) + points = attrs.get("points", None) + + if not ident or not points: + print('bad polygon "%s"' % ident) + return + + sep = ident.find(ID_SEPARATOR) + if sep < 0: + print('broken id "%s"' % ident) + return + + meshname = str(ident[:sep]) + num = int(ident[sep + 1:]) + + if not meshname in self.meshes: + print('unknown mesh "%s"' % meshname) + return + + #print 'mesh %s face %d: ' % (meshname, num) + matrix = self.matrices[-1] + transuv = [] + for p in numwsp.split(points.strip()): + u, v = commawsp.split(p.strip(), 2) + u = float(u) + v = float(v) + if matrix: + u, v = matrix.transform(u, v) + transuv.append((u / self.width, 1 - v / self.height)) + + for i, uv in enumerate(self.meshes[meshname].faces[num].uv): + uv[0] = transuv[i][0] + uv[1] = transuv[i][1] + + +def run_parser(filename): + editmode = Blender.Window.EditMode() + if editmode: + Blender.Window.EditMode(0) + Blender.Window.WaitCursor(1) + + try: + svg = saxexts.ParserFactory().make_parser("xml.sax.drivers.drv_xmlproc") + svg.setDocumentHandler(import_svg()) + svg.setErrorHandler(import_svg()) + svg.parse(filename) + + except Abort, e: + print "Error:", e.msg, " -> aborting ...\n" + Blender.Draw.PupMenu("Error%t|" + e.msg) + + Blender.Window.RedrawAll() + Blender.Window.WaitCursor(0) + if editmode: + Blender.Window.EditMode(1) + + +active = Blender.Scene.GetCurrent().objects.active +(basename, extname) = Blender.sys.splitext(Blender.Get("filename")) +filename = Blender.sys.basename(basename) + "-" + active.name + ".svg" + +registry = Blender.Registry.GetKey("UVImportExportSVG", False) +if registry and basename in registry: + filename = registry[basename] + +Blender.Window.FileSelector(run_parser, "Import SVG", filename) +