diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py
index 3ce3c2e50952..6ef19c3642b6 100644
--- a/src/Mod/Arch/ArchBuildingPart.py
+++ b/src/Mod/Arch/ArchBuildingPart.py
@@ -368,11 +368,16 @@ def onChanged(self,obj,prop):
ArchIFC.IfcProduct.onChanged(self, obj, prop)
- if prop == "Height":
+ # clean svg cache if needed
+ if prop in ["Placement","Group"]:
+ self.svgcache = None
+ self.shapecache = None
+
+ if (prop == "Height") and prop.Height.Value:
for child in obj.Group:
if Draft.getType(child) in ["Wall","Structure"]:
if not child.Height.Value:
- #print("Executing ",child.Label)
+ print("Executing ",child.Label)
child.Proxy.execute(child)
elif prop == "Placement":
@@ -398,9 +403,10 @@ def onChanged(self,obj,prop):
#print("angle before rotation:",shape.Placement.Rotation.Angle)
#print("rotation angle:",math.degrees(deltar.Angle))
shape.rotate(DraftVecUtils.tup(obj.Placement.Base), DraftVecUtils.tup(deltar.Axis), math.degrees(deltar.Angle))
- #print("angle after rotation:",shape.Placement.Rotation.Angle)
+ print("angle after rotation:",shape.Placement.Rotation.Angle)
child.Placement = shape.Placement
if deltap:
+ print("moving child")
child.Placement.move(deltap)
def execute(self,obj):
diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py
index 8e163e667015..f914df47d7c2 100644
--- a/src/Mod/Arch/ArchSectionPlane.py
+++ b/src/Mod/Arch/ArchSectionPlane.py
@@ -21,7 +21,16 @@
#* *
#***************************************************************************
-import FreeCAD,WorkingPlane,math,Draft,ArchCommands,DraftVecUtils,ArchComponent
+import FreeCAD
+import WorkingPlane
+import math
+import Draft
+import ArchCommands
+import DraftVecUtils
+import ArchComponent
+import os
+import re
+import tempfile
from FreeCAD import Vector
if FreeCAD.GuiUp:
import FreeCADGui
@@ -95,6 +104,7 @@ def makeSectionView(section,name="View"):
view.Label = translate("Arch","View of")+" "+section.Name
return view
+
def getSectionData(source):
"""Returns some common data from section planes and building parts"""
@@ -125,6 +135,8 @@ def getSectionData(source):
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():
@@ -136,7 +148,13 @@ def looksLikeDraft(o):
# 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 = []
@@ -227,7 +245,10 @@ def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesB
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
@@ -240,6 +261,26 @@ def getFillForObject(o, defaultFill, source):
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,
@@ -284,8 +325,9 @@ def getSVG(source,
for o in objs:
if Draft.getType(o) == "Space":
spaces.append(o)
- elif Draft.getType(o) in ["Dimension","Annotation","Label"]:
- drafts.append(o)
+ elif Draft.getType(o) in ["Dimension","Annotation","Label","DraftText"]:
+ if isOriented(o,cutplane):
+ drafts.append(o)
elif o.isDerivedFrom("Part::Part2DObject"):
drafts.append(o)
elif looksLikeDraft(o):
@@ -298,7 +340,11 @@ def getSVG(source,
archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
scaledLineWidth = linewidth/scale
- svgLineWidth = str(scaledLineWidth) + 'px'
+ if renderMode in ["Coin",2]:
+ # 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'
@@ -351,7 +397,17 @@ def getSVG(source,
source.Proxy.shapecache = [vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes]
# generating SVG
- if renderMode in ["Solid",1]:
+ if renderMode in ["Coin",2]:
+ # 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:
+ 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
@@ -383,7 +439,7 @@ def getSVG(source,
if hasattr(source,"Proxy"):
source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch]
else:
-
+ # Wireframe (0) mode
if not svgcache:
svgcache = ""
# render using the Drawing module
@@ -495,7 +551,8 @@ def getSVG(source,
def getDXF(obj):
- "returns a DXF representation from a TechDraw/Drawing view"
+ """returns a DXF representation from a TechDraw/Drawing view"""
+
allOn = True
if hasattr(obj,"AllOn"):
allOn = obj.AllOn
@@ -531,6 +588,194 @@ def getDXF(obj):
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):
+
+ """Returns an SVG fragment generated from a coin view"""
+
+ if not FreeCAD.GuiUp:
+ return ""
+
+ # 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 objs 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()
+ v.setName("TempRenderViewer")
+
+ 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 doesnt 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
+ #
+ 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("",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("","",svg,count=1)
+
+ # remove background rectangle
+ svg = re.sub("","",svg,count=1,flags=re.MULTILINE|re.DOTALL)
+
+ # embed everything in a scale group and scale the viewport
+ if factor:
+ if trans:
+ svg = svg.replace("","\n",1)
+ else:
+ svg = svg.replace("","\n",1)
+ svg = svg.replace("","\n")
+
+ # trigger viewer close
+ QtCore.QTimer.singleShot(0,closeViewer)
+
+ # strip svg tags (needed for TD Arch view)
+ svg = re.sub("<\?xml.*?>","",svg,flags=re.MULTILINE|re.DOTALL)
+ svg = re.sub("","",svg,flags=re.MULTILINE|re.DOTALL)
+ svg = re.sub("<\/svg>","",svg,flags=re.MULTILINE|re.DOTALL)
+
+ return svg
+
+
+def closeViewer():
+
+ """Close temporary viewers"""
+
+ mw = FreeCADGui.getMainWindow()
+ for sw in mw.findChildren(QtGui.QMdiSubWindow):
+ if sw.windowTitle() == "TempRenderViewer":
+ sw.close()
+
+
+
class _CommandSectionPlane:
"the Arch SectionPlane command definition"
diff --git a/src/Mod/TechDraw/App/DrawViewArch.cpp b/src/Mod/TechDraw/App/DrawViewArch.cpp
index 412c57f15cf2..aae10718f84a 100644
--- a/src/Mod/TechDraw/App/DrawViewArch.cpp
+++ b/src/Mod/TechDraw/App/DrawViewArch.cpp
@@ -47,8 +47,9 @@ using namespace std;
PROPERTY_SOURCE(TechDraw::DrawViewArch, TechDraw::DrawViewSymbol)
const char* DrawViewArch::RenderModeEnums[]= {"Wireframe",
- "Solid",
- NULL};
+ "Solid",
+ "Coin",
+ NULL};
DrawViewArch::DrawViewArch(void)
{