From a7c8a4677f480f8733e700c3bd2cabc579e3e061 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Mon, 16 Jul 2018 17:21:17 -0300 Subject: [PATCH] Arch: Support for IfcGrid --- src/Mod/Arch/ArchAxis.py | 119 ++++++++++++++-------- src/Mod/Arch/ArchBuildingPart.py | 7 +- src/Mod/Arch/importIFC.py | 165 ++++++++++++++++++++++++++----- 3 files changed, 221 insertions(+), 70 deletions(-) diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index 467897b8dc..d118f4181b 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -57,7 +57,7 @@ def makeAxis(num=5,size=1000,name="Axes"): if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return - obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name) + obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Axis") obj.Label = translate("Arch",name) _Axis(obj) if FreeCAD.GuiUp: @@ -83,7 +83,7 @@ def makeAxisSystem(axes,name="Axis System"): if not isinstance(axes,list): axes = [axes] - obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",name) + obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","AxisSystem") obj.Label = translate("Arch",name) _AxisSystem(obj) obj.Axes = axes @@ -97,7 +97,7 @@ def makeGrid(name="Grid"): '''makeGrid(): makes a grid object''' - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Grid") obj.Label = translate("Arch",name) ArchGrid(obj) if FreeCAD.GuiUp: @@ -208,6 +208,8 @@ class _Axis: obj.addProperty("App::PropertyFloatList","Angles","Axis", QT_TRANSLATE_NOOP("App::Property","The angles of each axis")) if not "Labels" in pl: obj.addProperty("App::PropertyStringList","Labels","Axis", QT_TRANSLATE_NOOP("App::Property","The label of each axis")) + if not "CustomNumber" in pl: + obj.addProperty("App::PropertyString","CustomNumber","Axis", QT_TRANSLATE_NOOP("App::Property","An optional custom bubble number")) if not "Length" in pl: obj.addProperty("App::PropertyLength","Length","Axis", QT_TRANSLATE_NOOP("App::Property","The length of the axes")) obj.Length=3000 @@ -265,6 +267,21 @@ class _Axis: pts.append(e.Vertexes[0].Point) return pts + def getAxisData(self,obj): + data = [] + num = 0 + for e in obj.Shape.Edges: + axdata = [] + axdata.append(e.Vertexes[0].Point) + axdata.append(e.Vertexes[-1].Point) + if obj.ViewObject: + axdata.append(obj.ViewObject.Proxy.getNumber(obj.ViewObject,num)) + else: + axdata.append(str(num)) + data.append(axdata) + num += 1 + return data + class _ViewProviderAxis: @@ -490,52 +507,13 @@ class _ViewProviderAxis: self.onChanged(vobj,"ShowLabel") elif prop in ["NumberingStyle","StartNumber"]: if hasattr(self,"bubbletexts"): - chars = "abcdefghijklmnopqrstuvwxyz" - roman=(('M',1000),('CM',900),('D',500),('CD',400), - ('C',100),('XC',90),('L',50),('XL',40), - ('X',10),('IX',9),('V',5),('IV',4),('I',1)) num = 0 if hasattr(vobj,"StartNumber"): if vobj.StartNumber > 1: num = vobj.StartNumber-1 alt = False for t in self.bubbletexts: - if hasattr(vobj,"NumberingStyle"): - if vobj.NumberingStyle == "1,2,3": - t.string = str(num+1) - elif vobj.NumberingStyle == "01,02,03": - t.string = str(num+1).zfill(2) - elif vobj.NumberingStyle == "001,002,003": - t.string = str(num+1).zfill(3) - elif vobj.NumberingStyle == "A,B,C": - result = "" - base = num/26 - if base: - result += chars[base].upper() - remainder = num % 26 - result += chars[remainder].upper() - t.string = result - elif vobj.NumberingStyle == "a,b,c": - result = "" - base = num/26 - if base: - result += chars[base] - remainder = num % 26 - result += chars[remainder] - t.string = result - elif vobj.NumberingStyle == "I,II,III": - result = "" - n = num - n += 1 - for numeral, integer in roman: - while n >= integer: - result += numeral - n -= integer - t.string = result - elif vobj.NumberingStyle == "L0,L1,L2": - t.string = "L"+str(num) - else: - t.string = str(num+1) + t.string = self.getNumber(vobj,num) num += 1 if hasattr(vobj,"BubblePosition"): if vobj.BubblePosition == "Both": @@ -590,6 +568,50 @@ class _ViewProviderAxis: self.labels.addChild(st) self.labelset.addChild(self.labels) + def getNumber(self,vobj,num): + + chars = "abcdefghijklmnopqrstuvwxyz" + roman=(('M',1000),('CM',900),('D',500),('CD',400), + ('C',100),('XC',90),('L',50),('XL',40), + ('X',10),('IX',9),('V',5),('IV',4),('I',1)) + if hasattr(vobj.Object,"CustomNumber") and vobj.Object.CustomNumber: + return vobj.Object.CustomNumber.encode("utf8") + elif hasattr(vobj,"NumberingStyle"): + if vobj.NumberingStyle == "1,2,3": + return str(num+1) + elif vobj.NumberingStyle == "01,02,03": + return str(num+1).zfill(2) + elif vobj.NumberingStyle == "001,002,003": + return str(num+1).zfill(3) + elif vobj.NumberingStyle == "A,B,C": + result = "" + base = num/26 + if base: + result += chars[base].upper() + remainder = num % 26 + result += chars[remainder].upper() + return result + elif vobj.NumberingStyle == "a,b,c": + result = "" + base = num/26 + if base: + result += chars[base] + remainder = num % 26 + result += chars[remainder] + return result + elif vobj.NumberingStyle == "I,II,III": + result = "" + n = num + n += 1 + for numeral, integer in roman: + while n >= integer: + result += numeral + n -= integer + return result + elif vobj.NumberingStyle == "L0,L1,L2": + return "L"+str(num) + else: + return str(num+1) def setEdit(self,vobj,mode=0): @@ -841,6 +863,13 @@ class _AxisSystem: pts.extend([p.add(v) for p in bset]) return pts + def getAxisData(self,obj): + data = [] + for axis in obj.Axes: + if hasattr(axis,"Proxy") and hasattr(axis.Proxy,"getAxisData"): + data.append(axis.Proxy.getAxisData(axis)) + return data + class _ViewProviderAxisSystem: @@ -855,6 +884,10 @@ class _ViewProviderAxisSystem: import Arch_rc return ":/icons/Arch_Axis_System_Tree.svg" + def isShow(self): + + return True + def claimChildren(self): if hasattr(self,"axes"): diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py index b3cf89fede..afab0f5997 100644 --- a/src/Mod/Arch/ArchBuildingPart.py +++ b/src/Mod/Arch/ArchBuildingPart.py @@ -219,13 +219,16 @@ def makeFloor(objectslist=None,baseobj=None,name="Floor"): def makeBuilding(objectslist=None,baseobj=None,name="Building"): - """overwrites ArchBuilding.makeBiulding""" + """overwrites ArchBuilding.makeBuilding""" obj = makeBuildingPart(objectslist) obj.Label = name obj.IfcRole = "Building" - obj.addProperty("App::PropertyEnumeration","BuildingType","Arch",QT_TRANSLATE_NOOP("App::Property","The type of this building")) + obj.addProperty("App::PropertyEnumeration","BuildingType","Building",QT_TRANSLATE_NOOP("App::Property","The type of this building")) obj.BuildingType = BuildingTypes + if FreeCAD.GuiUp: + obj.ViewObject.ShowLevel = False + obj.ViewObject.ShowLabel = False return obj diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index a7f4e3c5a5..a36713ff30 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -461,6 +461,15 @@ def insert(filename,docname,skip=[],only=[],root=None): if it.Items[0].id() == r.id(): colors[m.RepresentedMaterial.id()] = (c.Red,c.Green,c.Blue) + # move IfcGrids from products to annotations + tp = [] + for product in products: + if product.is_a("IfcGrid") and not (product in annotations): + annotations.append(product) + else: + tp.append(product) + products = tp + if only: # only import a list of IDs and their children ids = [] while only: @@ -1000,28 +1009,67 @@ def insert(filename,docname,skip=[],only=[],root=None): scaling = getScaling(ifcfile) #print "scaling factor =",scaling for annotation in annotations: + anno = None aid = annotation.id() - if aid in skip: continue # user given id skip list - if annotation.is_a() in SKIP: continue # preferences-set type skip list - name = "Annotation" - if annotation.Name: - name = annotation.Name.encode("utf8") - if "annotation" not in name.lower(): - name = "Annotation " + name - if PREFIX_NUMBERS: name = "ID" + str(aid) + " " + name - shapes2d = [] - for rep in annotation.Representation.Representations: - if rep.RepresentationIdentifier in ["Annotation","FootPrint","Axis"]: - shapes2d.extend(setRepresentation(rep,scaling)) - if shapes2d: - sh = Part.makeCompound(shapes2d) - pc = str(int((float(count)/(len(products)+len(annotations))*100)))+"% " - if DEBUG: print(pc,"creating object ",aid," : Annotation with shape: ",sh) - o = FreeCAD.ActiveDocument.addObject("Part::Feature",name) - o.Shape = sh - p = getPlacement(annotation.ObjectPlacement,scaling) - if p: # and annotation.is_a("IfcAnnotation"): - o.Placement = p + if aid in skip: + continue # user given id skip list + if annotation.is_a() in SKIP: + continue # preferences-set type skip list + if annotation.is_a("IfcGrid"): + axes = [] + uvwaxes = () + if annotation.UAxes: + uvwaxes = annotation.UAxes + if annotation.VAxes: + uvwaxes = uvwaxes + annotation.VAxes + if annotation.WAxes: + uvwaxes = uvwaxes + annotation.WAxes + for axis in uvwaxes: + if axis.AxisCurve: + sh = setRepresentation(axis.AxisCurve,scaling) + if sh and (len(sh[0].Vertexes) == 2): # currently only straight axes are supported + sh = sh[0] + l = sh.Length + pl = FreeCAD.Placement() + pl.Base = sh.Vertexes[0].Point + pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),sh.Vertexes[-1].Point.sub(sh.Vertexes[0].Point)) + o = Arch.makeAxis(1,l) + o.Length = l + o.Placement = pl + o.CustomNumber = axis.AxisTag + axes.append(o) + if axes: + name = "Grid" + if annotation.Name: + name = annotation.Name.encode("utf8") + if PREFIX_NUMBERS: + name = "ID" + str(aid) + " " + name + anno = Arch.makeAxisSystem(axes,name) + else: + name = "Annotation" + if annotation.Name: + name = annotation.Name.encode("utf8") + if "annotation" not in name.lower(): + name = "Annotation " + name + if PREFIX_NUMBERS: name = "ID" + str(aid) + " " + name + shapes2d = [] + for rep in annotation.Representation.Representations: + if rep.RepresentationIdentifier in ["Annotation","FootPrint","Axis"]: + shapes2d.extend(setRepresentation(rep,scaling)) + if shapes2d: + sh = Part.makeCompound(shapes2d) + pc = str(int((float(count)/(len(products)+len(annotations))*100)))+"% " + if DEBUG: print(pc,"creating object ",aid," : Annotation with shape: ",sh) + anno = FreeCAD.ActiveDocument.addObject("Part::Feature",name) + anno.Shape = sh + p = getPlacement(annotation.ObjectPlacement,scaling) + if p: # and annotation.is_a("IfcAnnotation"): + anno.Placement = p + # placing in container if needed + if anno: + for host,children in additions.items(): + if (aid in children) and (host in objects.keys()): + Arch.addComponents(anno,objects[host]) count += 1 @@ -1206,6 +1254,52 @@ def export(exportList,filename): if ifctype == "IfcGroup": groups[obj.Name] = [o.Name for o in obj.Group] continue + if (Draft.getType(obj) == "BuildingPart") and hasattr(obj,"IfcRole") and (obj.IfcRole == "Undefined"): + ifctype = "IfcBuildingStorey" # export BuildingParts as Storeys if their type wasn't explicitely set + + # export grids + + if ifctype in ["IfcAxis","IfcAxisSystem","IfcGrid"]: + ifctype = "IfcGrid" + ifcaxes = [] + ifcpols = [] + for axg in obj.Proxy.getAxisData(obj): + ifcaxg = [] + for ax in axg: + p1 = ifcfile.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[0]).multiply(0.001))) + p2 = ifcfile.createIfcCartesianPoint(tuple(FreeCAD.Vector(ax[1]).multiply(0.001))) + pol = ifcfile.createIfcPolyline([p1,p2]) + ifcpols.append(pol) + axis = ifcfile.createIfcGridAxis(ax[2],pol,True) + ifcaxg.append(axis) + if len(ifcaxes) < 3: + ifcaxes.append(ifcaxg) + else: + ifcaxes[2] = ifcaxes[2]+ifcaxg # IfcGrid can have max 3 axes systems + u = None + v = None + w = None + if ifcaxes: + u = ifcaxes[0] + if len(ifcaxes) > 1: + v = ifcaxes[1] + if len(ifcaxes) > 2: + v = ifcaxes[2] + if DEBUG: print(str(count).ljust(3)," : ", ifctype, " (",str(len(ifcpols)),"axes ) : ",name) + xvc = ifcfile.createIfcDirection((1.0,0.0,0.0)) + zvc = ifcfile.createIfcDirection((0.0,0.0,1.0)) + ovc = ifcfile.createIfcCartesianPoint((0.0,0.0,0.0)) + gpl = ifcfile.createIfcAxis2Placement3D(ovc,zvc,xvc) + plac = ifcfile.createIfcLocalPlacement(None,gpl) + cset = ifcfile.createIfcGeometricCurveSet(ifcpols) + #subc = ifcfile.createIfcGeometricRepresentationSubContext('FootPrint','Model',context,None,"MODEL_VIEW",None,None,None,None,None) + srep = ifcfile.createIfcShapeRepresentation(context,'FootPrint',"GeometricCurveSet",ifcpols) + pdef = ifcfile.createIfcProductDefinitionShape(None,None,[srep]) + grid = ifcfile.createIfcGrid(uid,history,name,description,None,plac,pdef,u,v,w) + products[obj.Name] = grid + count += 1 + continue + from ArchComponent import IFCTYPES if ifctype not in IFCTYPES: ifctype = "IfcBuildingElementProxy" @@ -1534,7 +1628,7 @@ def export(exportList,filename): f = products[floor.Name] if children: ifcfile.createIfcRelContainedInSpatialStructure(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'StoreyLink','',children,f) - floors.append(floor.Name) + floors.append(f) defaulthost = f for building in Draft.getObjectsOfType(objectslist,"Building"): objs = Draft.getGroupContents(building,walls=True,addgroups=True) @@ -1545,7 +1639,7 @@ def export(exportList,filename): if not (c.Name in treated): if c.Name != building.Name: # getGroupContents + addgroups will include the building itself if c.Name in products.keys(): - if Draft.getType(c) == "Floor": + if Draft.getType(c) in ["Floor","BuildingPart"]: childfloors.append(products[c.Name]) treated.append(c.Name) elif not (c.Name in treated): @@ -1580,12 +1674,14 @@ def export(exportList,filename): if not buildings: if DEBUG: print("No building found. Adding default building") buildings = [ifcfile.createIfcBuilding(ifcopenshell.guid.compress(uuid.uuid1().hex),history,"Default Building",'',None,None,None,None,"ELEMENT",None,None,None)] + if floors: + ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'BuildingLink','',buildings[0],floors) ifcfile.createIfcRelAggregates(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'SiteLink','',sites[0],buildings) untreated = [] for k,v in products.items(): if not(k in treated): if k != buildings[0].Name: - if not(Draft.getType(FreeCAD.ActiveDocument.getObject(k)) in ["Site","Building","Floor"]): + if not(Draft.getType(FreeCAD.ActiveDocument.getObject(k)) in ["Site","Building","Floor","BuildingPart"]): untreated.append(v) if untreated: if not defaulthost: @@ -2241,6 +2337,17 @@ def setRepresentation(representation,scaling=1000): pts.append(c) return Part.makePolygon(pts) + def getLine(ent): + pts = [] + p1 = getVector(ent.Pnt) + p1.multiply(scaling) + pts.append(p1) + p2 = getVector(ent.Dir) + p2.multiply(scaling) + p2 = p1.add(p2) + pts.append(p2) + return Part.makePolygon(pts) + def getCircle(ent): c = ent.Position.Location.Coordinates c = FreeCAD.Vector(c[0],c[1],c[2] if len(c) > 2 else 0) @@ -2250,9 +2357,15 @@ def setRepresentation(representation,scaling=1000): def getCurveSet(ent): result = [] - for el in ent.Elements: + if ent.is_a() in ["IfcGeometricCurveSet","IfcGeometricSet"]: + elts = ent.Elements + elif ent.is_a() in ["IfcLine","IfcPolyline","IfcCircle","IfcTrimmedCurve"]: + elts = [ent] + for el in elts: if el.is_a("IfcPolyline"): result.append(getPolyline(el)) + elif el.is_a("IfcLine"): + result.append(getLine(el)) elif el.is_a("IfcCircle"): result.append(getCircle(el)) elif el.is_a("IfcTrimmedCurve"): @@ -2291,6 +2404,8 @@ def setRepresentation(representation,scaling=1000): result.append(r) else: result = preresult + elif representation.is_a() in ["IfcPolyline","IfcCircle","IfcTrimmedCurve"]: + result = getCurveSet(representation) return result