/// <summary> /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// </summary> /// <param name="solidInfo">The solid information.</param> /// <returns>False if the return solid is empty; true otherwise.</returns> protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) { int numVoids = Voids.Count; if (numVoids == 0) { return(true); } // We only cut body representation items. if (solidInfo.RepresentationType != IFCRepresentationIdentifier.Body) { return(true); } if (!(solidInfo.GeometryObject is Solid)) { string typeName = (solidInfo.GeometryObject is Mesh) ? "mesh" : "instance"; Importer.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); return(true); } for (int voidIdx = 0; voidIdx < numVoids; voidIdx++) { Solid voidObject = Voids[voidIdx].GeometryObject as Solid; if (voidObject == null) { Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + Voids[voidIdx].Id + "), ignoring.", false); return(true); } solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, Voids[voidIdx].Id, (solidInfo.GeometryObject as Solid), voidObject, BooleanOperationsType.Difference, null); if (solidInfo.GeometryObject == null || (solidInfo.GeometryObject as Solid).Faces.IsEmpty) { return(false); } } return(true); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> public IList <GeometryObject> CreateGeometry( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { IList <GeometryObject> firstSolids = FirstOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); if (firstSolids != null) { foreach (GeometryObject potentialSolid in firstSolids) { if (!(potentialSolid is Solid)) { Importer.TheLog.LogError((FirstOperand as IFCRepresentationItem).Id, "Can't perform Boolean operation on a Mesh.", false); return(firstSolids); } } } IList <GeometryObject> secondSolids = null; if (SecondOperand != null) { try { using (IFCImportShapeEditScope.BuildPreferenceSetter setter = new IFCImportShapeEditScope.BuildPreferenceSetter(shapeEditScope, IFCImportShapeEditScope.BuildPreferenceType.ForceSolid)) { // Before we process the second operand, we are going to see if there is a uniform material set for the first operand // (corresponding to the solid in the Boolean operation). We will try to suggest the same material for the voids to avoid arbitrary // setting of material information for the cut faces. IFCStyledItem firstOperandStyledItem = GetStyledItemFromOperand(FirstOperand as IFCRepresentationItem); using (IFCImportShapeEditScope.IFCMaterialStack stack = new IFCImportShapeEditScope.IFCMaterialStack(shapeEditScope, firstOperandStyledItem, null)) { secondSolids = SecondOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); } } } catch (Exception ex) { // We will allow something to be imported, in the case where the second operand is invalid. // If the first (base) operand is invalid, we will still fail the import of this solid. if (SecondOperand is IFCRepresentationItem) { Importer.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false); } else { throw ex; } secondSolids = null; } } IList <GeometryObject> resultSolids = null; if (firstSolids == null) { resultSolids = secondSolids; } else if (secondSolids == null || BooleanOperator == null) { if (BooleanOperator == null) { Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", false); } resultSolids = firstSolids; } else { BooleanOperationsType booleanOperationsType = BooleanOperationsType.Difference; switch (BooleanOperator) { case IFCBooleanOperator.Difference: booleanOperationsType = BooleanOperationsType.Difference; break; case IFCBooleanOperator.Intersection: booleanOperationsType = BooleanOperationsType.Intersect; break; case IFCBooleanOperator.Union: booleanOperationsType = BooleanOperationsType.Union; break; default: Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", true); break; } resultSolids = new List <GeometryObject>(); foreach (GeometryObject firstSolid in firstSolids) { Solid resultSolid = (firstSolid as Solid); int secondId = (SecondOperand == null) ? -1 : (SecondOperand as IFCRepresentationItem).Id; XYZ suggestedShiftDirection = (SecondOperand == null) ? null : SecondOperand.GetSuggestedShiftDirection(lcs); foreach (GeometryObject secondSolid in secondSolids) { resultSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, secondId, resultSolid, secondSolid as Solid, booleanOperationsType, suggestedShiftDirection); if (resultSolid == null) { break; } } if (resultSolid != null) { resultSolids.Add(resultSolid); } } } return(resultSolids); }
/// <summary> /// Create geometry for an IfcHalfSpaceSolid. /// </summary> /// <param name="shapeEditScope">The shape edit scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>A list containing one geometry for the IfcHalfSpaceSolid.</returns> protected virtual IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform unscaledLcs, Transform scaledLcs, string guid) { IFCPlane ifcPlane = BaseSurface as IFCPlane; Plane plane = ifcPlane.Plane; XYZ origin = plane.Origin; XYZ xVec = plane.XVec; XYZ yVec = plane.YVec; // Set some huge boundaries for now. const double largeCoordinateValue = 100000; XYZ[] corners = new XYZ[4] { unscaledLcs.OfPoint((xVec * -largeCoordinateValue) + (yVec * -largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * largeCoordinateValue) + (yVec * -largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * largeCoordinateValue) + (yVec * largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * -largeCoordinateValue) + (yVec * largeCoordinateValue) + origin) }; IList <CurveLoop> loops = new List <CurveLoop>(); CurveLoop loop = new CurveLoop(); for (int ii = 0; ii < 4; ii++) { if (AgreementFlag) { loop.Append(Line.CreateBound(corners[(5 - ii) % 4], corners[(4 - ii) % 4])); } else { loop.Append(Line.CreateBound(corners[ii], corners[(ii + 1) % 4])); } } loops.Add(loop); XYZ normal = unscaledLcs.OfVector(AgreementFlag ? -plane.Normal : plane.Normal); SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); Solid baseSolid = GeometryCreationUtilities.CreateExtrusionGeometry(loops, normal, largeCoordinateValue, solidOptions); if (BaseBoundingCurve != null) { CurveLoop polygonalBoundary = BaseBoundingCurve.CurveLoop; Transform unscaledTotalTransform = unscaledLcs.Multiply(BaseBoundingCurveTransform); Transform scaledTotalTransform = scaledLcs.Multiply(BaseBoundingCurveTransform); // Make sure this bounding polygon extends below base of half-space soild. Transform moveBaseTransform = Transform.Identity; moveBaseTransform.Origin = new XYZ(0, 0, -largeCoordinateValue); unscaledTotalTransform = unscaledTotalTransform.Multiply(moveBaseTransform); scaledTotalTransform = scaledTotalTransform.Multiply(moveBaseTransform); CurveLoop transformedPolygonalBoundary = IFCGeometryUtil.CreateTransformed(polygonalBoundary, Id, unscaledTotalTransform, scaledTotalTransform); IList <CurveLoop> boundingLoops = new List <CurveLoop>(); boundingLoops.Add(transformedPolygonalBoundary); Solid boundingSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boundingLoops, unscaledTotalTransform.BasisZ, 2.0 * largeCoordinateValue, solidOptions); baseSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, BaseBoundingCurve.Id, baseSolid, boundingSolid, BooleanOperationsType.Intersect, null); } IList <GeometryObject> returnList = new List <GeometryObject>(); returnList.Add(baseSolid); return(returnList); }
/// <summary> /// Creates or populates Revit elements based on the information contained in this class. /// </summary> /// <param name="doc">The document.</param> protected override void Create(Document doc) { Transform lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransform : Transform.Identity; if (ProductRepresentation != null) { if (ProductRepresentation.IsValid()) { using (IFCImportShapeEditScope shapeEditScope = IFCImportShapeEditScope.Create(doc, this)) { shapeEditScope.GraphicsStyleId = m_GraphicsStyleId; shapeEditScope.CategoryId = CategoryId; // The name can be added as well. but it is usually less useful than 'oid' string myId = GlobalId; // + "(" + Name + ")"; ProductRepresentation.CreateProductRepresentation(shapeEditScope, lcs, lcs, myId); int numSolids = Solids.Count; int numVoids = Voids.Count; if ((numSolids > 0) && (numVoids > 0)) { // We may have some GeometryInstances. These need to be "exploded" to do the cutting. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { if (Solids[solidIdx].GeometryObject is GeometryInstance) { //// This code currently doesn't work, so commented out. //GeometryInstance geomInst = Solids[solidIdx].GeometryObject as GeometryInstance; //GeometryElement geomElem = geomInst.GetInstanceGeometry(); //foreach (GeometryObject geomObj in geomElem) //{ //if (geomObj is Solid) //Solids.Add(IFCSolidInfo.Create(Solids[solidIdx].Id, geomObj as Solid)); //else if (geomObj is Mesh) //Solids.Add(IFCSolidInfo.Create(Solids[solidIdx].Id, geomObj as Mesh)); //else if (geomObj is GeometryInstance) //IFCImportFile.TheLog.LogError(Solids[solidIdx].Id, "Can't cut nested mapped items, ignoring " + numVoids + " void(s).", false); // Other items are irrelevant here. //} //Solids.RemoveAt(solidIdx); //solidIdx--; //numSolids--; } } // This may be different than before, with the addition of solids from FamilyInstances. numSolids = Solids.Count; // Attempt to cut each solid with each void. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { if (!(Solids[solidIdx].GeometryObject is Solid)) { // Assume that we only deal with solids, meshes and instances. string typeName = (Solids[solidIdx].GeometryObject is Mesh) ? "mesh" : "instance"; IFCImportFile.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); continue; } for (int voidIdx = 0; voidIdx < numVoids; voidIdx++) { if (!(Voids[voidIdx].GeometryObject is Solid)) { IFCImportFile.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + Voids[voidIdx].Id + "), ignoring.", false); continue; } Solids[solidIdx].GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(Solids[solidIdx].Id, Voids[voidIdx].Id, Solids[solidIdx].GeometryObject as Solid, Voids[voidIdx].GeometryObject as Solid, BooleanOperationsType.Difference); if ((Solids[solidIdx].GeometryObject as Solid).Faces.IsEmpty) { Solids.RemoveAt(solidIdx); solidIdx--; numSolids--; break; } } } } bool addedCurves = shapeEditScope.AddPlanViewCurves(FootprintCurves, Id); if ((numSolids > 0 || addedCurves)) { if (GlobalId != null) { // If the GlobalId is null, this is a fake IfcProduct that we don't want to create into a DirectShape, or // add to the caches in any way. We only wanted to gather its geometry. DirectShape shape = Importer.TheCache.UseElementByGUID <DirectShape>(doc, GlobalId); if (shape == null) { shape = IFCElementUtil.CreateElement(doc, CategoryId, Importer.ImportAppGUID(), GlobalId); } List <GeometryObject> directShapeGeometries = new List <GeometryObject>(); foreach (IFCSolidInfo geometryObject in Solids) { // We need to check if the solid created is good enough for DirectShape. If not, warn and use a fallback Mesh. GeometryObject currObject = geometryObject.GeometryObject; if (currObject is Solid) { Solid solid = currObject as Solid; if (!shape.IsValidGeometry(solid)) { IFCImportFile.TheLog.LogWarning(Id, "Couldn't create valid solid, reverting to mesh.", false); directShapeGeometries.AddRange(IFCGeometryUtil.CreateMeshesFromSolid(solid)); currObject = null; } } if (currObject != null) { directShapeGeometries.Add(currObject); } } // We will use the first IfcTypeObject id, if it exists. In general, there should be 0 or 1. ElementId typeId = ElementId.InvalidElementId; foreach (IFCTypeObject typeObject in TypeObjects) { if (typeObject.IsValidForCreation && typeObject.CreatedElementId != ElementId.InvalidElementId) { typeId = typeObject.CreatedElementId; break; } } shape.SetShape(directShapeGeometries); shapeEditScope.SetPlanViewRep(shape); if (typeId != ElementId.InvalidElementId) { shape.SetTypeId(typeId); } PresentationLayerNames.UnionWith(shapeEditScope.PresentationLayerNames); m_CreatedElementId = shape.Id; } } } } else { IFCImportFile.TheLog.LogWarning(Id, "There is no valid geometry for this " + EntityType.ToString() + "; entity will not be built.", false); } } base.Create(doc); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> public IList <GeometryObject> CreateGeometry( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { IList <GeometryObject> firstSolids = FirstOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); if (firstSolids != null) { foreach (GeometryObject potentialSolid in firstSolids) { if (!(potentialSolid is Solid)) { IFCImportFile.TheLog.LogError((FirstOperand as IFCRepresentationItem).Id, "Can't perform Boolean operation on a Mesh.", false); return(firstSolids); } } } IList <GeometryObject> secondSolids = null; if (SecondOperand != null) { try { using (IFCImportShapeEditScope.IFCTargetSetter setter = new IFCImportShapeEditScope.IFCTargetSetter(shapeEditScope, TessellatedShapeBuilderTarget.Solid, TessellatedShapeBuilderFallback.Abort)) { secondSolids = SecondOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); } } catch (Exception ex) { // We will allow something to be imported, in the case where the second operand is invalid. // If the first (base) operand is invalid, we will still fail the import of this solid. if (SecondOperand is IFCRepresentationItem) { IFCImportFile.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false); } else { throw ex; } secondSolids = null; } } IList <GeometryObject> resultSolids = null; if (firstSolids == null) { resultSolids = secondSolids; } else if (secondSolids == null) { resultSolids = firstSolids; } else { BooleanOperationsType booleanOperationsType = BooleanOperationsType.Difference; switch (BooleanOperator) { case IFCBooleanOperator.Difference: booleanOperationsType = BooleanOperationsType.Difference; break; case IFCBooleanOperator.Intersection: booleanOperationsType = BooleanOperationsType.Intersect; break; case IFCBooleanOperator.Union: booleanOperationsType = BooleanOperationsType.Union; break; default: IFCImportFile.TheLog.LogError(Id, "Invalid BooleanOperationsType.", true); break; } resultSolids = new List <GeometryObject>(); foreach (GeometryObject firstSolid in firstSolids) { Solid resultSolid = (firstSolid as Solid); int secondId = (SecondOperand == null) ? -1 : (SecondOperand as IFCRepresentationItem).Id; foreach (GeometryObject secondSolid in secondSolids) { resultSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, secondId, resultSolid, secondSolid as Solid, booleanOperationsType); if (resultSolid == null) { break; } } if (resultSolid != null) { resultSolids.Add(resultSolid); } } } return(resultSolids); }
/// <summary> /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// </summary> /// <param name="solidInfo">The solid information.</param> /// <returns>False if the return solid is empty; true otherwise.</returns> protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) { // We only cut "Body" representation items. if (solidInfo.RepresentationType != IFCRepresentationIdentifier.Body) { return(true); } IList <IFCVoidInfo> voidsToUse = null; List <IFCVoidInfo> partVoids = null; IList <IFCVoidInfo> parentVoids = (Decomposes as IFCProduct)?.Voids; if (parentVoids != null) { partVoids = new List <IFCVoidInfo>(); partVoids.AddRange(Voids); partVoids.AddRange(parentVoids); voidsToUse = partVoids; } else { voidsToUse = Voids; } int numVoids = voidsToUse.Count; if (numVoids == 0) { return(true); } if (!(solidInfo.GeometryObject is Solid)) { string typeName = (solidInfo.GeometryObject is Mesh) ? "mesh" : "instance"; Importer.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); return(true); } foreach (IFCVoidInfo voidInfo in voidsToUse) { Solid voidObject = voidInfo.GeometryObject as Solid; if (voidObject == null) { Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + voidInfo.Id + "), ignoring.", false); continue; } var voidTransform = voidInfo.TotalTransform; if (voidTransform != null && voidTransform.IsIdentity == false) { // Transform the void into the space of the solid. var t = ObjectLocation.TotalTransform.Inverse.Multiply(voidTransform); voidObject = SolidUtils.CreateTransformed(voidObject, t); } solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, voidInfo.Id, (solidInfo.GeometryObject as Solid), voidObject, BooleanOperationsType.Difference, null); if (solidInfo.GeometryObject == null || (solidInfo.GeometryObject as Solid).Faces.IsEmpty) { return(false); } } return(true); }
/// <summary> /// Creates or populates Revit elements based on the information contained in this class. /// </summary> /// <param name="doc">The document.</param> protected override void Create(Document doc) { bool preventInstances = false; IFCElement element = this as IFCElement; if (element != null) { IFCOpeningElement openingElement = element as IFCOpeningElement; if (openingElement != null) { preventInstances = true; } foreach (IFCFeatureElement opening in element.Openings) { try { preventInstances = true; // Create the actual Revit element based on the IFCFeatureElement here. ElementId openingId = CreateElement(doc, opening); // This gets around the issue that the Boolean operation between the void(s) in the IFCFeatureElement and // the solid(s) in the IFCElement may use the Graphics Style of the voids in the resulting Solid(s), meaning // that some faces may disappear when we turn off the visibility of IfcOpeningElements. IList <IFCSolidInfo> voids = IFCElement.CloneElementGeometry(doc, opening, this, true); if (voids != null) { foreach (IFCSolidInfo voidGeom in voids) { Voids.Add(voidGeom); } } } catch (Exception ex) { Importer.TheLog.LogError(opening.Id, ex.Message, false); } } } if (HasValidTopLevelGeometry()) { using (IFCImportShapeEditScope shapeEditScope = IFCImportShapeEditScope.Create(doc, this)) { shapeEditScope.GraphicsStyleId = GraphicsStyleId; shapeEditScope.CategoryId = CategoryId; shapeEditScope.PreventInstances = preventInstances; // The name can be added as well. but it is usually less useful than 'oid' string myId = GlobalId; // + "(" + Name + ")"; Transform lcs = IFCImportFile.TheFile.IFCProject.WorldCoordinateSystem; if (lcs == null) { lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransform : Transform.Identity; } else if (ObjectLocation != null) { lcs = lcs.Multiply(ObjectLocation.TotalTransform); } ProductRepresentation.CreateProductRepresentation(shapeEditScope, lcs, lcs, myId); int numSolids = Solids.Count; int numVoids = Voids.Count; if ((numSolids > 0) && (numVoids > 0)) { // This may be different than before, with the addition of solids from FamilyInstances. numSolids = Solids.Count; // Attempt to cut each solid with each void. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { // We only cut body representation items. if (Solids[solidIdx].RepresentationType != IFCRepresentationIdentifier.Body) { continue; } if (!(Solids[solidIdx].GeometryObject is Solid)) { string typeName = (Solids[solidIdx].GeometryObject is Mesh) ? "mesh" : "instance"; Importer.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); continue; } for (int voidIdx = 0; voidIdx < numVoids; voidIdx++) { if (!(Voids[voidIdx].GeometryObject is Solid)) { Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + Voids[voidIdx].Id + "), ignoring.", false); continue; } Solids[solidIdx].GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(Solids[solidIdx].Id, Voids[voidIdx].Id, Solids[solidIdx].GeometryObject as Solid, Voids[voidIdx].GeometryObject as Solid, BooleanOperationsType.Difference, null); if ((Solids[solidIdx].GeometryObject as Solid).Faces.IsEmpty) { Solids.RemoveAt(solidIdx); solidIdx--; numSolids--; break; } } } } bool addedCurves = shapeEditScope.AddPlanViewCurves(FootprintCurves, Id); if ((numSolids > 0 || addedCurves)) { if (GlobalId != null) { // If the GlobalId is null, this is a fake IfcProduct that we don't want to create into a DirectShape, or // add to the caches in any way. We only wanted to gather its geometry. DirectShape shape = Importer.TheCache.UseElementByGUID <DirectShape>(doc, GlobalId); if (shape == null) { shape = IFCElementUtil.CreateElement(doc, CategoryId, GlobalId, null, Id); } List <GeometryObject> directShapeGeometries = new List <GeometryObject>(); foreach (IFCSolidInfo geometryObject in Solids) { // We need to check if the solid created is good enough for DirectShape. If not, warn and use a fallback Mesh. GeometryObject currObject = geometryObject.GeometryObject; if (currObject is Solid) { Solid solid = currObject as Solid; if (!shape.IsValidGeometry(solid)) { Importer.TheLog.LogWarning(Id, "Couldn't create valid solid, reverting to mesh.", false); directShapeGeometries.AddRange(IFCGeometryUtil.CreateMeshesFromSolid(solid)); currObject = null; } } if (currObject != null) { directShapeGeometries.Add(currObject); } } // We will use the first IfcTypeObject id, if it exists. In general, there should be 0 or 1. ElementId typeId = ElementId.InvalidElementId; foreach (IFCTypeObject typeObject in TypeObjects) { if (typeObject.IsValidForCreation && typeObject.CreatedElementId != ElementId.InvalidElementId) { typeId = typeObject.CreatedElementId; break; } } shape.SetShape(directShapeGeometries); shapeEditScope.SetPlanViewRep(shape); if (typeId != ElementId.InvalidElementId) { shape.SetTypeId(typeId); } PresentationLayerNames.UnionWith(shapeEditScope.PresentationLayerNames); CreatedElementId = shape.Id; CreatedGeometry = directShapeGeometries; } } } } else { if (this is IFCElement || this is IFCGrid) { IList <IFCEntity> visitedEntities = new List <IFCEntity>(); visitedEntities.Add(this); if (!HasValidSubElementGeometry(visitedEntities)) { // We will not warn if this is an IfcSpatialStructureElement; those aren't expected to have their own geometry. Importer.TheLog.LogWarning(Id, "There is no valid geometry for this " + EntityType.ToString() + "; entity will not be built.", false); } } } base.Create(doc); }