private Line RepairLineAndReport(int id, XYZ startPoint, XYZ endPoint, double gap) { string gapAsString = IFCUnitUtil.FormatLengthAsString(gap); Importer.TheLog.LogWarning(id, "Repaired gap of size " + gapAsString + " in IfcCompositeCurve.", false); return(Line.CreateBound(startPoint, endPoint)); }
/// <summary> /// Check for any occurence where distance of two vertices are too narrow (within the tolerance) /// </summary> /// <param name="entityId">The integer number representing the current IFC entity Id</param> /// <param name="shapeEditScope">the shapeEditScope</param> /// <param name="inputVerticesList">Input list of the vertices</param> /// <param name="outputVerticesList">Output List of the valid vertices, i.e. not vertices that are too close to each other</param> /// <returns></returns> public static void CheckAnyDistanceVerticesWithinTolerance(int entityId, IFCImportShapeEditScope shapeEditScope, IList <XYZ> inputVerticesList, out IList <XYZ> outputVerticesList) { // Check triangle that is too narrow (2 vertices are within the tolerance double shortSegmentTolerance = shapeEditScope.TryToCreateSolid() ? shapeEditScope.Document.Application.ShortCurveTolerance : shapeEditScope.Document.Application.VertexTolerance; int lastVertex = 0; IList <XYZ> vertList = new List <XYZ>(); outputVerticesList = vertList; for (int ii = 1; ii <= inputVerticesList.Count; ii++) { int currIdx = (ii % inputVerticesList.Count); double dist = inputVerticesList[lastVertex].DistanceTo(inputVerticesList[currIdx]); if (dist >= shortSegmentTolerance) { vertList.Add(inputVerticesList[lastVertex]); lastVertex = currIdx; } else { string distAsString = IFCUnitUtil.FormatLengthAsString(dist); string shortDistAsString = IFCUnitUtil.FormatLengthAsString(shortSegmentTolerance); string warningString = "Distance between vertices " + lastVertex + " and " + currIdx + " is " + distAsString + ", which is less than the minimum " + (shapeEditScope.TryToCreateSolid() ? "Solid" : "Mesh") + " distance of " + shortDistAsString + ", removing second point."; Importer.TheLog.LogComment(entityId, warningString, false); } } }
override protected void Process(IFCAnyHandle solid) { base.Process(solid); // We will not fail if the direction is not given, but instead assume it to be normal to the swept area. IFCAnyHandle direction = IFCImportHandleUtil.GetRequiredInstanceAttribute(solid, "ExtrudedDirection", false); if (direction != null) { Direction = IFCPoint.ProcessNormalizedIFCDirection(direction); } else { Direction = XYZ.BasisZ; } bool found = false; Depth = IFCImportHandleUtil.GetRequiredScaledLengthAttribute(solid, "Depth", out found); if (!found || MathUtil.IsAlmostZero(Depth)) { string depthAsString = IFCUnitUtil.FormatLengthAsString(Depth); Importer.TheLog.LogError(solid.StepId, "extrusion depth of " + depthAsString + " is invalid, aborting.", true); } if (Depth < 0.0) { // Reverse depth and orientation. Depth = -Depth; Direction = -Direction; Importer.TheLog.LogWarning(solid.StepId, "negative extrusion depth is invalid, reversing direction.", false); } }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; double radius = IFCImportHandleUtil.GetRequiredScaledLengthAttribute(ifcCurve, "Radius", out found); if (!found) { Importer.TheLog.LogError(ifcCurve.StepId, "Cannot find the radius of this circle", false); return; } try { SetCurve(Arc.Create(Position.Origin, radius, 0, 2.0 * Math.PI, Position.BasisX, Position.BasisY)); } catch (Exception ex) { if (ex.Message.Contains("too small")) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(radius); Importer.TheLog.LogError(Id, "Found a circle with radius of " + lengthAsString + ", ignoring.", false); } else { Importer.TheLog.LogError(Id, ex.Message, false); } SetCurve(null); } }
/// <summary> /// Return the materials' names and thicknesses if the object is created with IFCMaterialLayerSetUsage information. /// The thickness is returned as a string followed by its unit /// If the object is not created with IFCMaterialLayerSetUsage information, then only the materials' names are returned /// </summary> /// <returns>A list in which each entry is the material's names followed by their thicknesses if the thicknesses are available</returns> public IList <string> GetMaterialsNamesAndThicknesses() { IList <string> result = new List <string>(); string thickness = null; string name = null; // If this object is created with IFCMaterialLayerSetUsage information // then the material layer thickness will be added after the name of each layer. if (MaterialSelect is IFCMaterialLayerSetUsage) { IFCMaterialLayerSet materialLayerSet = (MaterialSelect as IFCMaterialLayerSetUsage).MaterialLayerSet; IList <IFCMaterialLayer> materialLayers; IFCMaterial material; if (materialLayerSet != null) { materialLayers = materialLayerSet.MaterialLayers; } else { materialLayers = new List <IFCMaterialLayer>(); } foreach (IFCMaterialLayer materialLayer in materialLayers) { if (materialLayer == null) { continue; } material = materialLayer.Material; if (material == null || string.IsNullOrWhiteSpace(material.Name)) { continue; } name = material.Name; thickness = IFCUnitUtil.FormatLengthAsString(materialLayer.LayerThickness); result.Add(name + ": " + thickness); } } else { IList <IFCMaterial> materials = GetMaterials(); foreach (IFCMaterial material in materials) { name = material.Name; if (string.IsNullOrWhiteSpace(name)) { continue; } result.Add(name); } } return(result); }
/// <summary> /// Check for any occurence where distance of two vertices are too narrow (within the tolerance) /// </summary> /// <param name="entityId">The integer number representing the current IFC entity Id</param> /// <param name="shapeEditScope">the shapeEditScope</param> /// <param name="inputVerticesList">Input list of the vertices</param> /// <param name="outputVerticesList">Output List of the valid vertices, i.e. not vertices that are too close to each other</param> /// <returns></returns> public static void CheckAnyDistanceVerticesWithinTolerance(int entityId, IFCImportShapeEditScope shapeEditScope, IList <XYZ> inputVerticesList, out IList <XYZ> outputVerticesList) { // Check triangle that is too narrow (2 vertices are within the tolerance) double shortSegmentTolerance = shapeEditScope.TryToCreateSolid() ? shapeEditScope.Document.Application.ShortCurveTolerance : shapeEditScope.Document.Application.VertexTolerance; int lastVertex = 0; IList <XYZ> vertList = new List <XYZ>(); outputVerticesList = vertList; for (int ii = 1; ii <= inputVerticesList.Count; ii++) { int currIdx = (ii % inputVerticesList.Count); double dist = inputVerticesList[lastVertex].DistanceTo(inputVerticesList[currIdx]); if (dist >= shortSegmentTolerance) { vertList.Add(inputVerticesList[lastVertex]); lastVertex = currIdx; } else if (Importer.TheOptions.VerboseLogging) { // Because of the way garbage collection works with the API, calling FormatLengthAsString too often // (i.e. millions of times) can cause IFC import to run out of memory. As such, we limit the // calls to VerboseLogging only, which is used for debugging. string distAsString = IFCUnitUtil.FormatLengthAsString(dist); string shortDistAsString = IFCUnitUtil.FormatLengthAsString(shortSegmentTolerance); string warningString = "Distance between vertices " + lastVertex + " and " + currIdx + " is " + distAsString + ", which is less than the minimum " + (shapeEditScope.TryToCreateSolid() ? "Solid" : "Mesh") + " distance of " + shortDistAsString + ", removing second point."; Importer.TheLog.LogComment(entityId, warningString, false); } } }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; bool sameSense = IFCImportHandleUtil.GetRequiredBooleanAttribute(ifcCurve, "SenseAgreement", out found); if (!found) { sameSense = true; } IFCAnyHandle basisCurve = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "BasisCurve", true); IFCCurve ifcBasisCurve = IFCCurve.ProcessIFCCurve(basisCurve); if (ifcBasisCurve == null || (ifcBasisCurve.IsEmpty())) { // LOG: ERROR: Error processing BasisCurve # for IfcTrimmedCurve #. return; } if (ifcBasisCurve.Curve == null) { // LOG: ERROR: Expected a single curve, not a curve loop for BasisCurve # for IfcTrimmedCurve #. return; } IFCData trim1 = ifcCurve.GetAttribute("Trim1"); if (trim1.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCData trim2 = ifcCurve.GetAttribute("Trim2"); if (trim2.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } // Note that these are the "unprocessed" values. These can be used for, e.g., adding up the IFC parameter length // of the file, to account for export errors. The "processed" values can be determined from the Revit curves. Trim1 = GetRawTrimParameter(trim1); Trim2 = GetRawTrimParameter(trim2); IFCTrimmingPreference trimPreference = IFCEnums.GetSafeEnumerationAttribute <IFCTrimmingPreference>(ifcCurve, "MasterRepresentation", IFCTrimmingPreference.Parameter); double param1 = 0.0, param2 = 0.0; Curve baseCurve = ifcBasisCurve.Curve; try { GetTrimParameters(ifcBasisCurve.Id, trim1, trim2, baseCurve, trimPreference, out param1, out param2); if (NeedToReverseBaseCurve(baseCurve, param1, param2, trimPreference)) { Importer.TheLog.LogWarning(Id, "Invalid Param1 > Param2 for non-cyclic IfcTrimmedCurve using Cartesian trimming preference, reversing.", false); baseCurve = baseCurve.CreateReversed(); GetTrimParameters(ifcBasisCurve.Id, trim1, trim2, baseCurve, trimPreference, out param1, out param2); } } catch (Exception ex) { Importer.TheLog.LogError(ifcCurve.StepId, ex.Message, false); return; } if (MathUtil.IsAlmostEqual(param1, param2)) { Importer.TheLog.LogError(Id, "Param1 = Param2 for IfcTrimmedCurve #, ignoring.", false); return; } Curve curve = null; if (baseCurve.IsCyclic) { double period = baseCurve.Period; if (!sameSense) { MathUtil.Swap(ref param1, ref param2); } // We want to make sure both values are within period of one another. param1 = MathUtil.PutInRange(param1, 0, period); param2 = MathUtil.PutInRange(param2, 0, period); if (param2 < param1) { param2 = MathUtil.PutInRange(param2, param1 + period / 2, period); } // This is effectively an unbound curve. double numberOfPeriods = (param2 - param1) / period; if (MathUtil.IsAlmostEqual(numberOfPeriods, Math.Round(numberOfPeriods))) { Importer.TheLog.LogWarning(Id, "Start and end parameters indicate a zero-length closed curve, assuming unbound is intended.", false); curve = baseCurve; } else { curve = baseCurve.Clone(); if (!SafelyBoundCurve(curve, param1, param2)) { return; } } } else { if (param1 > param2 - MathUtil.Eps()) { Importer.TheLog.LogWarning(Id, "Param1 > Param2 for IfcTrimmedCurve #, reversing.", false); MathUtil.Swap(ref param1, ref param2); sameSense = !sameSense; } Curve copyCurve = baseCurve.Clone(); double length = param2 - param1; if (length <= IFCImportFile.TheFile.ShortCurveTolerance) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(length); Importer.TheLog.LogError(Id, "curve length of " + lengthAsString + " is invalid, ignoring.", false); return; } if (!SafelyBoundCurve(copyCurve, param1, param2)) { return; } if (sameSense) { curve = copyCurve; } else { curve = copyCurve.CreateReversed(); } } CurveLoop curveLoop = new CurveLoop(); curveLoop.Append(curve); SetCurveLoop(curveLoop); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; bool sameSense = IFCImportHandleUtil.GetRequiredBooleanAttribute(ifcCurve, "SenseAgreement", out found); if (!found) { sameSense = true; } IFCAnyHandle basisCurve = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "BasisCurve", true); IFCCurve ifcBasisCurve = IFCCurve.ProcessIFCCurve(basisCurve); if (ifcBasisCurve == null || (ifcBasisCurve.Curve == null && ifcBasisCurve.CurveLoop == null)) { // LOG: ERROR: Error processing BasisCurve # for IfcTrimmedCurve #. return; } if (ifcBasisCurve.Curve == null) { // LOG: ERROR: Expected a single curve, not a curve loop for BasisCurve # for IfcTrimmedCurve #. return; } IFCData trim1 = ifcCurve.GetAttribute("Trim1"); if (trim1.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCData trim2 = ifcCurve.GetAttribute("Trim2"); if (trim2.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCTrimmingPreference trimPreference = IFCEnums.GetSafeEnumerationAttribute <IFCTrimmingPreference>(ifcCurve, "MasterRepresentation", IFCTrimmingPreference.Parameter); double param1 = 0.0, param2 = 0.0; try { GetTrimParameters(trim1, trim2, ifcBasisCurve, trimPreference, out param1, out param2); } catch (Exception ex) { Importer.TheLog.LogError(ifcCurve.StepId, ex.Message, false); return; } Curve baseCurve = ifcBasisCurve.Curve; if (baseCurve.IsCyclic) { if (!sameSense) { MathUtil.Swap(ref param1, ref param2); } if (param2 < param1) { param2 = MathUtil.PutInRange(param2, param1 + Math.PI, 2 * Math.PI); } if (param2 - param1 > 2.0 * Math.PI - MathUtil.Eps()) { Importer.TheLog.LogWarning(ifcCurve.StepId, "IfcTrimmedCurve length is greater than 2*PI, leaving unbound.", false); Curve = baseCurve; return; } Curve = baseCurve.Clone(); try { Curve.MakeBound(param1, param2); } catch (Exception ex) { if (ex.Message.Contains("too small")) { Curve = null; Importer.TheLog.LogError(Id, "curve length is invalid, ignoring.", false); return; } else { throw ex; } } } else { if (MathUtil.IsAlmostEqual(param1, param2)) { Importer.TheLog.LogError(Id, "Param1 = Param2 for IfcTrimmedCurve #, ignoring.", false); return; } if (param1 > param2 - MathUtil.Eps()) { Importer.TheLog.LogWarning(Id, "Param1 > Param2 for IfcTrimmedCurve #, reversing.", false); MathUtil.Swap(ref param1, ref param2); return; } Curve copyCurve = baseCurve.Clone(); double length = param2 - param1; if (length <= IFCImportFile.TheFile.Document.Application.ShortCurveTolerance) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(length); Importer.TheLog.LogError(Id, "curve length of " + lengthAsString + " is invalid, ignoring.", false); return; } copyCurve.MakeBound(param1, param2); if (sameSense) { Curve = copyCurve; } else { Curve = copyCurve.CreateReversed(); } } CurveLoop = new CurveLoop(); CurveLoop.Append(Curve); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); // We are going to attempt minor repairs for small but reasonable gaps between Line/Line and Line/Arc pairs. As such, we want to collect the // curves before we create the curve loop. IList <IFCAnyHandle> segments = IFCAnyHandleUtil.GetValidAggregateInstanceAttribute <List <IFCAnyHandle> >(ifcCurve, "Segments"); if (segments == null) { Importer.TheLog.LogError(Id, "Invalid IfcCompositeCurve with no segments.", true); } double shortCurveTol = IFCImportFile.TheFile.Document.Application.ShortCurveTolerance; // need List<> so that we can AddRange later. List <Curve> curveSegments = new List <Curve>(); Segments.Clear(); ShortGapRepairer shortGapRepairer = new ShortGapRepairer(shortCurveTol); foreach (IFCAnyHandle segment in segments) { IFCCurve currCurve = ProcessIFCCompositeCurveSegment(segment); if (currCurve != null) { Segments.Add(currCurve); IList <Curve> newCurves = currCurve.GetCurves(); if (newCurves != null && newCurves.Count != 0) { curveSegments.AddRange(newCurves); // If we had a gap, we weren't able to correct it before getting new curves. shortGapRepairer.ClearGapInformation(); } else { Line gapRepairLine = shortGapRepairer.AddToGap(currCurve.Id, currCurve.BackupCurveStartLocation, currCurve.BackupCurveEndLocation); if (gapRepairLine != null) { curveSegments.Add(gapRepairLine); } } } } int numSegments = curveSegments.Count; if (numSegments == 0) { Importer.TheLog.LogError(Id, "Invalid IfcCompositeCurve with no segments.", true); } try { // We are going to try to reverse or tweak segments as necessary to make the CurveLoop. // For each curve, it is acceptable if it can be appended to the end of the existing loop, or prepended to its start, // possibly after reversing the curve, and possibly with some tweaking. // NOTE: we do not do any checks yet to repair the endpoints of the curveloop to make them closed. // NOTE: this is not expected to be perfect with dirty data, but is expected to not change already valid data. // curveLoopStartPoint and curveLoopEndPoint will change over time as we add new curves to the start or end of the CurveLoop. XYZ curveLoopStartPoint = curveSegments[0].GetEndPoint(0); XYZ curveLoopEndPoint = curveSegments[0].GetEndPoint(1); double vertexEps = IFCImportFile.TheFile.Document.Application.VertexTolerance; // This is intended to be "relatively large". The idea here is that the user would rather have the information presented // than thrown away because of a gap that is architecturally insignificant. double gapVertexEps = Math.Max(vertexEps, 0.01); // 1/100th of a foot, or 3.048 mm. // canRepairFirst may change over time, as we may potentially add curves to the start of the CurveLoop. bool canRepairFirst = (curveSegments[0] is Line); for (int ii = 1; ii < numSegments; ii++) { XYZ nextStartPoint = curveSegments[ii].GetEndPoint(0); XYZ nextEndPoint = curveSegments[ii].GetEndPoint(1); // These will be set below. bool attachNextSegmentToEnd = false; bool reverseNextSegment = false; double minGap = 0.0; // Scoped to prevent distLoopEndPtToNextStartPt and others from being used later on. { // Find the minimum gap between the current curve segment and the existing curve loop. If it is too large, we will give up. double distLoopEndPtToNextStartPt = curveLoopEndPoint.DistanceTo(nextStartPoint); double distLoopEndPtToNextEndPt = curveLoopEndPoint.DistanceTo(nextEndPoint); double distLoopStartPtToNextEndPt = curveLoopStartPoint.DistanceTo(nextEndPoint); double distLoopStartPtToNextStartPt = curveLoopStartPoint.DistanceTo(nextStartPoint); // Determine the minimum gap between the two curves. If it is too large, we'll give up before trying anything. double minStartGap = Math.Min(distLoopStartPtToNextEndPt, distLoopStartPtToNextStartPt); double minEndGap = Math.Min(distLoopEndPtToNextStartPt, distLoopEndPtToNextEndPt); minGap = Math.Min(minStartGap, minEndGap); // If the minimum distance between the two curves is greater than gapVertexEps (which is the larger of our two tolerances), // we can't fix the issue. if (minGap > gapVertexEps) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(minGap); string maxGapAsString = IFCUnitUtil.FormatLengthAsString(gapVertexEps); throw new InvalidOperationException("IfcCompositeCurve contains a gap of " + lengthAsString + " that is greater than the maximum gap size of " + maxGapAsString + " and cannot be repaired."); } // We have a possibility to add the segment. What we do depends on the gap distance. // If the current curve loop's closest end to the next segment is its end (vs. start) point, set attachNextSegmentToEnd to true. attachNextSegmentToEnd = (MathUtil.IsAlmostEqual(distLoopEndPtToNextStartPt, minGap)) || (MathUtil.IsAlmostEqual(distLoopEndPtToNextEndPt, minGap)); // We need to reverse the next segment if: // 1. We are attaching the next segment to the end of the curve loop, and the next segment's closest end to the current curve loop is its end (vs. start) point. // 2. We are attaching the next segment to the start of the curve loop, and the next segment's closest end to the current curve loop is its start (vs. end) point. reverseNextSegment = (MathUtil.IsAlmostEqual(distLoopEndPtToNextEndPt, minGap)) || (MathUtil.IsAlmostEqual(distLoopStartPtToNextStartPt, minGap)); } if (reverseNextSegment) { curveSegments[ii] = curveSegments[ii].CreateReversed(); MathUtil.Swap <XYZ>(ref nextStartPoint, ref nextEndPoint); } // If minGap is less than vertexEps, we won't need to do any repairing - just fix the orientation if necessary. if (minGap < vertexEps) { if (attachNextSegmentToEnd) { // Update the curve loop end point to be the end point of the next segment after potentially being reversed. curveLoopEndPoint = nextEndPoint; } else { canRepairFirst = curveSegments[ii] is Line; curveLoopStartPoint = nextStartPoint; // Update the curve loop start point to be the start point of the next segment, now at the beginning of the loop, // after potentially being reversed. Curve tmpCurve = curveSegments[ii]; curveSegments.RemoveAt(ii); curveSegments.Insert(0, tmpCurve); } continue; } // The gap is too big for CurveLoop, but smaller than our maximum tolerance - we will try to fix the gap by extending // one of the line segments around the gap. If the gap is between two Arcs, we will try to introduce a short // segment between them, as long as the gap is larger than the short curve tolerance. bool canRepairNext = curveSegments[ii] is Line; bool createdRepairLine = false; if (attachNextSegmentToEnd) { // Update the curve loop end point to be the end point of the next segment after potentially being reversed. XYZ originalCurveLoopEndPoint = curveLoopEndPoint; curveLoopEndPoint = nextEndPoint; if (canRepairNext) { curveSegments[ii] = RepairLineAndReport(Id, originalCurveLoopEndPoint, curveLoopEndPoint, minGap); } else if (curveSegments[ii - 1] is Line) // = canRepairCurrent, only used here. { curveSegments[ii - 1] = RepairLineAndReport(Id, curveSegments[ii - 1].GetEndPoint(0), curveSegments[ii].GetEndPoint(0), minGap); } else { // Can't add a line to fix a gap that is smaller than the short curve tolerance. // This will result in mutliple curve loops. if (minGap < shortCurveTol + MathUtil.Eps()) { Importer.TheLog.LogError(Id, "IfcCompositeCurve contains a gap between two non-linear segments that is too short to be repaired by a connecting segment.", false); } else { try { Line repairLine = Line.CreateBound(originalCurveLoopEndPoint, curveSegments[ii].GetEndPoint(0)); curveSegments.Insert(ii, repairLine); ii++; // Skip the repair line as we've already "added" it and the non-linear segment to our growing loop. numSegments++; createdRepairLine = true; } catch { Importer.TheLog.LogError(Id, "IfcCompositeCurve contains a gap between two non-linear segments that can't be fixed.", false); } } } } else { XYZ originalCurveLoopStartPoint = curveLoopStartPoint; curveLoopStartPoint = nextStartPoint; if (canRepairNext) { curveSegments[ii] = RepairLineAndReport(Id, curveLoopStartPoint, originalCurveLoopStartPoint, minGap); } else if (canRepairFirst) { curveSegments[0] = RepairLineAndReport(Id, curveSegments[ii].GetEndPoint(1), curveSegments[0].GetEndPoint(1), minGap); } else { // Can't add a line to fix a gap that is smaller than the short curve tolerance. // In the future, we may fix this gap by intersecting the two curves and extending one of them. if (minGap < shortCurveTol + MathUtil.Eps()) { Importer.TheLog.LogError(Id, "IfcCompositeCurve contains a gap between two non-linear segments that is too short to be repaired by a connecting segment.", true); } Line repairLine = Line.CreateBound(curveSegments[ii].GetEndPoint(1), originalCurveLoopStartPoint); curveSegments.Insert(0, repairLine); ii++; // Skip the repair line as we've already "added" it and the non-linear curve to our growing loop. numSegments++; } // Either canRepairFirst was already true, or canRepairNext was true and we added it to the front of the loop, // or we added a short repair line to the front of the loop. In any of these cases, the front curve segement of the // loop is now a line segment. if (!canRepairFirst && !canRepairNext && !createdRepairLine) { Importer.TheLog.LogError(Id, "IfcCompositeCurve contains a gap between two non-linear segments that can't be fixed.", true); } canRepairFirst = true; // Move the curve to the front of the loop. Curve tmpCurve = curveSegments[ii]; curveSegments.RemoveAt(ii); curveSegments.Insert(0, tmpCurve); } } SetCurveLoops(curveSegments); } catch (Exception ex) { Importer.TheLog.LogError(Id, ex.Message, true); } }