/***************************************************/ public static bool IsClockwise(this PolyCurve curve, Point viewPoint, double tolerance = Tolerance.Distance) { Plane plane = curve.FitPlane(tolerance); Point projectedPoint = viewPoint.Project(plane); Vector vector = (projectedPoint - viewPoint).Normalise(); return(IsClockwise(curve, vector)); }
/***************************************************/ public static double Area(this PolyCurve curve) { if (curve.Curves.Count == 1 && curve.Curves[0] is Circle) { return((curve.Curves[0] as Circle).Area()); } if (!curve.IsClosed()) { return(0); } Plane p = curve.FitPlane(); if (p == null) { return(0.0); // points are collinear } Point sPt = curve.StartPoint(); double area = 0; foreach (ICurve c in curve.SubParts()) { if (c is NurbsCurve) { throw new NotImplementedException("Area of NurbsCurve is not imlemented yet so the area of this PolyCurve cannot be calculated"); } Point ePt = c.IEndPoint(); Vector prod = CrossProduct(sPt - p.Origin, ePt - p.Origin); area += prod * p.Normal * 0.5; if (c is Arc) { Arc arc = c as Arc; double radius = arc.Radius; double angle = arc.Angle(); double arcArea = (angle - Math.Sin(angle)) * radius * radius * 0.5; if (arc.CoordinateSystem.Z.DotProduct(p.Normal) > 0) { area += arcArea; } else { area -= arcArea; } } sPt = ePt.Clone(); } return(Math.Abs(area)); }
/***************************************************/ 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); }
/***************************************************/ 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); }
public static double Area(this PolyCurve curve, double tolerance = Tolerance.Distance) { if (curve == null) { BH.Engine.Reflection.Compute.RecordError("Cannot query area as the geometry is null."); return(double.NaN); } if (curve.Curves.Count == 1 && curve.Curves[0] is Circle) { return((curve.Curves[0] as Circle).Area(tolerance)); } if (!curve.IsClosed(tolerance)) { Reflection.Compute.RecordWarning("Cannot calculate area for an open curve."); return(0); } Plane p = curve.FitPlane(tolerance); if (p == null) { return(0.0); // points are collinear } Point sPt = curve.StartPoint(); double area = 0; foreach (ICurve c in curve.SubParts()) { if (c is NurbsCurve) { Reflection.Compute.RecordError("Area for NurbsuCurve is not implemented."); return(double.NaN); } Point ePt = c.IEndPoint(); Vector prod = CrossProduct(sPt - p.Origin, ePt - p.Origin); area += prod * p.Normal * 0.5; if (c is Arc) { Arc arc = c as Arc; double radius = arc.Radius; double angle = arc.Angle(); double arcArea = (angle - Math.Sin(angle)) * radius * radius * 0.5; if (arc.CoordinateSystem.Z.DotProduct(p.Normal) > 0) { area += arcArea; } else { area -= arcArea; } } sPt = ePt.DeepClone(); } return(Math.Abs(area)); }