From 2b10890a045c425d4274ce585a8d43f9c2dc6cc8 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sun, 15 Dec 2013 17:45:12 -0200 Subject: [PATCH] Arch: Structural nodes - closes #1313 --- src/Mod/Arch/ArchStructure.py | 179 ++++++++++++++++++++++++--------- src/Mod/Arch/ArchWall.py | 183 +++++++++++++++++----------------- 2 files changed, 222 insertions(+), 140 deletions(-) diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 3ad374b30..5bddfbe23 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -536,6 +536,8 @@ class _Structure(ArchComponent.Component): str(translate("Arch","The element numbers to exclude when this structure is based on axes"))) obj.addProperty("App::PropertyEnumeration","Role","Arch", str(translate("Arch","The role of this structural element"))) + obj.addProperty("App::PropertyVectorList","Nodes","Arch", + str(translate("Arch","The structural nodes of this element"))) self.Type = "Structure" obj.Length = 1 obj.Width = 1 @@ -543,35 +545,8 @@ class _Structure(ArchComponent.Component): obj.Role = Roles def execute(self,obj): - self.createGeometry(obj) + "creates the structure shape" - def onChanged(self,obj,prop): - self.hideSubobjects(obj,prop) - if prop in ["Base","Tool","Length","Width","Height","Normal","Additions","Subtractions","Axes"]: - self.createGeometry(obj) - - def getAxisPoints(self,obj): - "returns the gridpoints of linked axes" - import DraftGeomUtils - pts = [] - if len(obj.Axes) == 1: - for e in obj.Axes[0].Shape.Edges: - pts.append(e.Vertexes[0].Point) - elif len(obj.Axes) >= 2: - set1 = obj.Axes[0].Shape.Edges - set2 = obj.Axes[1].Shape.Edges - for e1 in set1: - for e2 in set2: - pts.extend(DraftGeomUtils.findIntersection(e1,e2)) - return pts - - def getAxisPlacement(self,obj): - "returns an axis placement" - if obj.Axes: - return obj.Axes[0].Placement - return None - - def createGeometry(self,obj): import Part, DraftGeomUtils # getting default values @@ -609,10 +584,14 @@ class _Structure(ArchComponent.Component): if base.Solids: pass elif base.Faces: + self.BaseProfile = base + self.ExtrusionVector = normal base = base.extrude(normal) elif (len(base.Wires) == 1): if base.Wires[0].isClosed(): base = Part.Face(base.Wires[0]) + self.BaseProfile = base + self.ExtrusionVector = normal base = base.extrude(normal) elif obj.Base.isDerivedFrom("Mesh::Feature"): @@ -623,19 +602,31 @@ class _Structure(ArchComponent.Component): base = sh else: if obj.Normal == Vector(0,0,0): - normal = Vector(0,0,1) + if length > height: + normal = Vector(1,0,0).multiply(length) + else: + normal = Vector(0,0,1).multiply(height) else: - normal = Vector(obj.Normal) - normal = normal.multiply(height) - l2 = length/2 or 0.5 - w2 = width/2 or 0.5 - v1 = Vector(-l2,-w2,0) - v2 = Vector(l2,-w2,0) - v3 = Vector(l2,w2,0) - v4 = Vector(-l2,w2,0) + normal = Vector(obj.Normal).multiply(height) + self.ExtrusionVector = normal + if length > height: + h2 = height/2 or 0.5 + w2 = width/2 or 0.5 + v1 = Vector(0,-w2,-h2) + v2 = Vector(0,-w2,h2) + v3 = Vector(0,w2,h2) + v4 = Vector(0,w2,-h2) + else: + l2 = length/2 or 0.5 + w2 = width/2 or 0.5 + v1 = Vector(-l2,-w2,0) + v2 = Vector(l2,-w2,0) + v3 = Vector(l2,w2,0) + v4 = Vector(-l2,w2,0) base = Part.makePolygon([v1,v2,v3,v4,v1]) base = Part.Face(base) - base = base.extrude(normal) + self.BaseProfile = base + base = base.extrude(self.ExtrusionVector) base = self.processSubShapes(obj,base) @@ -657,7 +648,6 @@ class _Structure(ArchComponent.Component): obj.Shape = Part.makeCompound(fsh) # finalizing - else: if base: if not base.isNull(): @@ -672,16 +662,116 @@ class _Structure(ArchComponent.Component): if not DraftGeomUtils.isNull(pl): obj.Placement = pl + def onChanged(self,obj,prop): + self.hideSubobjects(obj,prop) + if prop == "Shape": + if obj.Nodes: + if hasattr(self,"nodes"): + if self.nodes: + if obj.Nodes != self.nodes: + # nodes are set manually: don't touch them + return + else: + # nodes haven't been calculated yet, but are set (file load) + # we calculate the nodes now but don't change the property + if hasattr(self,"BaseProfile") and hasattr(self,"ExtrusionVector"): + p1 = self.BaseProfile.CenterOfMass + p2 = p1.add(self.ExtrusionVector) + self.nodes = [p1,p2] + return + if hasattr(self,"BaseProfile") and hasattr(self,"ExtrusionVector"): + p1 = self.BaseProfile.CenterOfMass + p2 = p1.add(self.ExtrusionVector) + self.nodes = [p1,p2] + #print "calculating nodes: ",self.nodes + obj.Nodes = self.nodes + + def getAxisPoints(self,obj): + "returns the gridpoints of linked axes" + import DraftGeomUtils + pts = [] + if len(obj.Axes) == 1: + for e in obj.Axes[0].Shape.Edges: + pts.append(e.Vertexes[0].Point) + elif len(obj.Axes) >= 2: + set1 = obj.Axes[0].Shape.Edges + set2 = obj.Axes[1].Shape.Edges + for e1 in set1: + for e2 in set2: + pts.extend(DraftGeomUtils.findIntersection(e1,e2)) + return pts + + def getAxisPlacement(self,obj): + "returns an axis placement" + if obj.Axes: + return obj.Axes[0].Placement + return None class _ViewProviderStructure(ArchComponent.ViewProviderComponent): "A View Provider for the Structure object" def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) + vobj.addProperty("App::PropertyBool","ShowNodes","Arch","If the nodes are visible or not").ShowNodes = False + vobj.addProperty("App::PropertyFloat","NodeLine","Base","The width of the nodes line") + vobj.addProperty("App::PropertyFloat","NodeSize","Base","The size of the node points") + vobj.addProperty("App::PropertyColor","NodeColor","Base","The color of the nodes line") + vobj.NodeColor = (1.0,1.0,1.0,1.0) + vobj.NodeSize = 6 def getIcon(self): import Arch_rc return ":/icons/Arch_Structure_Tree.svg" + + def updateData(self,obj,prop): + if prop == "Nodes": + if obj.Nodes: + if hasattr(self,"nodes"): + p = [] + for n in obj.Nodes: + p.append([n.x,n.y,n.z]) + self.coords.point.setValues(0,len(p),p) + self.pointset.numPoints.setValue(len(p)) + self.lineset.coordIndex.setValues(0,len(p)+1,range(len(p))+[-1]) + + def onChanged(self,vobj,prop): + if prop == "ShowNodes": + if hasattr(self,"nodes"): + vobj.Annotation.removeChild(self.nodes) + del self.nodes + if vobj.ShowNodes: + from pivy import coin + self.nodes = coin.SoAnnotation() + self.coords = coin.SoCoordinate3() + self.mat = coin.SoMaterial() + self.pointstyle = coin.SoDrawStyle() + self.pointstyle.style = coin.SoDrawStyle.POINTS + self.pointset = coin.SoType.fromName("SoBrepPointSet").createInstance() + self.linestyle = coin.SoDrawStyle() + self.linestyle.style = coin.SoDrawStyle.LINES + self.lineset = coin.SoType.fromName("SoBrepEdgeSet").createInstance() + self.nodes.addChild(self.coords) + self.nodes.addChild(self.mat) + self.nodes.addChild(self.pointstyle) + self.nodes.addChild(self.pointset) + self.nodes.addChild(self.linestyle) + self.nodes.addChild(self.lineset) + vobj.Annotation.addChild(self.nodes) + self.updateData(vobj.Object,"Nodes") + self.onChanged(vobj,"NodeColor") + self.onChanged(vobj,"NodeLine") + self.onChanged(vobj,"NodeSize") + elif prop == "NodeColor": + if hasattr(self,"mat"): + l = vobj.NodeColor + self.mat.diffuseColor.setValue([l[0],l[1],l[2]]) + elif prop == "NodeLine": + if hasattr(self,"linestyle"): + self.linestyle.lineWidth = vobj.NodeLine + elif prop == "NodeSize": + if hasattr(self,"pointstyle"): + self.pointstyle.pointSize = vobj.NodeSize + ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) class _Profile(Draft._DraftObject): @@ -695,13 +785,6 @@ class _Profile(Draft._DraftObject): Draft._DraftObject.__init__(self,obj,"Profile") def execute(self,obj): - self.createGeometry(obj) - - def onChanged(self,obj,prop): - if prop in ["Width","Height","WebThickness","FlangeThickness"]: - self.createGeometry(obj) - - def createGeometry(self,obj): import Part pl = obj.Placement p1 = Vector(-obj.Width/2,-obj.Height/2,0) @@ -720,6 +803,10 @@ class _Profile(Draft._DraftObject): p = Part.Face(p) obj.Shape = p obj.Placement = pl + + def onChanged(self,obj,prop): + if prop in ["Width","Height","WebThickness","FlangeThickness"]: + self.execute(obj) FreeCADGui.addCommand('Arch_Structure',_CommandStructure()) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 3ed2d26c0..b3364b805 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -395,100 +395,8 @@ class _Wall(ArchComponent.Component): obj.Length = 1 def execute(self,obj): - self.createGeometry(obj) - - def onChanged(self,obj,prop): - self.hideSubobjects(obj,prop) - if prop in ["Base","Height","Width","Align","Additions","Subtractions","Face"]: - self.createGeometry(obj) - # propagate movements to children windows - if prop == "Placement": - if obj.Shape: - if not obj.Shape.isNull(): - vo = obj.Shape.Placement.Base - vn = obj.Placement.Base - if not DraftVecUtils.equals(vo,vn): - delta = vn.sub(vo) - for o in obj.OutList: - if (Draft.getType(o) == "Window") or Draft.isClone(o,"Window"): - o.Placement.move(delta) - ArchComponent.Component.onChanged(self,obj,prop) - - def getDefaultValues(self,obj): - "returns normal,width,height values from this wall" - length = 1 - if hasattr(obj,"Length"): - if obj.Length: - length = obj.Length - width = 1 - if hasattr(obj,"Width"): - if obj.Width: - width = obj.Width - height = normal = None - if hasattr(obj,"Height"): - if obj.Height: - height = obj.Height - else: - for p in obj.InList: - if Draft.getType(p) == "Floor": - height = p.Height - if not height: - height = 1 - if hasattr(obj,"Normal"): - if obj.Normal == Vector(0,0,0): - normal = Vector(0,0,1) - else: - normal = Vector(obj.Normal) - else: - normal = Vector(0,0,1) - return normal,length,width,height - - def getBase(self,obj,wire,normal,width,height): - "returns a full shape from a base wire" - import DraftGeomUtils,Part - flat = False - if hasattr(obj.ViewObject,"DisplayMode"): - flat = (obj.ViewObject.DisplayMode == "Flat 2D") - dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) - if not DraftVecUtils.isNull(dvec): - dvec.normalize() - if obj.Align == "Left": - dvec.multiply(width) - if hasattr(obj,"Offset"): - if obj.Offset: - dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset) - wire = DraftGeomUtils.offsetWire(wire,dvec2) - w2 = DraftGeomUtils.offsetWire(wire,dvec) - w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) - sh = DraftGeomUtils.bind(w1,w2) - elif obj.Align == "Right": - dvec.multiply(width) - dvec = dvec.negative() - if hasattr(obj,"Offset"): - if obj.Offset: - dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset) - wire = DraftGeomUtils.offsetWire(wire,dvec2) - w2 = DraftGeomUtils.offsetWire(wire,dvec) - w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) - sh = DraftGeomUtils.bind(w1,w2) - elif obj.Align == "Center": - dvec.multiply(width/2) - w1 = DraftGeomUtils.offsetWire(wire,dvec) - dvec = dvec.negative() - w2 = DraftGeomUtils.offsetWire(wire,dvec) - sh = DraftGeomUtils.bind(w1,w2) - # fixing self-intersections - sh.fix(0.1,0,1) - self.BaseProfile = sh - if height and (not flat): - norm = Vector(normal).multiply(height) - sh = sh.extrude(norm) - self.ExtrusionVector = norm - return sh - - def createGeometry(self,obj): "builds the wall shape" - + import Part, DraftGeomUtils pl = obj.Placement normal,length,width,height = self.getDefaultValues(obj) @@ -577,6 +485,93 @@ class _Wall(ArchComponent.Component): obj.Shape = base if not DraftGeomUtils.isNull(pl): obj.Placement = pl + + def onChanged(self,obj,prop): + self.hideSubobjects(obj,prop) + # propagate movements to children windows + if prop == "Placement": + if obj.Shape: + if not obj.Shape.isNull(): + vo = obj.Shape.Placement.Base + vn = obj.Placement.Base + if not DraftVecUtils.equals(vo,vn): + delta = vn.sub(vo) + for o in obj.OutList: + if (Draft.getType(o) == "Window") or Draft.isClone(o,"Window"): + o.Placement.move(delta) + ArchComponent.Component.onChanged(self,obj,prop) + + def getDefaultValues(self,obj): + "returns normal,width,height values from this wall" + length = 1 + if hasattr(obj,"Length"): + if obj.Length: + length = obj.Length + width = 1 + if hasattr(obj,"Width"): + if obj.Width: + width = obj.Width + height = normal = None + if hasattr(obj,"Height"): + if obj.Height: + height = obj.Height + else: + for p in obj.InList: + if Draft.getType(p) == "Floor": + height = p.Height + if not height: + height = 1 + if hasattr(obj,"Normal"): + if obj.Normal == Vector(0,0,0): + normal = Vector(0,0,1) + else: + normal = Vector(obj.Normal) + else: + normal = Vector(0,0,1) + return normal,length,width,height + + def getBase(self,obj,wire,normal,width,height): + "returns a full shape from a base wire" + import DraftGeomUtils,Part + flat = False + if hasattr(obj.ViewObject,"DisplayMode"): + flat = (obj.ViewObject.DisplayMode == "Flat 2D") + dvec = DraftGeomUtils.vec(wire.Edges[0]).cross(normal) + if not DraftVecUtils.isNull(dvec): + dvec.normalize() + if obj.Align == "Left": + dvec.multiply(width) + if hasattr(obj,"Offset"): + if obj.Offset: + dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset) + wire = DraftGeomUtils.offsetWire(wire,dvec2) + w2 = DraftGeomUtils.offsetWire(wire,dvec) + w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) + sh = DraftGeomUtils.bind(w1,w2) + elif obj.Align == "Right": + dvec.multiply(width) + dvec = dvec.negative() + if hasattr(obj,"Offset"): + if obj.Offset: + dvec2 = DraftVecUtils.scaleTo(dvec,obj.Offset) + wire = DraftGeomUtils.offsetWire(wire,dvec2) + w2 = DraftGeomUtils.offsetWire(wire,dvec) + w1 = Part.Wire(DraftGeomUtils.sortEdges(wire.Edges)) + sh = DraftGeomUtils.bind(w1,w2) + elif obj.Align == "Center": + dvec.multiply(width/2) + w1 = DraftGeomUtils.offsetWire(wire,dvec) + dvec = dvec.negative() + w2 = DraftGeomUtils.offsetWire(wire,dvec) + sh = DraftGeomUtils.bind(w1,w2) + # fixing self-intersections + sh.fix(0.1,0,1) + self.BaseProfile = sh + if height and (not flat): + self.ExtrusionVector = Vector(normal).multiply(height) + sh = sh.extrude(self.ExtrusionVector) + return sh + class _ViewProviderWall(ArchComponent.ViewProviderComponent): "A View Provider for the Wall object" @@ -596,7 +591,7 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): return ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj)+["Flat 2D"] def setDisplayMode(self,mode): - self.Object.Proxy.createGeometry(self.Object) + self.Object.Proxy.execute(self.Object) if mode == "Flat 2D": return "Flat Lines" else: