Exemplo n.º 1
0
        public static PolyCurve Offset(this PolyCurve curve, double offset, Vector normal = null, bool tangentExtensions = false, double tolerance = Tolerance.Distance)
        {
            if (curve == null || curve.Length() < tolerance)
            {
                return(null);
            }

            //if there are only Line segmensts switching to polyline method which is more reliable
            if (curve.Curves.All(x => x is Line))
            {
                Polyline polyline = ((Polyline)curve).Offset(offset, normal, tangentExtensions, tolerance);
                if (polyline == null)
                {
                    return(null);
                }

                return(new PolyCurve {
                    Curves = polyline.SubParts().Cast <ICurve>().ToList()
                });
            }

            List <ICurve> subParts = curve.SubParts();

            //Check if contains any circles, if so, handle them explicitly, and offset any potential leftovers by backcalling this method
            if (subParts.Any(x => x is Circle))
            {
                IEnumerable <Circle> circles            = subParts.Where(x => x is Circle).Cast <Circle>().Select(x => x.Offset(offset, normal, tangentExtensions, tolerance));
                PolyCurve            nonCirclePolyCurve = new PolyCurve {
                    Curves = curve.Curves.Where(x => !(x is Circle)).ToList()
                };
                if (nonCirclePolyCurve.Curves.Count != 0)
                {
                    nonCirclePolyCurve = nonCirclePolyCurve.Offset(offset, normal, tangentExtensions, tolerance);
                }

                nonCirclePolyCurve.Curves.AddRange(circles);
                return(nonCirclePolyCurve);
            }

            if (!curve.IsPlanar(tolerance))
            {
                BH.Engine.Reflection.Compute.RecordError("Offset works only on planar curves");
                return(null);
            }

            if (curve.IsSelfIntersecting(tolerance))
            {
                BH.Engine.Reflection.Compute.RecordError("Offset works only on non-self intersecting curves");
                return(null);
            }

            if (offset == 0)
            {
                return(curve);
            }

            bool isClosed = curve.IsClosed(tolerance);

            if (normal == null)
            {
                if (!isClosed)
                {
                    BH.Engine.Reflection.Compute.RecordError("Normal is missing. Normal vector is not needed only for closed curves");
                    return(null);
                }
                else
                {
                    normal = curve.Normal();
                }
            }

            if (offset > 0.05 * curve.Length())
            {
                return((curve.Offset(offset / 2, normal, tangentExtensions, tolerance))?.Offset(offset / 2, normal, tangentExtensions, tolerance));
            }

            PolyCurve result = new PolyCurve();

            Vector normalNormalised = normal.Normalise();

            //First - offseting each individual element
            List <ICurve> offsetCurves = new List <ICurve>();

            foreach (ICurve crv in subParts)
            {
                if (crv.IOffset(offset, normal, false, tolerance) != null)
                {
                    offsetCurves.Add(crv.IOffset(offset, normal, false, tolerance));
                }
            }

            int counter = 0;

            //removing curves that are on a wrong side of the main curve
            for (int i = 0; i < offsetCurves.Count; i++)
            {
                Point  sp        = offsetCurves[i].IStartPoint();
                Point  ep        = offsetCurves[i].IEndPoint();
                Point  mp        = offsetCurves[i].IPointAtParameter(0.5);
                Point  spOnCurve = curve.ClosestPoint(sp);
                Point  epOnCurve = curve.ClosestPoint(ep);
                Point  mpOnCurve = curve.ClosestPoint(mp);
                Vector sTan      = curve.TangentAtPoint(spOnCurve, tolerance);
                Vector eTan      = curve.TangentAtPoint(epOnCurve, tolerance);
                Vector mTan      = curve.TangentAtPoint(mpOnCurve, tolerance);
                Vector sCheck    = sp - spOnCurve;
                Vector eCheck    = ep - epOnCurve;
                Vector mCheck    = mp - mpOnCurve;
                Vector sCP       = sTan.CrossProduct(sCheck).Normalise();
                Vector eCP       = eTan.CrossProduct(eCheck).Normalise();
                Vector mCP       = mTan.CrossProduct(mCheck).Normalise();
                if (offset > 0)
                {
                    if (sCP.IsEqual(normalNormalised, tolerance) && eCP.IsEqual(normalNormalised, tolerance) && mCP.IsEqual(normalNormalised, tolerance))
                    {
                        offsetCurves.RemoveAt(i);
                        i--;
                        counter++;
                    }
                }
                else
                {
                    if (!sCP.IsEqual(normalNormalised, tolerance) && !eCP.IsEqual(normalNormalised, tolerance) && !mCP.IsEqual(normalNormalised, tolerance))
                    {
                        offsetCurves.RemoveAt(i);
                        i--;
                        counter++;
                    }
                }
            }

            //Again if there are only Line segments switching to polyline method as it is more reliable
            if (offsetCurves.All(x => x is Line))
            {
                Polyline polyline = new Polyline {
                    ControlPoints = curve.DiscontinuityPoints()
                };
                result.Curves.AddRange(polyline.Offset(offset, normal, tangentExtensions, tolerance).SubParts());
                return(result);
            }

            bool connectingError = false;

            //Filleting offset curves to create continuous curve
            for (int i = 0; i < offsetCurves.Count; i++)
            {
                int j;
                if (i == offsetCurves.Count - 1)
                {
                    if (isClosed)
                    {
                        j = 0;
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    j = i + 1;
                }

                PolyCurve temp = offsetCurves[i].Fillet(offsetCurves[j], tangentExtensions, true, false, tolerance);
                if (temp == null) //trying to fillet with next curve
                {
                    offsetCurves.RemoveAt(j);

                    if (j == 0)
                    {
                        i--;
                    }

                    if (j == offsetCurves.Count)
                    {
                        j = 0;
                    }
                    temp = offsetCurves[i].Fillet(offsetCurves[j], tangentExtensions, true, false, tolerance);
                }

                if (!(temp == null)) //inserting filetted curves
                {
                    if (j != 0)
                    {
                        offsetCurves.RemoveRange(i, 2);
                        offsetCurves.InsertRange(i, temp.Curves);
                    }
                    else
                    {
                        offsetCurves.RemoveAt(i);
                        offsetCurves.RemoveAt(0);
                        offsetCurves.InsertRange(i - 1, temp.Curves);
                    }
                    i = i + temp.Curves.Count - 2;
                }
                else
                {
                    connectingError = true;
                }
            }

            //removing curves that are to close to the main curve
            for (int i = 0; i < offsetCurves.Count; i++)
            {
                if ((offsetCurves[i].IPointAtParameter(0.5).Distance(curve) + tolerance < Math.Abs(offset) &&
                     (offsetCurves[i].IStartPoint().Distance(curve) + tolerance < Math.Abs(offset) ||
                      offsetCurves[i].IEndPoint().Distance(curve) + tolerance < Math.Abs(offset))))
                {
                    PolyCurve temp = offsetCurves[((i - 1) + offsetCurves.Count) % offsetCurves.Count].Fillet(offsetCurves[(i + 1) % offsetCurves.Count], tangentExtensions, true, false, tolerance);
                    if (temp != null)
                    {
                        if (i == 0)
                        {
                            offsetCurves.RemoveRange(0, 2);
                            offsetCurves.RemoveAt(offsetCurves.Count - 1);
                            offsetCurves.InsertRange(0, temp.Curves);
                            i = temp.Curves.Count - 1;
                        }
                        else if (i == offsetCurves.Count - 1)
                        {
                            offsetCurves.RemoveRange(i - 1, 2);
                            offsetCurves.RemoveAt(0);
                            offsetCurves.InsertRange(offsetCurves.Count - 1, temp.Curves);
                            i = offsetCurves.Count - 1;
                        }
                        else
                        {
                            offsetCurves.RemoveRange(i - 1, 3);
                            offsetCurves.InsertRange(i - 1, temp.Curves);
                            i = i - 3 + temp.Curves.Count;
                        }
                    }

                    if (offsetCurves.Count < 1)
                    {
                        Reflection.Compute.ClearCurrentEvents();
                        Reflection.Compute.RecordError("Method failed to produce correct offset. Returning null.");
                        return(null);
                    }
                    counter++;
                }
            }

            Reflection.Compute.ClearCurrentEvents();

            if (connectingError)
            {
                Reflection.Compute.RecordWarning("Couldn't connect offset subCurves properly.");
            }

            if (offsetCurves.Count == 0)
            {
                Reflection.Compute.RecordError("Method failed to produce correct offset. Returning null.");
                return(null);
            }

            List <PolyCurve> resultList = Compute.IJoin(offsetCurves, tolerance);

            if (resultList.Count == 1)
            {
                result = resultList[0];
            }
            else
            {
                result.Curves = offsetCurves;
                Reflection.Compute.RecordWarning("Offset may be wrong. Please inspect the results.");
            }

            if (counter > 0)
            {
                Reflection.Compute.RecordWarning("Reduced " + counter + " line(s). Please inspect the results.");
            }

            if (result.IsSelfIntersecting(tolerance) || result.CurveIntersections(curve, tolerance).Count != 0)
            {
                Reflection.Compute.RecordWarning("Intersections occured. Please inspect the results.");
            }

            if (isClosed && !result.IsClosed(tolerance))
            {
                Reflection.Compute.RecordError("Final curve is not closed. Please inspect the results.");
            }

            return(result);
        }
Exemplo n.º 2
0
        /***************************************************/

        public static double Distance(this Point point, PolyCurve curve)
        {
            return(point.Distance(curve.ClosestPoint(point)));
        }
Exemplo n.º 3
0
        /***************************************************/


        public static List <Point> SortAlongCurve(this List <Point> points, PolyCurve curve, double tolerance = Tolerance.Distance, double angleTolerance = Tolerance.Angle)
        {
            List <Tuple <Point, double> > cData = points.Select(p => new Tuple <Point, double>(p.Clone(), curve.ParameterAtPoint(curve.ClosestPoint(p)))).ToList();

            cData.Sort(delegate(Tuple <Point, double> d1, Tuple <Point, double> d2)
            {
                return(d1.Item2.CompareTo(d2.Item2));
            });

            return(cData.Select(d => d.Item1).ToList());
        }
Exemplo n.º 4
0
        /***************************************************/

        public static bool IsContaining(this PolyCurve curve, List <Point> points, bool acceptOnEdge = true, double tolerance = Tolerance.Distance)
        {
            // Todo:
            // - to be replaced with a general method for a nurbs curve?
            // - this is very problematic for edge cases (cutting line going through a sharp corner, to be superseded?

            BoundingBox box = curve.Bounds();

            if (points.Any(x => !box.IsContaining(x, true, tolerance)))
            {
                return(false);
            }

            if (!curve.IsClosed(tolerance))
            {
                return(false);
            }

            if (curve.Curves.Count == 1 && curve.Curves[0] is Circle)
            {
                return(IsContaining(curve.Curves[0] as Circle, points, acceptOnEdge, tolerance));
            }

            Plane  p     = curve.FitPlane(tolerance);
            double sqTol = tolerance * tolerance;

            if (p == null)
            {
                if (acceptOnEdge)
                {
                    foreach (Point pt in points)
                    {
                        if (curve.ClosestPoint(pt).SquareDistance(pt) > sqTol)
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
                else
                {
                    return(false);
                }
            }

            List <ICurve> subParts       = curve.SubParts();
            List <Vector> edgeDirections = subParts.Where(s => s is Line).Select(c => (c as Line).Direction()).ToList();

            foreach (Point pt in points)
            {
                Point pPt = pt.Project(p);
                if (pPt.SquareDistance(pt) > sqTol) // not on the same plane
                {
                    return(false);
                }

                Point  end       = p.Origin;                                                                                                 // Avrage of control points
                Vector direction = (end - pPt).Normalise();                                                                                  // Gets a line cutting through the curves and the point
                while (direction.SquareLength() <= 0.5 || edgeDirections.Any(e => 1 - Math.Abs(e.DotProduct(direction)) <= Tolerance.Angle)) // not zeroa or parallel to edges
                {
                    end       = end.Translate(Create.RandomVectorInPlane(p, true));
                    direction = (end - pPt).Normalise();
                }

                Line ray = new Line {
                    Start = pPt, End = end
                };
                ray.Infinite = true;
                List <Point> intersects      = new List <Point>();
                List <Point> extraIntersects = new List <Point>();

                foreach (ICurve subPart in subParts)
                {
                    List <Point> iPts = subPart.ILineIntersections(ray, false, tolerance);   // LineIntersection ignores the `false`
                    foreach (Point iPt in iPts)
                    {
                        double signedAngle = direction.SignedAngle(subPart.ITangentAtPoint(iPt, tolerance), p.Normal);
                        if ((subPart.IStartPoint().SquareDistance(iPt) <= sqTol)) // Keep intersections from beeing counted twice?
                        {
                            if (signedAngle >= -Tolerance.Angle)                  // tangent is to the left of the direction
                            {
                                intersects.Add(iPt);
                            }
                            else
                            {
                                extraIntersects.Add(iPt);
                            }
                        }
                        else if ((subPart.IEndPoint().SquareDistance(iPt) <= sqTol))
                        {
                            if (signedAngle <= Tolerance.Angle)     // tangent is to the rigth of the direction
                            {
                                intersects.Add(iPt);
                            }
                            else
                            {
                                extraIntersects.Add(iPt);
                            }
                        }
                        else if (Math.Abs(signedAngle) <= Tolerance.Angle)  // They are parallel
                        {
                            extraIntersects.Add(iPt);
                        }
                        else
                        {
                            intersects.Add(iPt);
                        }
                    }
                }

                if (intersects.Count == 0)  // did not intersect the curve (strange)
                {
                    return(false);
                }

                if ((pPt.ClosestPoint(intersects.Union(extraIntersects)).SquareDistance(pPt) <= sqTol)) // if any intersection point is the point
                {
                    if (acceptOnEdge)
                    {
                        continue;
                    }
                    else
                    {
                        return(false);
                    }
                }

                intersects.Add(pPt);
                intersects = intersects.SortCollinear(tolerance);
                for (int j = 0; j < intersects.Count; j++)  // Even indecies on a colinerar sort is outside the region
                {
                    if (j % 2 == 0 && intersects[j] == pPt)
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
Exemplo n.º 5
0
        /***************************************************/

        public static bool IsContaining(this PolyCurve curve, List <Point> points, bool acceptOnEdge = true, double tolerance = Tolerance.Distance)
        {
            // Todo:
            // - to be replaced with a general method for a nurbs curve?
            // - this is very problematic for edge cases (cutting line going through a sharp corner, to be superseded?

            if (curve.IsClosed(tolerance))
            {
                Plane  p     = curve.FitPlane(tolerance);
                double sqTol = tolerance * tolerance;

                if (p == null)
                {
                    if (acceptOnEdge)
                    {
                        foreach (Point pt in points)
                        {
                            if (curve.ClosestPoint(pt).SquareDistance(pt) > sqTol)
                            {
                                return(false);
                            }
                        }
                        return(true);
                    }
                    else
                    {
                        return(false);
                    }
                }
                else
                {
                    List <ICurve> subParts       = curve.SubParts();
                    List <Vector> edgeDirections = subParts.Where(s => s is Line).Select(c => (c as Line).Direction()).ToList();
                    foreach (Point pt in points)
                    {
                        Point pPt = pt.Project(p);
                        if (pPt.SquareDistance(pt) <= sqTol)
                        {
                            Point  end       = p.Origin;
                            Vector direction = (end - pPt).Normalise();
                            while (direction.SquareLength() <= sqTol || edgeDirections.Any(e => 1 - Math.Abs(e.DotProduct(direction)) <= Tolerance.Angle))
                            {
                                end       = end.Translate(Create.RandomVectorInPlane(p, true));
                                direction = (end - pPt).Normalise();
                            }

                            Line ray = new Line {
                                Start = pPt, End = end
                            };
                            ray.Infinite = true;
                            List <Point> intersects      = new List <Point>();
                            List <Point> extraIntersects = new List <Point>();

                            foreach (ICurve subPart in subParts)
                            {
                                List <Point> iPts = subPart.ILineIntersections(ray, false, tolerance);
                                foreach (Point iPt in iPts)
                                {
                                    double signedAngle = direction.SignedAngle(subPart.ITangentAtPoint(iPt, tolerance), p.Normal);
                                    if ((subPart.IStartPoint().SquareDistance(iPt) <= sqTol))
                                    {
                                        if (signedAngle >= -Tolerance.Angle)
                                        {
                                            intersects.Add(iPt);
                                        }
                                        else
                                        {
                                            extraIntersects.Add(iPt);
                                        }
                                    }
                                    else if ((subPart.IEndPoint().SquareDistance(iPt) <= sqTol))
                                    {
                                        if (signedAngle <= Tolerance.Angle)
                                        {
                                            intersects.Add(iPt);
                                        }
                                        else
                                        {
                                            extraIntersects.Add(iPt);
                                        }
                                    }
                                    else if (Math.Abs(signedAngle) <= Tolerance.Angle)
                                    {
                                        extraIntersects.Add(iPt);
                                    }
                                    else
                                    {
                                        intersects.Add(iPt);
                                    }
                                }
                            }

                            if (intersects.Count == 0)
                            {
                                return(false);
                            }

                            if ((pPt.ClosestPoint(intersects.Union(extraIntersects)).SquareDistance(pPt) <= sqTol))
                            {
                                if (acceptOnEdge)
                                {
                                    continue;
                                }
                                else
                                {
                                    return(false);
                                }
                            }

                            intersects.Add(pPt);
                            intersects = intersects.SortCollinear(tolerance);
                            for (int j = 0; j < intersects.Count; j++)
                            {
                                if (j % 2 == 0 && intersects[j] == pPt)
                                {
                                    return(false);
                                }
                            }
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    return(true);
                }
            }
            return(false);
        }