Files
create/src/Mod/Arch/ArchSectionPlane.py
2020-05-18 12:18:36 +02:00

1436 lines
57 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 WorkingPlane
import math
import Draft
import ArchCommands
import DraftVecUtils
import ArchComponent
import os
import re
import tempfile
import uuid
import time
from FreeCAD import Vector
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from DraftTools 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 TechDraw and Drawing modules
ISRENDERING = False # flag to prevent concurrent runs of the coin renderer
def makeSectionPlane(objectslist=None,name="Section"):
"""makeSectionPlane([objectslist]) : Creates a Section plane objects including the
given objects. If no object is given, the whole document will be considered."""
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name)
obj.Label = translate("Arch",name)
_SectionPlane(obj)
if FreeCAD.GuiUp:
_ViewProviderSectionPlane(obj.ViewObject)
if objectslist:
obj.Objects = objectslist
bb = FreeCAD.BoundBox()
for o in Draft.getGroupContents(objectslist):
if hasattr(o,"Shape") and hasattr(o.Shape,"BoundBox"):
bb.add(o.Shape.BoundBox)
obj.Placement = FreeCAD.DraftWorkingPlane.getPlacement()
obj.Placement.Base = bb.Center
if FreeCAD.GuiUp:
margin = bb.XLength*0.1
obj.ViewObject.DisplayLength = bb.XLength+margin
obj.ViewObject.DisplayHeight = bb.YLength+margin
return obj
def makeSectionView(section,name="View"):
"""makeSectionView(section) : Creates a Drawing view of the given Section Plane
in the active Page object (a new page will be created if none exists"""
page = None
for o in FreeCAD.ActiveDocument.Objects:
if o.isDerivedFrom("Drawing::FeaturePage"):
page = o
break
if not page:
page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage","Page")
page.Template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg')
view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython",name)
page.addObject(view)
_ArchDrawingView(view)
view.Source = section
view.Label = translate("Arch","View of")+" "+section.Name
return view
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.getGroupContents(objs,walls=True,addgroups=True)
return objs,cutplane,onlySolids,clip,direction
def looksLikeDraft(o):
"""Does this object look like a Draft shape? (flat, no solid, etc)"""
# If there is no shape at all ignore it
if not hasattr(o, 'Shape') or o.Shape.isNull():
return False
# If there are solids in the object, it will be handled later
# by getCutShapes
if len(o.Shape.Solids) > 0:
return False
# If we have a shape, but no volume, it looks like a flat 2D object
return o.Shape.Volume < 0.0000001 # add a little tolerance...
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 = []
if cutvolume:
for o, shapeList in objectShapes:
tmpSshapes = []
for sh in shapeList:
for sol in sh.Solids:
if sol.Volume < 0:
sol.reverse()
c = sol.cut(cutvolume)
s = sol.section(cutface)
try:
wires = DraftGeomUtils.findWires(s.Edges)
for w in wires:
f = Part.Face(w)
tmpSshapes.append(f)
#s = Part.Wire(s.Edges)
#s = Part.Face(s)
except Part.OCCError:
#print "ArchDrawingView: unable to get a face"
tmpSshapes.append(s)
shapes.extend(c.Solids)
#sshapes.append(s)
if showHidden:
c = sol.cut(invcutvolume)
hshapes.append(c)
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, 'Color') and material.Color:
return material.Color
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 getSVG(source,
renderMode="Wireframe",
allOn=False,
showHidden=False,
scale=1,
rotation=0,
linewidth=1,
lineColor=(0.0,0.0,0.0),
fontsize=1,
showFill=False,
fillColor=(0.8,0.8,0.8),
techdraw=False,
fillSpaces=False,
cutlinewidth=0,
joinArch=False):
"""
returns an SVG fragment from an Arch SectionPlane or BuildingPart. If
allOn is True, all cut objects are shown, regardless if they are visible or not.
renderMode can be Wireframe (default) or Solid to use the Arch solid renderer. If
showHidden is True, the hidden geometry above the section plane is shown in dashed line.
If showFill is True, the cut areas get filled with a pattern.
lineColor -- Color of lines for the renderMode "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,DraftGeomUtils
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 = []
windows = []
cutface = None
for o in objs:
if Draft.getType(o) == "Space":
spaces.append(o)
elif Draft.getType(o) in ["AngularDimension","LinearDimension","Annotation","Label","Text"]:
if isOriented(o,cutplane):
drafts.append(o)
elif o.isDerivedFrom("Part::Part2DObject"):
drafts.append(o)
elif looksLikeDraft(o):
drafts.append(o)
else:
nonspaces.append(o)
if Draft.getType(o) == "Window":
windows.append(o)
objs = nonspaces
archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
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 = archUserParameters.GetFloat("CutLineThickness",2)
svgCutLineWidth = str(scaledLineWidth * st) + 'px'
yt = archUserParameters.GetFloat("SymbolLineThickness",0.6)
svgSymbolLineWidth = str(linewidth * yt)
hiddenPattern = archUserParameters.GetString("archHiddenPattern","30,10")
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 = None
if hasattr(source,"Proxy"):
if hasattr(source.Proxy,"svgcache") and source.Proxy.svgcache:
svgcache = source.Proxy.svgcache[0]
# empty caches if we want to force-recalculate for certain properties
if source.Proxy.svgcache[1] != renderMode:
svgcache = None
if source.Proxy.svgcache[2] != showHidden:
svgcache = None
if source.Proxy.svgcache[3] != showFill:
svgcache = None
if source.Proxy.svgcache[4] != fillSpaces:
svgcache = None
source.Proxy.shapecache = None
if source.Proxy.svgcache[5] != joinArch:
svgcache = None
source.Proxy.shapecache = None
# 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 = getCameraData(source.ViewObject.ViewData)
else:
cameradata = None
if not svgcache:
if renderMode in ["Coin mono",3]:
svgcache = getCoinSVG(cutplane,objs,cameradata,linewidth="SVGLINEWIDTH",facecolor="#ffffff")
else:
svgcache = getCoinSVG(cutplane,objs,cameradata,linewidth="SVGLINEWIDTH")
if hasattr(source,"Proxy"):
source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch]
elif renderMode in ["Solid",1]:
if not svgcache:
svgcache = ''
# render using the Arch Vector Renderer
import ArchVRM, WorkingPlane
wp = WorkingPlane.plane()
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.setFromPlacement(pl)
#wp.inverse()
render = ArchVRM.Renderer()
render.setWorkingPlane(wp)
render.addObjects(objs)
if showHidden:
render.cut(cutplane,showHidden)
else:
render.cut(cutplane)
svgcache += '<g transform="scale(1,-1)">\n'
svgcache += render.getViewSVG(linewidth="SVGLINEWIDTH")
svgcache += fillpattern
svgcache += render.getSectionSVG(linewidth="SVGCUTLINEWIDTH",
fillpattern="sectionfill")
if showHidden:
svgcache += render.getHiddenSVG(linewidth="SVGLINEWIDTH")
svgcache += '</g>\n'
# print(render.info())
if hasattr(source,"Proxy"):
source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch]
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]
invcutvolume = source.Proxy.shapecache[5]
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 not svgcache:
svgcache = ""
# render using the Drawing module
import Drawing, Part
if vshapes:
baseshape = Part.makeCompound(vshapes)
style = {'stroke': "SVGLINECOLOR",
'stroke-width': "SVGLINEWIDTH"}
svgcache += Drawing.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 += Drawing.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.getSVG(s,direction=direction.negative(),linewidth=0,fillstyle="sectionfill",color=(0,0,0))
# temporarily disabling fill patterns
svgcache += Draft.getSVG(s, direction=direction.negative(),
linewidth=0,
fillstyle=Draft.getrgb(objectFill),
color=lineColor)
svgcache += "</g>\n"
sshapes = Part.makeCompound(sshapes)
style = {'stroke': "SVGLINECOLOR",
'stroke-width': "SVGCUTLINEWIDTH"}
svgcache += Drawing.projectToSVG(
sshapes, direction,
hStyle=style, h0Style=style, h1Style=style,
vStyle=style, v0Style=style, v1Style=style)
if hasattr(source,"Proxy"):
source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch]
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.getSVG(d, scale=scale, linewidth=svgSymbolLineWidth,
fontsize=fontsize, direction=direction, color=lineColor,
techdraw=techdraw, rotation=rotation)
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.getSVG(s, scale=scale, linewidth=svgSymbolLineWidth,
fontsize=fontsize, 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:
cutwindows = [w.Name for w in windows if w.Shape.BoundBox.intersect(cutface.BoundBox)]
if windows:
sh = []
for w in windows:
if not hasattr(w.Proxy,"sshapes"):
w.Proxy.execute(w)
if hasattr(w.Proxy,"sshapes"):
if w.Proxy.sshapes and (w.Name in cutwindows):
c = Part.makeCompound(w.Proxy.sshapes)
c.Placement = w.Placement
sh.append(c)
# buggy for now...
#if hasattr(w.Proxy,"vshapes"):
# if w.Proxy.vshapes:
# c = Part.makeCompound(w.Proxy.vshapes)
# c.Placement = w.Placement
# sh.append(c)
if sh:
if not techdraw:
svg += '<g transform="scale(1,-1)">'
for s in sh:
svg += Draft.getSVG(s, scale=scale,
linewidth=svgSymbolLineWidth,
fontsize=fontsize, fillstyle="none",
direction=direction, color=lineColor,
techdraw=techdraw, rotation=rotation)
if not techdraw:
svg += '</g>'
return svg
def getDXF(obj):
"""returns a DXF representation from a TechDraw/Drawing view"""
allOn = True
if hasattr(obj,"AllOn"):
allOn = obj.AllOn
elif hasattr(obj,"AlwaysOn"):
allOn = obj.AlwaysOn
showHidden = False
if hasattr(obj,"showCut"):
showHidden = obj.showCut
elif hasattr(obj,"showHidden"):
showHidden = obj.showHidden
result = []
import Drawing,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)
# separate spaces and Draft objects
spaces = []
nonspaces = []
drafts = []
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(Drawing.projectToDXF(Part.makeCompound(vshapes),direction))
if sshapes:
result.append(Drawing.projectToDXF(Part.makeCompound(sshapes),direction))
if hshapes:
result.append(Drawing.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
rn = coin.SoSeparator()
boundbox = FreeCAD.BoundBox()
for obj in objs:
if hasattr(obj.ViewObject,"RootNode") and obj.ViewObject.RootNode:
ncopy = obj.ViewObject.RootNode.copy()
rn.addChild(ncopy)
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
v = FreeCADGui.createViewer()
viewername = "Temp" + str(uuid.uuid4().hex[:8])
v.setName(viewername)
vv = v.getViewer()
vv.setBackgroundColor(1,1,1)
v.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)
rn.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)
rn.insertChild(sep,0)
# set scenegraph
vv.setSceneGraph(rn)
# set camera
if cameradata:
v.setCamera(cameradata)
else:
v.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")
v.setCameraOrientation(rot.Q)
# this is needed to set correct focal depth, otherwise saving doesn't work properly
v.fitAll()
# save view
#print("saving to",svgfile)
v.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.plane()
wp.alignToPointAndAxis_SVG(Vector(0,0,0),cutplane.normalAt(0,0),0)
p = wp.getLocalCoords(markervec)
orlength = FreeCAD.Vector(p.x,p.y,0).Length
marker = re.findall("<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.getLocalCoords(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("<line x1=.*?stroke=\"\#ffffff\".*?\/>","",svg,count=1)
# remove background rectangle
svg = re.sub("<path.*?>","",svg,count=1,flags=re.MULTILINE|re.DOTALL)
# set face color to white
if facecolor:
res = re.findall("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("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(viewername))
# strip svg tags (needed for TD Arch view)
svg = re.sub("<\?xml.*?>","",svg,flags=re.MULTILINE|re.DOTALL)
svg = re.sub("<svg.*?>","",svg,flags=re.MULTILINE|re.DOTALL)
svg = re.sub("<\/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 _CommandSectionPlane:
"the Arch SectionPlane command definition"
def GetResources(self):
return {'Pixmap' : 'Arch_SectionPlane',
'Accel': "S, E",
'MenuText': QT_TRANSLATE_NOOP("Arch_SectionPlane","Section Plane"),
'ToolTip': QT_TRANSLATE_NOOP("Arch_SectionPlane","Creates a section plane object, including the selected objects")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
sel = FreeCADGui.Selection.getSelection()
ss = "["
for o in sel:
if len(ss) > 1:
ss += ","
ss += "FreeCAD.ActiveDocument."+o.Name
ss += "]"
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Section Plane"))
FreeCADGui.addModule("Arch")
FreeCADGui.doCommand("section = Arch.makeSectionPlane("+ss+")")
#FreeCADGui.doCommand("section.Placement = FreeCAD.DraftWorkingPlane.getPlacement()")
#FreeCADGui.doCommand("Arch.makeSectionView(section)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
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
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
def onChanged(self,obj,prop):
# clean svg cache if needed
if prop in ["Placement","Objects","OnlySolids","UseMaterialColorForFill","Clip"]:
self.svgcache = None
self.shapecache = None
def getNormal(self,obj):
return obj.Shape.Faces[0].normalAt(0,0)
def __getstate__(self):
return None
def __setstate__(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
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()
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])
sep = coin.SoSeparator()
psep = coin.SoSeparator()
fsep = 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)
sep.addChild(fsep)
sep.addChild(psep)
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"]:
self.onChanged(obj.ViewObject,"DisplayLength")
self.onChanged(obj.ViewObject,"CutView")
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 = []
for v in [[-ld,-hd],[ld,-hd],[ld,hd],[-ld,hd]]:
if hasattr(vobj,"ArrowSize"):
l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1
else:
l1 = 0.1
l2 = l1/3
pl = FreeCAD.Placement(vobj.Object.Placement)
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]])
verts.extend(fverts+[fverts[0]])
self.lcoords.point.setValues(verts)
self.fcoords.point.setValues(fverts)
elif prop == "LineWidth":
self.drawstyle.lineWidth = vobj.LineWidth
elif prop in ["CutView","CutMargin"]:
if hasattr(vobj,"CutView") and FreeCADGui.ActiveDocument.ActiveView:
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
if vobj.CutView:
if self.clip:
sg.removeChild(self.clip)
self.clip = None
for o in Draft.getGroupContents(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
return
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def setEdit(self,vobj,mode):
taskd = SectionPlaneTaskPanel()
taskd.obj = vobj.Object
taskd.update()
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self,vobj,mode):
FreeCADGui.Control.closeDialog()
return False
def doubleClicked(self,vobj):
self.setEdit(vobj,None)
def setupContextMenu(self,vobj,menu):
"""CONTEXT MENU setup"""
from PySide import QtCore,QtGui
action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"),"Toggle Cutview",menu)
action1.triggered.connect(lambda f=self.contextCutview, arg=vobj:f(arg))
menu.addAction(action1)
def contextCutview(self,vobj):
"""CONTEXT MENU command to toggle CutView property on and off"""
if vobj.CutView:
vobj.CutView = False
else: vobj.CutView = True
class _ArchDrawingView:
def __init__(self, obj):
obj.Proxy = self
self.setProperties(obj)
def setProperties(self,obj):
pl = obj.PropertiesList
if not "Source" in pl:
obj.addProperty("App::PropertyLink", "Source", "Base", QT_TRANSLATE_NOOP("App::Property","The linked object"))
if not "RenderingMode" in pl:
obj.addProperty("App::PropertyEnumeration", "RenderingMode", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The rendering mode to use"))
obj.RenderingMode = ["Solid","Wireframe"]
obj.RenderingMode = "Wireframe"
if not "ShowCut" in pl:
obj.addProperty("App::PropertyBool", "ShowCut", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If cut geometry is shown or not"))
if not "ShowFill" in pl:
obj.addProperty("App::PropertyBool", "ShowFill", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If cut geometry is filled or not"))
if not "LineWidth" in pl:
obj.addProperty("App::PropertyFloat", "LineWidth", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The line width of the rendered objects"))
obj.LineWidth = 0.35
if not "FontSize" in pl:
obj.addProperty("App::PropertyLength", "FontSize", "Drawing view", QT_TRANSLATE_NOOP("App::Property","The size of the texts inside this object"))
obj.FontSize = 12
if not "AlwaysOn" in pl:
obj.addProperty("App::PropertyBool", "AlwaysOn", "Drawing view", QT_TRANSLATE_NOOP("App::Property","If checked, source objects are displayed regardless of being visible in the 3D model"))
if not "LineColor" in pl:
obj.addProperty("App::PropertyColor", "LineColor", "Drawing view",QT_TRANSLATE_NOOP("App::Property","The line color of the projected objects"))
if not "FillColor" in pl:
obj.addProperty("App::PropertyColor", "FillColor", "Drawing view",QT_TRANSLATE_NOOP("App::Property","The color of the cut faces (if turned on)"))
obj.FillColor = (0.8, 0.8, 0.8)
self.Type = "ArchSectionView"
def onDocumentRestored(self, obj):
self.setProperties(obj)
def execute(self, obj):
if hasattr(obj,"Source"):
if obj.Source:
svgbody = getSVG(source=obj.Source,
renderMode=obj.RenderingMode,
allOn=getattr(obj, 'AlwaysOn', False),
showHidden=obj.ShowCut,
scale=obj.Scale,
linewidth=obj.LineWidth,
lineColor=obj.LineColor,
fontsize=obj.FontSize,
showFill=obj.ShowFill,
fillColor=obj.FillColor)
if svgbody:
result = '<g id="' + obj.Name + '"'
result += ' transform="'
result += 'rotate('+str(obj.Rotation)+','+str(obj.X)+','+str(obj.Y)+') '
result += 'translate('+str(obj.X)+','+str(obj.Y)+') '
result += 'scale('+str(obj.Scale)+','+str(obj.Scale)+')'
result += '">\n'
result += svgbody
result += '</g>\n'
obj.ViewResult = result
def __getstate__(self):
return self.Type
def __setstate__(self,state):
if state:
self.Type = state
def getDisplayModes(self,vobj):
modes=["Default"]
return modes
def setDisplayMode(self,mode):
return mode
def getDXF(self,obj):
"returns a DXF representation of the view"
if obj.RenderingMode == "Solid":
print("Unable to get DXF from Solid mode: ",obj.Label)
return ""
result = []
import Drawing
if not hasattr(self,"baseshape"):
self.onChanged(obj,"Source")
if hasattr(self,"baseshape"):
if self.baseshape:
result.append(Drawing.projectToDXF(self.baseshape,self.direction))
if hasattr(self,"sectionshape"):
if self.sectionshape:
result.append(Drawing.projectToDXF(self.sectionshape,self.direction))
if hasattr(self,"hiddenshape"):
if self.hiddenshape:
result.append(Drawing.projectToDXF(self.hiddenshape,self.direction))
return result
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 int(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)
else:
return QtGui.QIcon(":/icons/Tree_Part.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.getGroupContents(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))
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_SectionPlane',_CommandSectionPlane())