# -*- coding: utf8 -*- #*************************************************************************** #* Copyright (c) 2013 Yorik van Havre * #* * #* 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__ = "https://www.freecad.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" ] AreaCalculationType = [ "XY-plane projection", "At Center of Mass" ] import FreeCAD import ArchComponent import ArchCommands import Draft from draftutils import params if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from draftutils.translate 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 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 _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","Identical to Horizontal Area")) 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 if not "AreaCalculationType" in pl: obj.addProperty("App::PropertyEnumeration", "AreaCalculationType", "Space",QT_TRANSLATE_NOOP("App::Property","Defines the calculation type for the horizontal area and its perimeter length")) obj.AreaCalculationType = AreaCalculationType self.Type = "Space" 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.get_group_contents(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 addObject(self,obj,child): "Adds an object to this Space" if not child in obj.Group: g = obj.Group g.append(child) obj.Group = g 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: # compute area even if we are not calculating the shape if obj.Shape and obj.Shape.Solids: if hasattr(obj.Area,"Value"): a = self.getArea(obj) if obj.Area.Value != a: obj.Area = a 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.HorizontalArea,"Value"): if hasattr(obj,"AreaCalculationType"): if obj.AreaCalculationType == "At Center of Mass": a = self.getArea(obj) obj.HorizontalArea = a if hasattr(obj,"Area"): obj.Area = obj.HorizontalArea 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 return self.face.Area else: return 0 def getFootprint(self,obj): "returns a face that represents the footprint of this space at the center of mass" import Part import 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) vobj.Transparency = params.get_param_arch("defaultSpaceTransparency") vobj.LineWidth = params.get_param_view("DefaultShapeLineWidth") vobj.LineColor = ArchCommands.getDefaultColor("Space") vobj.DrawStyle = ["Solid","Dashed","Dotted","Dashdot"][params.get_param_arch("defaultSpaceStyle")] 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, $longname, $description and for finishes $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 = params.get_param("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 = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") 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 = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") 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 = params.get_param("dimPrecision") if not "ShowUnit" in pl: vobj.addProperty("App::PropertyBool", "ShowUnit", "Space",QT_TRANSLATE_NOOP("App::Property","Show the unit suffix")) vobj.ShowUnit = params.get_param("showUnit") 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.Object.Placement.multVec(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 hasattr(vobj.Object,"LongName"): t = t.replace("$longname",vobj.Object.LongName) if hasattr(vobj.Object,"Description"): t = t.replace("$description",vobj.Object.Description) 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]) self.onChanged(vobj, "TextPosition") 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]) self.onChanged(vobj, "TextPosition") 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 == "Transparency": if hasattr(vobj,"DisplayMode"): vobj.DisplayMode = "Wireframe" if vobj.Transparency == 100 else "Flat Lines" def setEdit(self, vobj, mode): if mode != 0: return None 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()