From c168a7d14bff05aa6a1ff5dde2b43f6637974bbe Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 16 Aug 2019 19:03:49 -0300 Subject: [PATCH] Arch/TD: Support for Arch BuildingParts in TD ArchView --- src/Mod/Arch/ArchSectionPlane.py | 234 +++++++++++++++++--------- src/Mod/Draft/Draft.py | 2 +- src/Mod/TechDraw/App/DrawViewArch.cpp | 20 ++- src/Mod/TechDraw/App/DrawViewArch.h | 6 +- src/Mod/TechDraw/Gui/Command.cpp | 35 +--- 5 files changed, 180 insertions(+), 117 deletions(-) diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index dad02306d3..8e163e6670 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -95,7 +95,37 @@ 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""" + + 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): + # If there is no shape at all ignore it if not hasattr(o, 'Shape') or o.Shape.isNull(): return False @@ -106,7 +136,7 @@ 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,section,showHidden,groupSshapesByObject=False): +def getCutShapes(objs,cutplane,onlySolids,clip,joinArch,showHidden,groupSshapesByObject=False): import Part,DraftGeomUtils shapes = [] @@ -114,28 +144,53 @@ def getCutShapes(objs,section,showHidden,groupSshapesByObject=False): sshapes = [] objectShapes = [] objectSshapes = [] - for o in objs: - if o.isDerivedFrom("Part::Feature"): - if o.Shape.isNull(): - pass - elif section.OnlySolids: - if o.Shape.isValid(): - solids = [] - solids.extend(o.Shape.Solids) - shapes.extend(solids) - - objectShapes.append((o, solids)) + 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: - print(section.Label,": Skipping invalid object:",o.Label) + shtypes.setdefault(o.Material.Name if (hasattr(o,"Material") and o.Material) else "None",[]).append(o.Shape.copy()) + elif o.isDerivedFrom("Part::Feature"): + 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: - shapes.append(o.Shape) - objectShapes.append((o, [o.Shape])) - clip = False - if hasattr(section, "Clip"): - clip = section.Clip - cutface,cutvolume,invcutvolume = ArchCommands.getCutVolume(section.Shape.copy(),shapes,clip) - shapes =[] + print("ArchSectionPlane: Fusing Arch objects produced non-solid results") + shapes.append(v1) + objectShapes.append((k,[v1])) + else: + for o in objs: + if o.isDerivedFrom("Part::Feature"): + 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 = [] @@ -172,22 +227,37 @@ def getCutShapes(objs,section,showHidden,groupSshapesByObject=False): else: return shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume -def getFillForObject(o, defaultFill, section): - if hasattr(section, 'UseMaterialColorForFill') and section.UseMaterialColorForFill: +def getFillForObject(o, defaultFill, source): + + 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 o.Material.Color - + return material.Color return defaultFill -def getSVG(section, 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): +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): - """getSVG(section, [renderMode, allOn, showHidden, scale, rotation, - linewidth, lineColor, fontsize, showFill, fillColor, techdraw, fillSpaces]): - - returns an SVG fragment from an Arch section plane. If + """ + 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. @@ -198,12 +268,10 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale fillSpaces - If True, shows space objects as filled surfaces """ - if not section.Objects: - return "" import Part,DraftGeomUtils - p = FreeCAD.Placement(section.Placement) - direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) - objs = Draft.getGroupContents(section.Objects,walls=True,addgroups=True) + objs, cutplane, onlySolids, clip, direction = getSectionData(source) + if not objs: + return "" if not allOn: objs = Draft.removeHidden(objs) @@ -231,8 +299,12 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") scaledLineWidth = linewidth/scale svgLineWidth = str(scaledLineWidth) + 'px' - st = archUserParameters.GetFloat("CutLineThickness",2) - svgCutLineWidth = str(scaledLineWidth * st) + '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") @@ -243,35 +315,40 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale fillpattern += '' svgLineColor = Draft.getrgb(lineColor) svg = '' - # reading cached version svgcache = None - if hasattr(section.Proxy,"svgcache") and section.Proxy.svgcache: - svgcache = section.Proxy.svgcache[0] - if section.Proxy.svgcache[1] != renderMode: - svgcache = None - if section.Proxy.svgcache[2] != showHidden: - svgcache = None - if section.Proxy.svgcache[3] != showFill: - svgcache = None - if section.Proxy.svgcache[4] != fillSpaces: - 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 - if hasattr(section.Proxy,"boolcache") and section.Proxy.boolcache: - vshapes = section.Proxy.boolcache[0] - hshapes = section.Proxy.boolcache[1] - sshapes = section.Proxy.boolcache[2] - cutface = section.Proxy.boolcache[3] - cutvolume = section.Proxy.boolcache[4] - invcutvolume = section.Proxy.boolcache[5] - objectSshapes = section.Proxy.boolcache[6] - else: - if showFill: - vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes = getCutShapes(objs,section,showHidden, True) + if 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: - vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,section,showHidden) - objectSshapes = [] - section.Proxy.boolcache = [vshapes,hshapes,sshapes,cutface,cutvolume,invcutvolume,objectSshapes] + 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] # generating SVG if renderMode in ["Solid",1]: @@ -280,15 +357,20 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale # render using the Arch Vector Renderer import ArchVRM, WorkingPlane wp = WorkingPlane.plane() - wp.setFromPlacement(section.Placement) + 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(section.Shape,showHidden) + render.cut(cutplane,showHidden) else: - render.cut(section.Shape) + render.cut(cutplane) svgcache += '\n' svgcache += render.getViewSVG(linewidth="SVGLINEWIDTH") svgcache += fillpattern @@ -298,7 +380,8 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale svgcache += render.getHiddenSVG(linewidth="SVGLINEWIDTH") svgcache += '\n' # print(render.info()) - section.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces] + if hasattr(source,"Proxy"): + source.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces,joinArch] else: if not svgcache: @@ -329,7 +412,7 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale for o, shapes in objectSshapes: for s in shapes: if s.Edges: - objectFill = getFillForObject(o, fillColor, section) + 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(), @@ -344,7 +427,8 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale sshapes, direction, hStyle=style, h0Style=style, h1Style=style, vStyle=style, v0Style=style, v1Style=style) - section.Proxy.svgcache = [svgcache,renderMode,showHidden,showFill,fillSpaces] + 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) @@ -361,7 +445,7 @@ def getSVG(section, renderMode="Wireframe", allOn=False, showHidden=False, scale if not techdraw: svg += '' - # filter out spaces not cut by the section plane + # 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: @@ -426,12 +510,10 @@ def getDXF(obj): import Drawing,Part if not obj.Source: return result - section = obj.Source - if not section.Objects: + source = obj.Source + objs,cutplane,onlySolids,clip,direction = getSectionData(source) + if not objs: return result - p = FreeCAD.Placement(section.Placement) - direction = p.Rotation.multVec(FreeCAD.Vector(0,0,1)) - objs = Draft.getGroupContents(section.Objects,walls=True,addgroups=True) if not allOn: objs = Draft.removeHidden(objs) # separate spaces and Draft objects @@ -439,7 +521,7 @@ def getDXF(obj): 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,section,showHidden) + 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: @@ -538,7 +620,7 @@ class _SectionPlane: # clean svg cache if needed if prop in ["Placement","Objects","OnlySolids","UseMaterialColorForFill","Clip"]: self.svgcache = None - self.boolcache = None + self.shapecache = None def getNormal(self,obj): diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index b91de89e2e..ff13ae8754 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -5503,7 +5503,7 @@ class _Shape2DView(_DraftObject): else: objs = obj.Base.Group cutplane = Part.makePlane(1000,1000,FreeCAD.Vector(-500,-500,0)) - m = 1500 + m = 1 if obj.Base.ViewObject and hasattr(obj.Base.ViewObject,"CutMargin"): m = obj.Base.ViewObject.CutMargin.Value cutplane.translate(FreeCAD.Vector(0,0,m)) diff --git a/src/Mod/TechDraw/App/DrawViewArch.cpp b/src/Mod/TechDraw/App/DrawViewArch.cpp index 4dca98553a..bf138b89ac 100644 --- a/src/Mod/TechDraw/App/DrawViewArch.cpp +++ b/src/Mod/TechDraw/App/DrawViewArch.cpp @@ -54,7 +54,7 @@ DrawViewArch::DrawViewArch(void) { static const char *group = "Arch view"; - ADD_PROPERTY_TYPE(Source ,(0),group,App::Prop_None,"Section Plane object for this view"); + ADD_PROPERTY_TYPE(Source ,(0),group,App::Prop_None,"SectionPlane or BuildingPart object for this view"); Source.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(AllOn ,(false),group,App::Prop_None,"If hidden objects must be shown or not"); RenderMode.setEnums(RenderModeEnums); @@ -62,8 +62,10 @@ DrawViewArch::DrawViewArch(void) ADD_PROPERTY_TYPE(FillSpaces ,(false),group,App::Prop_None,"If True, Arch Spaces are shown as a colored area"); ADD_PROPERTY_TYPE(ShowHidden ,(false),group,App::Prop_None,"If the hidden geometry behind the section plane is shown or not"); ADD_PROPERTY_TYPE(ShowFill ,(false),group,App::Prop_None,"If cut areas must be filled with a hatch pattern or not"); - ADD_PROPERTY_TYPE(LineWidth,(0.35),group,App::Prop_None,"Line width of this view"); + ADD_PROPERTY_TYPE(LineWidth,(0.25),group,App::Prop_None,"Line width of this view"); ADD_PROPERTY_TYPE(FontSize,(12.0),group,App::Prop_None,"Text size for this view"); + ADD_PROPERTY_TYPE(CutLineWidth,(0.50),group,App::Prop_None,"Width of cut lines of this view"); + ADD_PROPERTY_TYPE(JoinArch ,(false),group,App::Prop_None,"If True, walls and structure will be fused by material"); ScaleType.setValue("Custom"); } @@ -81,7 +83,9 @@ short DrawViewArch::mustExecute() const ShowHidden.isTouched() || ShowFill.isTouched() || LineWidth.isTouched() || - FontSize.isTouched()); + FontSize.isTouched() || + CutLineWidth.isTouched() || + JoinArch.isTouched()); } if ((bool) result) { return result; @@ -115,7 +119,9 @@ App::DocumentObjectExecReturn *DrawViewArch::execute(void) << ",fontsize=" << FontSize.getValue() << ",techdraw=True" << ",rotation=" << Rotation.getValue() - << ",fillSpaces=" << (FillSpaces.getValue() ? "True" : "False"); + << ",fillSpaces=" << (FillSpaces.getValue() ? "True" : "False") + << ",cutlinewidth=" << CutLineWidth.getValue() + << ",joinArch=" << (JoinArch.getValue() ? "True" : "False"); Base::Interpreter().runString("import ArchSectionPlane"); Base::Interpreter().runStringArg("svgBody = ArchSectionPlane.getSVG(App.activeDocument().%s %s)", @@ -145,7 +151,7 @@ std::string DrawViewArch::getSVGTail(void) void DrawViewArch::Restore(Base::XMLReader &reader) { // this is temporary code for backwards compat (within v0.17). can probably be deleted once there are no development -// fcstd files with old property types in use. +// fcstd files with old property types in use. reader.readElement("Properties"); int Cnt = reader.getAttributeAsInteger("Count"); @@ -175,9 +181,9 @@ void DrawViewArch::Restore(Base::XMLReader &reader) static_cast(schemaProp)->setScope(App::LinkScope::Global); static_cast(schemaProp)->setValue(link.getValue()); } - + } else { - // has Source prop isn't PropertyLink or PropertyLinkGlobal! + // has Source prop isn't PropertyLink or PropertyLinkGlobal! Base::Console().Log("DrawViewArch::Restore - old Document Source is weird: %s\n", TypeName); // no idea } diff --git a/src/Mod/TechDraw/App/DrawViewArch.h b/src/Mod/TechDraw/App/DrawViewArch.h index e430216e34..4d9aa55e66 100644 --- a/src/Mod/TechDraw/App/DrawViewArch.h +++ b/src/Mod/TechDraw/App/DrawViewArch.h @@ -50,6 +50,8 @@ public: App::PropertyBool ShowFill; App::PropertyFloat LineWidth; App::PropertyFloat FontSize; + App::PropertyFloat CutLineWidth; + App::PropertyBool JoinArch; /** @name methods override Feature */ //@{ @@ -61,7 +63,7 @@ public: virtual const char* getViewProviderName(void) const override { return "TechDrawGui::ViewProviderArch"; } - + virtual short mustExecute() const override; void Restore(Base::XMLReader &reader) override; @@ -72,7 +74,7 @@ protected: Base::BoundBox3d bbox; std::string getSVGHead(void); std::string getSVGTail(void); - + private: static const char* RenderModeEnums[]; }; diff --git a/src/Mod/TechDraw/Gui/Command.cpp b/src/Mod/TechDraw/Gui/Command.cpp index a9f126e75e..a1cce71095 100644 --- a/src/Mod/TechDraw/Gui/Command.cpp +++ b/src/Mod/TechDraw/Gui/Command.cpp @@ -79,16 +79,6 @@ using namespace TechDrawGui; using namespace std; -bool isArchSection(App::DocumentObject* obj) -{ - bool result = true; - App::Property* prop1 = obj->getPropertyByName("Objects"); - App::Property* prop2 = obj->getPropertyByName("OnlySolids"); - if ( (!prop1) || (!prop2) ) { - result = false; - } - return result; -} //=========================================================================== // TechDraw_NewPageDef (default template) @@ -1057,34 +1047,17 @@ void CmdTechDrawArchView::activated(int iMsg) return; } - std::vector objects = getSelection().getObjectsOfType(App::DocumentObject::getClassTypeId()); - if (objects.empty()) { + const std::vector objects = getSelection().getObjectsOfType(App::DocumentObject::getClassTypeId()); + if (objects.size() != 1) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Select at least one object.")); - return; - } - int ifound = 0; - bool found = false; - for (auto& obj: objects) { - if (isArchSection(obj)) { - found = true; - break; - } - ifound++; - } - App::DocumentObject* archObj; - if (found) { - archObj = objects[ifound]; - } else { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("There is no Section Plane in selection.")); + QObject::tr("Select exactly one object.")); return; } std::string PageName = page->getNameInDocument(); std::string FeatName = getUniqueObjectName("ArchView"); - std::string SourceName = archObj->getNameInDocument(); + std::string SourceName = objects.front()->getNameInDocument(); openCommand("Create ArchView"); doCommand(Doc,"App.activeDocument().addObject('TechDraw::DrawViewArch','%s')",FeatName.c_str()); doCommand(Doc,"App.activeDocument().%s.Source = App.activeDocument().%s",FeatName.c_str(),SourceName.c_str());