From 91dde33ef2128b9d80cbfcdcb9b5126d1ef9bd03 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Thu, 2 Sep 2021 12:21:29 +0200 Subject: [PATCH] Arch: Added option to references to fuse the base objects by material --- src/Mod/Arch/ArchBuildingPart.py | 57 +++++++++++++++++--------- src/Mod/Arch/ArchReference.py | 69 +++++++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py index 86e3957def..f7f6a39029 100644 --- a/src/Mod/Arch/ArchBuildingPart.py +++ b/src/Mod/Arch/ArchBuildingPart.py @@ -348,6 +348,11 @@ class BuildingPart(ArchIFC.IfcProduct): if not "SavedInventor" in pl: obj.addProperty("App::PropertyFileIncluded","SavedInventor","BuildingPart",QT_TRANSLATE_NOOP("App::Property","This property stores an inventor representation for this object")) obj.setEditorMode("SavedInventor",2) + if not "OnlySolids" in pl: + obj.addProperty("App::PropertyBool","OnlySolids","BuildingPart",QT_TRANSLATE_NOOP("App::Property","If true, only solids will be collected by this object when referenced from other files")) + obj.OnlySolids = True + if not "MaterialsTable" in pl: + obj.addProperty("App::PropertyMap","MaterialsTable","BuildingPart",QT_TRANSLATE_NOOP("App::Property","A MaterialName:SolidIndexesList map that relates material names with solid indexes to be used when referencing this object from other files")) self.Type = "BuildingPart" @@ -412,17 +417,21 @@ class BuildingPart(ArchIFC.IfcProduct): def execute(self,obj): # gather all the child shapes into a compound - shapes = self.getShapes(obj) + shapes,materialstable = self.getShapes(obj) if shapes: - f = [] - for s in shapes: - f.extend(s.Faces) - #print("faces before compound:",len(f)) import Part - obj.Shape = Part.makeCompound(f) - #print("faces after compound:",len(obj.Shape.Faces)) - #print("recomputing ",obj.Label) + if obj.OnlySolids: + f = [] + for s in shapes: + f.extend(s.Solids) + #print("faces before compound:",len(f)) + obj.Shape = Part.makeCompound(f) + #print("faces after compound:",len(obj.Shape.Faces)) + #print("recomputing ",obj.Label) + else: + obj.Shape = Part.makeCompound(shapes) obj.Area = self.getArea(obj) + obj.MaterialsTable = materialstable def getArea(self,obj): @@ -440,11 +449,22 @@ class BuildingPart(ArchIFC.IfcProduct): "recursively get the shapes of objects inside this BuildingPart" shapes = [] + solidindex = 0 + materialstable = {} for child in Draft.get_group_contents(obj): if not Draft.get_type(child) in ["Space"]: - if hasattr(child,'Shape'): - shapes.extend(child.Shape.Faces) - return shapes + if hasattr(child,'Shape') and child.Shape: + shapes.append(child.Shape) + for solid in child.Shape.Solids: + matname = "Undefined" + if hasattr(child,"Material") and child.Material: + matname = child.Material.Name + if matname in materialstable: + materialstable[matname] = materialstable[matname]+","+str(solidindex) + else: + materialstable[matname] = str(solidindex) + solidindex += 1 + return shapes,materialstable def getSpaces(self,obj): @@ -651,13 +671,14 @@ class ViewProviderBuildingPart: colors = [] for child in Draft.get_group_contents(obj): - if hasattr(child,'Shape') and (hasattr(child.ViewObject,"DiffuseColor") or hasattr(child.ViewObject,"ShapeColor")): - if hasattr(child.ViewObject,"DiffuseColor") and len(child.ViewObject.DiffuseColor) == len(child.Shape.Faces): - colors.extend(child.ViewObject.DiffuseColor) - else: - c = child.ViewObject.ShapeColor[:3]+(child.ViewObject.Transparency/100.0,) - for i in range(len(child.Shape.Faces)): - colors.append(c) + if not Draft.get_type(child) in ["Space"]: + if hasattr(child,'Shape') and (hasattr(child.ViewObject,"DiffuseColor") or hasattr(child.ViewObject,"ShapeColor")): + if hasattr(child.ViewObject,"DiffuseColor") and len(child.ViewObject.DiffuseColor) == len(child.Shape.Faces): + colors.extend(child.ViewObject.DiffuseColor) + else: + c = child.ViewObject.ShapeColor[:3]+(child.ViewObject.Transparency/100.0,) + for i in range(len(child.Shape.Faces)): + colors.append(c) return colors def onChanged(self,vobj,prop): diff --git a/src/Mod/Arch/ArchReference.py b/src/Mod/Arch/ArchReference.py index 8010ebe6e9..25b227746b 100644 --- a/src/Mod/Arch/ArchReference.py +++ b/src/Mod/Arch/ArchReference.py @@ -102,6 +102,8 @@ class ArchReference: obj.ReferenceMode = "Transient" obj.removeProperty("TransientReference") FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" TransientReference property to ReferenceMode\n") + if not "FuseArch" in pl: + obj.addProperty("App::PropertyBool","FuseArch", "Reference", QT_TRANSLATE_NOOP("App::Property","Fuse objects of same material")) self.Type = "Reference" def onDocumentRestored(self,obj): @@ -158,11 +160,9 @@ class ArchReference: f = zdoc.open(self.parts[obj.Part][1]) shapedata = f.read() f.close() - import Part - shape = Part.Shape() if sys.version_info.major >= 3: shapedata = shapedata.decode("utf8") - shape.importBrepFromString(shapedata) + shape = self.cleanShape(shapedata,obj,self.parts[obj.Part][2]) obj.Shape = shape if not pl.isIdentity(): obj.Placement = pl @@ -170,6 +170,51 @@ class ArchReference: print("Part not found in file") self.reload = False + def cleanShape(self,shapedata,obj,materials): + + "cleans the imported shape" + + import Part + shape = Part.Shape() + shape.importBrepFromString(shapedata) + if obj.FuseArch and materials: + # separate lone edges + shapes = [] + for edge in shape.Edges: + found = False + for solid in shape.Solids: + if found: + break + for soledge in solid.Edges: + if found: + break + if edge.hashCode() == soledge.hashCode(): + found = True + break + else: + shapes.append(edge) + print("solids:",len(shape.Solids),"mattable:",materials) + for key,solindexes in materials.items(): + if key == "Undefined": + # do not join objects with no defined material + for solindex in [int(i) for i in solindexes.split(",")]: + shapes.append(shape.Solids[solindex]) + else: + fusion = None + for solindex in [int(i) for i in solindexes.split(",")]: + if not fusion: + fusion = shape.Solids[solindex] + else: + fusion = fusion.fuse(shape.Solids[solindex]) + if fusion: + shapes.append(fusion) + shape = Part.makeCompound(shapes) + try: + shape = shape.removeSplitter() + except: + print(obj.Label,": error removing splitter") + return shape + def getFile(self,obj,filename=None): "gets a valid file, if possible" @@ -206,6 +251,7 @@ class ArchReference: "returns a list of Part-based objects in a FCStd file" parts = {} + materials = {} filename = self.getFile(obj,filename) if not filename: return parts @@ -214,6 +260,7 @@ class ArchReference: name = None label = None part = None + materials = {} writemode = False for line in docf: if sys.version_info.major >= 3: @@ -236,11 +283,23 @@ class ArchReference: if n: part = n[0] writemode = False - if name and label and part: - parts[name] = [label,part] + elif "" in line: + writemode = False + elif "" in line: + if name and label and part: + parts[name] = [label,part,materials] name = None label = None part = None + materials = {} + writemode = False return parts def getColors(self,obj):