#*************************************************************************** #* * #* 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 * #* * #*************************************************************************** from __future__ import print_function __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 ## @package importSH3D # \ingroup ARCH # \brief SH3D (SweetHome3D) file format importer # # This module provides tools to import SH3D files created from Sweet Home 3D. DEBUG = True if open.__module__ in ['__builtin__','io']: 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"]) th,tf = tempfile.mkstemp(suffix=".obj") f = pyopen(tf,"wb") f.write(data) f.close() os.close(th) 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"]) th,tf = tempfile.mkstemp(suffix=".obj") f = pyopen(tf,"wb") f.write(data) f.close() os.close(th) 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)