X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=utils%2FModeller%2Fyasim_import.py;h=6a862cbd6008254610d10cb76b3aeaf5c6c08d3d;hb=4dc76922c88187269957737f7a5d23017f8d51ad;hp=69b370dd66dcfbae009a9f714aa536c1d369a470;hpb=fdd252e1c5b445a305d2cd2b1693ed3922105645;p=flightgear.git diff --git a/utils/Modeller/yasim_import.py b/utils/Modeller/yasim_import.py index 69b370dd6..6a862cbd6 100644 --- a/utils/Modeller/yasim_import.py +++ b/utils/Modeller/yasim_import.py @@ -9,7 +9,7 @@ __author__ = "Melchior FRANZ < mfranz # aon : at >" __url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"] -__version__ = "0.1" +__version__ = "0.2" __bpydoc__ = """\ yasim_import.py loads and visualizes a YASim FDM geometry ========================================================= @@ -20,7 +20,7 @@ It is recommended to load the model superimposed over a greyed out and immutable (1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...") (2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty") (3) rename scene to yasim (not required) - (4) link to scene1 (F10 -> "Output" tab -> arrow button left of text entry "No Set Scene" -> "scene1") + (4) link to scene1 (F10 -> "Output" tab in "Buttons Window" -> arrow button left of text entry "No Set Scene" -> "scene1") (5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...") This is good enough for simple checks. But if you are working on the YASim configuration, then you need a @@ -58,9 +58,9 @@ Possible variables are: Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view. The cursor coordinates display in the script area, however, shows the coordinates in YASim space. -Note that object names don't contain XML indices but element numbers. YASim_hstab#2 is the third -hstab in the whole file, not necessarily in its parent XML group. A floating point part in the -object name (e.g. YASim_hstab#2.004) only means that the geometry has been reloaded that often. +Note that object names don't contain XML indices but element numbers. YASim_flap0#2 is the third +flap0 in the whole file, not necessarily in its parent XML group. A floating point part in the +object name (e.g. YASim_flap0#2.004) only means that the geometry has been reloaded that often. It's an unavoidable consequence of how Blender deals with meshes. @@ -68,22 +68,40 @@ Elements are displayed as follows: cockpit -> monkey head fuselage -> blue "tube" (with only 12 sides for less clutter); center at "a" - vstab -> red with yellow flaps - wing/mstab/hstab -> green with yellow flaps/spoilers/slats (always 20 cm deep); - symmetric surfaces are only displayed on the left side + vstab -> red with yellow control surfaces (flap0, flap1, slat, spoiler) + wing/mstab/hstab -> green with yellow control surfaces (which are always 20 cm deep); + symmetric surfaces are only displayed on the left side, unless + the "Mirror" button is active thrusters (jet/propeller/thruster) -> dashed line from center to actionpt; arrow from actionpt along thrust vector (always 1 m long); propeller circle - rotor -> radius and rel_len_blade_start circle, direction arrow, - normal and forward vector, one blade at phi0 + rotor -> radius and rel_len_blade_start circle, normal and forward vector, + one blade at phi0 with direction arrow near blade tip gear -> contact point and compression vector (no arrow head) - tank -> cube (10 cm side length) - weight -> inverted cone - ballast -> cylinder - hitch -> circle (10 cm diameter) + tank -> magenta cube (10 cm side length) + weight -> inverted cyan cone + ballast -> yellow cylinder + hitch -> hexagon (10 cm diameter) hook -> dashed line for up angle, T-line for down angle launchbar -> dashed line for up angles, T-line for down angles + (launchbar and holdback each) + +The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces +(flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage +that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value, +to [0, 0, 0]. Turning mirroring off restores the object center. + + + +Environment variable BLENDER_YASIM_IMPORT can be set to a space-separated list of options: + + $ BLENDER_YASIM_IMPORT="mirror verbose" blender + +whereby: + + verbose ... enables verbose logs + mirror ... enables mirroring of symmetric surfaces """ @@ -106,11 +124,12 @@ Elements are displayed as follows: #-------------------------------------------------------------------------------- -import Blender, BPyMessages, string, math +import Blender, BPyMessages, string, math, os from Blender.Mathutils import * from xml.sax import handler, make_parser +CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "") YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) ORIGIN = Vector(0, 0, 0) X = Vector(1, 0, 0) @@ -119,33 +138,35 @@ Z = Vector(0, 0, 1) DEG2RAD = math.pi / 180 RAD2DEG = 180 / math.pi +NO_EVENT = 0 +RELOAD_BUTTON = 1 +CURSOR_BUTTON = 2 +MIRROR_BUTTON = 3 + + class Global: + verbose = "verbose" in CONFIG path = "" matrix = None + data = None cursor = ORIGIN last_cursor = Vector(Blender.Window.GetCursorPos()) + mirror_button = Blender.Draw.Create("mirror" in CONFIG) + class Abort(Exception): - def __init__(self, msg): + def __init__(self, msg, term = None): self.msg = msg + self.term = term -def log(msg): - #print(msg) # uncomment to get verbose log messages - pass - - -def error(msg): - print(("\033[31;1mError: %s\033[m" % msg)) - Blender.Draw.PupMenu("Error%t|" + msg) +def log(msg): + if Global.verbose: + print(msg) -def getfloat(attrs, key, default): - if attrs.has_key(key): - return float(attrs[key]) - return default def draw_dashed_line(mesh, start, end): @@ -154,13 +175,14 @@ def draw_dashed_line(mesh, start, end): n = len(mesh.verts) for i in range(int(1 + 0.5 * (end - start).length / w)): a = start + 2 * i * step - b = start + (2 * i + 1) * step + b = a + step if (b - end).length < step.length: b = end mesh.verts.extend([a, b]) mesh.edges.extend([n + 2 * i, n + 2 * i + 1]) + def draw_arrow(mesh, start, end): v = end - start m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start) @@ -171,6 +193,7 @@ def draw_arrow(mesh, start, end): mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]]) + def draw_circle(mesh, numpoints, radius, matrix): n = len(mesh.verts) for i in range(numpoints): @@ -182,6 +205,7 @@ def draw_circle(mesh, numpoints, radius, matrix): mesh.edges.extend([[n + i, n + i1]]) + class Item: scene = Blender.Scene.GetCurrent() @@ -190,13 +214,21 @@ class Item: for f in mesh.faces: f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL - def set_color(self, mesh, name, color): - mat = Blender.Material.New(name) + def set_color(self, obj, color): + mat = Blender.Material.New() mat.setRGBCol(color[0], color[1], color[2]) mat.setAlpha(color[3]) mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW + obj.transp = True + + mesh = obj.getData(mesh = True) mesh.materials += [mat] + for f in mesh.faces: + f.smooth = True + mesh.calcNormals() + + class Cockpit(Item): def __init__(self, center): @@ -206,12 +238,15 @@ class Cockpit(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Tank(Item): def __init__(self, name, center): mesh = Blender.Mesh.Primitives.Cube() mesh.transform(ScaleMatrix(0.05, 4)) obj = self.scene.objects.new(mesh, name) obj.setMatrix(TranslationMatrix(center) * Global.matrix) + self.set_color(obj, [1, 0, 1, 0.5]) + class Ballast(Item): @@ -220,6 +255,8 @@ class Ballast(Item): mesh.transform(ScaleMatrix(0.05, 4)) obj = self.scene.objects.new(mesh, name) obj.setMatrix(TranslationMatrix(center) * Global.matrix) + self.set_color(obj, [1, 1, 0, 0.5]) + class Weight(Item): @@ -228,6 +265,8 @@ class Weight(Item): mesh.transform(ScaleMatrix(0.05, 4)) obj = self.scene.objects.new(mesh, name) obj.setMatrix(TranslationMatrix(center) * Global.matrix) + self.set_color(obj, [0, 1, 1, 0.5]) + class Gear(Item): @@ -239,6 +278,7 @@ class Gear(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Hook(Item): def __init__(self, name, center, length, up_angle, dn_angle): mesh = Blender.Mesh.New() @@ -252,6 +292,7 @@ class Hook(Item): obj.setMatrix(TranslationMatrix(center) * Global.matrix) + class Launchbar(Item): def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle): mesh = Blender.Mesh.New() @@ -266,13 +307,15 @@ class Launchbar(Item): obj.setMatrix(TranslationMatrix(lb) * Global.matrix) + class Hitch(Item): def __init__(self, name, center): - mesh = Blender.Mesh.Primitives.Circle(8, 0.1) + mesh = Blender.Mesh.Primitives.Circle(6, 0.1) obj = self.scene.objects.new(mesh, name) obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix) + class Thrust: def set_actionpt(self, p): self.actionpt = p @@ -281,6 +324,7 @@ class Thrust: self.thrustvector = d + class Thruster(Thrust, Item): def __init__(self, name, center, thrustvector): (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector) @@ -294,6 +338,7 @@ class Thruster(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Propeller(Thrust, Item): def __init__(self, name, center, radius): (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X) @@ -313,6 +358,7 @@ class Propeller(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Jet(Thrust, Item): def __init__(self, name, center, rotate): (self.name, self.center, self.actionpt) = (name, center, center) @@ -327,6 +373,7 @@ class Jet(Thrust, Item): obj.setMatrix(TranslationMatrix(self.center) * Global.matrix) + class Fuselage(Item): def __init__(self, name, a, b, width, taper, midpoint): numvert = 12 @@ -351,10 +398,10 @@ class Fuselage(Item): mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]]) mesh.verts.extend([ORIGIN, length * X]) - self.set_color(mesh, name + "mat", [0, 0, 0.5, 0.4]) obj = self.scene.objects.new(mesh, name) - obj.transp = True obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix) + self.set_color(obj, [0, 0, 0.5, 0.4]) + class Rotor(Item): @@ -375,17 +422,18 @@ class Rotor(Item): draw_arrow(mesh, ORIGIN, up * invert) draw_arrow(mesh, ORIGIN, fwd * invert) b += 0.1 * X + direction * chord * Y - draw_arrow(mesh, b, b + 0.5 * radius * direction * Y) + draw_arrow(mesh, b, b + min(0.5 * radius, 1) * direction * Y) obj = self.scene.objects.new(mesh, name) obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix) + class Wing(Item): def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral): # <1--0--2 # \ | / # 4-3-5 - is_vstab = name.startswith("YASim_vstab") + self.is_symmetric = not name.startswith("YASim_vstab#") mesh = Blender.Mesh.New() mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X]) tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X @@ -394,16 +442,23 @@ class Wing(Item): mesh.verts.extend([tip, tipfore, tipaft]) mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]]) - self.set_color(mesh, name + "mat", [[0, 0.5, 0, 0.5], [0.5, 0, 0, 0.5]][is_vstab]) self.make_twosided(mesh) obj = self.scene.objects.new(mesh, name) - obj.transp = True - m = Euler(dihedral, -incidence, 0).toMatrix().resize4x4() - m *= TranslationMatrix(root) - obj.setMatrix(m * Global.matrix) + mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4()) + self.set_color(obj, [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric]) (self.obj, self.mesh) = (obj, mesh) + if self.is_symmetric and Global.mirror_button.val: + mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR) + mod[Blender.Modifier.Settings.AXIS_X] = False + mod[Blender.Modifier.Settings.AXIS_Y] = True + mod[Blender.Modifier.Settings.AXIS_Z] = False + mesh.transform(TranslationMatrix(root)) # must move object center to x axis + obj.setMatrix(Global.matrix) + else: + obj.setMatrix(TranslationMatrix(root) * Global.matrix) + def add_flap(self, name, start, end): a = Vector(self.mesh.verts[2].co) b = Vector(self.mesh.verts[5].co) @@ -416,55 +471,57 @@ class Wing(Item): mesh.verts.extend([i0, i1, i0 + c, i1 + c]) mesh.faces.extend([[0, 1, 3, 2]]) - self.set_color(mesh, name + "mat", [0.8, 0.8, 0, 0.9]) self.make_twosided(mesh) obj = self.scene.objects.new(mesh, name) - obj.transp = True obj.setMatrix(m) + self.set_color(obj, [0.8, 0.8, 0, 0.9]) + + if self.is_symmetric and Global.mirror_button.val: + mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR) + mod[Blender.Modifier.Settings.AXIS_X] = False + mod[Blender.Modifier.Settings.AXIS_Y] = True + mod[Blender.Modifier.Settings.AXIS_Z] = False -class import_yasim(handler.ContentHandler): + +class import_yasim(handler.ErrorHandler, handler.ContentHandler): ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \ "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \ "rotorgear", "tow", "winch", "solve-weight"] + # err_handler + def warning(self, exception): + print((self.error_string("Warning", exception))) + def error(self, exception): - raise Abort(str(exception)) + print((self.error_string("Error", exception))) def fatalError(self, exception): - raise Abort(str(exception)) + raise Abort(str(exception), self.error_string("Fatal", exception)) + + def error_string(self, tag, e): + (column, line) = (e.getColumnNumber(), e.getLineNumber()) + return "%s: %s\n%s%s^" % (tag, str(e), Global.data[line - 1], column * ' ') - def warning(self, exception): - print(("WARNING: " + str(exception))) # doc_handler - def setDocumentLocator(self, whatever): - pass + def setDocumentLocator(self, locator): + self.locator = locator def startDocument(self): self.tags = [] self.counter = {} self.items = [None] - pass def endDocument(self): for o in Item.scene.objects: o.sel = True - def characters(self, data): - pass - - def ignorableWhitespace(self, data, start, length): - pass - - def processingInstruction(self, target, data): - pass - def startElement(self, tag, attrs): if len(self.tags) == 0 and tag != "airplane": - raise Abort("this isn't a YASim config file") + raise Abort("this isn't a YASim config file (bad root tag at line %d)" % self.locator.getLineNumber()) self.tags.append(tag) path = string.join(self.tags, '/') @@ -485,15 +542,15 @@ class import_yasim(handler.ContentHandler): a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"])) b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"])) width = float(attrs["width"]) - taper = getfloat(attrs, "taper", 1) - midpoint = getfloat(attrs, "midpoint", 0.5) + taper = float(attrs.get("taper", 1)) + midpoint = float(attrs.get("midpoint", 0.5)) log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \ (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint)) item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint) elif tag == "gear": c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) - compression = getfloat(attrs, "compression", 1) + compression = float(attrs.get("compression", 1)) up = Z * compression if attrs.has_key("upx"): up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression @@ -503,7 +560,7 @@ class import_yasim(handler.ContentHandler): elif tag == "jet": c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) - rotate = getfloat(attrs, "rotate", 0.0) + rotate = float(attrs.get("rotate", 0)) log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate)) item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate) @@ -521,7 +578,8 @@ class import_yasim(handler.ContentHandler): elif tag == "actionpt": if not isinstance(parent, Thrust): - raise Abort("%s is not part of a thruster/propeller/jet" % path) + raise Abort("%s is not part of a thruster/propeller/jet at line %d" \ + % (path, self.locator.getLineNumber())) c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2])) @@ -529,7 +587,8 @@ class import_yasim(handler.ContentHandler): elif tag == "dir": if not isinstance(parent, Thrust): - raise Abort("%s is not part of a thruster/propeller/jet" % path) + raise Abort("%s is not part of a thruster/propeller/jet at line %d" \ + % (path, self.locator.getLineNumber())) c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2])) @@ -552,9 +611,9 @@ class import_yasim(handler.ContentHandler): elif tag == "hook": c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) - length = getfloat(attrs, "length", 1.0) - up_angle = getfloat(attrs, "up-angle", 0.0) - down_angle = getfloat(attrs, "down-angle", 70.0) + length = float(attrs.get("length", 1)) + up_angle = float(attrs.get("up-angle", 0)) + down_angle = float(attrs.get("down-angle", 70)) log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \ % (tag, c[0], c[1], c[2], length, up_angle, down_angle)) item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle) @@ -566,11 +625,11 @@ class import_yasim(handler.ContentHandler): elif tag == "launchbar": c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) - length = getfloat(attrs, "length", 1.0) - up_angle = getfloat(attrs, "up-angle", -45.0) - down_angle = getfloat(attrs, "down-angle", 45.0) - holdback = Vector(getfloat(attrs, "holdback-x", c[0]), getfloat(attrs, "holdback-y", c[1]), getfloat(attrs, "holdback-z", c[2])) - holdback_length = getfloat(attrs, "holdback-length", 2.0) + length = float(attrs.get("length", 1)) + up_angle = float(attrs.get("up-angle", -45)) + down_angle = float(attrs.get("down-angle", 45)) + holdback = Vector(float(attrs.get("holdback-x", c[0])), float(attrs.get("holdback-y", c[1])), float(attrs.get("holdback-z", c[2]))) + holdback_length = float(attrs.get("holdback-length", 2)) log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \ % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \ holdback[0], holdback[1], holdback[2], holdback_length)) @@ -580,18 +639,19 @@ class import_yasim(handler.ContentHandler): root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"])) length = float(attrs["length"]) chord = float(attrs["chord"]) - incidence = getfloat(attrs, "incidence", 0.0) - twist = getfloat(attrs, "twist", 0.0) - taper = getfloat(attrs, "taper", 1.0) - sweep = getfloat(attrs, "sweep", 0.0) - dihedral = getfloat(attrs, "dihedral", [0.0, 90.0][tag == "vstab"]) + incidence = float(attrs.get("incidence", 0)) + twist = float(attrs.get("twist", 0)) + taper = float(attrs.get("taper", 1)) + sweep = float(attrs.get("sweep", 0)) + dihedral = float(attrs.get("dihedral", [0, 90][tag == "vstab"])) log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \ % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral)) item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral) elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler": if not isinstance(parent, Wing): - raise Abort("%s is not part of a wing or stab" % path) + raise Abort("%s is not part of a wing or stab at line %d" \ + % (path, self.locator.getLineNumber())) start = float(attrs["start"]) end = float(attrs["end"]) @@ -599,17 +659,17 @@ class import_yasim(handler.ContentHandler): parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end) elif tag == "rotor": - c = Vector(getfloat(attrs, "x", 0.0), getfloat(attrs, "y", 0.0), getfloat(attrs, "z", 0.0)) - norm = Vector(getfloat(attrs, "nx", 0.0), getfloat(attrs, "ny", 0.0), getfloat(attrs, "nz", 1.0)) - fwd = Vector(getfloat(attrs, "fx", 1.0), getfloat(attrs, "fy", 0.0), getfloat(attrs, "fz", 0.0)) - diameter = getfloat(attrs, "diameter", 10.2) - numblades = int(getfloat(attrs, "numblades", 4)) - chord = getfloat(attrs, "chord", 0.3) - twist = getfloat(attrs, "twist", 0.0) - taper = getfloat(attrs, "taper", 1.0) - rel_len_blade_start = getfloat(attrs, "rel-len-blade-start", 0.0) - phi0 = getfloat(attrs, "phi0", 0) - ccw = not not getfloat(attrs, "ccw", 0) + c = Vector(float(attrs.get("x", 0)), float(attrs.get("y", 0)), float(attrs.get("z", 0))) + norm = Vector(float(attrs.get("nx", 0)), float(attrs.get("ny", 0)), float(attrs.get("nz", 1))) + fwd = Vector(float(attrs.get("fx", 1)), float(attrs.get("fy", 0)), float(attrs.get("fz", 0))) + diameter = float(attrs.get("diameter", 10.2)) + numblades = int(attrs.get("numblades", 4)) + chord = float(attrs.get("chord", 0.3)) + twist = float(attrs.get("twist", 0)) + taper = float(attrs.get("taper", 1)) + rel_len_blade_start = float(attrs.get("rel-len-blade-start", 0)) + phi0 = float(attrs.get("phi0", 0)) + ccw = not not int(attrs.get("ccw", 0)) log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \ + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \ @@ -628,11 +688,11 @@ class import_yasim(handler.ContentHandler): self.items.pop() -def extract_matrix(path, tag): + +def extract_matrix(filedata, tag): v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 } has_offsets = False - f = open(path) - for line in f.readlines(): + for line in filedata: line = string.strip(line) if not line.startswith(""): continue @@ -644,23 +704,21 @@ def extract_matrix(path, tag): (key, value) = string.split(assignment, '=', 2) v[string.strip(key)] = float(string.strip(value)) has_offsets = True - f.close() - matrix = None - if has_offsets: - print(("using offsets: x=%f y=%f z=%f h=%f p=%f r=%f" % (v['x'], v['y'], v['z'], v['h'], v['p'], v['r']))) - matrix = Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() - matrix *= TranslationMatrix(Vector(v['x'], v['y'], v['z'])) - return matrix + + if not has_offsets: + return None + + print(("using offsets: x=%f y=%f z=%f h=%f p=%f r=%f" % (v['x'], v['y'], v['z'], v['h'], v['p'], v['r']))) + return Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() * TranslationMatrix(Vector(v['x'], v['y'], v['z'])) -def run_parser(path): + +def load_yasim_config(path): if BPyMessages.Error_NoFile(path): return - editmode = Blender.Window.EditMode() - if editmode: - Blender.Window.EditMode(0) Blender.Window.WaitCursor(1) + Blender.Window.EditMode(0) print(("loading '%s'" % path)) try: @@ -668,80 +726,99 @@ def run_parser(path): if o.name.startswith("YASim_"): Item.scene.objects.unlink(o) + f = open(path) + Global.data = f.readlines() + f.close + + Global.path = path Global.matrix = YASIM_MATRIX - matrix = extract_matrix(path, "offsets") + matrix = extract_matrix(Global.data, "offsets") if matrix: Global.matrix *= matrix.invert() - yasim = make_parser() - yasim.setContentHandler(import_yasim()) - yasim.setErrorHandler(import_yasim()) - yasim.parse(path) - + Global.yasim.parse(path) Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False) - Global.path = path + Global.data = None except Abort, e: - print "Error:", e.msg, " -> aborting ...\n" + print(("%s\nAborting ..." % (e.term or e.msg))) Blender.Draw.PupMenu("Error%t|" + e.msg) Blender.Window.RedrawAll() Blender.Window.WaitCursor(0) - if editmode: - Blender.Window.EditMode(1) -def draw(): + +def gui_draw(): from Blender import BGL, Draw (width, height) = Blender.Window.GetAreaSize() BGL.glClearColor(0.4, 0.4, 0.45, 1) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) - Draw.PushButton("Reload YASim", 0, 5, 5, 100, 28) - Draw.PushButton("Update Cursor", 1, width - 650, 5, 100, 28) + BGL.glColor3f(1, 1, 1) + BGL.glRasterPos2f(5, 55) + Draw.Text("FlightGear YASim Import: '%s'" % Global.path) - BGL.glRasterPos2f(120, 15) - Draw.Text(Global.path) + Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file") + Global.mirror_button = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror_button.val, \ + "show symmetric surfaces on both sides (reloads config)") + Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display (in YASim coordinate system)") - BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Distance from last") - Blender.Draw.GetStringWidth("Current"), 24) + BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24) Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor)) c = Global.cursor - Global.last_cursor BGL.glRasterPos2f(width - 530, 7) - Draw.Text("Distance from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f" % (c[0], c[1], c[2], c.length)) + Draw.Text("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f m" % (c[0], c[1], c[2], c.length)) -def event(ev, value): + +def gui_event(ev, value): if ev == Blender.Draw.ESCKEY: Blender.Draw.Exit() -def button(n): - if n == 0: - run_parser(Global.path) - elif n == 1: + +def gui_button(n): + if n == NO_EVENT: + return + + elif n == RELOAD_BUTTON: + load_yasim_config(Global.path) + + elif n == CURSOR_BUTTON: Global.last_cursor = Global.cursor Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert() d = Global.cursor - Global.last_cursor print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \ % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length))) + + elif n == MIRROR_BUTTON: + load_yasim_config(Global.path) + Blender.Draw.Redraw(1) + def main(): + log(6 * "\n") registry = Blender.Registry.GetKey("FGYASimImportExport", False) if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])): path = registry["path"] else: path = "" + xml_handler = import_yasim() + Global.yasim = make_parser() + Global.yasim.setContentHandler(xml_handler) + Global.yasim.setErrorHandler(xml_handler) - log(6 * "\n") if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT): - Blender.Draw.Register(draw, event, button) + Blender.Draw.Register(gui_draw, gui_event, gui_button) + + Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path) - Blender.Window.FileSelector(run_parser, "Import YASim Configuration File", path) main()