#*************************************************************************** #* Copyright (c) 2011 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 * #* * #*************************************************************************** import FreeCAD import WorkingPlane import math import Draft import ArchCommands import DraftVecUtils import ArchComponent import os import re import tempfile import uuid import time from FreeCAD import Vector if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from DraftTools import translate from pivy import coin from PySide.QtCore import QT_TRANSLATE_NOOP else: # \cond def translate(ctxt,txt): return txt def QT_TRANSLATE_NOOP(ctxt,txt): return txt # \endcond ## @package ArchSectionPlane # \ingroup ARCH # \brief The Section plane object and tools # # This module provides tools to build Section plane objects. # It also contains functionality to produce SVG rendering of # section planes, to be used in TechDraw and Drawing modules ISRENDERING = False # flag to prevent concurrent runs of the coin renderer def makeSectionPlane(objectslist=None,name="Section"): """makeSectionPlane([objectslist]) : Creates a Section plane objects including the given objects. If no object is given, the whole document will be considered.""" if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name) obj.Label = translate("Arch",name) _SectionPlane(obj) if FreeCAD.GuiUp: _ViewProviderSectionPlane(obj.ViewObject) if objectslist: obj.Objects = objectslist bb = FreeCAD.BoundBox() for o in Draft.getGroupContents(objectslist): if hasattr(o,"Shape") and hasattr(o.Shape,"BoundBox"): bb.add(o.Shape.BoundBox) obj.Placement = FreeCAD.DraftWorkingPlane.getPlacement() obj.Placement.Base = bb.Center if FreeCAD.GuiUp: margin = bb.XLength*0.1 obj.ViewObject.DisplayLength = bb.XLength+margin obj.ViewObject.DisplayHeight = bb.YLength+margin return obj def makeSectionView(section,name="View"): """makeSectionView(section) : Creates a Drawing view of the given Section Plane in the active Page object (a new page will be created if none exists""" page = None for o in FreeCAD.ActiveDocument.Objects: if o.isDerivedFrom("Drawing::FeaturePage"): page = o break if not page: page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage","Page") page.Template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg') view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython",name) page.addObject(view) _ArchDrawingView(view) view.Source = section view.Label = translate("Arch","View of")+" "+section.Name return view def getSectionData(source): """Returns some common data from section planes and building parts""" if hasattr(source,"Objects"): objs = source.Objects cutplane = source.Shape elif hasattr(source,"Group"): import Part objs = source.Group cutplane = Part.makePlane(1000,1000,FreeCAD.Vector(-500,-500,0)) m = 1 if source.ViewObject and hasattr(source.ViewObject,"CutMargin"): m = source.ViewObject.CutMargin.Value cutplane.translate(FreeCAD.Vector(0,0,m)) cutplane.Placement = cutplane.Placement.multiply(source.Placement) onlySolids = True if hasattr(source,"OnlySolids"): onlySolids = source.OnlySolids clip = False if hasattr(source,"Clip"): clip = source.Clip p = FreeCAD.Placement(source.Placement) direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) if objs: objs = Draft.getGroupContents(objs,walls=True,addgroups=True) return objs,cutplane,onlySolids,clip,direction def looksLikeDraft(o): """Does this object look like a Draft shape? (flat, no solid, etc)""" # If there is no shape at all ignore it if not hasattr(o, 'Shape') or o.Shape.isNull(): return False # If there are solids in the object, it will be handled later # by getCutShapes if len(o.Shape.Solids) > 0: return False # If we have a shape, but no volume, it looks like a flat 2D object return o.Shape.Volume < 0.0000001 # add a little tolerance... def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesByObject=False): """ returns a list of shapes (visible, hidden, cut lines...) obtained from performing a series of booleans against the given cut plane """ import Part,DraftGeomUtils shapes = [] hshapes = [] sshapes = [] objectShapes = [] objectSshapes = [] if joinArch: shtypes = {} for o in objs: if Draft.getType(o) in ["Wall","Structure"]: if o.Shape.isNull(): pass elif onlySolids: shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).extend(o.Shape.Solids) else: shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).append(o.Shape.copy()) elif hasattr(o,'Shape'): if o.Shape.isNull(): pass elif onlySolids: shapes.extend(o.Shape.Solids) objectShapes.append((o, o.Shape.Solids)) else: shapes.append(o.Shape.copy()) objectShapes.append((o,[o.Shape.copy()])) for k,v in shtypes.items(): v1 = v.pop() if v: v1 = v1.multiFuse(v) v1 = v1.removeSplitter() if v1.Solids: shapes.extend(v1.Solids) objectShapes.append((k,v1.Solids)) else: print("ArchSectionPlane: Fusing Arch objects produced non-solid results") shapes.append(v1) objectShapes.append((k,[v1])) else: for o in objs: if hasattr(o,'Shape'): if o.Shape.isNull(): pass elif onlySolids: if o.Shape.isValid(): shapes.extend(o.Shape.Solids) objectShapes.append((o,o.Shape.Solids)) else: shapes.append(o.Shape) objectShapes.append((o,[o.Shape])) cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(cutplane,shapes,clip) shapes = [] if cutvolume: for o, shapeList in objectShapes: tmpSshapes = [] for sh in shapeList: for sol in sh.Solids: if sol.Volume < 0: sol.reverse() c = sol.cut(cutvolume) s = sol.section(cutface) try: wires = DraftGeomUtils.findWires(s.Edges) for w in wires: f = Part.Face(w) tmpSshapes.append(f) #s = Part.Wire(s.Edges) #s = Part.Face(s) except Part.OCCError: #print "ArchDrawingView: unable to get a face" tmpSshapes.append(s) shapes.extend(c.Solids) #sshapes.append(s) if showHidden: c = sol.cut(invcutvolume) hshapes.append(c) if len(tmpSshapes) > 0: sshapes.extend(tmpSshapes) if groupSshapesByObject: objectSshapes.append((o, tmpSshapes)) if groupSshapesByObject: return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes else: return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume def getFillForObject(o, defaultFill, source): """returns a color tuple from an object's material""" if hasattr(source, 'UseMaterialColorForFill') and source.UseMaterialColorForFill: material = None if hasattr(o, 'Material') and o.Material: material = o.Material elif isinstance(o,str): material = FreeCAD.ActiveDocument.getObject(o) if material: if hasattr(material, 'Color') and material.Color: return material.Color return defaultFill def isOriented(obj,plane): """determines if an annotation is facing the cutplane or not""" norm1 = plane.normalAt(0,0) if hasattr(obj,"Placement"): norm2 = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) elif hasattr(obj,"Normal"): norm2 = obj.Normal if norm2.Length < 0.01: return True else: return True a = norm1.getAngle(norm2) if (a < 0.01) or (abs(a-math.pi) < 0.01): return True return False def getSVG(source, renderMode="Wireframe", allOn=False, showHidden=False, scale=1, rotation=0, linewidth=1, lineColor=(0.0,0.0,0.0), fontsize=1, showFill=False, fillColor=(0.8,0.8,0.8), techdraw=False, fillSpaces=False, cutlinewidth=0, joinArch=False): """ returns an SVG fragment from an Arch SectionPlane or BuildingPart. If allOn is True, all cut objects are shown, regardless if they are visible or not. renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If showHidden is True, the hidden geometry above the section plane is shown in dashed line. If showFill is True, the cut areas get filled with a pattern. lineColor -- Color of lines for the renderMode "Wireframe". fillColor -- If showFill is True and renderMode is "Wireframe", the cut areas are filled with fillColor. fillSpaces - If True, shows space objects as filled surfaces """ import Part,DraftGeomUtils objs, cutplane, onlySolids, clip, direction = getSectionData(source) if not objs: return "" if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects spaces = [] nonspaces = [] drafts = [] windows = [] cutface = None for o in objs: if Draft.getType(o) == "Space": spaces.append(o) elif Draft.getType(o) in ["AngularDimension","LinearDimension","Annotation","Label","Text"]: if isOriented(o,cutplane): drafts.append(o) elif o.isDerivedFrom("Part::Part2DObject"): drafts.append(o) elif looksLikeDraft(o): drafts.append(o) else: nonspaces.append(o) if Draft.getType(o) == "Window": windows.append(o) objs = nonspaces archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") scaledLineWidth = linewidth/scale if renderMode in ["Coin",2,"Coin mono",3]: # don't scale linewidths in coin mode svgLineWidth = str(linewidth) + 'px' else: svgLineWidth = str(scaledLineWidth) + 'px' if cutlinewidth: scaledCutLineWidth = cutlinewidth/scale svgCutLineWidth = str(scaledCutLineWidth) + 'px' else: st = archUserParameters.GetFloat("CutLineThickness",2) svgCutLineWidth = str(scaledLineWidth * st) + 'px' yt = archUserParameters.GetFloat("SymbolLineThickness",0.6) svgSymbolLineWidth = str(linewidth * yt) hiddenPattern = archUserParameters.GetString("archHiddenPattern","30,10") svgHiddenPattern = hiddenPattern.replace(" ","") fillpattern = '= 12: d = floatlist camtype = "orthographic" if len(floatlist) == 13: if d[12] == 1: camtype = "perspective" if camtype == "orthographic": c = "#Inventor V2.1 ascii\n\n\nOrthographicCamera {\n viewportMapping ADJUST_CAMERA\n " else: c = "#Inventor V2.1 ascii\n\n\nPerspectiveCamera {\n viewportMapping ADJUST_CAMERA\n " c += "position " + str(d[0]) + " " + str(d[1]) + " " + str(d[2]) + "\n " c += "orientation " + str(d[3]) + " " + str(d[4]) + " " + str(d[5]) + " " + str(d[6]) + "\n " c += "aspectRatio " + str(d[9]) + "\n " c += "focalDistance " + str(d[10]) + "\n " if camtype == "orthographic": c += "height " + str(d[11]) + "\n\n}\n" else: c += "heightAngle " + str(d[11]) + "\n\n}\n" return c def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,facecolor=None): """Returns an SVG fragment generated from a coin view""" if not FreeCAD.GuiUp: return "" # do not allow concurrent runs # wait until the other rendering has finished global ISRENDERING while ISRENDERING: time.sleep(0.1) ISRENDERING = True # a name to save a temp file svgfile = tempfile.mkstemp(suffix=".svg")[1] # set object lighting to single face to get black fills # but this creates artifacts in svg output, triangulation gets visible... ldict = {} if singleface: for obj in objs: if hasattr(obj,"ViewObject") and hasattr(obj.ViewObject,"Lighting"): ldict[obj.Name] = obj.ViewObject.Lighting obj.ViewObject.Lighting = "One side" # get nodes to render rn = coin.SoSeparator() boundbox = FreeCAD.BoundBox() for obj in objs: if hasattr(obj.ViewObject,"RootNode") and obj.ViewObject.RootNode: ncopy = obj.ViewObject.RootNode.copy() rn.addChild(ncopy) if hasattr(obj,"Shape") and hasattr(obj.Shape,"BoundBox"): boundbox.add(obj.Shape.BoundBox) # reset lighting of objects if ldict: for obj in objs: if obj.Name in ldict: obj.ViewObject.Lighting = ldict[obj.Name] # create viewer v = FreeCADGui.createViewer() viewername = "Temp" + str(uuid.uuid4().hex[:8]) v.setName(viewername) vv = v.getViewer() vv.setBackgroundColor(1,1,1) v.redraw() # set clip plane clip = coin.SoClipPlane() norm = cutplane.normalAt(0,0).negative() proj = DraftVecUtils.project(cutplane.CenterOfMass,norm) dist = proj.Length if proj.getAngle(norm) > 1: dist = -dist clip.on = True plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) #dir, position on dir clip.plane.setValue(plane) rn.insertChild(clip,0) # add white marker at scene bound box corner markervec = FreeCAD.Vector(10,10,10) a = cutplane.normalAt(0,0).getAngle(markervec) if (a < 0.01) or (abs(a-math.pi) < 0.01): markervec = FreeCAD.Vector(10,-10,10) boundbox.enlarge(10) # so the marker don't overlap the objects sep = coin.SoSeparator() mat = coin.SoMaterial() mat.diffuseColor.setValue([1,1,1]) sep.addChild(mat) coords = coin.SoCoordinate3() coords.point.setValues([[boundbox.XMin,boundbox.YMin,boundbox.ZMin], [boundbox.XMin+markervec.x,boundbox.YMin+markervec.y,boundbox.ZMin+markervec.z]]) sep.addChild(coords) lset = coin.SoIndexedLineSet() lset.coordIndex.setValues(0,2,[0,1]) sep.addChild(lset) rn.insertChild(sep,0) # set scenegraph vv.setSceneGraph(rn) # set camera if cameradata: v.setCamera(cameradata) else: v.setCameraType("Orthographic") #rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),cutplane.normalAt(0,0)) vx = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0)) vy = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0)) vz = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1)) rot = FreeCAD.Rotation(vx,vy,vz,"ZXY") v.setCameraOrientation(rot.Q) # this is needed to set correct focal depth, otherwise saving doesn't work properly v.fitAll() # save view #print("saving to",svgfile) v.saveVectorGraphic(svgfile,1) # number is pixel size # set linewidth placeholder f = open(svgfile,"r") svg = f.read() f.close() svg = svg.replace("stroke-width:1.0;","stroke-width:"+str(linewidth)+";") svg = svg.replace("stroke-width=\"1px","stroke-width=\""+str(linewidth)) # find marker and calculate scale factor and translation # factor = None trans = None import WorkingPlane wp = WorkingPlane.plane() wp.alignToPointAndAxis_SVG(Vector(0,0,0),cutplane.normalAt(0,0),0) p = wp.getLocalCoords(markervec) orlength = FreeCAD.Vector(p.x,p.y,0).Length marker = re.findall("",svg) if marker: marker = marker[0].split("\"") x1 = float(marker[1]) y1 = float(marker[3]) x2 = float(marker[5]) y2 = float(marker[7]) p1 = FreeCAD.Vector(x1,y1,0) p2 = FreeCAD.Vector(x2,y2,0) factor = orlength/p2.sub(p1).Length if factor: orig = wp.getLocalCoords(FreeCAD.Vector(boundbox.XMin,boundbox.YMin,boundbox.ZMin)) orig = FreeCAD.Vector(orig.x,-orig.y,0) scaledp1 = FreeCAD.Vector(p1.x*factor,p1.y*factor,0) trans = orig.sub(scaledp1) # remove marker svg = re.sub("","",svg,count=1) # remove background rectangle svg = re.sub("","",svg,count=1,flags=re.MULTILINE|re.DOTALL) # set face color to white if facecolor: res = re.findall("fill:(.*?); stroke:(.*?);",svg) pairs = [] for pair in res: if (pair not in pairs) and (pair[0] == pair[1]) and(pair[0] not in ["#0a0a0a"]): # coin seems to be rendering a lot of lines as thin triangles with the #0a0a0a color... pairs.append(pair) for pair in pairs: svg = re.sub("fill:"+pair[0]+"; stroke:"+pair[1]+";","fill:"+facecolor+"; stroke:"+facecolor+";",svg) # embed everything in a scale group and scale the viewport if factor: if trans: svg = svg.replace("","\n",1) else: svg = svg.replace("","\n",1) svg = svg.replace("","\n") # trigger viewer close QtCore.QTimer.singleShot(1,lambda: closeViewer(viewername)) # strip svg tags (needed for TD Arch view) svg = re.sub("<\?xml.*?>","",svg,flags=re.MULTILINE|re.DOTALL) svg = re.sub("","",svg,flags=re.MULTILINE|re.DOTALL) svg = re.sub("<\/svg>","",svg,flags=re.MULTILINE|re.DOTALL) ISRENDERING = False return svg def closeViewer(name): """Close temporary viewers""" mw = FreeCADGui.getMainWindow() for sw in mw.findChildren(QtGui.QMdiSubWindow): if sw.windowTitle() == name: sw.close() class _CommandSectionPlane: "the Arch SectionPlane command definition" def GetResources(self): return {'Pixmap' : 'Arch_SectionPlane', 'Accel': "S, E", 'MenuText': QT_TRANSLATE_NOOP("Arch_SectionPlane","Section Plane"), 'ToolTip': QT_TRANSLATE_NOOP("Arch_SectionPlane","Creates a section plane object, including the selected objects")} def IsActive(self): return not FreeCAD.ActiveDocument is None def Activated(self): sel = FreeCADGui.Selection.getSelection() ss = "[" for o in sel: if len(ss) > 1: ss += "," ss += "FreeCAD.ActiveDocument."+o.Name ss += "]" FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Section Plane")) FreeCADGui.addModule("Arch") FreeCADGui.doCommand("section = Arch.makeSectionPlane("+ss+")") #FreeCADGui.doCommand("section.Placement = FreeCAD.DraftWorkingPlane.getPlacement()") #FreeCADGui.doCommand("Arch.makeSectionView(section)") FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() class _SectionPlane: "A section plane object" def __init__(self,obj): obj.Proxy = self self.setProperties(obj) def setProperties(self,obj): pl = obj.PropertiesList if not "Placement" in pl: obj.addProperty("App::PropertyPlacement","Placement","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The placement of this object")) if not "Shape" in pl: obj.addProperty("Part::PropertyPartShape","Shape","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The shape of this object")) if not "Objects" in pl: obj.addProperty("App::PropertyLinkList","Objects","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The objects that must be considered by this section plane. Empty means the whole document.")) if not "OnlySolids" in pl: obj.addProperty("App::PropertyBool","OnlySolids","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If false, non-solids will be cut too, with possible wrong results.")) obj.OnlySolids = True if not "Clip" in pl: obj.addProperty("App::PropertyBool","Clip","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If True, resulting views will be clipped to the section plane area.")) if not "UseMaterialColorForFill" in pl: obj.addProperty("App::PropertyBool","UseMaterialColorForFill","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If true, the color of the objects material will be used to fill cut areas.")) obj.UseMaterialColorForFill = False self.Type = "SectionPlane" def onDocumentRestored(self,obj): self.setProperties(obj) def execute(self,obj): import Part l = 1 h = 1 if obj.ViewObject: if hasattr(obj.ViewObject,"DisplayLength"): l = obj.ViewObject.DisplayLength.Value h = obj.ViewObject.DisplayHeight.Value elif hasattr(obj.ViewObject,"DisplaySize"): # old objects l = obj.ViewObject.DisplaySize.Value h = obj.ViewObject.DisplaySize.Value p = Part.makePlane(l,h,Vector(l/2,-h/2,0),Vector(0,0,-1)) # make sure the normal direction is pointing outwards, you never know what OCC will decide... if p.normalAt(0,0).getAngle(obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))) > 1: p.reverse() p.Placement = obj.Placement obj.Shape = p def onChanged(self,obj,prop): # clean svg cache if needed if prop in ["Placement","Objects","OnlySolids","UseMaterialColorForFill","Clip"]: self.svgcache = None self.shapecache = None def getNormal(self,obj): return obj.Shape.Faces[0].normalAt(0,0) def __getstate__(self): return None def __setstate__(self,state): return None class _ViewProviderSectionPlane: "A View Provider for Section Planes" def __init__(self,vobj): vobj.Proxy = self self.setProperties(vobj) def setProperties(self,vobj): pl = vobj.PropertiesList d = 0 if "DisplaySize" in pl: d = vobj.DisplaySize.Value vobj.removeProperty("DisplaySize") if not "DisplayLength" in pl: vobj.addProperty("App::PropertyLength","DisplayLength","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display length of this section plane")) if d: vobj.DisplayLength = d else: vobj.DisplayLength = 1000 if not "DisplayHeight" in pl: vobj.addProperty("App::PropertyLength","DisplayHeight","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display height of this section plane")) if d: vobj.DisplayHeight = d else: vobj.DisplayHeight = 1000 if not "ArrowSize" in pl: vobj.addProperty("App::PropertyLength","ArrowSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the arrows of this section plane")) vobj.ArrowSize = 50 if not "Transparency" in pl: vobj.addProperty("App::PropertyPercent","Transparency","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The transparency of this object")) vobj.Transparency = 85 if not "LineWidth" in pl: vobj.addProperty("App::PropertyFloat","LineWidth","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The line width of this object")) vobj.LineWidth = 1 if not "CutDistance" in pl: vobj.addProperty("App::PropertyLength","CutDistance","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view")) if not "LineColor" in pl: vobj.addProperty("App::PropertyColor","LineColor","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The color of this object")) vobj.LineColor = ArchCommands.getDefaultColor("Helpers") if not "CutView" in pl: vobj.addProperty("App::PropertyBool","CutView","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view")) if not "CutMargin" in pl: vobj.addProperty("App::PropertyLength","CutMargin","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The distance between the cut plane and the actual view cut (keep this a very small value but not zero)")) vobj.CutMargin = 1 def onDocumentRestored(self,vobj): self.setProperties(vobj) def getIcon(self): import Arch_rc return ":/icons/Arch_SectionPlane_Tree.svg" def claimChildren(self): # buggy at the moment so it's disabled - it will for ex. swallow a building object directly at the root of the document... #if hasattr(self,"Object") and hasattr(self.Object,"Objects"): # return self.Object.Objects return [] def attach(self,vobj): self.Object = vobj.Object self.clip = None self.mat1 = coin.SoMaterial() self.mat2 = coin.SoMaterial() self.fcoords = coin.SoCoordinate3() #fs = coin.SoType.fromName("SoBrepFaceSet").createInstance() # this causes a FreeCAD freeze for me fs = coin.SoIndexedFaceSet() fs.coordIndex.setValues(0,7,[0,1,2,-1,0,2,3]) self.drawstyle = coin.SoDrawStyle() self.drawstyle.style = coin.SoDrawStyle.LINES self.lcoords = coin.SoCoordinate3() ls = coin.SoType.fromName("SoBrepEdgeSet").createInstance() ls.coordIndex.setValues(0,57,[0,1,-1,2,3,4,5,-1,6,7,8,9,-1,10,11,-1,12,13,14,15,-1,16,17,18,19,-1,20,21,-1,22,23,24,25,-1,26,27,28,29,-1,30,31,-1,32,33,34,35,-1,36,37,38,39,-1,40,41,42,43,44]) sep = coin.SoSeparator() psep = coin.SoSeparator() fsep = coin.SoSeparator() fsep.addChild(self.mat2) fsep.addChild(self.fcoords) fsep.addChild(fs) psep.addChild(self.mat1) psep.addChild(self.drawstyle) psep.addChild(self.lcoords) psep.addChild(ls) sep.addChild(fsep) sep.addChild(psep) vobj.addDisplayMode(sep,"Default") self.onChanged(vobj,"DisplayLength") self.onChanged(vobj,"LineColor") self.onChanged(vobj,"Transparency") self.onChanged(vobj,"CutView") def getDisplayModes(self,vobj): return ["Default"] def getDefaultDisplayMode(self): return "Default" def setDisplayMode(self,mode): return mode def updateData(self,obj,prop): if prop in ["Placement"]: self.onChanged(obj.ViewObject,"DisplayLength") self.onChanged(obj.ViewObject,"CutView") return def onChanged(self,vobj,prop): if prop == "LineColor": if hasattr(vobj,"LineColor"): l = vobj.LineColor self.mat1.diffuseColor.setValue([l[0],l[1],l[2]]) self.mat2.diffuseColor.setValue([l[0],l[1],l[2]]) elif prop == "Transparency": if hasattr(vobj,"Transparency"): self.mat2.transparency.setValue(vobj.Transparency/100.0) elif prop in ["DisplayLength","DisplayHeight","ArrowSize"]: if hasattr(vobj,"DisplayLength") and hasattr(vobj,"DisplayHeight"): ld = vobj.DisplayLength.Value/2 hd = vobj.DisplayHeight.Value/2 elif hasattr(vobj,"DisplaySize"): # old objects ld = vobj.DisplaySize.Value/2 hd = vobj.DisplaySize.Value/2 else: ld = 1 hd = 1 verts = [] fverts = [] for v in [[-ld,-hd],[ld,-hd],[ld,hd],[-ld,hd]]: if hasattr(vobj,"ArrowSize"): l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1 else: l1 = 0.1 l2 = l1/3 pl = FreeCAD.Placement(vobj.Object.Placement) p1 = pl.multVec(Vector(v[0],v[1],0)) p2 = pl.multVec(Vector(v[0],v[1],-l1)) p3 = pl.multVec(Vector(v[0]-l2,v[1],-l1+l2)) p4 = pl.multVec(Vector(v[0]+l2,v[1],-l1+l2)) p5 = pl.multVec(Vector(v[0],v[1]-l2,-l1+l2)) p6 = pl.multVec(Vector(v[0],v[1]+l2,-l1+l2)) verts.extend([[p1.x,p1.y,p1.z],[p2.x,p2.y,p2.z]]) fverts.append([p1.x,p1.y,p1.z]) verts.extend([[p2.x,p2.y,p2.z],[p3.x,p3.y,p3.z],[p4.x,p4.y,p4.z],[p2.x,p2.y,p2.z]]) verts.extend([[p2.x,p2.y,p2.z],[p5.x,p5.y,p5.z],[p6.x,p6.y,p6.z],[p2.x,p2.y,p2.z]]) verts.extend(fverts+[fverts[0]]) self.lcoords.point.setValues(verts) self.fcoords.point.setValues(fverts) elif prop == "LineWidth": self.drawstyle.lineWidth = vobj.LineWidth elif prop in ["CutView","CutMargin"]: if hasattr(vobj,"CutView") and FreeCADGui.ActiveDocument.ActiveView: sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if vobj.CutView: if self.clip: sg.removeChild(self.clip) self.clip = None for o in Draft.getGroupContents(vobj.Object.Objects,walls=True): if hasattr(o.ViewObject,"Lighting"): o.ViewObject.Lighting = "One side" self.clip = coin.SoClipPlane() self.clip.on.setValue(True) norm = vobj.Object.Proxy.getNormal(vobj.Object) mp = vobj.Object.Shape.CenterOfMass mp = DraftVecUtils.project(mp,norm) dist = mp.Length #- 0.1 # to not clip exactly on the section object norm = norm.negative() marg = 1 if hasattr(vobj,"CutMargin"): marg = vobj.CutMargin.Value if mp.getAngle(norm) > 1: dist += marg dist = -dist else: dist -= marg plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) self.clip.plane.setValue(plane) sg.insertChild(self.clip,0) else: if self.clip: sg.removeChild(self.clip) self.clip = None return def __getstate__(self): return None def __setstate__(self,state): return None def setEdit(self,vobj,mode): taskd = SectionPlaneTaskPanel() taskd.obj = vobj.Object taskd.update() FreeCADGui.Control.showDialog(taskd) return True def unsetEdit(self,vobj,mode): FreeCADGui.Control.closeDialog() return False def doubleClicked(self,vobj): self.setEdit(vobj,None) def setupContextMenu(self,vobj,menu): """CONTEXT MENU setup""" from PySide import QtCore,QtGui action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"),"Toggle Cutview",menu) action1.triggered.connect(lambda f=self.contextCutview, arg=vobj:f(arg)) menu.addAction(action1) def contextCutview(self,vobj): """CONTEXT MENU command to toggle CutView property on and off""" if vobj.CutView: vobj.CutView = False else: vobj.CutView = True class _ArchDrawingView: def __init__(self, obj): obj.Proxy = self self.setProperties(obj) def setProperties(self,obj): pl = obj.PropertiesList if not "Source" in pl: obj.addProperty("App::PropertyLink", "Source", "Base", QT_TRANSLATE_NOOP("App::Property","The linked object")) if not "RenderingMode" in pl: obj.addProperty("App::PropertyEnumeration", "RenderingMode", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The rendering mode to use")) obj.RenderingMode = ["Solid","Wireframe"] obj.RenderingMode = "Wireframe" if not "ShowCut" in pl: obj.addProperty("App::PropertyBool", "ShowCut", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If cut geometry is shown or not")) if not "ShowFill" in pl: obj.addProperty("App::PropertyBool", "ShowFill", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If cut geometry is filled or not")) if not "LineWidth" in pl: obj.addProperty("App::PropertyFloat", "LineWidth", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The line width of the rendered objects")) obj.LineWidth = 0.35 if not "FontSize" in pl: obj.addProperty("App::PropertyLength", "FontSize", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The size of the texts inside this object")) obj.FontSize = 12 if not "AlwaysOn" in pl: obj.addProperty("App::PropertyBool", "AlwaysOn", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If checked, source objects are displayed regardless of being visible in the 3D model")) if not "LineColor" in pl: obj.addProperty("App::PropertyColor", "LineColor", "Drawing view",QT_TRANSLATE_NOOP("App::Property","The line color of the projected objects")) if not "FillColor" in pl: obj.addProperty("App::PropertyColor", "FillColor", "Drawing view",QT_TRANSLATE_NOOP("App::Property","The color of the cut faces (if turned on)")) obj.FillColor = (0.8, 0.8, 0.8) self.Type = "ArchSectionView" def onDocumentRestored(self, obj): self.setProperties(obj) def execute(self, obj): if hasattr(obj,"Source"): if obj.Source: svgbody = getSVG(source=obj.Source, renderMode=obj.RenderingMode, allOn=getattr(obj, 'AlwaysOn', False), showHidden=obj.ShowCut, scale=obj.Scale, linewidth=obj.LineWidth, lineColor=obj.LineColor, fontsize=obj.FontSize, showFill=obj.ShowFill, fillColor=obj.FillColor) if svgbody: result = '