From 3510c71f2ba962562c9e17fabe0997129b304279 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Wed, 15 Jun 2016 21:24:03 -0300 Subject: [PATCH] Arch: First version of Sweethome3D importer - issue #2584 --- src/Mod/Arch/CMakeLists.txt | 1 + src/Mod/Arch/Init.py | 1 + src/Mod/Arch/importSH3D.py | 205 ++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 src/Mod/Arch/importSH3D.py diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt index 9865e011f..d778e8ff4 100644 --- a/src/Mod/Arch/CMakeLists.txt +++ b/src/Mod/Arch/CMakeLists.txt @@ -37,6 +37,7 @@ SET(Arch_SRCS ArchProfile.py import3DS.py ArchPrecast.py + importSH3D.py ) SET(Dice3DS_SRCS diff --git a/src/Mod/Arch/Init.py b/src/Mod/Arch/Init.py index 46ebd63bb..cf397746a 100644 --- a/src/Mod/Arch/Init.py +++ b/src/Mod/Arch/Init.py @@ -29,3 +29,4 @@ FreeCAD.addExportType("WebGL file (*.html)","importWebGL") FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") FreeCAD.addImportType("3D Studio mesh (*.3ds)","import3DS") +FreeCAD.addImportType("SweetHome3D XML export (*.zip)","importSH3D") diff --git a/src/Mod/Arch/importSH3D.py b/src/Mod/Arch/importSH3D.py new file mode 100644 index 000000000..67f48cefd --- /dev/null +++ b/src/Mod/Arch/importSH3D.py @@ -0,0 +1,205 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2016 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +__title__ = "FreeCAD SweetHome3D importer" +__author__ = "Yorik van Havre" +__url__ = "http://www.freecadweb.org" + +import os,zipfile,xml.sax,FreeCAD,Part,Draft,Arch,Mesh,tempfile,math,Sketcher + +DEBUG = True + +if open.__module__ == '__builtin__': + pyopen = open # because we'll redefine open below + + +def open(filename): + "called when freecad wants to open a file" + docname = (os.path.splitext(os.path.basename(filename))[0]).encode("utf8") + doc = FreeCAD.newDocument(docname) + doc.Label = decode(docname) + FreeCAD.ActiveDocument = doc + read(filename) + return doc + + +def insert(filename,docname): + "called when freecad wants to import a file" + try: + doc = FreeCAD.getDocument(docname) + except NameError: + doc = FreeCAD.newDocument(docname) + FreeCAD.ActiveDocument = doc + read(filename) + return doc + + +def decode(name): + "decodes encoded strings" + try: + decodedName = (name.decode("utf8")) + except UnicodeDecodeError: + try: + decodedName = (name.decode("latin1")) + except UnicodeDecodeError: + FreeCAD.Console.PrintError(translate("Arch","Error: Couldn't determine character encoding")) + decodedName = name + return decodedName + + +def read(filename): + "reads the file and creates objects in the active document" + + z = zipfile.ZipFile(filename) + homexml = z.read("Home.xml") + handler = SH3DHandler(z) + xml.sax.parseString(homexml,handler) + FreeCAD.ActiveDocument.recompute() + if not handler.makeIndividualWalls: + delete = [] + walls = [] + for k,lines in handler.lines.items(): + sk = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","Walls_trace") + for l in lines: + for edge in l.Shape.Edges: + sk.addGeometry(edge.Curve) + delete.append(l.Name) + FreeCAD.ActiveDocument.recompute() + k = k.split(";") + walls.append(Arch.makeWall(baseobj=sk,width=float(k[0]),height=float(k[1]))) + for d in delete: + FreeCAD.ActiveDocument.removeObject(d) + w = walls.pop() + w.Additions = walls + w.Subtractions = handler.windows + g = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Furniture") + g.Group = handler.furniture + FreeCAD.ActiveDocument.recompute() + + +class SH3DHandler(xml.sax.ContentHandler): + + def __init__(self,z): + + self.makeIndividualWalls = False + self.z = z + self.windows = [] + self.furniture = [] + self.lines = {} + + def startElement(self, tag, attributes): + + if tag == "wall": + name = attributes["id"] + p1 = FreeCAD.Vector(float(attributes["xStart"])*10,float(attributes["yStart"])*10,0) + p2 = FreeCAD.Vector(float(attributes["xEnd"])*10,float(attributes["yEnd"])*10,0) + height = float(attributes["height"])*10 + thickness = float(attributes["thickness"])*10 + if DEBUG: print "Creating wall: ",name + line = Draft.makeLine(p1,p2) + if self.makeIndividualWalls: + wall = Arch.makeWall(baseobj=line,width=thickness,height=height,name=name) + wall.Label = name + else: + self.lines.setdefault(str(thickness)+";"+str(height),[]).append(line) + + elif tag == "pieceOfFurniture": + name = attributes["name"] + data = self.z.read(attributes["model"]) + tf = tempfile.mkstemp(suffix=".obj")[1] + f = pyopen(tf,"wb") + f.write(data) + f.close() + m = Mesh.read(tf) + fx = (float(attributes["width"])/100)/m.BoundBox.XLength + fy = (float(attributes["height"])/100)/m.BoundBox.YLength + fz = (float(attributes["depth"])/100)/m.BoundBox.ZLength + mat = FreeCAD.Matrix() + mat.scale(1000*fx,1000*fy,1000*fz) + mat.rotateX(math.pi/2) + mat.rotateZ(math.pi) + if DEBUG: print "Creating furniture: ",name + if "angle" in attributes.keys(): + mat.rotateZ(float(attributes["angle"])) + m.transform(mat) + os.remove(tf) + p = m.BoundBox.Center.negative() + p = p.add(FreeCAD.Vector(float(attributes["x"])*10,float(attributes["y"])*10,0)) + p = p.add(FreeCAD.Vector(0,0,m.BoundBox.Center.z-m.BoundBox.ZMin)) + m.Placement.Base = p + obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",name) + obj.Mesh = m + self.furniture.append(obj) + + elif tag == "doorOrWindow": + name = attributes["name"] + data = self.z.read(attributes["model"]) + tf = tempfile.mkstemp(suffix=".obj")[1] + f = pyopen(tf,"wb") + f.write(data) + f.close() + m = Mesh.read(tf) + fx = (float(attributes["width"])/100)/m.BoundBox.XLength + fy = (float(attributes["height"])/100)/m.BoundBox.YLength + fz = (float(attributes["depth"])/100)/m.BoundBox.ZLength + mat = FreeCAD.Matrix() + mat.scale(1000*fx,1000*fy,1000*fz) + mat.rotateX(math.pi/2) + m.transform(mat) + b = m.BoundBox + v1 = FreeCAD.Vector(b.XMin,b.YMin-500,b.ZMin) + v2 = FreeCAD.Vector(b.XMax,b.YMin-500,b.ZMin) + v3 = FreeCAD.Vector(b.XMax,b.YMax+500,b.ZMin) + v4 = FreeCAD.Vector(b.XMin,b.YMax+500,b.ZMin) + sub = Part.makePolygon([v1,v2,v3,v4,v1]) + sub = Part.Face(sub) + sub = sub.extrude(FreeCAD.Vector(0,0,b.ZLength)) + os.remove(tf) + shape = Arch.getShapeFromMesh(m) + if not shape: + shape=Part.Shape() + shape.makeShapeFromMesh(m.Topology,0.100000) + shape = shape.removeSplitter() + if shape: + if DEBUG: print "Creating window: ",name + if "angle" in attributes.keys(): + shape.rotate(shape.BoundBox.Center,FreeCAD.Vector(0,0,1),math.degrees(float(attributes["angle"]))) + sub.rotate(shape.BoundBox.Center,FreeCAD.Vector(0,0,1),math.degrees(float(attributes["angle"]))) + p = shape.BoundBox.Center.negative() + p = p.add(FreeCAD.Vector(float(attributes["x"])*10,float(attributes["y"])*10,0)) + p = p.add(FreeCAD.Vector(0,0,shape.BoundBox.Center.z-shape.BoundBox.ZMin)) + if "elevation" in attributes.keys(): + p = p.add(FreeCAD.Vector(0,0,float(attributes["elevation"])*10)) + shape.translate(p) + sub.translate(p) + obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body") + obj.Shape = shape + subobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_sub") + subobj.Shape = sub + if FreeCAD.GuiUp: + subobj.ViewObject.hide() + win = Arch.makeWindow(baseobj=obj,name=name) + win.Label = name + win.Subvolume = subobj + self.windows.append(win) + else: + print("importSH3D: Error creating shape for door/window "+name)