Skip to content

Commit

Permalink
Arch: Added Equipment tool
Browse files Browse the repository at this point in the history
The purpose of this tool is to handle standalone objects such as
furniture or sanitary equipment in a building. It is mesh-based,
and accepts either a mesh or a part shape as its base object.
  • Loading branch information
yorikvanhavre committed Aug 29, 2014
1 parent 67b54f8 commit cef2c1b
Show file tree
Hide file tree
Showing 10 changed files with 1,865 additions and 11 deletions.
3 changes: 2 additions & 1 deletion src/Mod/Arch/Arch.py
Expand Up @@ -44,4 +44,5 @@
from ArchStairs import *
from ArchRebar import *
from ArchFrame import *
from ArchPanel import *
from ArchPanel import *
from ArchEquipment import *
265 changes: 265 additions & 0 deletions src/Mod/Arch/ArchEquipment.py
@@ -0,0 +1,265 @@
# -*- coding: utf8 -*-

#***************************************************************************
#* *
#* Copyright (c) 2014 *
#* 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 Equipment"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"

import FreeCAD,Draft,ArchComponent,DraftVecUtils,ArchCommands,Units
from FreeCAD import Vector
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from DraftTools import translate
else:
def translate(ctxt,txt):
return txt

# presets
Roles = ["Furniture", "Hydro Equipment", "Electric Equipment"]


def makeEquipment(baseobj=None,placement=None,name=translate("Arch","Equipment")):
"makeEquipment([baseobj,placement,name]): creates an equipment object from the given base object"
obj = FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython",name)
obj.Label = name
_Equipment(obj)
if baseobj:
obj.Base = baseobj
if placement:
obj.Placement = placement
if FreeCAD.GuiUp:
_ViewProviderEquipment(obj.ViewObject)


def createMeshView(obj,direction=FreeCAD.Vector(0,0,-1),outeronly=False,largestonly=False):
"""createMeshView(obj,[direction,outeronly,largestonly]): creates a flat shape that is the
projection of the given mesh object in the given direction (default = on the XY plane). If
outeronly is True, only the outer contour is taken into consideration, discarding the inner
holes. If largestonly is True, only the largest segment of the given mesh will be used."""

import Mesh, math, Part, DraftGeomUtils
if not obj.isDerivedFrom("Mesh::Feature"):
return
mesh = obj.Mesh

# 1. Flattening the mesh
proj = []
for f in mesh.Facets:
nf = []
for v in f.Points:
v = FreeCAD.Vector(v)
a = v.negative().getAngle(direction)
l = math.cos(a)*v.Length
p = v.add(FreeCAD.Vector(direction).multiply(l))
p = DraftVecUtils.rounded(p)
nf.append(p)
proj.append(nf)
flatmesh = Mesh.Mesh(proj)

# 2. Removing wrong faces
facets = []
for f in flatmesh.Facets:
if f.Normal.getAngle(direction) < math.pi:
facets.append(f)
cleanmesh = Mesh.Mesh(facets)

#Mesh.show(cleanmesh)

# 3. Getting the bigger mesh from the planar segments
if largestonly:
c = cleanmesh.getSeparateComponents()
#print c
cleanmesh = c[0]
segs = cleanmesh.getPlanarSegments(1)
meshes = []
for s in segs:
f = [cleanmesh.Facets[i] for i in s]
meshes.append(Mesh.Mesh(f))
a = 0
for m in meshes:
if m.Area > a:
boundarymesh = m
a = m.Area
#Mesh.show(boundarymesh)
cleanmesh = boundarymesh

# 4. Creating a Part and getting the contour

shape = None
for f in cleanmesh.Facets:
p = Part.makePolygon(f.Points+[f.Points[0]])
#print p,len(p.Vertexes),p.isClosed()
try:
p = Part.Face(p)
if shape:
shape = shape.fuse(p)
else:
shape = p
except:
pass
shape = shape.removeSplitter()

# 5. Extracting the largest wire

if outeronly:
count = 0
largest = None
for w in shape.Wires:
if len(w.Vertexes) > count:
count = len(w.Vertexes)
largest = w
if largest:
try:
f = Part.Face(w)
except:
print "Unable to produce a face from the outer wire."
else:
shape = f

return shape


class _CommandEquipment:
"the Arch Equipment command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_Equipment',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Equipment","Equipment"),
'Accel': "E, Q",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Equipment","Creates an equipment object from a selected object (Part or Mesh)")}

def IsActive(self):
return not FreeCAD.ActiveDocument is None

def Activated(self):
s = FreeCADGui.Selection.getSelection()
if not s:
FreeCAD.Console.PrintError(translate("Arch","You must select a base object first!"))
else:
base = s[0].Name
FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Equipment")))
FreeCADGui.addModule("Arch")
FreeCADGui.doCommand("Arch.makeEquipment(FreeCAD.ActiveDocument." + base + ")")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
return


class _Command3Views:
"the Arch 3Views command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_3Views',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_3Views","3 views from mesh"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_3Views","Creates 3 views (top, front, side) from a mesh-based object")}

def IsActive(self):
return not FreeCAD.ActiveDocument is None

def Activated(self):
s = FreeCADGui.Selection.getSelection()
if len(s) != 1:
FreeCAD.Console.PrintError(translate("Arch","You must select exactly one base object"))
else:
obj = s[0]
if not obj.isDerivedFrom("Mesh::Feature"):
FreeCAD.Console.PrintError(translate("Arch","The selected object must be a mesh"))
else:
if obj.Mesh.CountFacets > 1000:
msgBox = QtGui.QMessageBox()
msgBox.setText(translate("Arch","This mesh has more than 1000 facets."))
msgBox.setInformativeText(translate("Arch","This operation can take a long time. Proceed?"))
msgBox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
msgBox.setDefaultButton(QtGui.QMessageBox.Cancel)
ret = msgBox.exec_()
if ret == QtGui.QMessageBox.Cancel:
return
elif obj.Mesh.CountFacets >= 500:
FreeCAD.Console.PrintWarning(translate("Arch","The mesh has more than 500 facets. This will take a couple of minutes..."))
FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create 3 views")))
FreeCADGui.addModule("Arch")
FreeCADGui.addModule("Part")
FreeCADGui.doCommand("s1 = Arch.createMeshView(FreeCAD.ActiveDocument." + obj.Name + ",FreeCAD.Vector(0,0,-1),outeronly=False,largestonly=False)")
FreeCADGui.doCommand("Part.show(s1)")
FreeCADGui.doCommand("s2 = Arch.createMeshView(FreeCAD.ActiveDocument." + obj.Name + ",FreeCAD.Vector(1,0,0),outeronly=False,largestonly=False)")
FreeCADGui.doCommand("Part.show(s2)")
FreeCADGui.doCommand("s3 = Arch.createMeshView(FreeCAD.ActiveDocument." + obj.Name + ",FreeCAD.Vector(0,1,0),outeronly=False,largestonly=False)")
FreeCADGui.doCommand("Part.show(s3)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
return


class _Equipment(ArchComponent.Component):
"The Equipment object"
def __init__(self,obj):
ArchComponent.Component.__init__(self,obj)
obj.addProperty("Part::PropertyPartShape","TopView","Arch",translate("Arch","an optional 2D shape representing a top view of this equipment"))
obj.addProperty("Part::PropertyPartShape","FrontView","Arch",translate("Arch","an optional 2D shape representing a front view of this equipment"))
obj.addProperty("Part::PropertyPartShape","SideView","Arch",translate("Arch","an optional 2D shape representing a side view of this equipment"))
obj.addProperty("App::PropertyString","Model","Arch",translate("Arch","The model description of this equipment"))
obj.addProperty("App::PropertyString","Url","Arch",translate("Arch","The url of the product page of this equipment"))
obj.addProperty("App::PropertyEnumeration","Role","Arch",translate("Arch","The role of this equipment"))
self.Type = "Equipment"
obj.Role = Roles
obj.Proxy = self

def onChanged(self,obj,prop):
self.hideSubobjects(obj,prop)

def execute(self,obj):
pl = obj.Placement
m = None
if obj.Base:

if obj.Base.isDerivedFrom("Part::Feature"):
base = obj.Base.Shape.copy()
base = self.processSubShapes(obj,base,pl)
if base:
import Mesh
m = Mesh.Mesh(base.tessellate(1))

elif obj.Base.isDerivedFrom("Mesh::Feature"):
m = obj.Base.Mesh.copy()

if m:
if not pl.isNull():
m.Placement = pl
obj.Mesh = m


class _ViewProviderEquipment(ArchComponent.ViewProviderComponent):
"A View Provider for the Equipment object"

def __init__(self,vobj):
ArchComponent.ViewProviderComponent.__init__(self,vobj)

def getIcon(self):
import Arch_rc
return ":/icons/Arch_Equipment_Tree.svg"


if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Equipment',_CommandEquipment())
FreeCADGui.addCommand('Arch_3Views', _Command3Views())
10 changes: 5 additions & 5 deletions src/Mod/Arch/Arch_rc.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Mod/Arch/CMakeLists.txt
Expand Up @@ -27,6 +27,7 @@ SET(Arch_SRCS
TestArch.py
ArchFrame.py
ArchPanel.py
ArchEquipment.py
)
SOURCE_GROUP("" FILES ${Arch_SRCS})

Expand Down
8 changes: 4 additions & 4 deletions src/Mod/Arch/InitGui.py
Expand Up @@ -73,22 +73,22 @@ def Initialize(self):
"Arch_Floor","Arch_Building","Arch_Site",
"Arch_Window","Arch_Roof","Arch_Axis",
"Arch_SectionPlane","Arch_Space","Arch_Stairs",
"Arch_Panel",
"Arch_Panel","Arch_Equipment",
"Arch_Frame","Arch_Add","Arch_Remove","Arch_Survey"]
self.utilities = ["Arch_SplitMesh","Arch_MeshToShape",
"Arch_SelectNonSolidMeshes","Arch_RemoveShape",
"Arch_CloseHoles","Arch_MergeWalls","Arch_Check",
"Arch_IfcExplorer","Arch_ToggleIfcBrepFlag"]
"Arch_IfcExplorer","Arch_ToggleIfcBrepFlag","Arch_3Views"]

# draft tools
self.drafttools = ["Draft_Line","Draft_Wire","Draft_Circle","Draft_Arc","Draft_Ellipse",
"Draft_Polygon","Draft_Rectangle", "Draft_Text",
"Draft_Dimension", "Draft_BSpline","Draft_Point","Draft_ShapeString",
"Draft_Dimension", "Draft_BSpline","Draft_Point",
"Draft_Facebinder","Draft_BezCurve"]
self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset",
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
"Draft_Drawing","Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
"Draft_PathArray","Draft_Clone"]
"Draft_Clone"]
self.extramodtools = ["Draft_WireToBSpline","Draft_AddPoint","Draft_DelPoint"]
self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup",
"Draft_SelectGroup","Draft_SelectPlane",
Expand Down
3 changes: 3 additions & 0 deletions src/Mod/Arch/Resources/Arch.qrc
Expand Up @@ -42,7 +42,10 @@
<file>icons/Arch_Frame_Tree.svg</file>
<file>icons/Arch_Panel.svg</file>
<file>icons/Arch_Panel_Tree.svg</file>
<file>icons/Arch_Equipment.svg</file>
<file>icons/Arch_Equipment_Tree.svg</file>
<file>icons/Arch_Survey.svg</file>
<file>icons/Arch_3Views.svg</file>
<file>icons/IFC.svg</file>
<file>icons/Arch_StructuralSystem.svg</file>
<file>icons/Arch_StructuralSystem_Tree.svg</file>
Expand Down

0 comments on commit cef2c1b

Please sign in to comment.