Arch: Misc improvements to curtain wall
- Can be based on an edge like normal wall - Now has a 'host' property to embed into another object (tree only) - Mullions have different height and width properties - Mullions or panels can be disabled
This commit is contained in:
@@ -147,12 +147,7 @@ class CommandArchCurtainWall:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Curtain Wall"))
|
||||
FreeCADGui.addModule("Draft")
|
||||
FreeCADGui.addModule("Arch")
|
||||
FreeCADGui.doCommand("baseline = Draft.makeLine(FreeCAD."+str(self.points[0])+",FreeCAD."+str(self.points[1])+")")
|
||||
FreeCADGui.doCommand("base = FreeCAD.ActiveDocument.addObject('Part::Extrusion','Extrude')")
|
||||
FreeCADGui.doCommand("base.Base = baseline")
|
||||
FreeCADGui.doCommand("base.DirMode = 'Custom'")
|
||||
FreeCADGui.doCommand("base.Dir = App.Vector(FreeCAD.DraftWorkingPlane.axis)")
|
||||
FreeCADGui.doCommand("base.LengthFwd = 1000")
|
||||
FreeCADGui.doCommand("base = Draft.makeLine(FreeCAD."+str(self.points[0])+",FreeCAD."+str(self.points[1])+")")
|
||||
FreeCADGui.doCommand("obj = Arch.makeCurtainWall(base)")
|
||||
FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
@@ -173,6 +168,15 @@ class CurtainWall(ArchComponent.Component):
|
||||
def setProperties(self,obj):
|
||||
|
||||
pl = obj.PropertiesList
|
||||
vsize = 50
|
||||
hsize = 50
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
if not "Host" in pl:
|
||||
obj.addProperty("App::PropertyLink","Host","CurtainWall",QT_TRANSLATE_NOOP("App::Property","An optional host object for this curtain wall"))
|
||||
if not "Height" in pl:
|
||||
obj.addProperty("App::PropertyLength","Height","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The height of the curtain wall, if based on an edge"))
|
||||
obj.Height = p.GetFloat("WallHeight",3000)
|
||||
if not "VerticalMullionNumber" in pl:
|
||||
obj.addProperty("App::PropertyInteger","VerticalMullionNumber","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of vertical mullions"))
|
||||
@@ -183,11 +187,19 @@ class CurtainWall(ArchComponent.Component):
|
||||
if not "VerticalSections" in pl:
|
||||
obj.addProperty("App::PropertyInteger","VerticalSections","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of vertical sections of this curtain wall"))
|
||||
obj.VerticalSections = 4
|
||||
if not "VerticalMullionSize" in pl:
|
||||
obj.addProperty("App::PropertyLength","VerticalMullionSize","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The size of the vertical mullions, if no profile is used"))
|
||||
obj.VerticalMullionSize = 100
|
||||
obj.VerticalSections = 1
|
||||
if "VerticalMullionSize" in pl:
|
||||
# obsolete
|
||||
vsize = obj.VerticalMullionSize.Value
|
||||
obj.removeProperty("VerticalMullionSize")
|
||||
if not "VerticalMullionHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength","VerticalMullionHeight","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The height of the vertical mullions profile, if no profile is used"))
|
||||
obj.VerticalMullionHeight = vsize
|
||||
if not "VerticalMullionWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength","VerticalMullionWidth","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The width of the vertical mullions profile, if no profile is used"))
|
||||
obj.VerticalMullionWidth = vsize
|
||||
if not "VerticalMullionProfile" in pl:
|
||||
obj.addProperty("App::PropertyLink","VerticalMullionProfile","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","A profile for vertical mullions (disables vertical mullion size)"))
|
||||
@@ -201,11 +213,19 @@ class CurtainWall(ArchComponent.Component):
|
||||
if not "HorizontalSections" in pl:
|
||||
obj.addProperty("App::PropertyInteger","HorizontalSections","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of horizontal sections of this curtain wall"))
|
||||
obj.HorizontalSections = 4
|
||||
if not "HorizontalMullionSize" in pl:
|
||||
obj.addProperty("App::PropertyLength","HorizontalMullionSize","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The size of the horizontal mullions, if no profile is used"))
|
||||
obj.HorizontalMullionSize = 50
|
||||
obj.HorizontalSections = 1
|
||||
if "HorizontalMullionSize" in pl:
|
||||
# obsolete
|
||||
hsize = obj.HorizontalMullionSize.Value
|
||||
obj.removeProperty("HorizontalMullionSize")
|
||||
if not "HorizontalMullionHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength","HorizontalMullionHeight","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The height of the horizontal mullions profile, if no profile is used"))
|
||||
obj.HorizontalMullionHeight = hsize
|
||||
if not "HorizontalMullionWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength","HorizontalMullionWidth","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The width of the horizontal mullions profile, if no profile is used"))
|
||||
obj.HorizontalMullionWidth = hsize
|
||||
if not "HorizontalMullionProfile" in pl:
|
||||
obj.addProperty("App::PropertyLink","HorizontalMullionProfile","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","A profile for horizontal mullions (disables horizontal mullion size)"))
|
||||
@@ -268,9 +288,6 @@ class CurtainWall(ArchComponent.Component):
|
||||
if not hasattr(obj.Base,"Shape"):
|
||||
FreeCAD.Console.PrintLog(obj.Label+": invalid base\n")
|
||||
return
|
||||
if not obj.Base.Shape.Faces:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": no faces in base\n")
|
||||
return
|
||||
if obj.VerticalMullionProfile:
|
||||
if not hasattr(obj.VerticalMullionProfile,"Shape"):
|
||||
FreeCAD.Console.PrintLog(obj.Label+": invalid vertical mullion profile\n")
|
||||
@@ -283,13 +300,23 @@ class CurtainWall(ArchComponent.Component):
|
||||
if not hasattr(obj.DiagonalMullionProfile,"Shape"):
|
||||
FreeCAD.Console.PrintLog(obj.Label+": invalid diagonal mullion profile\n")
|
||||
return
|
||||
if (not obj.HorizontalSections) or (not obj.VerticalSections):
|
||||
return
|
||||
|
||||
facets = []
|
||||
|
||||
faces = []
|
||||
if obj.Base.Shape.Faces:
|
||||
faces = obj.Base.Shape.Faces
|
||||
elif obj.Height.Value and obj.VerticalDirection.Length:
|
||||
ext = FreeCAD.Vector(obj.VerticalDirection)
|
||||
ext.normalize()
|
||||
ext = ext.multiply(obj.Height.Value)
|
||||
faces = [edge.extrude(ext) for edge in obj.Base.Shape.Edges]
|
||||
if not faces:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": unable to build base faces\n")
|
||||
return
|
||||
|
||||
# subdivide the faces into quads
|
||||
for face in obj.Base.Shape.Faces:
|
||||
for face in faces:
|
||||
|
||||
fp = face.ParameterRange
|
||||
|
||||
@@ -309,12 +336,16 @@ class CurtainWall(ArchComponent.Component):
|
||||
vertsec = obj.HorizontalSections
|
||||
horizsec = obj.VerticalSections
|
||||
|
||||
hstep = (fp[1]-fp[0])/vertsec
|
||||
vstep = (fp[3]-fp[2])/horizsec
|
||||
hstep = (fp[1]-fp[0])
|
||||
if vertsec:
|
||||
hstep = hstep/vertsec
|
||||
vstep = (fp[3]-fp[2])
|
||||
if horizsec:
|
||||
vstep = vstep/horizsec
|
||||
|
||||
# construct facets
|
||||
for i in range(vertsec):
|
||||
for j in range(horizsec):
|
||||
for i in range(vertsec or 1):
|
||||
for j in range(horizsec or 1):
|
||||
p0 = face.valueAt(fp[0]+i*hstep,fp[2]+j*vstep)
|
||||
p1 = face.valueAt(fp[0]+(i+1)*hstep,fp[2]+j*vstep)
|
||||
p2 = face.valueAt(fp[0]+(i+1)*hstep,fp[2]+(j+1)*vstep)
|
||||
@@ -364,7 +395,7 @@ class CurtainWall(ArchComponent.Component):
|
||||
# construct vertical mullions
|
||||
vmullions = []
|
||||
vprofile = self.getMullionProfile(obj,"Vertical")
|
||||
if vprofile:
|
||||
if vprofile and vertsec:
|
||||
for vedge in vedges:
|
||||
vn = self.edgenormals[vedge.hashCode()]
|
||||
if (vn.x != 0) or (vn.y != 0):
|
||||
@@ -380,7 +411,7 @@ class CurtainWall(ArchComponent.Component):
|
||||
# construct horizontal mullions
|
||||
hmullions = []
|
||||
hprofile = self.getMullionProfile(obj,"Horizontal")
|
||||
if hprofile:
|
||||
if hprofile and horizsec:
|
||||
for hedge in hedges:
|
||||
rot = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),-90)
|
||||
vn = self.edgenormals[hedge.hashCode()]
|
||||
@@ -493,14 +524,15 @@ class CurtainWall(ArchComponent.Component):
|
||||
|
||||
import Part,DraftGeomUtils
|
||||
|
||||
prop1 = getattr(obj,direction+"MullionProfile")
|
||||
prop2 = getattr(obj,direction+"MullionSize").Value
|
||||
if prop1:
|
||||
profile = prop1.Shape.copy()
|
||||
prof = getattr(obj,direction+"MullionProfile")
|
||||
proh = getattr(obj,direction+"MullionHeight").Value
|
||||
prow = getattr(obj,direction+"MullionWidth").Value
|
||||
if prof:
|
||||
profile = prof.Shape.copy()
|
||||
else:
|
||||
if not prop2:
|
||||
if (not proh) or (not prow):
|
||||
return None
|
||||
profile = Part.Face(Part.makePlane(prop2,prop2,FreeCAD.Vector(-prop2/2,-prop2/2,0)))
|
||||
profile = Part.Face(Part.makePlane(prow,proh,FreeCAD.Vector(-prow/2,-proh/2,0)))
|
||||
return profile
|
||||
|
||||
def getProjectedLength(self,v,ref):
|
||||
|
||||
@@ -871,7 +871,7 @@ class _ArchMultiMaterialTaskPanel:
|
||||
thick = FreeCAD.Units.Quantity(d).Value
|
||||
else:
|
||||
thick = FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).Value
|
||||
th += thick
|
||||
th += abs(thick)
|
||||
if not thick:
|
||||
suffix = " ("+translate("Arch","depends on the object")+")"
|
||||
val = FreeCAD.Units.Quantity(th,FreeCAD.Units.Length).UserString
|
||||
|
||||
@@ -135,7 +135,7 @@ def joinWalls(walls,delete=False):
|
||||
"""Join the given list of walls into one sketch-based wall.
|
||||
|
||||
Take the first wall in the list, and adds on the other walls in the list.
|
||||
Return the modified first wall.
|
||||
Return the modified first wall.
|
||||
|
||||
Setting delete to True, will delete the other walls. Only join walls
|
||||
if the walls have the same width, height and alignment.
|
||||
@@ -188,7 +188,7 @@ def joinWalls(walls,delete=False):
|
||||
return base
|
||||
|
||||
def mergeShapes(w1,w2):
|
||||
"""Not currently implemented.
|
||||
"""Not currently implemented.
|
||||
|
||||
Return a Shape built on two walls that share same properties and have a
|
||||
coincident endpoint.
|
||||
@@ -270,7 +270,7 @@ class _CommandWall:
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Arch_Wall","Creates a wall object from scratch or from a selected object (wire, face or solid)")}
|
||||
|
||||
def IsActive(self):
|
||||
"""Determines whether or not the Arch Wall tool is active.
|
||||
"""Determines whether or not the Arch Wall tool is active.
|
||||
|
||||
Inactive commands are indicated by a greyed-out icon in the menus and
|
||||
toolbars.
|
||||
@@ -579,7 +579,7 @@ class _CommandWall:
|
||||
FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetInt("WallAlignment",i)
|
||||
|
||||
def setContinue(self,i):
|
||||
"""Simple callback to set if the interactive mode will restart when finished.
|
||||
"""Simple callback to set if the interactive mode will restart when finished.
|
||||
|
||||
This allows for several walls to be placed one after another.
|
||||
"""
|
||||
@@ -608,7 +608,7 @@ class _CommandWall:
|
||||
|
||||
|
||||
class _CommandMergeWalls:
|
||||
"""The command definition for the Arch workbench's gui tool, Arch MergeWalls.
|
||||
"""The command definition for the Arch workbench's gui tool, Arch MergeWalls.
|
||||
|
||||
A tool for merging walls.
|
||||
|
||||
@@ -626,7 +626,7 @@ class _CommandMergeWalls:
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Arch_MergeWalls","Merges the selected walls, if possible")}
|
||||
|
||||
def IsActive(self):
|
||||
"""Determines whether or not the Arch MergeWalls tool is active.
|
||||
"""Determines whether or not the Arch MergeWalls tool is active.
|
||||
|
||||
Inactive commands are indicated by a greyed-out icon in the menus and
|
||||
toolbars.
|
||||
@@ -674,7 +674,7 @@ class _CommandMergeWalls:
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
class _Wall(ArchComponent.Component):
|
||||
"""The Wall object.
|
||||
"""The Wall object.
|
||||
|
||||
Turns a <App::FeaturePython> into a wall object, then uses a
|
||||
<Part::Feature> to create the wall's shape.
|
||||
@@ -967,7 +967,7 @@ class _Wall(ArchComponent.Component):
|
||||
obj.Area = obj.Length.Value * obj.Height.Value
|
||||
|
||||
def onBeforeChange(self,obj,prop):
|
||||
"""Method called before the object has a property changed.
|
||||
"""Method called before the object has a property changed.
|
||||
|
||||
Specifically, this method is called before the value changes.
|
||||
|
||||
@@ -1000,8 +1000,8 @@ class _Wall(ArchComponent.Component):
|
||||
"""
|
||||
|
||||
if prop == "Length":
|
||||
if (obj.Base and obj.Length.Value
|
||||
and hasattr(self,"oldLength") and (self.oldLength is not None)
|
||||
if (obj.Base and obj.Length.Value
|
||||
and hasattr(self,"oldLength") and (self.oldLength is not None)
|
||||
and (self.oldLength != obj.Length.Value)):
|
||||
|
||||
if hasattr(obj.Base,'Shape'):
|
||||
@@ -1030,7 +1030,7 @@ class _Wall(ArchComponent.Component):
|
||||
|
||||
def getFootprint(self,obj):
|
||||
"""Get the faces that make up the base/foot of the wall.
|
||||
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of <Part.Face>
|
||||
@@ -1047,7 +1047,7 @@ class _Wall(ArchComponent.Component):
|
||||
|
||||
def getExtrusionData(self,obj):
|
||||
"""Get data needed to extrude the wall from a base object.
|
||||
|
||||
|
||||
take the Base object, and find a base face to extrude
|
||||
out, a vector to define the extrusion direction and distance.
|
||||
|
||||
@@ -1086,7 +1086,7 @@ class _Wall(ArchComponent.Component):
|
||||
if hasattr(obj.Base.Proxy, 'getWidths'):
|
||||
# Return a list of Width corresponding to indexes of sorted
|
||||
# edges of Sketch.
|
||||
widths = obj.Base.Proxy.getWidths(obj.Base)
|
||||
widths = obj.Base.Proxy.getWidths(obj.Base)
|
||||
|
||||
# Get width of each edge/wall segment from ArchWall.OverrideWidth if
|
||||
# Base Object does not provide it
|
||||
@@ -1130,7 +1130,7 @@ class _Wall(ArchComponent.Component):
|
||||
if hasattr(obj.Base.Proxy, 'getAligns'):
|
||||
# Return a list of Align corresponds to indexes of sorted
|
||||
# edges of Sketch.
|
||||
aligns = obj.Base.Proxy.getAligns(obj.Base)
|
||||
aligns = obj.Base.Proxy.getAligns(obj.Base)
|
||||
# Get align of each edge/wall segment from ArchWall.OverrideAlign if
|
||||
# Base Object does not provide it
|
||||
if not aligns:
|
||||
@@ -1270,7 +1270,7 @@ class _Wall(ArchComponent.Component):
|
||||
# if not sketch, e.g. Dwire, can have wire which is 3d
|
||||
# so not on the placement's working plane - below
|
||||
# applied to Sketch not applicable here
|
||||
#normal = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1))
|
||||
#normal = obj.Base.getGlobalPlacement().Rotation.multVec(FreeCAD.Vector(0,0,1))
|
||||
#normal = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))
|
||||
|
||||
if self.basewires:
|
||||
@@ -1325,10 +1325,12 @@ class _Wall(ArchComponent.Component):
|
||||
if curAligns == "Left":
|
||||
off = obj.Offset.Value
|
||||
if layers:
|
||||
curWidth = abs(layers[i])
|
||||
off = off+layeroffset
|
||||
dvec.multiply(abs(layers[i]))
|
||||
layeroffset += abs(layers[i])
|
||||
dvec.multiply(curWidth)
|
||||
layeroffset += abs(curWidth)
|
||||
else:
|
||||
curWidth = widths
|
||||
dvec.multiply(width)
|
||||
|
||||
# Now DraftGeomUtils.offsetWire() support
|
||||
@@ -1344,7 +1346,7 @@ class _Wall(ArchComponent.Component):
|
||||
w2 = DraftGeomUtils.offsetWire(wire, dvec,
|
||||
bind=False,
|
||||
occ=False,
|
||||
widthList=widths,
|
||||
widthList=curWidth,
|
||||
offsetMode=None,
|
||||
alignList=aligns,
|
||||
normal=normal,
|
||||
@@ -1355,7 +1357,7 @@ class _Wall(ArchComponent.Component):
|
||||
w1 = DraftGeomUtils.offsetWire(wire, dvec,
|
||||
bind=False,
|
||||
occ=False,
|
||||
widthList=widths,
|
||||
widthList=curWidth,
|
||||
offsetMode="BasewireMode",
|
||||
alignList=aligns,
|
||||
normal=normal,
|
||||
@@ -1366,10 +1368,12 @@ class _Wall(ArchComponent.Component):
|
||||
dvec = dvec.negative()
|
||||
off = obj.Offset.Value
|
||||
if layers:
|
||||
curWidth = abs(layers[i])
|
||||
off = off+layeroffset
|
||||
dvec.multiply(abs(layers[i]))
|
||||
layeroffset += abs(layers[i])
|
||||
dvec.multiply(curWidth)
|
||||
layeroffset += abs(curWidth)
|
||||
else:
|
||||
curWidth = widths
|
||||
dvec.multiply(width)
|
||||
|
||||
# Now DraftGeomUtils.offsetWire() support similar effect as ArchWall Offset
|
||||
@@ -1381,7 +1385,7 @@ class _Wall(ArchComponent.Component):
|
||||
w2 = DraftGeomUtils.offsetWire(wire, dvec,
|
||||
bind=False,
|
||||
occ=False,
|
||||
widthList=widths,
|
||||
widthList=curWidth,
|
||||
offsetMode=None,
|
||||
alignList=aligns,
|
||||
normal=normal,
|
||||
@@ -1390,8 +1394,8 @@ class _Wall(ArchComponent.Component):
|
||||
w1 = DraftGeomUtils.offsetWire(wire, dvec,
|
||||
bind=False,
|
||||
occ=False,
|
||||
widthList=widths,
|
||||
offsetMode="BasewireMode",
|
||||
widthList=curWidth,
|
||||
offsetMode=None,
|
||||
alignList=aligns,
|
||||
normal=normal,
|
||||
basewireOffset=off)
|
||||
@@ -1402,13 +1406,15 @@ class _Wall(ArchComponent.Component):
|
||||
#elif obj.Align == "Center":
|
||||
elif curAligns == "Center":
|
||||
if layers:
|
||||
off = width/2-layeroffset
|
||||
totalwidth=sum([abs(l) for l in layers])
|
||||
curWidth = abs(layers[i])
|
||||
off = totalwidth/2-layeroffset
|
||||
d1 = Vector(dvec).multiply(off)
|
||||
w1 = DraftGeomUtils.offsetWire(wire,d1)
|
||||
layeroffset += abs(layers[i])
|
||||
off = width/2-layeroffset
|
||||
w1 = DraftGeomUtils.offsetWire(wire, d1)
|
||||
layeroffset += curWidth
|
||||
off = totalwidth/2-layeroffset
|
||||
d1 = Vector(dvec).multiply(off)
|
||||
w2 = DraftGeomUtils.offsetWire(wire,d1)
|
||||
w2 = DraftGeomUtils.offsetWire(wire, d1)
|
||||
else:
|
||||
dvec.multiply(width)
|
||||
|
||||
@@ -1427,7 +1433,7 @@ class _Wall(ArchComponent.Component):
|
||||
offsetMode="BasewireMode",
|
||||
alignList=aligns,
|
||||
normal=normal)
|
||||
|
||||
|
||||
|
||||
sh = DraftGeomUtils.bind(w1,w2)
|
||||
|
||||
@@ -1435,6 +1441,10 @@ class _Wall(ArchComponent.Component):
|
||||
del aligns[0:edgeNum]
|
||||
if sh:
|
||||
|
||||
if layers and (layers[i] < 0):
|
||||
# layers with negative values are not drawn
|
||||
continue
|
||||
|
||||
sh.fix(0.1,0,1) # fixes self-intersecting wires
|
||||
|
||||
f = Part.Face(sh)
|
||||
@@ -1538,7 +1548,7 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent):
|
||||
"""Add display modes' data to the coin scenegraph.
|
||||
|
||||
Add each display mode as a coin node, whose parent is this view
|
||||
provider.
|
||||
provider.
|
||||
|
||||
Each display mode's node includes the data needed to display the object
|
||||
in that mode. This might include colors of faces, or the draw style of
|
||||
|
||||
Reference in New Issue
Block a user