Arch: [WIP] Curtain Wall tool
This commit is contained in:
@@ -65,3 +65,4 @@ from ArchPipe import *
|
||||
from ArchBuildingPart import *
|
||||
from ArchReference import *
|
||||
from ArchTruss import *
|
||||
from ArchCurtainWall import *
|
||||
|
||||
583
src/Mod/Arch/ArchCurtainWall.py
Normal file
583
src/Mod/Arch/ArchCurtainWall.py
Normal file
@@ -0,0 +1,583 @@
|
||||
# -*- coding: utf8 -*-
|
||||
|
||||
#***************************************************************************
|
||||
#* Copyright (c) 2020 Yorik van Havre <yorik@uncreated.net> *
|
||||
#* *
|
||||
#* 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 Arch Curtain Wall"
|
||||
__author__ = "Yorik van Havre"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
import math,sys
|
||||
import FreeCAD
|
||||
import Draft
|
||||
import ArchComponent
|
||||
import ArchCommands
|
||||
import DraftVecUtils
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from DraftTools import translate
|
||||
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 ArchCurtainWall
|
||||
# \ingroup ARCH
|
||||
# \brief The Curtain Wall object and tools
|
||||
#
|
||||
# This module provides tools to build Curtain wall objects.
|
||||
|
||||
"""
|
||||
Curtain wall tool
|
||||
|
||||
Abstract: Curtain walls need a surface to work on (base).
|
||||
They then divide that surface into a grid, by intersecting
|
||||
it with a grid of planes, forming pseudorectangular facets.
|
||||
|
||||
The vertical lines can then receive one type of profile
|
||||
(vertical mullions), the horizontal ones another
|
||||
(horizontal mullions), and the facets a third (panels).
|
||||
|
||||
The surface can be prepared before applying the curtain wall
|
||||
tool on it, which allow for more complex panel/mullion
|
||||
configuration.
|
||||
|
||||
We then have two cases, depending on the surface: Either the
|
||||
four corners of each facet form a plane, in which case the
|
||||
panel filling is rectangular, or they don't, in which case
|
||||
the facet is triangulated and receives a third mullion
|
||||
(diagonal mullion).
|
||||
"""
|
||||
|
||||
|
||||
def makeCurtainWall(baseobj=None,name="Curtain Wall"):
|
||||
|
||||
"""
|
||||
makeCurtainWall([baseobj]): Creates a curtain wall in the active document
|
||||
"""
|
||||
|
||||
if not FreeCAD.ActiveDocument:
|
||||
FreeCAD.Console.PrintError("No active document. Aborting\n")
|
||||
return
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CurtainWall")
|
||||
obj.Label = translate("Arch","Curtain Wall")
|
||||
CurtainWall(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
ViewProviderCurtainWall(obj.ViewObject)
|
||||
if baseobj:
|
||||
obj.Base = baseobj
|
||||
if FreeCAD.GuiUp:
|
||||
baseobj.ViewObject.hide()
|
||||
return obj
|
||||
|
||||
|
||||
class CommandArchCurtainWall:
|
||||
|
||||
|
||||
"the Arch Curtain Wall command definition"
|
||||
|
||||
def GetResources(self):
|
||||
|
||||
return {'Pixmap' : 'Arch_CurtainWall',
|
||||
'MenuText': QT_TRANSLATE_NOOP("Arch_CurtainWall","Curtain Wall"),
|
||||
'Accel': "C, W",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Arch_CurtainWall","Creates a curtain wall object from selected line or from scratch")}
|
||||
|
||||
def IsActive(self):
|
||||
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
if len(sel) > 1:
|
||||
FreeCAD.Console.PrintError(translate("Arch","Please select only one base object or none")+"\n")
|
||||
elif len(sel) == 1:
|
||||
# build on selection
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Curtain Wall"))
|
||||
FreeCADGui.addModule("Draft")
|
||||
FreeCADGui.addModule("Arch")
|
||||
FreeCADGui.doCommand("obj = Arch.makeCurtainWall(FreeCAD.ActiveDocument."+FreeCADGui.Selection.getSelection()[0].Name+")")
|
||||
FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
else:
|
||||
# interactive line drawing
|
||||
self.points = []
|
||||
if hasattr(FreeCAD,"DraftWorkingPlane"):
|
||||
FreeCAD.DraftWorkingPlane.setup()
|
||||
if hasattr(FreeCADGui,"Snapper"):
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
|
||||
|
||||
def getPoint(self,point=None,obj=None):
|
||||
|
||||
"""Callback for clicks during interactive mode"""
|
||||
|
||||
if point is None:
|
||||
# cancelled
|
||||
return
|
||||
self.points.append(point)
|
||||
if len(self.points) == 1:
|
||||
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint)
|
||||
elif len(self.points) == 2:
|
||||
FreeCADGui.Control.closeDialog()
|
||||
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("obj = Arch.makeCurtainWall(base)")
|
||||
FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
class CurtainWall(ArchComponent.Component):
|
||||
|
||||
|
||||
"The curtain wall object"
|
||||
|
||||
def __init__(self,obj):
|
||||
|
||||
ArchComponent.Component.__init__(self,obj)
|
||||
self.setProperties(obj)
|
||||
obj.IfcType = "Curtain Wall"
|
||||
|
||||
def setProperties(self,obj):
|
||||
|
||||
pl = obj.PropertiesList
|
||||
if not "VerticalMullionNumber" in pl:
|
||||
obj.addProperty("App::PropertyInteger","VerticalMullionNumber","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of vertical mullions"))
|
||||
obj.setEditorMode("VerticalMullionNumber",1)
|
||||
if not "VerticalDirection" in pl:
|
||||
obj.addProperty("App::PropertyVector","VerticalDirection","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The vertical direction of this curtain wall"))
|
||||
obj.VerticalDirection = FreeCAD.Vector(0,0,1)
|
||||
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
|
||||
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)"))
|
||||
if not "HorizontalMullionNumber" in pl:
|
||||
obj.addProperty("App::PropertyInteger","HorizontalMullionNumber","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of horizontal mullions"))
|
||||
obj.setEditorMode("HorizontalMullionNumber",1)
|
||||
if not "HorizontalDirection" in pl:
|
||||
obj.addProperty("App::PropertyVector","HorizontalDirection","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The horizontal direction of this curtain wall"))
|
||||
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
|
||||
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)"))
|
||||
if not "DiagonalMullionNumber" in pl:
|
||||
obj.addProperty("App::PropertyInteger","DiagonalMullionNumber","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of diagonal mullions"))
|
||||
obj.setEditorMode("DiagonalMullionNumber",1)
|
||||
if not "DiagonalMullionSize" in pl:
|
||||
obj.addProperty("App::PropertyLength","DiagonalMullionSize","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The size of the diagonal mullions, if any, if no profile is used"))
|
||||
obj.DiagonalMullionSize = 50
|
||||
if not "DiagonalMullionProfile" in pl:
|
||||
obj.addProperty("App::PropertyLink","DiagonalMullionProfile","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","A profile for diagonal mullions, if any (disables horizontal mullion size)"))
|
||||
if not "PanelNumber" in pl:
|
||||
obj.addProperty("App::PropertyInteger","PanelNumber","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The number of panels"))
|
||||
obj.setEditorMode("PanelNumber",1)
|
||||
if not "PanelThickness" in pl:
|
||||
obj.addProperty("App::PropertyLength","PanelThickness","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The thickness of the panels"))
|
||||
obj.PanelThickness = 20
|
||||
if not "SwapHorizontalVertical" in pl:
|
||||
obj.addProperty("App::PropertyBool","SwapHorizontalVertical","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","Swaps horizontal and vertical lines"))
|
||||
if not "Normal" in pl:
|
||||
obj.addProperty("App::PropertyVector","Normal","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","The normal direction of this curtain wall"))
|
||||
if not "Refine" in pl:
|
||||
obj.addProperty("App::PropertyBool","Refine","CurtainWall",
|
||||
QT_TRANSLATE_NOOP("App::Property","Perform subtractions between components so none overlap"))
|
||||
|
||||
|
||||
def onDocumentRestored(self,obj):
|
||||
|
||||
ArchComponent.Component.onDocumentRestored(self,obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def onChanged(self,obj,prop):
|
||||
|
||||
ArchComponent.Component.onChanged(self,obj,prop)
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part,DraftGeomUtils
|
||||
|
||||
pl = obj.Placement
|
||||
|
||||
# test properties
|
||||
if not obj.Base:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": no base\n")
|
||||
return
|
||||
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")
|
||||
return
|
||||
if obj.HorizontalMullionProfile:
|
||||
if not hasattr(obj.HorizontalMullionProfile,"Shape"):
|
||||
FreeCAD.Console.PrintLog(obj.Label+": invalid horizontal mullion profile\n")
|
||||
return
|
||||
if obj.DiagonalMullionProfile:
|
||||
if not hasattr(obj.DiagonalMullionProfile,"Shape"):
|
||||
FreeCAD.Console.PrintLog(obj.Label+": invalid diagonal mullion profile\n")
|
||||
return
|
||||
|
||||
# identify normal, vdir and hdir directions
|
||||
normal = obj.Normal
|
||||
if not normal.Length:
|
||||
normal = DraftGeomUtils.getNormal(obj.Base.Shape)
|
||||
if not normal:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": unable to calculate normal\n")
|
||||
return
|
||||
else:
|
||||
# set the normal if not yet set
|
||||
obj.Normal = normal
|
||||
normal.normalize()
|
||||
if obj.VerticalDirection.Length:
|
||||
vdir = obj.VerticalDirection
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": vertical direction not set\n")
|
||||
return
|
||||
vdir.normalize()
|
||||
if obj.HorizontalDirection.Length:
|
||||
hdir = obj.HorizontalDirection
|
||||
else:
|
||||
hdir = normal.cross(obj.VerticalDirection)
|
||||
if hdir and hdir.Length:
|
||||
obj.HorizontalDirection = hdir
|
||||
else:
|
||||
FreeCAD.Console.PrintLog(obj.Label+": unable to calculate horizontal direction\n")
|
||||
return
|
||||
hdir.normalize()
|
||||
#print(hdir,vdir,normal)
|
||||
|
||||
# calculate boundbox points
|
||||
verts = [v.Point for v in obj.Base.Shape.Vertexes]
|
||||
hverts = [self.getProjectedLength(v,hdir) for v in verts]
|
||||
vverts = [self.getProjectedLength(v,vdir) for v in verts]
|
||||
nverts = [self.getProjectedLength(v,normal) for v in verts]
|
||||
#print(hverts,vverts,nverts)
|
||||
MinH = min(hverts)
|
||||
MaxH = max(hverts)
|
||||
MinV = min(vverts)
|
||||
MaxV = max(vverts)
|
||||
MinN = min(nverts)
|
||||
MaxN = max(nverts)
|
||||
|
||||
# also define extended bbox to better boolean ops results
|
||||
ExtMinH = MinH-5
|
||||
ExtMaxH = MaxH+5
|
||||
ExtMinV = MinV-5
|
||||
ExtMaxV = MaxV+5
|
||||
ExtMinN = MinN-5
|
||||
ExtMaxN = MaxN+5
|
||||
|
||||
# construct vertical planes
|
||||
vplanes = []
|
||||
if obj.VerticalSections > 1:
|
||||
p0 = FreeCAD.Vector(normal).multiply(ExtMinN)
|
||||
p0 = p0.add(FreeCAD.Vector(hdir).multiply(MinH))
|
||||
p0 = p0.add(FreeCAD.Vector(vdir).multiply(ExtMinV))
|
||||
p1 = p0.add(FreeCAD.Vector(normal).multiply(ExtMaxN-ExtMinN))
|
||||
p2 = p1.add(FreeCAD.Vector(vdir).multiply(ExtMaxV-ExtMinV))
|
||||
p3 = p0.add(FreeCAD.Vector(vdir).multiply(ExtMaxV-ExtMinV))
|
||||
vplane = Part.Face(Part.makePolygon([p0,p1,p2,p3,p0]))
|
||||
vstep = FreeCAD.Vector(hdir).multiply((MaxH-MinH)/obj.VerticalSections)
|
||||
for i in range(1,obj.VerticalSections):
|
||||
vplane = vplane.translate(vstep)
|
||||
vplanes.append(vplane.copy())
|
||||
|
||||
# construct horizontal planes
|
||||
hplanes = []
|
||||
if obj.HorizontalSections > 1:
|
||||
p4 = FreeCAD.Vector(normal).multiply(ExtMinN)
|
||||
p4 = p4.add(FreeCAD.Vector(hdir).multiply(ExtMinH))
|
||||
p4 = p4.add(FreeCAD.Vector(vdir).multiply(MinV))
|
||||
p5 = p4.add(FreeCAD.Vector(normal).multiply(ExtMaxN-ExtMinN))
|
||||
p6 = p5.add(FreeCAD.Vector(hdir).multiply(ExtMaxH-ExtMinH))
|
||||
p7 = p4.add(FreeCAD.Vector(hdir).multiply(ExtMaxH-ExtMinH))
|
||||
hplane = Part.Face(Part.makePolygon([p4,p5,p6,p7,p4]))
|
||||
hstep = FreeCAD.Vector(vdir).multiply((MaxV-MinV)/obj.HorizontalSections)
|
||||
for i in range(1,obj.HorizontalSections):
|
||||
hplane = hplane.translate(hstep)
|
||||
hplanes.append(hplane.copy())
|
||||
|
||||
# apply sections
|
||||
baseshape = obj.Base.Shape.copy()
|
||||
for plane in vplanes+hplanes:
|
||||
baseshape = baseshape.cut(plane)
|
||||
|
||||
# make edge/normal relation table
|
||||
edgetable = {}
|
||||
for face in baseshape.Faces:
|
||||
for edge in face.Edges:
|
||||
ec = edge.hashCode()
|
||||
if ec in edgetable:
|
||||
edgetable[ec].append(face)
|
||||
else:
|
||||
edgetable[ec] = [face]
|
||||
self.edgenormals = {}
|
||||
for ec,faces in edgetable.items():
|
||||
if len(faces) == 1:
|
||||
self.edgenormals[ec] = faces[0].normalAt(0,0)
|
||||
else:
|
||||
n = faces[0].normalAt(0,0).cross(faces[1].normalAt(0,0))
|
||||
if n.Length:
|
||||
n.normalize()
|
||||
else:
|
||||
n = faces[0].normalAt(0,0)
|
||||
self.edgenormals[ec] = n
|
||||
|
||||
# construct vertical mullions
|
||||
vmullions = []
|
||||
vprofile = self.getMullionProfile(obj,"Vertical")
|
||||
if vprofile:
|
||||
vedges = self.getNormalEdges(baseshape.Edges,hdir)
|
||||
vmullions = self.makeMullions(vedges,vprofile,normal)
|
||||
|
||||
# construct horizontal mullions
|
||||
hmullions = []
|
||||
hprofile = self.getMullionProfile(obj,"Horizontal")
|
||||
if hprofile:
|
||||
hedges = self.getNormalEdges(baseshape.Edges,vdir)
|
||||
hmullions = self.makeMullions(hedges,hprofile,normal)
|
||||
|
||||
# construct panels
|
||||
panels = []
|
||||
dedges = []
|
||||
if obj.PanelThickness.Value:
|
||||
for face in baseshape.Faces:
|
||||
verts = [v.Point for v in face.OuterWire.OrderedVertexes]
|
||||
if len(verts) == 4:
|
||||
if DraftGeomUtils.isPlanar(verts):
|
||||
panel = self.makePanel(verts,obj.PanelThickness.Value)
|
||||
panels.append(panel)
|
||||
else:
|
||||
verts1 = [verts[0],verts[1],verts[2]]
|
||||
panel = self.makePanel(verts1,obj.PanelThickness.Value)
|
||||
panels.append(panel)
|
||||
verts2 = [verts[0],verts[2],verts[3]]
|
||||
panel = self.makePanel(verts2,obj.PanelThickness.Value)
|
||||
panels.append(panel)
|
||||
dedges.append(Part.makeLine(verts[0],verts[2]))
|
||||
|
||||
# construct diagonal mullions
|
||||
dmullions = []
|
||||
if dedges:
|
||||
n = (dedges[0].Vertexes[-1].Point.sub(dedges[0].Point))
|
||||
dprofile = self.getMullionProfile(obj,"Diagonal")
|
||||
if dprofile:
|
||||
dmullions = self.makeMullions(dedges,dprofile,normal)
|
||||
|
||||
# perform subtractions
|
||||
if obj.Refine:
|
||||
subvmullion = None
|
||||
subhmullion = None
|
||||
subdmullion = None
|
||||
if vmullions:
|
||||
subvmullion = vmullions[0].copy()
|
||||
for m in vmullions[1:]:
|
||||
subvmullion = subvmullion.fuse(m)
|
||||
if hmullions:
|
||||
subhmullion = hmullions[0].copy()
|
||||
for m in hmullions[1:]:
|
||||
subhmullion = subhmullion.fuse(m)
|
||||
if dmullions:
|
||||
subdmullion = dmullions[0].copy()
|
||||
for m in dmullions[1:]:
|
||||
subdmullion = subdmullion.fuse(m)
|
||||
if subvmullion:
|
||||
hmullions = [m.cut(subvmullion) for m in hmullions]
|
||||
if subhmullion:
|
||||
dmullions = [m.cut(subvmullion) for m in dmullions]
|
||||
dmullions = [m.cut(subhmullion) for m in dmullions]
|
||||
panels = [m.cut(subvmullion) for m in panels]
|
||||
panels = [m.cut(subhmullion) for m in panels]
|
||||
if subdmullion:
|
||||
panels = [m.cut(subdmullion) for m in panels]
|
||||
|
||||
# mount shape
|
||||
shape = Part.makeCompound(vmullions+hmullions+dmullions+panels)
|
||||
shape = self.processSubShapes(obj,shape,pl)
|
||||
self.applyShape(obj,shape,pl)
|
||||
obj.VerticalMullionNumber = len(vmullions)
|
||||
obj.HorizontalMullionNumber = len(hmullions)
|
||||
obj.DiagonalMullionNumber = len(dmullions)
|
||||
obj.PanelNumber = len(panels)
|
||||
|
||||
def makePanel(self,verts,thickness):
|
||||
|
||||
"""creates a panel from face points and thickness"""
|
||||
|
||||
import Part
|
||||
|
||||
panel = Part.Face(Part.makePolygon(verts+[verts[0]]))
|
||||
n = panel.normalAt(0,0)
|
||||
n.multiply(thickness)
|
||||
panel = panel.extrude(n)
|
||||
return panel
|
||||
|
||||
def makeMullions(self,edges,profile,upvec):
|
||||
|
||||
"""creates a list of mullions from a list of edges and a profile"""
|
||||
|
||||
mullions = []
|
||||
pcenter = FreeCAD.Vector(0,0,0)
|
||||
if hasattr(profile,"CenterOfMass"):
|
||||
center = profile.CenterOfMass
|
||||
for edge in edges:
|
||||
p0 = edge.Vertexes[0].Point
|
||||
p1 = edge.Vertexes[-1].Point
|
||||
axis = p1.sub(p0)
|
||||
if edge.hashCode() in self.edgenormals:
|
||||
normal = self.edgenormals[edge.hashCode()]
|
||||
else:
|
||||
normal = self.normal
|
||||
mullion = self.rotateProfile(profile,axis,normal)
|
||||
mullion = mullion.translate(p0.sub(center))
|
||||
mullion = mullion.extrude(p1.sub(p0))
|
||||
mullions.append(mullion)
|
||||
return mullions
|
||||
|
||||
def getMullionProfile(self,obj,direction):
|
||||
|
||||
"""returns a profile shape already properly oriented, ready for extrude"""
|
||||
|
||||
import Part,DraftGeomUtils
|
||||
|
||||
prop1 = getattr(obj,direction+"MullionProfile")
|
||||
prop2 = getattr(obj,direction+"MullionSize").Value
|
||||
if prop1:
|
||||
profile = prop1.Shape.copy()
|
||||
else:
|
||||
if not prop2:
|
||||
return None
|
||||
profile = Part.Face(Part.makePlane(prop2,prop2,FreeCAD.Vector(-prop2/2,-prop2/2,0)))
|
||||
return profile
|
||||
|
||||
def rotateProfile(self,profile,axis,normal):
|
||||
|
||||
"""returns a rotated profile"""
|
||||
|
||||
import Part,DraftGeomUtils
|
||||
|
||||
oaxis = DraftGeomUtils.getNormal(profile)
|
||||
if len(profile.Edges[0].Vertexes) > 1:
|
||||
oxvec = profile.Edges[0].Vertexes[-1].Point.sub(profile.Edges[0].Vertexes[0].Point)
|
||||
elif hasattr(profile.Curve,"Center"):
|
||||
oxvec = profile.Edges[0].Vertexes[-1].Point.sub(profile.Curve.Center)
|
||||
orot = FreeCAD.Rotation(oxvec,oaxis.cross(oxvec),oaxis,"XYZ")
|
||||
nrot = FreeCAD.Rotation(normal,axis.cross(normal),axis,"XYZ")
|
||||
r = nrot.multiply(orot.inverted())
|
||||
if hasattr(profile,"CenterOfMass"):
|
||||
c = profile.CenterOfMass
|
||||
elif hasattr(profile,"BoundBox"):
|
||||
c = profile.BoundBox.Center
|
||||
else:
|
||||
c = FreeCAD.Vector(0,0,0)
|
||||
rprofile = profile.copy().rotate(c,r.Axis,math.degrees(r.Angle))
|
||||
return rprofile
|
||||
|
||||
def getNormalEdges(self,edges,reference):
|
||||
|
||||
"""returns a list of edges normal to the given reference"""
|
||||
|
||||
result = []
|
||||
tolerance = 0.67 # we try to get all edges with angle > 45deg
|
||||
for edge in edges:
|
||||
if len(edge.Vertexes) > 1:
|
||||
v = edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point)
|
||||
a = v.getAngle(reference)
|
||||
if (a <= math.pi/2+tolerance) and (a >= math.pi/2-tolerance):
|
||||
result.append(edge)
|
||||
return result
|
||||
|
||||
def getProjectedLength(self,v,ref):
|
||||
|
||||
"""gets a signed length from projecting a vector on another"""
|
||||
|
||||
proj = DraftVecUtils.project(v,ref)
|
||||
if proj.getAngle(ref) < 1:
|
||||
return proj.Length
|
||||
else:
|
||||
return -proj.Length
|
||||
|
||||
|
||||
class ViewProviderCurtainWall(ArchComponent.ViewProviderComponent):
|
||||
|
||||
|
||||
"A View Provider for the CurtainWall object"
|
||||
|
||||
def __init__(self,vobj):
|
||||
|
||||
ArchComponent.ViewProviderComponent.__init__(self,vobj)
|
||||
|
||||
def getIcon(self):
|
||||
|
||||
import Arch_rc
|
||||
return ":/icons/Arch_CurtainWall_Tree.svg"
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Arch_CurtainWall',CommandArchCurtainWall())
|
||||
@@ -52,6 +52,7 @@ SET(Arch_SRCS
|
||||
OfflineRenderingUtils.py
|
||||
exportIFC.py
|
||||
ArchTruss.py
|
||||
ArchCurtainWall.py
|
||||
)
|
||||
|
||||
SET(Dice3DS_SRCS
|
||||
|
||||
@@ -58,7 +58,7 @@ class ArchWorkbench(FreeCADGui.Workbench):
|
||||
|
||||
# Set up command lists
|
||||
self.archtools = ["Arch_Wall", "Arch_Structure", "Arch_Rebar",
|
||||
"Arch_BuildingPart",
|
||||
"Arch_CurtainWall","Arch_BuildingPart",
|
||||
"Arch_Project", "Arch_Site", "Arch_Building",
|
||||
"Arch_Floor", "Arch_Reference",
|
||||
"Arch_Window", "Arch_Roof", "Arch_AxisTools",
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
<file>icons/Arch_CloseHoles.svg</file>
|
||||
<file>icons/Arch_Component.svg</file>
|
||||
<file>icons/Arch_Component_Clone.svg</file>
|
||||
<file>icons/Arch_CurtainWall.svg</file>
|
||||
<file>icons/Arch_CurtainWall_Tree.svg</file>
|
||||
<file>icons/Arch_CutLine.svg</file>
|
||||
<file>icons/Arch_CutPlane.svg</file>
|
||||
<file>icons/Arch_Equipment.svg</file>
|
||||
|
||||
227
src/Mod/Arch/Resources/icons/Arch_CurtainWall.svg
Normal file
227
src/Mod/Arch/Resources/icons/Arch_CurtainWall.svg
Normal file
@@ -0,0 +1,227 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2985"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||
sodipodi:docname="Arch_CurtainWall.svg">
|
||||
<defs
|
||||
id="defs2987">
|
||||
<linearGradient
|
||||
id="linearGradient3794">
|
||||
<stop
|
||||
style="stop-color:#edd400;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3796" />
|
||||
<stop
|
||||
style="stop-color:#fce94f;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3798" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3794"
|
||||
id="linearGradient3867"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="32.714748"
|
||||
y1="27.398352"
|
||||
x2="38.997726"
|
||||
y2="3.6523125"
|
||||
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,22.253893,65.739554)"
|
||||
spreadMethod="reflect" />
|
||||
<linearGradient
|
||||
id="linearGradient3794-8">
|
||||
<stop
|
||||
style="stop-color:#ffb400;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3796-5" />
|
||||
<stop
|
||||
style="stop-color:#ffea00;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3798-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="23.848686"
|
||||
x2="62.65237"
|
||||
y1="23.848686"
|
||||
x1="15.184971"
|
||||
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3886"
|
||||
xlink:href="#linearGradient3794-8"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient3794-1">
|
||||
<stop
|
||||
style="stop-color:#ffb400;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3796-2" />
|
||||
<stop
|
||||
style="stop-color:#ffea00;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3798-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="23.848686"
|
||||
x2="62.65237"
|
||||
y1="23.848686"
|
||||
x1="15.184971"
|
||||
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3886-0"
|
||||
xlink:href="#linearGradient3794-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3794"
|
||||
id="linearGradient3867-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="32.714748"
|
||||
y1="27.398352"
|
||||
x2="38.997726"
|
||||
y2="3.6523125"
|
||||
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,20.172664,63.335227)"
|
||||
spreadMethod="reflect" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.096831"
|
||||
inkscape:cx="35.741065"
|
||||
inkscape:cy="31.523595"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="739"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2997"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2990">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>Arch_Site</dc:title>
|
||||
<dc:date>2011-10-10</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:url(#linearGradient3867);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712607 18.416343,9.7795791 3.081208,3.4042685 Z"
|
||||
id="path3763"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 4.0627415,16.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
|
||||
id="path890-5"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 5.2974093,30.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
|
||||
id="path892-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 6.7565544,43.481706 21.277372,50.578904 38.047346,50.493083 54.4155,57.77384 56.62748,14.385588"
|
||||
id="path3763-7-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:url(#linearGradient3867);fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 18.639033,9.8487048 20.601037,52.227979"
|
||||
id="path854"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3867);fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 39.63247,8.4753024 39.436269,52.62038"
|
||||
id="path856"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 16.639033,9.8487048 18.53167,49.696073"
|
||||
id="path854-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="M 37.63247,8.4753024 37.436269,52.62038"
|
||||
id="path856-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 4.0627415,18.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
|
||||
id="path890"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 5.2974093,32.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
|
||||
id="path892"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712608 18.416343,9.7795792 3.081208,3.4042686 Z"
|
||||
id="path3763-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
216
src/Mod/Arch/Resources/icons/Arch_CurtainWall_Tree.svg
Normal file
216
src/Mod/Arch/Resources/icons/Arch_CurtainWall_Tree.svg
Normal file
@@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2985"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||
sodipodi:docname="Arch_CurtainWall_Tree.svg">
|
||||
<defs
|
||||
id="defs2987">
|
||||
<linearGradient
|
||||
id="linearGradient3794">
|
||||
<stop
|
||||
style="stop-color:#edd400;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3796" />
|
||||
<stop
|
||||
style="stop-color:#fce94f;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3798" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3794-8">
|
||||
<stop
|
||||
style="stop-color:#ffb400;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3796-5" />
|
||||
<stop
|
||||
style="stop-color:#ffea00;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3798-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="23.848686"
|
||||
x2="62.65237"
|
||||
y1="23.848686"
|
||||
x1="15.184971"
|
||||
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3886"
|
||||
xlink:href="#linearGradient3794-8"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient3794-1">
|
||||
<stop
|
||||
style="stop-color:#ffb400;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3796-2" />
|
||||
<stop
|
||||
style="stop-color:#ffea00;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3798-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="23.848686"
|
||||
x2="62.65237"
|
||||
y1="23.848686"
|
||||
x1="15.184971"
|
||||
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3886-0"
|
||||
xlink:href="#linearGradient3794-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3794"
|
||||
id="linearGradient3867-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="32.714748"
|
||||
y1="27.398352"
|
||||
x2="38.997726"
|
||||
y2="3.6523125"
|
||||
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,20.172664,63.335227)"
|
||||
spreadMethod="reflect" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.096831"
|
||||
inkscape:cx="53.636136"
|
||||
inkscape:cy="32.727474"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1360"
|
||||
inkscape:window-height="739"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2997"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2990">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>Arch_Site</dc:title>
|
||||
<dc:date>2011-10-10</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712607 18.416343,9.7795791 3.081208,3.4042685 Z"
|
||||
id="path3763"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#b8b8b8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 4.0627415,16.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
|
||||
id="path890-5"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#b8b8b8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 5.2974093,30.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
|
||||
id="path892-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#b8b8b8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="M 6.7565544,43.481706 21.277372,50.578904 38.047346,50.493083 54.4155,57.77384 56.62748,14.385588"
|
||||
id="path3763-7-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="M 18.639033,9.8487048 20.601037,52.227979"
|
||||
id="path854"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
d="M 39.63247,8.4753024 39.436269,52.62038"
|
||||
id="path856"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#b8b8b8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="M 16.639033,9.8487048 18.53167,49.696073"
|
||||
id="path854-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#b8b8b8;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="M 37.63247,8.4753024 37.436269,52.62038"
|
||||
id="path856-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 4.0627415,18.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
|
||||
id="path890"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
|
||||
d="m 5.2974093,32.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
|
||||
id="path892"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712608 18.416343,9.7795792 3.081208,3.4042686 Z"
|
||||
id="path3763-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
@@ -1191,7 +1191,15 @@ def getSplineNormal(edge):
|
||||
return n
|
||||
|
||||
def getNormal(shape):
|
||||
"""Find the normal of a shape, if possible."""
|
||||
"""Find the normal of a shape or list of points, if possible."""
|
||||
if isinstance(shape,(list,tuple)):
|
||||
if len(shape) >= 3:
|
||||
v1 = shape[1].sub(shape[0])
|
||||
v2 = shape[2].sub(shape[0])
|
||||
n = v2.cross(v1)
|
||||
if n.Length:
|
||||
return n
|
||||
return None
|
||||
n = Vector(0,0,1)
|
||||
if shape.isNull():
|
||||
return n
|
||||
@@ -1709,15 +1717,27 @@ def isCoplanar(faces, tolerance=0):
|
||||
|
||||
|
||||
def isPlanar(shape):
|
||||
"""Check if the given shape is planar."""
|
||||
if len(shape.Vertexes) <= 3:
|
||||
return True
|
||||
"""Check if the given shape or list of points is planar."""
|
||||
n = getNormal(shape)
|
||||
for p in shape.Vertexes[1:]:
|
||||
pv = p.Point.sub(shape.Vertexes[0].Point)
|
||||
rv = DraftVecUtils.project(pv,n)
|
||||
if not DraftVecUtils.isNull(rv):
|
||||
return False
|
||||
if not n:
|
||||
return False
|
||||
if isinstance(shape,list):
|
||||
if len(shape) <= 3:
|
||||
return True
|
||||
else:
|
||||
for v in shape[3:]:
|
||||
pv = v.sub(shape[0])
|
||||
rv = DraftVecUtils.project(pv,n)
|
||||
if not DraftVecUtils.isNull(rv):
|
||||
return False
|
||||
else:
|
||||
if len(shape.Vertexes) <= 3:
|
||||
return True
|
||||
for p in shape.Vertexes[1:]:
|
||||
pv = p.Point.sub(shape.Vertexes[0].Point)
|
||||
rv = DraftVecUtils.project(pv,n)
|
||||
if not DraftVecUtils.isNull(rv):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user