예제 #1
0
        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));
        }
예제 #2
0
        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);
            }
        }
예제 #3
0
        protected override void Process(IFCAnyHandle ifcCurve)
        {
            base.Process(ifcCurve);
            IFCAnyHandle pnt = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "Pnt", false);

            if (pnt == null)
            {
                return;
            }

            IFCAnyHandle dir = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "Dir", false);

            if (dir == null)
            {
                return;
            }

            XYZ pntXYZ = IFCPoint.ProcessScaledLengthIFCCartesianPoint(pnt);
            XYZ dirXYZ = IFCUnitUtil.ScaleLength(IFCPoint.ProcessIFCVector(dir));

            ParametericScaling = dirXYZ.GetLength();
            if (MathUtil.IsAlmostZero(ParametericScaling))
            {
                Importer.TheLog.LogWarning(ifcCurve.StepId, "Line has zero length, ignoring.", false);
                return;
            }

            Curve = Line.CreateUnbound(pntXYZ, dirXYZ / ParametericScaling);
        }
예제 #4
0
        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);
            }
        }
        /// <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);
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Process the IfcCartesianPointList handle.
        /// </summary>
        /// <param name="item">The handle</param>
        protected override void Process(IFCAnyHandle item)
        {
            base.Process(item);

            CoordList = new List <XYZ>();

            IList <IList <double> > coordList = IFCImportHandleUtil.GetListOfListOfDoubleAttribute(item, "CoordList");

            if (coordList != null)
            {
                foreach (IList <double> coord in coordList)
                {
                    // TODO: we expect size to be 2 or 3.  Warn if not?
                    if (coord == null)
                    {
                        continue;
                    }

                    int size = coord.Count;
                    CoordList.Add(new XYZ(
                                      (size > 0 ? IFCUnitUtil.ScaleLength(coord[0]) : 0.0),
                                      (size > 1 ? IFCUnitUtil.ScaleLength(coord[1]) : 0.0),
                                      (size > 2 ? IFCUnitUtil.ScaleLength(coord[2]) : 0.0)));
                }
            }
        }
예제 #7
0
        protected override void Process(IFCAnyHandle ifcPoint)
        {
            base.Process(ifcPoint);

            XYZ unScaledPoint = IFCPoint.IFCPointToXYZ(ifcPoint);

            XYZPoint = IFCUnitUtil.ScaleLength(unScaledPoint);
        }
예제 #8
0
        private double?GetTrimParameter(IFCData trim, IFCCurve basisCurve, IFCTrimmingPreference trimPreference, bool secondAttempt)
        {
            bool preferParam = !(trimPreference == IFCTrimmingPreference.Cartesian);

            if (secondAttempt)
            {
                preferParam = !preferParam;
            }
            double vertexEps = MathUtil.VertexEps;

            IFCAggregate trimAggregate = trim.AsAggregate();

            foreach (IFCData trimParam in trimAggregate)
            {
                if (!preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Instance))
                {
                    IFCAnyHandle trimParamInstance = trimParam.AsInstance();
                    XYZ          trimParamPt       = IFCPoint.ProcessScaledLengthIFCCartesianPoint(trimParamInstance);
                    if (trimParamPt == null)
                    {
                        IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Invalid trim point for basis curve.", false);
                        continue;
                    }

                    try
                    {
                        IntersectionResult result = basisCurve.Curve.Project(trimParamPt);
                        if (result.Distance < vertexEps)
                        {
                            return(result.Parameter);
                        }

                        IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false);
                    }
                    catch
                    {
                        IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false);
                    }
                }
                else if (preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Double))
                {
                    double trimParamDouble = trimParam.AsDouble();
                    if (basisCurve.Curve.IsCyclic)
                    {
                        trimParamDouble = IFCUnitUtil.ScaleAngle(trimParamDouble);
                    }
                    return(trimParamDouble);
                }
            }

            // Try again with opposite preference.
            if (!secondAttempt)
            {
                return(GetTrimParameter(trim, basisCurve, trimPreference, true));
            }

            return(null);
        }
예제 #9
0
        /// <summary>
        /// Scale the vertex according to the Project unit
        /// </summary>
        /// <param name="vertex">the vertex</param>
        /// <returns></returns>
        private XYZ applyProjectUnitScaleVertex(XYZ vertex)
        {
            double x            = IFCUnitUtil.ScaleLength(vertex.X);
            double y            = IFCUnitUtil.ScaleLength(vertex.Y);
            double z            = IFCUnitUtil.ScaleLength(vertex.Z);
            XYZ    scaledVertex = new XYZ(x, y, z);

            return(scaledVertex);
        }
        /// <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);
        }
예제 #11
0
        protected override void Process(IFCAnyHandle ifcVertexPoint)
        {
            base.Process(ifcVertexPoint);

            IFCAnyHandle vertexGeometry         = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcVertexPoint, "VertexGeometry", true);
            XYZ          unScaledVertexGeometry = IFCPoint.ProcessIFCPoint(vertexGeometry);

            VertexGeometry = IFCUnitUtil.ScaleLength(unScaledVertexGeometry);
        }
예제 #12
0
        /// <summary>
        /// Get the XYZ corresponding to the IfcCartesianPoint, scaled by the length scale.
        /// </summary>
        /// <param name="point">The IfcCartesianPoint entity handle.</param>
        /// <returns>The scaled XYZ value.</returns>
        public static XYZ ProcessScaledLengthIFCCartesianPoint(IFCAnyHandle point)
        {
            XYZ xyz = ProcessIFCCartesianPoint(point);

            if (xyz != null)
            {
                xyz = IFCUnitUtil.ScaleLength(xyz);
            }
            return(xyz);
        }
예제 #13
0
        /// <summary>
        /// Get the XYZ values corresponding to a list of IfcCartesianPoints, scaled by the length scale.
        /// </summary>
        /// <param name="points">The IfcCartesianPoint entity handles.</param>
        /// <returns>The scaled XYZ values.</returns>
        public static IList <XYZ> ProcessScaledLengthIFCCartesianPoints(IList <IFCAnyHandle> points)
        {
            if (points == null)
            {
                return(null);
            }

            IList <XYZ> xyzs = new List <XYZ>();

            foreach (IFCAnyHandle point in points)
            {
                XYZ xyz = ProcessIFCCartesianPoint(point);
                if (xyz == null)
                {
                    continue; // TODO: WARN
                }
                xyzs.Add(xyz);
            }

            IFCUnitUtil.ScaleLengths(xyzs);
            return(xyzs);
        }
예제 #14
0
        /// <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);
                }
            }
        }
예제 #15
0
        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);
        }
예제 #16
0
        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);
        }
예제 #17
0
        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);
            }
        }