Files
create/src/Mod/Arch/ArchSpace.py
Harald Geyer 7a87f8f887 Arch: Fix placement of Space objects imported from ArchiCAD
Any space objects imported from ArchiCAD (via IFC) are rotated
by 90° around the global z-Axis. When turning the same base
shape into an other Arch object like Wall or Slab, no such error
happens.

Turns out the shape creation in Space objects is handled
differently then in other Arch objects and just copying the usual
method fixes this. I have no idea why this makes a difference.

To document why this change is necessary, a test cases is added.
Upon editing TestArch.py a number of trailing spaces have been
removed accidentally.
2020-06-15 09:58:49 +02:00

818 lines
33 KiB
Python

# -*- coding: utf8 -*-
#***************************************************************************
#* Copyright (c) 2013 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 Space"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
SpaceTypes = [
"Undefined",
"Exterior",
"Exterior - Terrace",
"Office",
"Office - Enclosed",
"Office - Open Plan",
"Conference / Meeting / Multipurpose",
"Classroom / Lecture / Training For Penitentiary",
"Lobby",
"Lobby - For Hotel",
"Lobby - For Performing Arts Theater",
"Lobby - For Motion Picture Theater",
"Audience/Seating Area",
"Audience/Seating Area - For Gymnasium",
"Audience/Seating Area - For Exercise Center",
"Audience/Seating Area - For Convention Center",
"Audience/Seating Area - For Penitentiary",
"Audience/Seating Area - For Religious Buildings",
"Audience/Seating Area - For Sports Arena",
"Audience/Seating Area - For Performing Arts Theater",
"Audience/Seating Area - For Motion Picture Theater",
"Audience/Seating Area - For Transportation",
"Atrium",
"Atrium - First Three Floors",
"Atrium - Each Additional Floor",
"Lounge / Recreation",
"Lounge / Recreation - For Hospital",
"Dining Area",
"Dining Area - For Penitentiary",
"Dining Area - For Hotel",
"Dining Area - For Motel",
"Dining Area - For Bar Lounge/Leisure Dining",
"Dining Area - For Family Dining",
"Food Preparation",
"Laboratory",
"Restrooms",
"Dressing / Locker / Fitting",
"Room",
"Corridor / Transition",
"Corridor / Transition - For Hospital",
"Corridor / Transition - For Manufacturing Facility",
"Stairs",
"Active Storage",
"Active Storage - For Hospital",
"Inactive Storage",
"Inactive Storage - For Museum",
"Electrical / Mechanical",
"Gymnasium / Exercise Center",
"Gymnasium / Exercise Center - Playing Area",
"Gymnasium / Exercise Center - Exercise Area",
"Courthouse / Police Station / Penitentiary",
"Courthouse / Police Station / Penitentiary - Courtroom",
"Courthouse / Police Station / Penitentiary - Confinement Cells",
"Courthouse / Police Station / Penitentiary - Judges' Chambers",
"Fire Stations",
"Fire Stations - Engine Room",
"Fire Stations - Sleeping Quarters",
"Post Office - Sorting Area",
"Convention Center - Exhibit Space",
"Library",
"Library - Card File and Cataloging",
"Library - Stacks",
"Library - Reading Area",
"Hospital",
"Hospital - Emergency",
"Hospital - Recovery",
"Hospital - Nurses' Station",
"Hospital - Exam / Treatment",
"Hospital - Pharmacy",
"Hospital - Patient Room",
"Hospital - Operating Room",
"Hospital - Nursery",
"Hospital - Medical Supply",
"Hospital - Physical Therapy",
"Hospital - Radiology",
"Hospital - Laundry-Washing",
"Automotive - Service / Repair",
"Manufacturing",
"Manufacturing - Low Bay (< 7.5m Floor to Ceiling Height)",
"Manufacturing - High Bay (> 7.5m Floor to Ceiling Height)",
"Manufacturing - Detailed Manufacturing",
"Manufacturing - Equipment Room",
"Manufacturing - Control Room",
"Hotel / Motel Guest Rooms",
"Dormitory - Living Quarters",
"Museum",
"Museum - General Exhibition",
"Museum - Restoration",
"Bank / Office - Banking Activity Area",
"Workshop",
"Sales Area",
"Religious Buildings",
"Religious Buildings - Worship Pulpit, Choir",
"Religious Buildings - Fellowship Hall",
"Retail",
"Retail - Sales Area",
"Retail - Mall Concourse",
"Sports Arena",
"Sports Arena - Ring Sports Area",
"Sports Arena - Court Sports Area",
"Sports Arena - Indoor Playing Field Area",
"Warehouse",
"Warehouse - Fine Material Storage",
"Warehouse - Medium / Bulky Material Storage",
"Parking Garage - Garage Area",
"Transportation",
"Transportation - Airport / Concourse",
"Transportation - Air / Train / Bus - Baggage Area",
"Transportation - Terminal - Ticket Counter"
]
ConditioningTypes = [
"Unconditioned",
"Heated",
"Cooled",
"HeatedAndCooled",
"Vented",
"NaturallyVentedOnly"
]
import FreeCAD,ArchComponent,ArchCommands,math,Draft,sys
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 ArchSpace
# \ingroup ARCH
# \brief The Space object and tools
#
# This module provides tools to build Space objects.
# Spaces define an open volume inside or outside a
# building, ie. a room.
def makeSpace(objects=None,baseobj=None,name="Space"):
"""makeSpace([objects]): Creates a space object from the given objects. Objects can be one
document object, in which case it becomes the base shape of the space object, or a list of
selection objects as got from getSelectionEx(), or a list of tuples (object, subobjectname)"""
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Space")
obj.Label = translate("Arch",name)
_Space(obj)
if FreeCAD.GuiUp:
_ViewProviderSpace(obj.ViewObject)
if baseobj:
objects = baseobj
if objects:
if not isinstance(objects,list):
objects = [objects]
if len(objects) == 1:
obj.Base = objects[0]
if FreeCAD.GuiUp:
objects[0].ViewObject.hide()
else:
obj.Proxy.addSubobjects(obj,objects)
return obj
def addSpaceBoundaries(space,subobjects):
"""addSpaceBoundaries(space,subobjects): adds the given subobjects to the given space"""
import Draft
if Draft.getType(space) == "Space":
space.Proxy.addSubobjects(space,subobjects)
def removeSpaceBoundaries(space,objects):
"""removeSpaceBoundaries(space,objects): removes the given objects from the given spaces boundaries"""
import Draft
if Draft.getType(space) == "Space":
bounds = space.Boundaries
for o in objects:
for b in bounds:
if o.Name == b[0].Name:
bounds.remove(b)
break
space.Boundaries = bounds
class _CommandSpace:
"the Arch Space command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_Space',
'MenuText': QT_TRANSLATE_NOOP("Arch_Space","Space"),
'Accel': "S, P",
'ToolTip': QT_TRANSLATE_NOOP("Arch_Space","Creates a space object from selected boundary objects")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Space"))
FreeCADGui.addModule("Arch")
sel = FreeCADGui.Selection.getSelection()
if sel:
FreeCADGui.Control.closeDialog()
if len(sel) == 1:
FreeCADGui.doCommand("obj = Arch.makeSpace(FreeCADGui.Selection.getSelection())")
else:
FreeCADGui.doCommand("obj = Arch.makeSpace(FreeCADGui.Selection.getSelectionEx())")
FreeCADGui.addModule("Draft")
FreeCADGui.doCommand("Draft.autogroup(obj)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
else:
FreeCAD.Console.PrintMessage(translate("Arch","Please select a base object")+"\n")
FreeCADGui.Control.showDialog(ArchComponent.SelectionTaskPanel())
FreeCAD.ArchObserver = ArchComponent.ArchSelectionObserver(nextCommand="Arch_Space")
FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver)
class _Space(ArchComponent.Component):
"A space object"
def __init__(self,obj):
ArchComponent.Component.__init__(self,obj)
self.setProperties(obj)
obj.IfcType = "Space"
def setProperties(self,obj):
pl = obj.PropertiesList
if not "Boundaries" in pl:
obj.addProperty("App::PropertyLinkSubList","Boundaries", "Space",QT_TRANSLATE_NOOP("App::Property","The objects that make the boundaries of this space object"))
if not "Area" in pl:
obj.addProperty("App::PropertyArea", "Area", "Space",QT_TRANSLATE_NOOP("App::Property","The computed floor area of this space"))
if not "FinishFloor" in pl:
obj.addProperty("App::PropertyString", "FinishFloor", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the floor of this space"))
if not "FinishWalls" in pl:
obj.addProperty("App::PropertyString", "FinishWalls", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the walls of this space"))
if not "FinishCeiling" in pl:
obj.addProperty("App::PropertyString", "FinishCeiling", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the ceiling of this space"))
if not "Group" in pl:
obj.addProperty("App::PropertyLinkList", "Group", "Space",QT_TRANSLATE_NOOP("App::Property","Objects that are included inside this space, such as furniture"))
if not "SpaceType" in pl:
obj.addProperty("App::PropertyEnumeration","SpaceType", "Space",QT_TRANSLATE_NOOP("App::Property","The type of this space"))
obj.SpaceType = SpaceTypes
if not "FloorThickness" in pl:
obj.addProperty("App::PropertyLength", "FloorThickness","Space",QT_TRANSLATE_NOOP("App::Property","The thickness of the floor finish"))
if not "NumberOfPeople" in pl:
obj.addProperty("App::PropertyInteger", "NumberOfPeople","Space",QT_TRANSLATE_NOOP("App::Property","The number of people who typically occupy this space"))
if not "LightingPower" in pl:
obj.addProperty("App::PropertyFloat", "LightingPower", "Space",QT_TRANSLATE_NOOP("App::Property","The electric power needed to light this space in Watts"))
if not "EquipmentPower" in pl:
obj.addProperty("App::PropertyFloat", "EquipmentPower","Space",QT_TRANSLATE_NOOP("App::Property","The electric power needed by the equipment of this space in Watts"))
if not "AutoPower" in pl:
obj.addProperty("App::PropertyBool", "AutoPower", "Space",QT_TRANSLATE_NOOP("App::Property","If True, Equipment Power will be automatically filled by the equipment included in this space"))
if not "Conditioning" in pl:
obj.addProperty("App::PropertyEnumeration","Conditioning", "Space",QT_TRANSLATE_NOOP("App::Property","The type of air conditioning of this space"))
obj.Conditioning = ConditioningTypes
if not "Internal" in pl:
obj.addProperty("App::PropertyBool", "Internal", "Space",QT_TRANSLATE_NOOP("App::Property","Specifies if this space is internal or external"))
obj.Internal = True
self.Type = "Space"
obj.setEditorMode("HorizontalArea",2)
def onDocumentRestored(self,obj):
ArchComponent.Component.onDocumentRestored(self,obj)
self.setProperties(obj)
def execute(self,obj):
if self.clone(obj):
return
self.getShape(obj)
def onChanged(self,obj,prop):
if prop == "Group":
if hasattr(obj,"EquipmentPower"):
if obj.AutoPower:
p = 0
for o in Draft.getObjectsOfType(Draft.getGroupContents(obj.Group,addgroups=True),"Equipment"):
if hasattr(o,"EquipmentPower"):
p += o.EquipmentPower
if p != obj.EquipmentPower:
obj.EquipmentPower = p
elif prop == "Zone":
if obj.Zone:
if obj.Zone.ViewObject:
if hasattr(obj.Zone.ViewObject,"Proxy"):
if hasattr(obj.Zone.ViewObject.Proxy,"claimChildren"):
obj.Zone.ViewObject.Proxy.claimChildren()
if hasattr(obj,"Area"):
obj.setEditorMode('Area',1)
ArchComponent.Component.onChanged(self,obj,prop)
def addSubobjects(self,obj,subobjects):
"adds subobjects to this space"
objs = obj.Boundaries
for o in subobjects:
if isinstance(o,tuple) or isinstance(o,list):
if o[0].Name != obj.Name:
objs.append(tuple(o))
else:
for el in o.SubElementNames:
if "Face" in el:
if o.Object.Name != obj.Name:
objs.append((o.Object,el))
obj.Boundaries = objs
def getShape(self,obj):
"computes a shape from a base shape and/or boundary faces"
import Part
shape = None
faces = []
pl = obj.Placement
#print("starting compute")
# 1: if we have a base shape, we use it
if obj.Base:
if hasattr(obj.Base,'Shape'):
if obj.Base.Shape.Solids:
shape = obj.Base.Shape.copy()
shape = shape.removeSplitter()
# 2: if not, add all bounding boxes of considered objects and build a first shape
if shape:
#print("got shape from base object")
bb = shape.BoundBox
else:
bb = None
for b in obj.Boundaries:
if hasattr(b[0],'Shape'):
if not bb:
bb = b[0].Shape.BoundBox
else:
bb.add(b[0].Shape.BoundBox)
if not bb:
return
shape = Part.makeBox(bb.XLength,bb.YLength,bb.ZLength,FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin))
#print("created shape from boundbox")
# 3: identifying boundary faces
goodfaces = []
for b in obj.Boundaries:
if hasattr(b[0],'Shape'):
for sub in b[1]:
if "Face" in sub:
fn = int(sub[4:])-1
faces.append(b[0].Shape.Faces[fn])
#print("adding face ",fn," of object ",b[0].Name)
#print("total: ", len(faces), " faces")
# 4: get cutvolumes from faces
cutvolumes = []
for f in faces:
f = f.copy()
f.reverse()
cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(f,shape)
if cutvolume:
#print("generated 1 cutvolume")
cutvolumes.append(cutvolume.copy())
#Part.show(cutvolume)
for v in cutvolumes:
#print("cutting")
shape = shape.cut(v)
# 5: get the final shape
if shape:
if shape.Solids:
#print("setting objects shape")
shape = shape.Solids[0]
self.applyShape(obj,shape,pl)
if hasattr(obj.Area,"Value"):
a = self.getArea(obj)
if obj.Area.Value != a:
obj.Area = a
return
print("Arch: error computing space boundary for",obj.Label)
def getArea(self,obj,notouch=False):
"returns the horizontal area at the center of the space"
self.face = self.getFootprint(obj)
if self.face:
if not notouch:
if hasattr(obj,"PerimeterLength"):
if self.face.OuterWire.Length != obj.PerimeterLength.Value:
obj.PerimeterLength = self.face.OuterWire.Length
if hasattr(obj,"VerticalArea"):
a = 0
for f in obj.Shape.Faces:
ang = f.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1))
if (ang > 1.57) and (ang < 1.571):
a += f.Area
if a != obj.VerticalArea.Value:
obj.VerticalArea = a
#print "area of ",obj.Label," : ",f.Area
return self.face.Area
else:
return 0
def getFootprint(self,obj):
"returns a face that represents the footprint of this space"
import Part,DraftGeomUtils
if not hasattr(obj.Shape,"CenterOfMass"):
return None
try:
pl = Part.makePlane(1,1)
pl.translate(obj.Shape.CenterOfMass)
sh = obj.Shape.copy()
cutplane,v1,v2 = ArchCommands.getCutVolume(pl,sh)
e = sh.section(cutplane)
e = Part.__sortEdges__(e.Edges)
w = Part.Wire(e)
dv = FreeCAD.Vector(obj.Shape.CenterOfMass.x,obj.Shape.CenterOfMass.y,obj.Shape.BoundBox.ZMin)
dv = dv.sub(obj.Shape.CenterOfMass)
w.translate(dv)
return Part.Face(w)
except Part.OCCError:
return None
class _ViewProviderSpace(ArchComponent.ViewProviderComponent):
"A View Provider for Section Planes"
def __init__(self,vobj):
ArchComponent.ViewProviderComponent.__init__(self,vobj)
self.setProperties(vobj)
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
vobj.Transparency = prefs.GetInt("defaultSpaceTransparency",85)
vobj.LineWidth = Draft.getParam("linewidth")
vobj.LineColor = ArchCommands.getDefaultColor("Space")
vobj.DrawStyle = ["Solid","Dashed","Dotted","Dashdot"][prefs.GetInt("defaultSpaceStyle",2)]
if prefs.GetInt("defaultSpaceTransparency",85) == 100:
vobj.DisplayMode = "Wireframe"
def setProperties(self,vobj):
pl = vobj.PropertiesList
if not "Text" in pl:
vobj.addProperty("App::PropertyStringList", "Text", "Space",QT_TRANSLATE_NOOP("App::Property","The text to show. Use $area, $label, $tag, $floor, $walls, $ceiling to insert the respective data"))
vobj.Text = ["$label","$area"]
if not "FontName" in pl:
vobj.addProperty("App::PropertyFont", "FontName", "Space",QT_TRANSLATE_NOOP("App::Property","The name of the font"))
vobj.FontName = Draft.getParam("textfont","")
if not "TextColor" in pl:
vobj.addProperty("App::PropertyColor", "TextColor", "Space",QT_TRANSLATE_NOOP("App::Property","The color of the area text"))
vobj.TextColor = (0.0,0.0,0.0,1.0)
if not "FontSize" in pl:
vobj.addProperty("App::PropertyLength", "FontSize", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the text font"))
vobj.FontSize = Draft.getParam("textheight",10)
if not "FirstLine" in pl:
vobj.addProperty("App::PropertyLength", "FirstLine", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the first line of text"))
vobj.FirstLine = Draft.getParam("textheight",10)
if not "LineSpacing" in pl:
vobj.addProperty("App::PropertyFloat", "LineSpacing", "Space",QT_TRANSLATE_NOOP("App::Property","The space between the lines of text"))
vobj.LineSpacing = 1.0
if not "TextPosition" in pl:
vobj.addProperty("App::PropertyVectorDistance","TextPosition","Space",QT_TRANSLATE_NOOP("App::Property","The position of the text. Leave (0,0,0) for automatic position"))
if not "TextAlign" in pl:
vobj.addProperty("App::PropertyEnumeration", "TextAlign", "Space",QT_TRANSLATE_NOOP("App::Property","The justification of the text"))
vobj.TextAlign = ["Left","Center","Right"]
vobj.TextAlign = "Center"
if not "Decimals" in pl:
vobj.addProperty("App::PropertyInteger", "Decimals", "Space",QT_TRANSLATE_NOOP("App::Property","The number of decimals to use for calculated texts"))
vobj.Decimals = Draft.getParam("dimPrecision",2)
if not "ShowUnit" in pl:
vobj.addProperty("App::PropertyBool", "ShowUnit", "Space",QT_TRANSLATE_NOOP("App::Property","Show the unit suffix"))
vobj.ShowUnit = Draft.getParam("showUnit",True)
def onDocumentRestored(self,vobj):
self.setProperties(vobj)
def getIcon(self):
import Arch_rc
if hasattr(self,"Object"):
if hasattr(self.Object,"CloneOf"):
if self.Object.CloneOf:
return ":/icons/Arch_Space_Clone.svg"
return ":/icons/Arch_Space_Tree.svg"
def attach(self,vobj):
ArchComponent.ViewProviderComponent.attach(self,vobj)
from pivy import coin
self.color = coin.SoBaseColor()
self.font = coin.SoFont()
self.text1 = coin.SoAsciiText()
self.text1.string = " "
self.text1.justification = coin.SoAsciiText.LEFT
self.text2 = coin.SoAsciiText()
self.text2.string = " "
self.text2.justification = coin.SoAsciiText.LEFT
self.coords = coin.SoTransform()
self.header = coin.SoTransform()
self.label = coin.SoSwitch()
sep = coin.SoSeparator()
self.label.whichChild = 0
sep.addChild(self.coords)
sep.addChild(self.color)
sep.addChild(self.font)
sep.addChild(self.text2)
sep.addChild(self.header)
sep.addChild(self.text1)
self.label.addChild(sep)
vobj.Annotation.addChild(self.label)
self.onChanged(vobj,"TextColor")
self.onChanged(vobj,"FontSize")
self.onChanged(vobj,"FirstLine")
self.onChanged(vobj,"LineSpacing")
self.onChanged(vobj,"FontName")
self.Object = vobj.Object
# footprint mode
self.fmat = coin.SoMaterial()
self.fcoords = coin.SoCoordinate3()
self.fset = coin.SoIndexedFaceSet()
fhints = coin.SoShapeHints()
fhints.vertexOrdering = fhints.COUNTERCLOCKWISE
sep = coin.SoSeparator()
sep.addChild(self.fmat)
sep.addChild(self.fcoords)
sep.addChild(fhints)
sep.addChild(self.fset)
vobj.RootNode.addChild(sep)
def updateData(self,obj,prop):
if prop in ["Shape","Label","Tag","Area"]:
self.onChanged(obj.ViewObject,"Text")
self.onChanged(obj.ViewObject,"TextPosition")
def getTextPosition(self,vobj):
pos = FreeCAD.Vector()
if hasattr(vobj,"TextPosition"):
import DraftVecUtils
if DraftVecUtils.isNull(vobj.TextPosition):
try:
pos = vobj.Object.Shape.CenterOfMass
z = vobj.Object.Shape.BoundBox.ZMin
pos = FreeCAD.Vector(pos.x,pos.y,z)
except (AttributeError, RuntimeError):
pos = FreeCAD.Vector()
else:
pos = vobj.TextPosition
# placement's displacement will be already added by the coin node
pos = vobj.Object.Placement.inverse().multVec(pos)
return pos
def onChanged(self,vobj,prop):
if prop in ["Text","Decimals","ShowUnit"]:
if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"Text"):
self.text1.string.deleteValues(0)
self.text2.string.deleteValues(0)
text1 = []
text2 = []
first = True
for t in vobj.Text:
if t:
if hasattr(vobj.Object,"Area"):
from FreeCAD import Units
q = Units.Quantity(vobj.Object.Area.Value,Units.Area).getUserPreferred()
qt = vobj.Object.Area.Value/q[1]
if hasattr(vobj,"Decimals"):
if vobj.Decimals == 0:
qt = str(int(qt))
else:
f = "%."+str(abs(vobj.Decimals))+"f"
qt = f % qt
else:
qt = str(qt)
if hasattr(vobj,"ShowUnit"):
if vobj.ShowUnit:
qt = qt + q[2].replace("^2",u"\xb2") # square symbol
t = t.replace("$area",qt)
t = t.replace("$label",vobj.Object.Label)
if hasattr(vobj.Object,"Tag"):
t = t.replace("$tag",vobj.Object.Tag)
if hasattr(vobj.Object,"FinishFloor"):
t = t.replace("$floor",vobj.Object.FinishFloor)
if hasattr(vobj.Object,"FinishWalls"):
t = t.replace("$walls",vobj.Object.FinishWalls)
if hasattr(vobj.Object,"FinishCeiling"):
t = t.replace("$ceiling",vobj.Object.FinishCeiling)
if sys.version_info.major < 3:
t = t.encode("utf8")
if first:
text1.append(t)
else:
text2.append(t)
first = False
if text1:
self.text1.string.setValues(text1)
if text2:
self.text2.string.setValues(text2)
elif prop == "FontName":
if hasattr(self,"font") and hasattr(vobj,"FontName"):
self.font.name = str(vobj.FontName)
elif (prop == "FontSize"):
if hasattr(self,"font") and hasattr(vobj,"FontSize"):
self.font.size = vobj.FontSize.Value
if hasattr(vobj,"FirstLine"):
scale = vobj.FirstLine.Value/vobj.FontSize.Value
self.header.scaleFactor.setValue([scale,scale,scale])
elif (prop == "FirstLine"):
if hasattr(self,"header") and hasattr(vobj,"FontSize") and hasattr(vobj,"FirstLine"):
scale = vobj.FirstLine.Value/vobj.FontSize.Value
self.header.scaleFactor.setValue([scale,scale,scale])
elif prop == "TextColor":
if hasattr(self,"color") and hasattr(vobj,"TextColor"):
c = vobj.TextColor
self.color.rgb.setValue(c[0],c[1],c[2])
elif prop == "TextPosition":
if hasattr(self,"coords") and hasattr(self,"header") and hasattr(vobj,"TextPosition") and hasattr(vobj,"FirstLine"):
pos = self.getTextPosition(vobj)
self.coords.translation.setValue([pos.x,pos.y,pos.z+0.01]) # adding small z offset to separate from bottom face
up = vobj.FirstLine.Value * vobj.LineSpacing
self.header.translation.setValue([0,up,0])
elif prop == "LineSpacing":
if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"LineSpacing"):
self.text1.spacing = vobj.LineSpacing
self.text2.spacing = vobj.LineSpacing
self.onChanged(vobj,"TextPosition")
elif prop == "TextAlign":
if hasattr(self,"text1") and hasattr(self,"text2") and hasattr(vobj,"TextAlign"):
from pivy import coin
if vobj.TextAlign == "Center":
self.text1.justification = coin.SoAsciiText.CENTER
self.text2.justification = coin.SoAsciiText.CENTER
elif vobj.TextAlign == "Right":
self.text1.justification = coin.SoAsciiText.RIGHT
self.text2.justification = coin.SoAsciiText.RIGHT
else:
self.text1.justification = coin.SoAsciiText.LEFT
self.text2.justification = coin.SoAsciiText.LEFT
elif prop == "Visibility":
if vobj.Visibility:
self.label.whichChild = 0
else:
self.label.whichChild = -1
elif prop == "ShapeColor":
if hasattr(vobj,"ShapeColor"):
self.fmat.diffuseColor.setValue((vobj.ShapeColor[0],vobj.ShapeColor[1],vobj.ShapeColor[2]))
elif prop == "Transparency":
if hasattr(vobj,"Transparency"):
self.fmat.transparency.setValue(vobj.Transparency/100.0)
def setEdit(self,vobj,mode):
taskd = SpaceTaskPanel()
taskd.obj = self.Object
taskd.update()
taskd.updateBoundaries()
FreeCADGui.Control.showDialog(taskd)
return True
def getDisplayModes(self,vobj):
modes = ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj)+["Footprint"]
return modes
def setDisplayMode(self,mode):
self.fset.coordIndex.deleteValues(0)
self.fcoords.point.deleteValues(0)
if mode == "Footprint":
if hasattr(self,"Object"):
face = self.Object.Proxy.getFootprint(self.Object)
if face:
verts = []
fdata = []
idx = 0
tri = face.tessellate(1)
for v in tri[0]:
verts.append([v.x,v.y,v.z])
for f in tri[1]:
fdata.extend([f[0]+idx,f[1]+idx,f[2]+idx,-1])
idx += len(tri[0])
self.fcoords.point.setValues(verts)
self.fset.coordIndex.setValues(0,len(fdata),fdata)
return "Points"
return ArchComponent.ViewProviderComponent.setDisplayMode(self,mode)
class SpaceTaskPanel(ArchComponent.ComponentTaskPanel):
"A modified version of the Arch component task panel"
def __init__(self):
ArchComponent.ComponentTaskPanel.__init__(self)
self.editButton = QtGui.QPushButton(self.form)
self.editButton.setObjectName("editButton")
self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg"))
self.grid.addWidget(self.editButton, 4, 0, 1, 2)
self.editButton.setText(QtGui.QApplication.translate("Arch", "Set text position", None))
QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.setTextPos)
boundLabel = QtGui.QLabel(self.form)
self.grid.addWidget(boundLabel, 5, 0, 1, 2)
boundLabel.setText(QtGui.QApplication.translate("Arch", "Space boundaries", None))
self.boundList = QtGui.QListWidget(self.form)
self.grid.addWidget(self.boundList, 6, 0, 1, 2)
self.addCompButton = QtGui.QPushButton(self.form)
self.addCompButton.setObjectName("addCompButton")
self.addCompButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg"))
self.grid.addWidget(self.addCompButton, 7, 0, 1, 1)
self.addCompButton.setText(QtGui.QApplication.translate("Arch", "Add", None))
QtCore.QObject.connect(self.addCompButton, QtCore.SIGNAL("clicked()"), self.addBoundary)
self.delCompButton = QtGui.QPushButton(self.form)
self.delCompButton.setObjectName("delCompButton")
self.delCompButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg"))
self.grid.addWidget(self.delCompButton, 7, 1, 1, 1)
self.delCompButton.setText(QtGui.QApplication.translate("Arch", "Remove", None))
QtCore.QObject.connect(self.delCompButton, QtCore.SIGNAL("clicked()"), self.delBoundary)
def updateBoundaries(self):
self.boundList.clear()
if self.obj:
for b in self.obj.Boundaries:
s = b[0].Label
for n in b[1]:
s += ", " + n
it = QtGui.QListWidgetItem(s)
it.setToolTip(b[0].Name)
self.boundList.addItem(it)
def setTextPos(self):
FreeCADGui.runCommand("Draft_Edit")
def addBoundary(self):
if self.obj:
if FreeCADGui.Selection.getSelectionEx():
self.obj.Proxy.addSubobjects(self.obj,FreeCADGui.Selection.getSelectionEx())
self.updateBoundaries()
def delBoundary(self):
if self.boundList.currentRow() >= 0:
it = self.boundList.item(self.boundList.currentRow())
if it and self.obj:
on = it.toolTip()
bounds = self.obj.Boundaries
for b in bounds:
if b[0].Name == on:
bounds.remove(b)
break
self.obj.Boundaries = bounds
self.updateBoundaries()
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Space',_CommandSpace())