/// <summary> /// Create 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> protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { if (shapeEditScope.BuilderType != IFCShapeBuilderType.TessellatedShapeBuilder) { throw new InvalidOperationException("Currently BrepBuilder is only used to support IFCAdvancedFace"); } base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); // we would only be in this code if we are not processing and IfcAdvancedBrep, since IfcAdvancedBrep must have IfcAdvancedFace if (shapeEditScope.BuilderScope == null) { throw new InvalidOperationException("BuilderScope has not been initialized"); } TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; bool addFace = true; bool canTriangulate = (Bounds.Count == 1); ElementId materialId = GetMaterialElementId(shapeEditScope); // We can only really triangulate faces with one boundary with 4 vertices, // but we don't really know how many vertices the boundary has until later. // So this is just the first block. Later, we can try to extend to generic // polygons. tsBuilderScope.StartCollectingFace(materialId, canTriangulate); foreach (IFCFaceBound faceBound in Bounds) { faceBound.CreateShape(shapeEditScope, lcs, scaledLcs, guid); // If we can't create the outer face boundary, we will abort the creation of this face. // In that case, return, unless we can triangulate it. if (!tsBuilderScope.HaveActiveFace()) { addFace = false; break; } } tsBuilderScope.StopCollectingFace(addFace, false); IList <List <XYZ> > delayedFaceBoundaries = CreateTriangulation(tsBuilderScope.DelayedFaceBoundary); if (delayedFaceBoundaries != null) { bool extraFace = false; foreach (List <XYZ> delayedBoundary in delayedFaceBoundaries) { bool addTriangulatedFace = true; tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope), false); if (!tsBuilderScope.AddLoopVertices(Id, delayedBoundary)) { Importer.TheLog.LogComment(Id, "Bounded loop plane is slightly non-planar, couldn't triangulate.", false); addTriangulatedFace = false; } tsBuilderScope.StopCollectingFace(addTriangulatedFace, extraFace); extraFace = true; } } }
/// <summary> /// Return geometry for a particular representation item. /// </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>The created geometry.</returns> /// <remarks>As this doesn't inherit from IfcSolidModel, this is a non-virtual CreateGeometry function.</remarks> protected IList <GeometryObject> CreateGeometry(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { if (Shells.Count == 0) { return(null); } IList <GeometryObject> geomObjs = null; using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); foreach (IFCConnectedFaceSet faceSet in Shells) { faceSet.CreateShape(shapeEditScope, lcs, scaledLcs, guid); } geomObjs = tsBuilderScope.CreateGeometry(guid); } if (geomObjs == null || geomObjs.Count == 0) { return(null); } return(geomObjs); }
/// <summary> /// Create 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> protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { if (shapeEditScope.BuilderType != IFCShapeBuilderType.TessellatedShapeBuilder) { throw new InvalidOperationException("Currently BrepBuilder is only used to support IFCAdvancedFace"); } base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); // we would only be in this code if we are not processing and IfcAdvancedBrep, since IfcAdvancedBrep must have IfcAdvancedFace if (shapeEditScope.BuilderScope == null) { throw new InvalidOperationException("BuilderScope has not been initialized"); } TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope)); foreach (IFCFaceBound faceBound in Bounds) { faceBound.CreateShape(shapeEditScope, lcs, scaledLcs, guid); // If we can't create the outer face boundary, we will abort the creation of this face. In that case, return. if (!tsBuilderScope.HaveActiveFace()) { tsBuilderScope.AbortCurrentFace(); return; } } tsBuilderScope.StopCollectingFace(); }
private Tuple <IList <GeometryObject>, bool> CollectFaces(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); Outer.CreateShape(shapeEditScope, lcs, scaledLcs, guid); IList <GeometryObject> geomObjs = null; bool canRevertToMesh = tsBuilderScope.CanRevertToMesh(); int numCreatedFaces = tsBuilderScope.CreatedFacesCount; int numExpectedFaces = Outer.Faces.Count; if (numCreatedFaces == numExpectedFaces || (!canRevertToMesh && numCreatedFaces > 0)) { geomObjs = tsBuilderScope.CreateGeometry(guid); if (numCreatedFaces < numExpectedFaces) { Importer.TheLog.LogWarning(Outer.Id, "Processing " + numCreatedFaces + " valid faces out of " + numExpectedFaces + " total.", false); } } return(Tuple.Create(geomObjs, canRevertToMesh)); } }
/// <summary> /// Return geometry for a particular representation item. /// </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>The created geometry.</returns> protected override IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { if (Outer == null || Outer.Faces.Count == 0) { return(null); } IList <GeometryObject> geomObjs = null; bool canRevertToMesh = false; using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); Outer.CreateShape(shapeEditScope, lcs, scaledLcs, guid); if (tsBuilderScope.CreatedFacesCount == Outer.Faces.Count) { geomObjs = tsBuilderScope.CreateGeometry(guid); } canRevertToMesh = tsBuilderScope.CanRevertToMesh(); } if (geomObjs == null || geomObjs.Count == 0) { if (canRevertToMesh) { using (IFCImportShapeEditScope.BuildPreferenceSetter setter = new IFCImportShapeEditScope.BuildPreferenceSetter(shapeEditScope, IFCImportShapeEditScope.BuildPreferenceType.AnyMesh)) { using (BuilderScope newBuilderScope = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { TessellatedShapeBuilderScope newTsBuilderScope = newBuilderScope as TessellatedShapeBuilderScope; // Let's see if we can loosen the requirements a bit, and try again. newTsBuilderScope.StartCollectingFaceSet(); Outer.CreateShape(shapeEditScope, lcs, scaledLcs, guid); // This needs to be in scope so that we keep the mesh tolerance for vertices. if (newTsBuilderScope.CreatedFacesCount != 0) { if (newTsBuilderScope.CreatedFacesCount != Outer.Faces.Count) { Importer.TheLog.LogWarning (Outer.Id, "Processing " + newTsBuilderScope.CreatedFacesCount + " valid faces out of " + Outer.Faces.Count + " total.", false); } geomObjs = newTsBuilderScope.CreateGeometry(guid); } } } } } if (geomObjs == null || geomObjs.Count == 0) { // Couldn't use fallback, or fallback didn't work. Importer.TheLog.LogWarning(Id, "Couldn't create any geometry.", false); return(null); } return(geomObjs); }
/// <summary> /// Return geometry for a particular representation item. /// </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>The created geometry.</returns> /// <remarks>As this doesn't inherit from IfcSolidModel, this is a non-virtual CreateSolid function.</remarks> protected IList <GeometryObject> CreateGeometry( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { if (Shells.Count == 0) { return(null); } IList <GeometryObject> geomObjs = null; int numExpectedFaces = 0; foreach (IFCConnectedFaceSet faceSet in Shells) { numExpectedFaces += faceSet.Faces.Count; } // We are going to start by trying to create a Solid, even if we are passed a shell-based model, since we can frequently // do so. However, if we have even one missing face, we'll loosen the requirements and revert to mesh only. for (int pass = 0; pass < 2; pass++) { IFCImportShapeEditScope.BuildPreferenceType target = (pass == 0) ? IFCImportShapeEditScope.BuildPreferenceType.AnyGeometry : IFCImportShapeEditScope.BuildPreferenceType.AnyMesh; using (IFCImportShapeEditScope.BuildPreferenceSetter setter = new IFCImportShapeEditScope.BuildPreferenceSetter(shapeEditScope, target)) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); foreach (IFCConnectedFaceSet faceSet in Shells) { faceSet.CreateShape(shapeEditScope, lcs, scaledLcs, guid); } // If we are on our first pass, try again. If we are on our second pass, warn and create the best geometry we can. if (tsBuilderScope.CreatedFacesCount != numExpectedFaces) { if (pass == 0) { continue; } Importer.TheLog.LogWarning (Id, "Processing " + tsBuilderScope.CreatedFacesCount + " valid faces out of " + numExpectedFaces + " total.", false); } geomObjs = tsBuilderScope.CreateGeometry(guid); WarnOnTooFewCreatedFaces(geomObjs, numExpectedFaces); break; } } } if (geomObjs == null || geomObjs.Count == 0) { if (numExpectedFaces != 0) { Importer.TheLog.LogError (Id, "No valid geometry found. This may be due to slivery triangles or other similar geometric issues.", false); return(null); } } return(geomObjs); }
private void CreateTessellatedShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs) { TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; if (tsBuilderScope == null) { throw new InvalidOperationException("Expect a TessellatedShapeBuilderScope, but get a BrepBuilderScope instead"); } IList <XYZ> loopVertices = Bound.LoopVertices; int count = 0; if (loopVertices == null || ((count = loopVertices.Count) == 0)) { throw new InvalidOperationException("#" + Id + ": missing loop vertices, ignoring."); } if (count < 3) { throw new InvalidOperationException("#" + Id + ": too few loop vertices (" + count + "), ignoring."); } if (!Orientation) { loopVertices.Reverse(); } // Apply the transform IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { transformedVertices.Add(scaledLcs.OfPoint(vertex)); } // Check that the loop vertices don't contain points that are very close to one another; // if so, throw the point away and hope that the TessellatedShapeBuilder can repair the result. // Warn in this case. If the entire boundary is bad, report an error and don't add the loop vertices. IList <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = false; count = validVertices.Count; if (validVertices.Count < 3) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); bPotentiallyAbortFace = true; } else { if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } } if (bPotentiallyAbortFace && IsOuter) { tsBuilderScope.AbortCurrentFace(); } }
protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); // Create triangle face set from CoordIndex. We do not support the Normals yet at this point foreach (List <int> triIndex in CoordIndex) { // This is a defensive check in an unlikely situation that the index is larger than the data if (triIndex[0] > Coordinates.CoordList.Count || triIndex[1] > Coordinates.CoordList.Count || triIndex[2] > Coordinates.CoordList.Count) { continue; } tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope)); IList <XYZ> loopVertices = new List <XYZ>(); for (int ii = 0; ii < 3; ++ii) { int actualVIdx = triIndex[ii] - 1; if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } IList <double> v = Coordinates.CoordList[actualVIdx]; loopVertices.Add(new XYZ(v[0], v[1], v[2])); } IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { // Need to apply the project unit scaling here XYZ scaledVertex = applyProjectUnitScaleVertex(vertex); transformedVertices.Add(scaledLcs.OfPoint(scaledVertex)); } // Check triangle that is too narrow (2 vertices are within the tolerance IList <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = false; int count = validVertices.Count; if (validVertices.Count < 3) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); bPotentiallyAbortFace = true; } else { if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } } if (bPotentiallyAbortFace) { tsBuilderScope.AbortCurrentFace(); } else { tsBuilderScope.StopCollectingFace(); } } IList <GeometryObject> createdGeometries = tsBuilderScope.CreateGeometry(guid); if (createdGeometries != null) { foreach (GeometryObject createdGeometry in createdGeometries) { shapeEditScope.AddGeometry(IFCSolidInfo.Create(Id, createdGeometry)); } } } }
protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); // Create the face set from IFCIndexedPolygonalFace foreach (IFCIndexedPolygonalFace face in Faces) { // TODO: Consider adding ability to triangulate here. tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope), false); IList <XYZ> loopVertices = new List <XYZ>(); foreach (int vertInd in face.CoordIndex) { int actualVIdx = vertInd - 1; // IFC starts the list position at 1 if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } XYZ vertex = Coordinates.CoordList[actualVIdx]; loopVertices.Add(scaledLcs.OfPoint(vertex)); } List <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, loopVertices, out validVertices); bool bPotentiallyAbortFace = false; if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } // Handle holes if (face.InnerCoordIndices != null) { foreach (IList <int> innerLoop in face.InnerCoordIndices) { IList <XYZ> innerLoopVertices = new List <XYZ>(); foreach (int innerVerIdx in innerLoop) { int actualVIdx = innerVerIdx - 1; if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } XYZ vertex = Coordinates.CoordList[actualVIdx]; // add vertex to the loop innerLoopVertices.Add(scaledLcs.OfPoint(vertex)); } List <XYZ> validInnerV; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, innerLoopVertices, out validInnerV); if (!tsBuilderScope.AddLoopVertices(Id, validInnerV)) { bPotentiallyAbortFace = true; } } } tsBuilderScope.StopCollectingFace(!bPotentiallyAbortFace, false); } IList <GeometryObject> createdGeometries = tsBuilderScope.CreateGeometry(guid); if (createdGeometries != null) { foreach (GeometryObject createdGeometry in createdGeometries) { shapeEditScope.AddGeometry(IFCSolidInfo.Create(Id, createdGeometry)); } } } }
private void CreateTessellatedShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs) { TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; if (tsBuilderScope == null) { throw new InvalidOperationException("Expect a TessellatedShapeBuilderScope, but get a BrepBuilderScope instead"); } IList <XYZ> loopVertices = Bound.LoopVertices; int count = 0; if (loopVertices == null || ((count = loopVertices.Count) == 0)) { throw new InvalidOperationException("#" + Id + ": missing loop vertices, ignoring."); } if (count < 3) { throw new InvalidOperationException("#" + Id + ": too few loop vertices (" + count + "), ignoring."); } if (!Orientation) { loopVertices.Reverse(); } // Apply the transform IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { transformedVertices.Add(scaledLcs.OfPoint(vertex)); } // Check that the loop vertices don't contain points that are very close to one another; // if so, throw the point away and hope that the TessellatedShapeBuilder can repair the result. // Warn in this case. If the entire boundary is bad, report an error and don't add the loop vertices. List <XYZ> validVertices = null; for (int pass = 0; pass < 2; pass++) { if (pass == 1 && !tsBuilderScope.RevertToMeshIfPossible()) { break; } IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); count = validVertices.Count; if (count >= 3 || !IsOuter) { break; } } // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = (count < 3); if (bPotentiallyAbortFace) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); } else { bool maybeTryToTriangulate = tsBuilderScope.CanProcessDelayedFaceBoundary && (count == 4); bool tryToTriangulate = false; // Last check: check to see if the vertices are actually planar. // We are not going to be particularly fancy about how we pick the plane. if (count > 3) { XYZ planeNormal = null; XYZ firstPoint = validVertices[0]; XYZ secondPoint = validVertices[1]; XYZ firstDir = secondPoint - firstPoint; double bestLength = 0; for (int index = 2; index <= count; index++) { XYZ thirdPoint = validVertices[(index % count)]; XYZ currentPlaneNormal = firstDir.CrossProduct(thirdPoint - firstPoint); double planeNormalLength = currentPlaneNormal.GetLength(); if (planeNormalLength > 0.01) { planeNormal = currentPlaneNormal.Normalize(); break; } else if (maybeTryToTriangulate && (planeNormalLength > bestLength)) { planeNormal = currentPlaneNormal.Normalize(); bestLength = planeNormalLength; } firstPoint = secondPoint; secondPoint = thirdPoint; firstDir = secondPoint - firstPoint; } if (planeNormal == null) { // Even if we don't find a good normal, we will still see if the internal function can make sense of it. Importer.TheLog.LogComment(Id, "Bounded loop plane is likely non-planar, may triangulate.", false); } else { double vertexEps = IFCImportFile.TheFile.VertexTolerance; for (int index = 0; index < count; index++) { XYZ pointOnPlane = validVertices[index] - (validVertices[index] - firstPoint).DotProduct(planeNormal) * planeNormal; double distance = pointOnPlane.DistanceTo(validVertices[index]); if (distance > vertexEps * 10.0) { Importer.TheLog.LogComment(Id, "Bounded loop plane is non-planar, may triangulate.", false); tryToTriangulate = maybeTryToTriangulate; bPotentiallyAbortFace = !tryToTriangulate; break; } else if (distance > vertexEps) { if (!maybeTryToTriangulate) { Importer.TheLog.LogComment(Id, "Bounded loop plane is slightly non-planar, correcting.", false); validVertices[index] = pointOnPlane; } else { Importer.TheLog.LogComment(Id, "Bounded loop plane is slightly non-planar, will triangulate.", false); tryToTriangulate = maybeTryToTriangulate; } } } } } if (!bPotentiallyAbortFace) { if (tryToTriangulate) { tsBuilderScope.DelayedFaceBoundary = validVertices; } else { bPotentiallyAbortFace = !tsBuilderScope.AddLoopVertices(Id, validVertices); } } } if (bPotentiallyAbortFace && IsOuter) { tsBuilderScope.AbortCurrentFace(); } }
private void CreateTessellatedShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs) { TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; if (tsBuilderScope == null) { throw new InvalidOperationException("Expect a TessellatedShapeBuilderScope, but get a BrepBuilderScope instead"); } IList <XYZ> loopVertices = Bound.LoopVertices; int count = 0; if (loopVertices == null || ((count = loopVertices.Count) == 0)) { throw new InvalidOperationException("#" + Id + ": missing loop vertices, ignoring."); } if (count < 3) { throw new InvalidOperationException("#" + Id + ": too few loop vertices (" + count + "), ignoring."); } if (!Orientation) { loopVertices.Reverse(); } // Apply the transform IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { transformedVertices.Add(scaledLcs.OfPoint(vertex)); } // Check that the loop vertices don't contain points that are very close to one another; // if so, throw the point away and hope that the TessellatedShapeBuilder can repair the result. // Warn in this case. If the entire boundary is bad, report an error and don't add the loop vertices. IList <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = false; count = validVertices.Count; if (count < 3) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); bPotentiallyAbortFace = true; } else { // Last check: check to see if the vertices are actually planar. If not, for the vertices to be planar. // We are not going to be particularly fancy about how we pick the plane. XYZ planeNormal = null; bool foundNormal = false; XYZ firstPoint = validVertices[0]; XYZ secondPoint = validVertices[1]; XYZ firstDir = secondPoint - firstPoint; int thirdPointIndex = 2; for (; thirdPointIndex < count; thirdPointIndex++) { XYZ thirdPoint = validVertices[thirdPointIndex]; planeNormal = firstDir.CrossProduct(thirdPoint - firstPoint); if (!planeNormal.IsZeroLength()) { planeNormal = planeNormal.Normalize(); foundNormal = true; break; } } if (!foundNormal) { Importer.TheLog.LogComment(Id, "Loop is degenerate, ignoring.", false); bPotentiallyAbortFace = true; } else { double vertexEps = IFCImportFile.TheFile.Document.Application.VertexTolerance; for (++thirdPointIndex; thirdPointIndex < count; thirdPointIndex++) { XYZ pointOnPlane = validVertices[thirdPointIndex] - (validVertices[thirdPointIndex] - firstPoint).DotProduct(planeNormal) * planeNormal; if (pointOnPlane.DistanceTo(validVertices[thirdPointIndex]) > vertexEps) { Importer.TheLog.LogComment(Id, "Bounded loop plane is slightly non-planar, correcting.", false); validVertices[thirdPointIndex] = pointOnPlane; } } if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } } } if (bPotentiallyAbortFace && IsOuter) { tsBuilderScope.AbortCurrentFace(); } }