freecad-cam/Mod/BIM/ArchSectionPlane.py
2026-02-01 01:59:24 +01:00

1341 lines
55 KiB
Python

#***************************************************************************
#* Copyright (c) 2011 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 *
#* *
#***************************************************************************
import FreeCAD
import math
import Draft
import ArchCommands
import DraftVecUtils
import ArchComponent
import re
import tempfile
import uuid
import time
from FreeCAD import Vector
from draftutils import params
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from draftutils.translate import translate
from pivy import coin
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 ArchSectionPlane
# \ingroup ARCH
# \brief The Section plane object and tools
#
# This module provides tools to build Section plane objects.
# It also contains functionality to produce SVG rendering of
# section planes, to be used in the TechDraw module
ISRENDERING = False # flag to prevent concurrent runs of the coin renderer
def getSectionData(source):
"""Returns some common data from section planes and building parts"""
if hasattr(source,"Objects"):
objs = source.Objects
cutplane = source.Shape
elif hasattr(source,"Group"):
import Part
objs = source.Group
cutplane = Part.makePlane(1000,1000,FreeCAD.Vector(-500,-500,0))
m = 1
if source.ViewObject and hasattr(source.ViewObject,"CutMargin"):
m = source.ViewObject.CutMargin.Value
cutplane.translate(FreeCAD.Vector(0,0,m))
cutplane.Placement = cutplane.Placement.multiply(source.Placement)
onlySolids = True
if hasattr(source,"OnlySolids"):
onlySolids = source.OnlySolids
clip = False
if hasattr(source,"Clip"):
clip = source.Clip
p = FreeCAD.Placement(source.Placement)
direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1))
if objs:
objs = Draft.get_group_contents(objs, walls=True, addgroups=True)
return objs,cutplane,onlySolids,clip,direction
def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesByObject=False):
"""
returns a list of shapes (visible, hidden, cut lines...)
obtained from performing a series of booleans against the given cut plane
"""
import Part,DraftGeomUtils
shapes = []
hshapes = []
sshapes = []
objectShapes = []
objectSshapes = []
if joinArch:
shtypes = {}
for o in objs:
if Draft.getType(o) in ["Wall","Structure"]:
if o.Shape.isNull():
pass
elif onlySolids:
shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).extend(o.Shape.Solids)
else:
shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).append(o.Shape.copy())
elif hasattr(o,'Shape'):
if o.Shape.isNull():
pass
elif onlySolids:
shapes.extend(o.Shape.Solids)
objectShapes.append((o, o.Shape.Solids))
else:
shapes.append(o.Shape.copy())
objectShapes.append((o,[o.Shape.copy()]))
for k,v in shtypes.items():
v1 = v.pop()
if v:
v1 = v1.multiFuse(v)
v1 = v1.removeSplitter()
if v1.Solids:
shapes.extend(v1.Solids)
objectShapes.append((k,v1.Solids))
else:
print("ArchSectionPlane: Fusing Arch objects produced non-solid results")
shapes.append(v1)
objectShapes.append((k,[v1]))
else:
for o in objs:
if hasattr(o,'Shape'):
if o.Shape.isNull():
pass
elif onlySolids:
if o.Shape.isValid():
shapes.extend(o.Shape.Solids)
objectShapes.append((o,o.Shape.Solids))
else:
shapes.append(o.Shape)
objectShapes.append((o,[o.Shape]))
cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(cutplane,shapes,clip)
shapes = []
for o, shapeList in objectShapes:
tmpSshapes = []
for sh in shapeList:
for sub in (sh.SubShapes if sh.ShapeType == "Compound" else [sh]):
if cutvolume:
if sub.Volume < 0:
sub = sub.reversed() # Use reversed as sub is immutable.
c = sub.cut(cutvolume)
s = sub.section(cutface)
try:
wires = DraftGeomUtils.findWires(s.Edges)
for w in wires:
f = Part.Face(w)
tmpSshapes.append(f)
except Part.OCCError:
#print "ArchView: unable to get a face"
tmpSshapes.append(s)
shapes.extend(c.SubShapes if c.ShapeType == "Compound" else [c])
if showHidden:
c = sub.cut(invcutvolume)
hshapes.extend(c.SubShapes if c.ShapeType == "Compound" else [c])
else:
shapes.append(sub)
if len(tmpSshapes) > 0:
sshapes.extend(tmpSshapes)
if groupSshapesByObject:
objectSshapes.append((o, tmpSshapes))
if groupSshapesByObject:
return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes
else:
return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume
def getFillForObject(o, defaultFill, source):
"""returns a color tuple from an object's material"""
if hasattr(source, 'UseMaterialColorForFill') and source.UseMaterialColorForFill:
material = None
if hasattr(o, 'Material') and o.Material:
material = o.Material
elif isinstance(o,str):
material = FreeCAD.ActiveDocument.getObject(o)
if material:
if hasattr(material, 'SectionColor') and material.SectionColor:
return material.SectionColor
elif hasattr(material, 'Color') and material.Color:
return material.Color
elif hasattr(o,"ViewObject") and hasattr(o.ViewObject,"ShapeColor"):
return o.ViewObject.ShapeColor
return defaultFill
def isOriented(obj,plane):
"""determines if an annotation is facing the cutplane or not"""
norm1 = plane.normalAt(0,0)
if hasattr(obj,"Placement"):
norm2 = obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))
elif hasattr(obj,"Normal"):
norm2 = obj.Normal
if norm2.Length < 0.01:
return True
else:
return True
a = norm1.getAngle(norm2)
if (a < 0.01) or (abs(a-math.pi) < 0.01):
return True
return False
def update_svg_cache(source, renderMode, showHidden, showFill, fillSpaces, joinArch, allOn, objs):
"""
Returns None or cached SVG, clears shape cache if required
"""
svgcache = None
if hasattr(source,"Proxy"):
if hasattr(source.Proxy,"svgcache") and source.Proxy.svgcache:
# TODO check array bounds
svgcache = source.Proxy.svgcache[0]
# empty caches if we want to force-recalculate for certain properties
if (source.Proxy.svgcache[1] != renderMode
or source.Proxy.svgcache[2] != showHidden
or source.Proxy.svgcache[3] != showFill
or source.Proxy.svgcache[4] != fillSpaces
or source.Proxy.svgcache[5] != joinArch
or source.Proxy.svgcache[6] != allOn
or source.Proxy.svgcache[7] != set(objs)):
svgcache = None
if (source.Proxy.svgcache[4] != fillSpaces
or source.Proxy.svgcache[5] != joinArch
or source.Proxy.svgcache[6] != allOn
or source.Proxy.svgcache[7] != set(objs)):
source.Proxy.shapecache = None
return svgcache
def getSVG(source,
renderMode="Wireframe",
allOn=False,
showHidden=False,
scale=1,
rotation=0,
linewidth=1,
lineColor=(0.0, 0.0, 0.0),
fontsize=1,
linespacing=None,
showFill=False,
fillColor=(1.0, 1.0, 1.0),
techdraw=False,
fillSpaces=False,
cutlinewidth=0,
joinArch=False):
"""
Return an SVG fragment from an Arch SectionPlane or BuildingPart.
allOn
If it is `True`, all cut objects are shown, regardless of if they are
visible or not.
renderMode
Can be `'Wireframe'` (default) or `'Solid'` to use the Arch solid
renderer.
showHidden
If it is `True`, the hidden geometry above the section plane
is shown in dashed line.
showFill
If it is `True`, the cut areas get filled with a pattern.
lineColor
Color of lines for the `renderMode` is `'Wireframe'`.
fillColor
If `showFill` is `True` and `renderMode` is `'Wireframe'`,
the cut areas are filled with `fillColor`.
fillSpaces
If `True`, shows space objects as filled surfaces.
"""
import Part
objs, cutplane, onlySolids, clip, direction = getSectionData(source)
if not objs:
return ""
if not allOn:
objs = Draft.removeHidden(objs)
# separate spaces and Draft objects
spaces = []
nonspaces = []
drafts = [] # Only used for annotations.
windows = []
cutface = None
for o in objs:
if Draft.getType(o) == "Space":
spaces.append(o)
elif Draft.getType(o) in ["Dimension","AngularDimension","LinearDimension","Annotation","Label","Text","DraftText","Axis"]:
if isOriented(o,cutplane):
drafts.append(o)
elif o.isDerivedFrom("App::DocumentObjectGroup"):
# These will have been expanded by getSectionData already
pass
else:
nonspaces.append(o)
if Draft.getType(o.getLinkedObject()) == "Window": # To support Link of Windows(Doors)
windows.append(o)
objs = nonspaces
scaledLineWidth = linewidth/scale
if renderMode in ["Coin",2,"Coin mono",3]:
# don't scale linewidths in coin mode
svgLineWidth = str(linewidth) + 'px'
else:
svgLineWidth = str(scaledLineWidth) + 'px'
if cutlinewidth:
scaledCutLineWidth = cutlinewidth/scale
svgCutLineWidth = str(scaledCutLineWidth) + 'px'
else:
st = params.get_param_arch("CutLineThickness")
svgCutLineWidth = str(scaledLineWidth * st) + 'px'
yt = params.get_param_arch("SymbolLineThickness")
svgSymbolLineWidth = str(linewidth * yt)
hiddenPattern = params.get_param_arch("archHiddenPattern")
svgHiddenPattern = hiddenPattern.replace(" ","")
#fillpattern = '<pattern id="sectionfill" patternUnits="userSpaceOnUse" patternTransform="matrix(5,0,0,5,0,0)"'
#fillpattern += ' x="0" y="0" width="10" height="10">'
#fillpattern += '<g>'
#fillpattern += '<rect width="10" height="10" style="stroke:none; fill:#ffffff" /><path style="stroke:#000000; stroke-width:1" d="M0,0 l10,10" /></g></pattern>'
svgLineColor = Draft.getrgb(lineColor)
svg = ''
# reading cached version
svgcache = update_svg_cache(source, renderMode, showHidden, showFill, fillSpaces, joinArch, allOn, objs)
should_update_svg_cache = False
if showFill or not svgcache:
should_update_svg_cache = True
# generating SVG
if renderMode in ["Coin",2,"Coin mono",3]:
# render using a coin viewer
if hasattr(source.ViewObject,"ViewData") and source.ViewObject.ViewData:
cameradata = None#getCameraData(source.ViewObject.ViewData)
else:
cameradata = None
if should_update_svg_cache:
if renderMode in ["Coin mono",3]:
svgcache = getCoinSVG(cutplane,objs,cameradata,linewidth="SVGLINEWIDTH",facecolor="#ffffff")
else:
svgcache = getCoinSVG(cutplane,objs,cameradata,linewidth="SVGLINEWIDTH")
elif renderMode in ["Solid",1]:
if should_update_svg_cache:
svgcache = ''
# render using the Arch Vector Renderer
import ArchVRM, WorkingPlane
wp = WorkingPlane.PlaneBase()
pl = FreeCAD.Placement(source.Placement)
if source.ViewObject and hasattr(source.ViewObject,"CutMargin"):
mv = pl.multVec(FreeCAD.Vector(0,0,1))
mv.multiply(source.ViewObject.CutMargin)
pl.move(mv)
wp.align_to_placement(pl)
#wp.inverse()
render = ArchVRM.Renderer()
render.setWorkingPlane(wp)
render.addObjects(objs)
if showHidden:
render.cut(cutplane,showHidden)
else:
render.cut(cutplane)
g = '<g transform="scale(1,-1)">\n'
if hasattr(source.ViewObject,"RotateSolidRender"):
if (source.ViewObject.RotateSolidRender.Value != 0):
g = '<g transform="scale(1,-1) rotate('
g += str(source.ViewObject.RotateSolidRender.Value)
g += ')">\n'
svgcache += g
svgcache += render.getViewSVG(linewidth="SVGLINEWIDTH")
#svgcache += fillpattern
svgcache += render.getSectionSVG(linewidth="SVGCUTLINEWIDTH",fillpattern="#ffffff")
if showHidden:
svgcache += render.getHiddenSVG(linewidth="SVGLINEWIDTH")
svgcache += '</g>\n'
# print(render.info())
else:
# Wireframe (0) mode
if hasattr(source,"Proxy") and hasattr(source.Proxy,"shapecache") and source.Proxy.shapecache:
vshapes = source.Proxy.shapecache[0]
hshapes = source.Proxy.shapecache[1]
sshapes = source.Proxy.shapecache[2]
cutface = source.Proxy.shapecache[3]
# cutvolume = source.Proxy.shapecache[4] # Unused
# invcutvolume = source.Proxy.shapecache[5] # Unused
objectSshapes = source.Proxy.shapecache[6]
else:
if showFill:
vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes = getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,True)
else:
vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden)
objectSshapes = []
source.Proxy.shapecache = [vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes]
if should_update_svg_cache:
svgcache = ""
# render using the TechDraw module
import TechDraw, Part
if vshapes:
baseshape = Part.makeCompound(vshapes)
style = {'stroke': "SVGLINECOLOR",
'stroke-width': "SVGLINEWIDTH"}
svgcache += TechDraw.projectToSVG(
baseshape, direction,
hStyle=style, h0Style=style, h1Style=style,
vStyle=style, v0Style=style, v1Style=style)
if hshapes:
hshapes = Part.makeCompound(hshapes)
style = {'stroke': "SVGLINECOLOR",
'stroke-width': "SVGLINEWIDTH",
'stroke-dasharray': "SVGHIDDENPATTERN"}
svgcache += TechDraw.projectToSVG(
hshapes, direction,
hStyle=style, h0Style=style, h1Style=style,
vStyle=style, v0Style=style, v1Style=style)
if sshapes:
if showFill:
#svgcache += fillpattern
svgcache += '<g transform="rotate(180)">\n'
for o, shapes in objectSshapes:
for s in shapes:
if s.Edges:
objectFill = getFillForObject(o, fillColor, source)
# svg += Draft.get_svg(s,
# direction=direction.negative(),
# linewidth=0,
# fillstyle="sectionfill",
# color=(0,0,0))
# temporarily disabling fill patterns
svgcache += Draft.get_svg(s,
linewidth=0,
fillstyle=Draft.getrgb(objectFill,testbw=False),
direction=direction.negative(),
color=lineColor)
svgcache += "</g>\n"
sshapes = Part.makeCompound(sshapes)
style = {'stroke': "SVGLINECOLOR",
'stroke-width': "SVGCUTLINEWIDTH"}
svgcache += TechDraw.projectToSVG(
sshapes, direction,
hStyle=style, h0Style=style, h1Style=style,
vStyle=style, v0Style=style, v1Style=style)
if should_update_svg_cache:
if hasattr(source,"Proxy"):
source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch,allOn,set(objs)]
svgcache = svgcache.replace("SVGLINECOLOR",svgLineColor)
svgcache = svgcache.replace("SVGLINEWIDTH",svgLineWidth)
svgcache = svgcache.replace("SVGHIDDENPATTERN",svgHiddenPattern)
svgcache = svgcache.replace("SVGCUTLINEWIDTH",svgCutLineWidth)
svg += svgcache
if drafts:
if not techdraw:
svg += '<g transform="scale(1,-1)">'
for d in drafts:
svg += Draft.get_svg(d,
scale=scale,
linewidth=svgSymbolLineWidth,
fontsize=fontsize,
linespacing=linespacing,
direction=direction,
color=lineColor,
techdraw=techdraw,
rotation=rotation,
override=False)
if not techdraw:
svg += '</g>'
if not cutface:
# if we didn't calculate anything better, use the cutplane...
cutface = cutplane
# filter out spaces not cut by the source plane
if cutface and spaces:
spaces = [s for s in spaces if s.Shape.BoundBox.intersect(cutface.BoundBox)]
if spaces:
if not techdraw:
svg += '<g transform="scale(1,-1)">'
for s in spaces:
svg += Draft.get_svg(s,
scale=scale,
linewidth=svgSymbolLineWidth,
fontsize=fontsize,
linespacing=linespacing,
direction=direction,
color=lineColor,
techdraw=techdraw,
rotation=rotation,
fillspaces=fillSpaces)
if not techdraw:
svg += '</g>'
# add additional edge symbols from windows
cutwindows = []
if cutface and windows and BoundBoxValid(cutface.BoundBox):
cutwindows = [
w.Name for w in windows if BoundBoxValid(w.Shape.BoundBox) and w.Shape.BoundBox.intersect(cutface.BoundBox)
]
if windows:
sh = []
for w in windows:
if w.Name in cutwindows:
wlo = w.getLinkedObject() # To support Link of Windows(Doors)
if hasattr(wlo, "SymbolPlan") and wlo.SymbolPlan:
if not hasattr(wlo.Proxy, "sshapes"):
wlo.Proxy.execute(wlo)
if hasattr(wlo.Proxy, "sshapes") and wlo.Proxy.sshapes:
c = Part.makeCompound(wlo.Proxy.sshapes)
c.Placement = w.Placement
sh.append(c)
if sh:
if not techdraw:
svg += '<g transform="scale(1,-1)">'
for s in sh:
svg += Draft.get_svg(s,
scale=scale,
linewidth=svgSymbolLineWidth,
fontsize=fontsize,
linespacing=linespacing,
fillstyle="none",
direction=direction,
color=lineColor,
techdraw=techdraw,
rotation=rotation)
if not techdraw:
svg += '</g>'
return svg
def BoundBoxValid(boundBox)->bool:
"""Return true if boundBox has a non-zero volume"""
return boundBox.XLength > 0 and boundBox.YLength > 0 and boundBox.ZLength > 0
def getDXF(obj):
"""Return a DXF representation from a TechDraw view."""
allOn = getattr(obj, "AllOn", True)
showHidden = getattr(obj, "ShowHidden", False)
result = []
import TechDraw, Part
if not obj.Source:
return result
source = obj.Source
objs,cutplane,onlySolids,clip,direction = getSectionData(source)
if not objs:
return result
if not allOn:
objs = Draft.removeHidden(objs)
objs = [o for o in objs if ((not(Draft.getType(o) in ["Space","Dimension","Annotation"])) and (not (o.isDerivedFrom("Part::Part2DObject"))))]
vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,cutplane,onlySolids,clip,False,showHidden)
if vshapes:
result.append(TechDraw.projectToDXF(Part.makeCompound(vshapes),direction))
if sshapes:
result.append(TechDraw.projectToDXF(Part.makeCompound(sshapes),direction))
if hshapes:
result.append(TechDraw.projectToDXF(Part.makeCompound(hshapes),direction))
return result
def getCameraData(floatlist):
"""reconstructs a valid camera data string from stored values"""
c = ""
if len(floatlist) >= 12:
d = floatlist
camtype = "orthographic"
if len(floatlist) == 13:
if d[12] == 1:
camtype = "perspective"
if camtype == "orthographic":
c = "#Inventor V2.1 ascii\n\n\nOrthographicCamera {\n viewportMapping ADJUST_CAMERA\n "
else:
c = "#Inventor V2.1 ascii\n\n\nPerspectiveCamera {\n viewportMapping ADJUST_CAMERA\n "
c += "position " + str(d[0]) + " " + str(d[1]) + " " + str(d[2]) + "\n "
c += "orientation " + str(d[3]) + " " + str(d[4]) + " " + str(d[5]) + " " + str(d[6]) + "\n "
c += "aspectRatio " + str(d[9]) + "\n "
c += "focalDistance " + str(d[10]) + "\n "
if camtype == "orthographic":
c += "height " + str(d[11]) + "\n\n}\n"
else:
c += "heightAngle " + str(d[11]) + "\n\n}\n"
return c
def getCoinSVG(cutplane,objs,cameradata=None,linewidth=0.2,singleface=False,facecolor=None):
"""Returns an SVG fragment generated from a coin view"""
if not FreeCAD.GuiUp:
return ""
# do not allow concurrent runs
# wait until the other rendering has finished
global ISRENDERING
while ISRENDERING:
time.sleep(0.1)
ISRENDERING = True
# a name to save a temp file
svgfile = tempfile.mkstemp(suffix=".svg")[1]
# set object lighting to single face to get black fills
# but this creates artifacts in svg output, triangulation gets visible...
ldict = {}
if singleface:
for obj in objs:
if hasattr(obj,"ViewObject") and hasattr(obj.ViewObject,"Lighting"):
ldict[obj.Name] = obj.ViewObject.Lighting
obj.ViewObject.Lighting = "One side"
# get nodes to render
root_node = coin.SoSeparator()
boundbox = FreeCAD.BoundBox()
for obj in objs:
if hasattr(obj.ViewObject,"RootNode") and obj.ViewObject.RootNode:
old_visibility = obj.ViewObject.isVisible()
# ignore visibility as only visible objects are passed here
obj.ViewObject.show()
node_copy = obj.ViewObject.RootNode.copy()
root_node.addChild(node_copy)
if(old_visibility):
obj.ViewObject.show()
else:
obj.ViewObject.hide()
if hasattr(obj,"Shape") and hasattr(obj.Shape,"BoundBox"):
boundbox.add(obj.Shape.BoundBox)
# reset lighting of objects
if ldict:
for obj in objs:
if obj.Name in ldict:
obj.ViewObject.Lighting = ldict[obj.Name]
# create viewer
view_window = FreeCADGui.createViewer()
view_window_name = "Temp" + str(uuid.uuid4().hex[:8])
view_window.setName(view_window_name)
inventor_view = view_window.getViewer()
inventor_view.setBackgroundColor(1,1,1)
view_window.redraw()
# set clip plane
clip = coin.SoClipPlane()
norm = cutplane.normalAt(0,0).negative()
proj = DraftVecUtils.project(cutplane.CenterOfMass,norm)
dist = proj.Length
if proj.getAngle(norm) > 1:
dist = -dist
clip.on = True
plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist) #dir, position on dir
clip.plane.setValue(plane)
root_node.insertChild(clip,0)
# add white marker at scene bound box corner
markervec = FreeCAD.Vector(10,10,10)
a = cutplane.normalAt(0,0).getAngle(markervec)
if (a < 0.01) or (abs(a-math.pi) < 0.01):
markervec = FreeCAD.Vector(10,-10,10)
boundbox.enlarge(10) # so the marker don't overlap the objects
sep = coin.SoSeparator()
mat = coin.SoMaterial()
mat.diffuseColor.setValue([1,1,1])
sep.addChild(mat)
coords = coin.SoCoordinate3()
coords.point.setValues([[boundbox.XMin,boundbox.YMin,boundbox.ZMin],
[boundbox.XMin+markervec.x,boundbox.YMin+markervec.y,boundbox.ZMin+markervec.z]])
sep.addChild(coords)
lset = coin.SoIndexedLineSet()
lset.coordIndex.setValues(0,2,[0,1])
sep.addChild(lset)
root_node.insertChild(sep,0)
# set scenegraph
inventor_view.setSceneGraph(root_node)
# set camera
if cameradata:
view_window.setCamera(cameradata)
else:
view_window.setCameraType("Orthographic")
#rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),cutplane.normalAt(0,0))
vx = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(1,0,0))
vy = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,1,0))
vz = cutplane.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))
rot = FreeCAD.Rotation(vx,vy,vz,"ZXY")
view_window.setCameraOrientation(rot.Q)
# this is needed to set correct focal depth, otherwise saving doesn't work properly
view_window.fitAll()
# save view
#print("saving to",svgfile)
view_window.saveVectorGraphic(svgfile,1) # number is pixel size
# set linewidth placeholder
f = open(svgfile,"r")
svg = f.read()
f.close()
svg = svg.replace("stroke-width:1.0;","stroke-width:"+str(linewidth)+";")
svg = svg.replace("stroke-width=\"1px","stroke-width=\""+str(linewidth))
# find marker and calculate scale factor and translation
# <line x1="284.986" y1="356.166" x2="285.038" y2="356.166" stroke="#ffffff" stroke-width="1px" />
factor = None
trans = None
import WorkingPlane
wp = WorkingPlane.PlaneBase()
wp.align_to_point_and_axis_svg(Vector(0,0,0),cutplane.normalAt(0,0),0)
p = wp.get_local_coords(markervec)
orlength = FreeCAD.Vector(p.x,p.y,0).Length
marker = re.findall(r"<line x1=.*?stroke=\"\#ffffff\".*?\/>",svg)
if marker:
marker = marker[0].split("\"")
x1 = float(marker[1])
y1 = float(marker[3])
x2 = float(marker[5])
y2 = float(marker[7])
p1 = FreeCAD.Vector(x1,y1,0)
p2 = FreeCAD.Vector(x2,y2,0)
factor = orlength/p2.sub(p1).Length
if factor:
orig = wp.get_local_coords(FreeCAD.Vector(boundbox.XMin,boundbox.YMin,boundbox.ZMin))
orig = FreeCAD.Vector(orig.x,-orig.y,0)
scaledp1 = FreeCAD.Vector(p1.x*factor,p1.y*factor,0)
trans = orig.sub(scaledp1)
# remove marker
svg = re.sub(r"<line x1=.*?stroke=\"\#ffffff\".*?\/>","",svg,count=1)
# remove background rectangle
svg = re.sub(r"<path.*?>","",svg,count=1,flags=re.MULTILINE|re.DOTALL)
# set face color to white
if facecolor:
res = re.findall(r"fill:(.*?); stroke:(.*?);",svg)
pairs = []
for pair in res:
if (pair not in pairs) and (pair[0] == pair[1]) and(pair[0] not in ["#0a0a0a"]):
# coin seems to be rendering a lot of lines as thin triangles with the #0a0a0a color...
pairs.append(pair)
for pair in pairs:
svg = re.sub(r"fill:"+pair[0]+"; stroke:"+pair[1]+";","fill:"+facecolor+"; stroke:"+facecolor+";",svg)
# embed everything in a scale group and scale the viewport
if factor:
if trans:
svg = svg.replace("<g>","<g transform=\"translate("+str(trans.x)+" "+str(trans.y)+") scale("+str(factor)+","+str(factor)+")\">\n<g>",1)
else:
svg = svg.replace("<g>","<g transform=\"scale("+str(factor)+","+str(factor)+")\">\n<g>",1)
svg = svg.replace("</svg>","</g>\n</svg>")
# trigger viewer close
QtCore.QTimer.singleShot(1,lambda: closeViewer(view_window_name))
# strip svg tags (needed for TD Arch view)
svg = re.sub(r"<\?xml.*?>","",svg,flags=re.MULTILINE|re.DOTALL)
svg = re.sub(r"<svg.*?>","",svg,flags=re.MULTILINE|re.DOTALL)
svg = re.sub(r"<\/svg>","",svg,flags=re.MULTILINE|re.DOTALL)
ISRENDERING = False
return svg
def closeViewer(name):
"""Close temporary viewers"""
mw = FreeCADGui.getMainWindow()
for sw in mw.findChildren(QtGui.QMdiSubWindow):
if sw.windowTitle() == name:
sw.close()
class _SectionPlane:
"A section plane object"
def __init__(self,obj):
obj.Proxy = self
self.setProperties(obj)
def setProperties(self,obj):
pl = obj.PropertiesList
if not "Placement" in pl:
obj.addProperty("App::PropertyPlacement","Placement","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The placement of this object"))
if not "Shape" in pl:
obj.addProperty("Part::PropertyPartShape","Shape","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The shape of this object"))
if not "Objects" in pl:
obj.addProperty("App::PropertyLinkList","Objects","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The objects that must be considered by this section plane. Empty means the whole document."))
if not "OnlySolids" in pl:
obj.addProperty("App::PropertyBool","OnlySolids","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If false, non-solids will be cut too, with possible wrong results."))
obj.OnlySolids = True
if not "Clip" in pl:
obj.addProperty("App::PropertyBool","Clip","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If True, resulting views will be clipped to the section plane area."))
if not "UseMaterialColorForFill" in pl:
obj.addProperty("App::PropertyBool","UseMaterialColorForFill","SectionPlane",QT_TRANSLATE_NOOP("App::Property","If true, the color of the objects material will be used to fill cut areas."))
obj.UseMaterialColorForFill = False
if not "Depth" in pl:
obj.addProperty("App::PropertyLength","Depth","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Geometry further than this value will be cut off. Keep zero for unlimited."))
self.Type = "SectionPlane"
def onDocumentRestored(self,obj):
self.setProperties(obj)
def execute(self,obj):
import Part
l = 1
h = 1
if obj.ViewObject:
if hasattr(obj.ViewObject,"DisplayLength"):
l = obj.ViewObject.DisplayLength.Value
h = obj.ViewObject.DisplayHeight.Value
elif hasattr(obj.ViewObject,"DisplaySize"):
# old objects
l = obj.ViewObject.DisplaySize.Value
h = obj.ViewObject.DisplaySize.Value
p = Part.makePlane(l,h,Vector(l/2,-h/2,0),Vector(0,0,-1))
# make sure the normal direction is pointing outwards, you never know what OCC will decide...
if p.normalAt(0,0).getAngle(obj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,1))) > 1:
p.reverse()
p.Placement = obj.Placement
obj.Shape = p
self.svgcache = None
self.shapecache = None
def getNormal(self,obj):
return obj.Shape.Faces[0].normalAt(0,0)
def dumps(self):
return None
def loads(self,state):
return None
class _ViewProviderSectionPlane:
"A View Provider for Section Planes"
def __init__(self,vobj):
vobj.Proxy = self
self.setProperties(vobj)
def setProperties(self,vobj):
pl = vobj.PropertiesList
d = 0
if "DisplaySize" in pl:
d = vobj.DisplaySize.Value
vobj.removeProperty("DisplaySize")
if not "DisplayLength" in pl:
vobj.addProperty("App::PropertyLength","DisplayLength","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display length of this section plane"))
if d:
vobj.DisplayLength = d
else:
vobj.DisplayLength = 1000
if not "DisplayHeight" in pl:
vobj.addProperty("App::PropertyLength","DisplayHeight","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The display height of this section plane"))
if d:
vobj.DisplayHeight = d
else:
vobj.DisplayHeight = 1000
if not "ArrowSize" in pl:
vobj.addProperty("App::PropertyLength","ArrowSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the arrows of this section plane"))
vobj.ArrowSize = 50
if not "Transparency" in pl:
vobj.addProperty("App::PropertyPercent","Transparency","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The transparency of this object"))
vobj.Transparency = 85
if not "LineWidth" in pl:
vobj.addProperty("App::PropertyFloat","LineWidth","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The line width of this object"))
vobj.LineWidth = 1
if not "CutDistance" in pl:
vobj.addProperty("App::PropertyLength","CutDistance","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view"))
if not "LineColor" in pl:
vobj.addProperty("App::PropertyColor","LineColor","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The color of this object"))
vobj.LineColor = ArchCommands.getDefaultColor("Helpers")
if not "CutView" in pl:
vobj.addProperty("App::PropertyBool","CutView","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the cut in the 3D view"))
if not "CutMargin" in pl:
vobj.addProperty("App::PropertyLength","CutMargin","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The distance between the cut plane and the actual view cut (keep this a very small value but not zero)"))
vobj.CutMargin = 1
if not "ShowLabel" in pl:
vobj.addProperty("App::PropertyBool","ShowLabel","SectionPlane",QT_TRANSLATE_NOOP("App::Property","Show the label in the 3D view"))
if not "FontName" in pl:
vobj.addProperty("App::PropertyFont","FontName", "SectionPlane",QT_TRANSLATE_NOOP("App::Property","The name of the font"))
vobj.FontName = params.get_param("textfont")
if not "FontSize" in pl:
vobj.addProperty("App::PropertyLength","FontSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the text font"))
vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier")
def onDocumentRestored(self,vobj):
self.setProperties(vobj)
def getIcon(self):
import Arch_rc
return ":/icons/Arch_SectionPlane_Tree.svg"
def claimChildren(self):
# buggy at the moment so it's disabled - it will for ex. swallow a building object directly at the root of the document...
#if hasattr(self,"Object") and hasattr(self.Object,"Objects"):
# return self.Object.Objects
return []
def attach(self,vobj):
self.Object = vobj.Object
self.clip = None
self.mat1 = coin.SoMaterial()
self.mat2 = coin.SoMaterial()
self.fcoords = coin.SoCoordinate3()
#fs = coin.SoType.fromName("SoBrepFaceSet").createInstance() # this causes a FreeCAD freeze for me
fs = coin.SoIndexedFaceSet()
fs.coordIndex.setValues(0,7,[0,1,2,-1,0,2,3])
self.drawstyle = coin.SoDrawStyle()
self.drawstyle.style = coin.SoDrawStyle.LINES
self.lcoords = coin.SoCoordinate3()
import PartGui # Required for "SoBrepEdgeSet" (because a SectionPlane is not a Part::FeaturePython object).
ls = coin.SoType.fromName("SoBrepEdgeSet").createInstance()
ls.coordIndex.setValues(0,57,[0,1,-1,2,3,4,5,-1,6,7,8,9,-1,10,11,-1,12,13,14,15,-1,16,17,18,19,-1,20,21,-1,22,23,24,25,-1,26,27,28,29,-1,30,31,-1,32,33,34,35,-1,36,37,38,39,-1,40,41,42,43,44])
self.txtcoords = coin.SoTransform()
self.txtfont = coin.SoFont()
self.txtfont.name = ""
self.txt = coin.SoAsciiText()
self.txt.justification = coin.SoText2.LEFT
self.txt.string.setValue(" ")
sep = coin.SoSeparator()
psep = coin.SoSeparator()
fsep = coin.SoSeparator()
tsep = coin.SoSeparator()
fsep.addChild(self.mat2)
fsep.addChild(self.fcoords)
fsep.addChild(fs)
psep.addChild(self.mat1)
psep.addChild(self.drawstyle)
psep.addChild(self.lcoords)
psep.addChild(ls)
tsep.addChild(self.mat1)
tsep.addChild(self.txtcoords)
tsep.addChild(self.txtfont)
tsep.addChild(self.txt)
sep.addChild(fsep)
sep.addChild(psep)
sep.addChild(tsep)
vobj.addDisplayMode(sep,"Default")
self.onChanged(vobj,"DisplayLength")
self.onChanged(vobj,"LineColor")
self.onChanged(vobj,"Transparency")
self.onChanged(vobj,"CutView")
def getDisplayModes(self,vobj):
return ["Default"]
def getDefaultDisplayMode(self):
return "Default"
def setDisplayMode(self,mode):
return mode
def updateData(self,obj,prop):
if prop in ["Placement"]:
# for some reason the text doesn't rotate with the host placement??
self.txtcoords.rotation.setValue(obj.Placement.Rotation.Q)
self.onChanged(obj.ViewObject,"DisplayLength")
self.onChanged(obj.ViewObject,"CutView")
elif prop == "Label":
if hasattr(obj.ViewObject,"ShowLabel") and obj.ViewObject.ShowLabel:
self.txt.string = obj.Label
return
def onChanged(self,vobj,prop):
if prop == "LineColor":
if hasattr(vobj,"LineColor"):
l = vobj.LineColor
self.mat1.diffuseColor.setValue([l[0],l[1],l[2]])
self.mat2.diffuseColor.setValue([l[0],l[1],l[2]])
elif prop == "Transparency":
if hasattr(vobj,"Transparency"):
self.mat2.transparency.setValue(vobj.Transparency/100.0)
elif prop in ["DisplayLength","DisplayHeight","ArrowSize"]:
if hasattr(vobj,"DisplayLength") and hasattr(vobj,"DisplayHeight"):
ld = vobj.DisplayLength.Value/2
hd = vobj.DisplayHeight.Value/2
elif hasattr(vobj,"DisplaySize"):
# old objects
ld = vobj.DisplaySize.Value/2
hd = vobj.DisplaySize.Value/2
else:
ld = 1
hd = 1
verts = []
fverts = []
pl = FreeCAD.Placement(vobj.Object.Placement)
if hasattr(vobj,"ArrowSize"):
l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1
else:
l1 = 0.1
l2 = l1/3
for v in [[-ld,-hd],[ld,-hd],[ld,hd],[-ld,hd]]:
p1 = pl.multVec(Vector(v[0],v[1],0))
p2 = pl.multVec(Vector(v[0],v[1],-l1))
p3 = pl.multVec(Vector(v[0]-l2,v[1],-l1+l2))
p4 = pl.multVec(Vector(v[0]+l2,v[1],-l1+l2))
p5 = pl.multVec(Vector(v[0],v[1]-l2,-l1+l2))
p6 = pl.multVec(Vector(v[0],v[1]+l2,-l1+l2))
verts.extend([[p1.x,p1.y,p1.z],[p2.x,p2.y,p2.z]])
fverts.append([p1.x,p1.y,p1.z])
verts.extend([[p2.x,p2.y,p2.z],[p3.x,p3.y,p3.z],[p4.x,p4.y,p4.z],[p2.x,p2.y,p2.z]])
verts.extend([[p2.x,p2.y,p2.z],[p5.x,p5.y,p5.z],[p6.x,p6.y,p6.z],[p2.x,p2.y,p2.z]])
p7 = pl.multVec(Vector(-ld+l2,-hd+l2,0)) # text pos
verts.extend(fverts+[fverts[0]])
self.lcoords.point.setValues(verts)
self.fcoords.point.setValues(fverts)
self.txtcoords.translation.setValue([p7.x,p7.y,p7.z])
#self.txtfont.size = l1
elif prop == "LineWidth":
self.drawstyle.lineWidth = vobj.LineWidth
elif prop in ["CutView","CutMargin"]:
if hasattr(vobj, "CutView") \
and FreeCADGui.ActiveDocument.ActiveView \
and hasattr(FreeCADGui.ActiveDocument.ActiveView, "getSceneGraph"):
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
if vobj.CutView:
if self.clip:
sg.removeChild(self.clip)
self.clip = None
for o in Draft.get_group_contents(vobj.Object.Objects,
walls=True):
if hasattr(o.ViewObject,"Lighting"):
o.ViewObject.Lighting = "One side"
self.clip = coin.SoClipPlane()
self.clip.on.setValue(True)
norm = vobj.Object.Proxy.getNormal(vobj.Object)
mp = vobj.Object.Shape.CenterOfMass
mp = DraftVecUtils.project(mp,norm)
dist = mp.Length #- 0.1 # to not clip exactly on the section object
norm = norm.negative()
marg = 1
if hasattr(vobj,"CutMargin"):
marg = vobj.CutMargin.Value
if mp.getAngle(norm) > 1:
dist += marg
dist = -dist
else:
dist -= marg
plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),dist)
self.clip.plane.setValue(plane)
sg.insertChild(self.clip,0)
else:
if self.clip:
sg.removeChild(self.clip)
self.clip = None
elif prop == "ShowLabel":
if vobj.ShowLabel:
self.txt.string = vobj.Object.Label or " "
else:
self.txt.string = " "
elif prop == "FontName":
if hasattr(self,"txtfont") and hasattr(vobj,"FontName"):
if vobj.FontName:
self.txtfont.name = vobj.FontName
else:
self.txtfont.name = ""
elif prop == "FontSize":
if hasattr(self,"txtfont") and hasattr(vobj,"FontSize"):
self.txtfont.size = vobj.FontSize.Value
return
def dumps(self):
return None
def loads(self,state):
return None
def setEdit(self, vobj, mode):
if mode != 0:
return None
taskd = SectionPlaneTaskPanel()
taskd.obj = vobj.Object
taskd.update()
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode):
if mode != 0:
return None
FreeCADGui.Control.closeDialog()
return True
def doubleClicked(self, vobj):
self.edit()
def setupContextMenu(self, vobj, menu):
actionEdit = QtGui.QAction(translate("Arch", "Edit"),
menu)
QtCore.QObject.connect(actionEdit,
QtCore.SIGNAL("triggered()"),
self.edit)
menu.addAction(actionEdit)
actionToggleCutview = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"),
translate("Arch", "Toggle Cutview"),
menu)
actionToggleCutview.triggered.connect(lambda: self.toggleCutview(vobj))
menu.addAction(actionToggleCutview)
def edit(self):
FreeCADGui.ActiveDocument.setEdit(self.Object, 0)
def toggleCutview(self, vobj):
vobj.CutView = not vobj.CutView
class SectionPlaneTaskPanel:
'''A TaskPanel for all the section plane object'''
def __init__(self):
# the panel has a tree widget that contains categories
# for the subcomponents, such as additions, subtractions.
# the categories are shown only if they are not empty.
self.obj = None
self.form = QtGui.QWidget()
self.form.setObjectName("TaskPanel")
self.grid = QtGui.QGridLayout(self.form)
self.title = QtGui.QLabel(self.form)
self.grid.addWidget(self.title, 0, 0, 1, 2)
# tree
self.tree = QtGui.QTreeWidget(self.form)
self.grid.addWidget(self.tree, 1, 0, 1, 2)
self.tree.setColumnCount(1)
self.tree.header().hide()
# add / remove buttons
self.addButton = QtGui.QPushButton(self.form)
self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg"))
self.grid.addWidget(self.addButton, 3, 0, 1, 1)
self.delButton = QtGui.QPushButton(self.form)
self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg"))
self.grid.addWidget(self.delButton, 3, 1, 1, 1)
self.delButton.setEnabled(False)
# rotate / resize buttons
self.rlabel = QtGui.QLabel(self.form)
self.grid.addWidget(self.rlabel, 4, 0, 1, 2)
self.rotateXButton = QtGui.QPushButton(self.form)
self.grid.addWidget(self.rotateXButton, 5, 0, 1, 1)
self.rotateYButton = QtGui.QPushButton(self.form)
self.grid.addWidget(self.rotateYButton, 5, 1, 1, 1)
self.rotateZButton = QtGui.QPushButton(self.form)
self.grid.addWidget(self.rotateZButton, 6, 0, 1, 1)
self.resizeButton = QtGui.QPushButton(self.form)
self.grid.addWidget(self.resizeButton, 7, 0, 1, 1)
self.recenterButton = QtGui.QPushButton(self.form)
self.grid.addWidget(self.recenterButton, 7, 1, 1, 1)
QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement)
QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement)
QtCore.QObject.connect(self.rotateXButton, QtCore.SIGNAL("clicked()"), self.rotateX)
QtCore.QObject.connect(self.rotateYButton, QtCore.SIGNAL("clicked()"), self.rotateY)
QtCore.QObject.connect(self.rotateZButton, QtCore.SIGNAL("clicked()"), self.rotateZ)
QtCore.QObject.connect(self.resizeButton, QtCore.SIGNAL("clicked()"), self.resize)
QtCore.QObject.connect(self.recenterButton, QtCore.SIGNAL("clicked()"), self.recenter)
QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemSelectionChanged()"), self.onTreeClick)
self.update()
def isAllowedAlterSelection(self):
return True
def isAllowedAlterView(self):
return True
def getStandardButtons(self):
return QtGui.QDialogButtonBox.Ok
def getIcon(self,obj):
if hasattr(obj.ViewObject,"Proxy"):
return QtGui.QIcon(obj.ViewObject.Proxy.getIcon())
elif obj.isDerivedFrom("Sketcher::SketchObject"):
return QtGui.QIcon(":/icons/Sketcher_Sketch.svg")
elif obj.isDerivedFrom("App::DocumentObjectGroup"):
return QtGui.QApplication.style().standardIcon(QtGui.QStyle.SP_DirIcon)
elif hasattr(obj.ViewObject, "Icon"):
return QtGui.QIcon(obj.ViewObject.Icon)
return QtGui.QIcon(":/icons/Part_3D_object.svg")
def update(self):
'fills the treewidget'
self.tree.clear()
if self.obj:
for o in self.obj.Objects:
item = QtGui.QTreeWidgetItem(self.tree)
item.setText(0,o.Label)
item.setToolTip(0,o.Name)
item.setIcon(0,self.getIcon(o))
self.retranslateUi(self.form)
def addElement(self):
if self.obj:
added = False
for o in FreeCADGui.Selection.getSelection():
if o != self.obj:
ArchComponent.addToComponent(self.obj,o,"Objects")
added = True
if added:
self.update()
else:
FreeCAD.Console.PrintWarning("Please select objects in the 3D view or in the model tree before pressing the button\n")
def removeElement(self):
if self.obj:
it = self.tree.currentItem()
if it:
comp = FreeCAD.ActiveDocument.getObject(str(it.toolTip(0)))
ArchComponent.removeFromComponent(self.obj,comp)
self.update()
def rotate(self,axis):
if self.obj and self.obj.Shape and self.obj.Shape.Faces:
face = self.obj.Shape.copy()
import Part
face.rotate(self.obj.Placement.Base, axis, 90)
self.obj.Placement = face.Placement
self.obj.Proxy.execute(self.obj)
def rotateX(self):
self.rotate(FreeCAD.Vector(1,0,0))
def rotateY(self):
self.rotate(FreeCAD.Vector(0,1,0))
def rotateZ(self):
self.rotate(FreeCAD.Vector(0,0,1))
def getBB(self):
bb = FreeCAD.BoundBox()
if self.obj:
for o in Draft.get_group_contents(self.obj.Objects):
if hasattr(o,"Shape") and hasattr(o.Shape,"BoundBox"):
bb.add(o.Shape.BoundBox)
return bb
def resize(self):
if self.obj and self.obj.ViewObject:
bb = self.getBB()
n = self.obj.Proxy.getNormal(self.obj)
margin = bb.XLength*0.1
if (n.getAngle(FreeCAD.Vector(1,0,0)) < 0.1) or (n.getAngle(FreeCAD.Vector(-1,0,0)) < 0.1):
self.obj.ViewObject.DisplayLength = bb.YLength+margin
self.obj.ViewObject.DisplayHeight = bb.ZLength+margin
elif (n.getAngle(FreeCAD.Vector(0,1,0)) < 0.1) or (n.getAngle(FreeCAD.Vector(0,-1,0)) < 0.1):
self.obj.ViewObject.DisplayLength = bb.XLength+margin
self.obj.ViewObject.DisplayHeight = bb.ZLength+margin
elif (n.getAngle(FreeCAD.Vector(0,0,1)) < 0.1) or (n.getAngle(FreeCAD.Vector(0,0,-1)) < 0.1):
self.obj.ViewObject.DisplayLength = bb.XLength+margin
self.obj.ViewObject.DisplayHeight = bb.YLength+margin
self.obj.Proxy.execute(self.obj)
def recenter(self):
if self.obj:
self.obj.Placement.Base = self.getBB().Center
def onTreeClick(self):
if self.tree.selectedItems():
self.delButton.setEnabled(True)
else:
self.delButton.setEnabled(False)
def accept(self):
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
return True
def retranslateUi(self, TaskPanel):
TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Section plane settings", None))
self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None))
self.delButton.setToolTip(QtGui.QApplication.translate("Arch", "Remove highlighted objects from the list above", None))
self.addButton.setText(QtGui.QApplication.translate("Arch", "Add selected", None))
self.addButton.setToolTip(QtGui.QApplication.translate("Arch", "Add selected object(s) to the scope of this section plane", None))
self.title.setText(QtGui.QApplication.translate("Arch", "Objects seen by this section plane:", None))
self.rlabel.setText(QtGui.QApplication.translate("Arch", "Section plane placement:", None))
self.rotateXButton.setText(QtGui.QApplication.translate("Arch", "Rotate X", None))
self.rotateXButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane along the X axis", None))
self.rotateYButton.setText(QtGui.QApplication.translate("Arch", "Rotate Y", None))
self.rotateYButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane along the Y axis", None))
self.rotateZButton.setText(QtGui.QApplication.translate("Arch", "Rotate Z", None))
self.rotateZButton.setToolTip(QtGui.QApplication.translate("Arch", "Rotates the plane along the Z axis", None))
self.resizeButton.setText(QtGui.QApplication.translate("Arch", "Resize", None))
self.resizeButton.setToolTip(QtGui.QApplication.translate("Arch", "Resizes the plane to fit the objects in the list above", None))
self.recenterButton.setText(QtGui.QApplication.translate("Arch", "Center", None))
self.recenterButton.setToolTip(QtGui.QApplication.translate("Arch", "Centers the plane on the objects in the list above", None))