} //end doClip //TODO: test this private void IntegrateIntersections(ref List <DeepPoint> list) { for (int i = 0; i < list.Count; i++) { DeepPoint normal = list[i]; for (int j = 0; j < normal.intersections.Count; j++) { DeepPoint intersection = normal.intersections[j]; list.Insert(i + 1, intersection); i++; } } }
//TODO: test private void BuildIntersectionMap(ref Dictionary <PointF, int> fromMap, List <DeepPoint> from, ref Dictionary <PointF, int> toMap, List <DeepPoint> to) { for (int i = 0; i < from.Count; i++) { DeepPoint point = from[i]; if (point.type == DeepPoint.PointType.Intersection) { //we can do both at once since we should have the same number of intersections for (int j = 0; j < to.Count; j++) { if (to[j].p == point.p) { fromMap.Add(point.p, j); } } toMap.Add(point.p, i); } } }
private void doClip(PointF[] clip, PointF[] shape) { List <DeepPoint> deepShape = new List <DeepPoint>(Array.ConvertAll(shape, p => new DeepPoint(p, DeepPoint.PointType.Normal, p.InOrOut(clip)))); List <DeepPoint> deepClip = new List <DeepPoint>(Array.ConvertAll(clip, p => new DeepPoint(p, DeepPoint.PointType.Normal, p.InOrOut(shape)))); for (int i = 0; i < deepShape.Count; i++) { DeepPoint p1 = deepShape[i]; DeepPoint p2 = deepShape.NextAfter(i); //check for intersections for (int j = 0; j < deepClip.Count; j++) { DeepPoint c1 = deepClip[j]; DeepPoint c2 = deepClip.NextAfter(j); List <PointF> interOutput; if (Line.Intersection(p1, p2, c1, c2, out interOutput)) { foreach (PointF inter in interOutput) { //This ensures that we have the same intersection added to both (avoid precision errors) DeepPoint intersection = new DeepPoint(inter, DeepPoint.PointType.Intersection, DeepPoint.PointStatus.Undetermined); if (inter == p1.p || inter == p2.p || inter == c1.p || inter == c2.p) { if (inter == p1.p && inter == c1.p) { p1.overlap = true; c1.overlap = true; p1.type = DeepPoint.PointType.Intersection; c1.type = DeepPoint.PointType.Intersection; continue; } else if (inter != p2.p && inter != c1.p && inter != c2.p) { if (inter == p1.p) { p1.overlap = true; p1.type = DeepPoint.PointType.Intersection; } else { p1.intersections.Add(intersection); } c1.intersections.Add(intersection); } } else { //TODO: if intersection is same as p1,p2,c1,c2 -> make a note of it? p1.intersections.Add(intersection); c1.intersections.Add(intersection); } } } } //sort intersections by distance to p1 p1.SortIntersections(); //loop through intersections between p1 and p2 for (int j = 0; j < p1.intersections.Count; j++) { DeepPoint intersection = p1.intersections[j]; //if there's a previous intersection if (j > 0) { DeepPoint prev = p1.intersections[j - 1]; if (prev.status == DeepPoint.PointStatus.In) { intersection.status = DeepPoint.PointStatus.Out; //set inter pStatus to Out } else { intersection.status = DeepPoint.PointStatus.In; //set inter as In } } else if (p1.status == DeepPoint.PointStatus.In) { intersection.status = DeepPoint.PointStatus.Out; //set inter as Out } else { intersection.status = DeepPoint.PointStatus.In; //set inter as In } } } //sort all intersections in clip for (int i = 0; i < deepClip.Count; i++) { deepClip[i].SortIntersections(); } IntegrateIntersections(ref deepShape); IntegrateIntersections(ref deepClip); //Use these to jump from list to list Dictionary <PointF, int> shapeIntersectionToClipIndex = new Dictionary <PointF, int>(); Dictionary <PointF, int> clipIntersectionToShapeIndex = new Dictionary <PointF, int>(); BuildIntersectionMap(ref shapeIntersectionToClipIndex, deepShape, ref clipIntersectionToShapeIndex, deepClip); //start from entering points List <int> iEntering = new List <int>(); //Get entering intersections for (int i = 0; i < deepShape.Count; i++) { DeepPoint point = deepShape[i]; if (point.overlap || (point.type == DeepPoint.PointType.Intersection && point.status == DeepPoint.PointStatus.In)) { iEntering.Add(i); } } List <List <DeepPoint> > output = new List <List <DeepPoint> >(); List <DeepPoint> currentShape = new List <DeepPoint>(); bool allEnteringAreOverlap = true; foreach (int i in iEntering) { if (!deepShape[i].overlap) { allEnteringAreOverlap = false; break; } } bool hasNonOverlapIntersections = false; foreach (DeepPoint p in deepShape) { if (!p.overlap && p.type == DeepPoint.PointType.Intersection) { hasNonOverlapIntersections = true; break; } } //handle special cases if ((iEntering.Count == 0 || allEnteringAreOverlap) && !hasNonOverlapIntersections) { bool allInside = true; foreach (DeepPoint p in deepShape) { if (p.status != DeepPoint.PointStatus.In && !p.overlap) { allInside = false; break; } } if (allInside) { foreach (DeepPoint p in deepShape) { currentShape.Add(p); } } else { //check that deepClip are all inside allInside = true; foreach (DeepPoint p in deepClip) { if (p.status != DeepPoint.PointStatus.In && !p.overlap) { allInside = false; break; } } if (allInside) { foreach (DeepPoint p in deepClip) { currentShape.Add(p); } } else { return; } } output.Add(currentShape); pen.Width = 5; DrawLines(Array.ConvertAll(output[0].ToArray(), p => p.p), Color.Green); pen.Width = 2; return; } //TODO: add method to ignore entering points that were included in an output shape already //go through all of our entering points for (int mainCount = 0; mainCount < iEntering.Count; mainCount++) { int goToIndex = iEntering[mainCount]; bool complete = false; while (!complete) { //loop through all shape points starting at goToIndex for (int iCount = goToIndex; iCount < deepShape.Count + goToIndex; iCount++) { int i = iCount % deepShape.Count; DeepPoint p1 = deepShape[i]; DeepPoint p2 = deepShape.NextAfter(i); if (p1.overlap) { DeepPoint prev = deepShape.PrevBefore(i); if (prev.overlap || prev.status == DeepPoint.PointStatus.Out) { p1.tempStatus = DeepPoint.PointStatus.In; } else { p1.tempStatus = DeepPoint.PointStatus.Out; } } else { p1.tempStatus = p1.status; } if (p2.overlap) { if (p1.overlap || p1.status == DeepPoint.PointStatus.Out) { p2.tempStatus = DeepPoint.PointStatus.In; } else { p2.tempStatus = DeepPoint.PointStatus.Out; } } else { p2.tempStatus = p2.status; } if (p1.type == DeepPoint.PointType.Normal) { if (p1.tempStatus == DeepPoint.PointStatus.In) { //break when we get back to start if (currentShape.Count > 0 && currentShape[0].p == p1.p) { complete = true; break; } currentShape.Add(p1); //point2 must be heading outwards if (p2.type == DeepPoint.PointType.Intersection) { //go to clipPoints loop and start from intersection //goToIndex = shapeIntersectionToClipIndex[p2.p] + 1; //break; } } //we don't care about point2 here //if point1 is an outside normal point, // then point2 must either be an outside normal point OR an intersection going inwards. // The former doesn't not need to be handled, the latter will be handled upon looping } else //p1 is an intersection { //break when we get back to start if (currentShape.Count > 0 && currentShape[0].p == p1.p) { complete = true; break; } //we must add point 1 since it's on the border currentShape.Add(p1); //exiting if (p1.tempStatus == DeepPoint.PointStatus.Out) { //go to clipPoints loop and start from after intersection; goToIndex = (shapeIntersectionToClipIndex[p1.p] + 1) % deepClip.Count; break; } } } //end deepShape for //break while loop if complete if (complete) { break; } //loop through all clip points starting at goToIndex //we should only get here from a go to from shapePoints for (int iCount = goToIndex; iCount < deepClip.Count + goToIndex; iCount++) { int i = iCount % deepClip.Count; DeepPoint p1 = deepClip[i]; DeepPoint p2 = deepClip.NextAfter(i); if (p1.overlap) { DeepPoint prev = deepClip.PrevBefore(i); if (prev.overlap || prev.status == DeepPoint.PointStatus.Out) { p1.tempStatus = DeepPoint.PointStatus.In; } else { p1.tempStatus = DeepPoint.PointStatus.Out; } } else { p1.tempStatus = p1.status; } if (p2.overlap) { if (p1.overlap || p1.status == DeepPoint.PointStatus.Out) { p2.tempStatus = DeepPoint.PointStatus.In; } else { p2.tempStatus = DeepPoint.PointStatus.Out; } } else { p2.tempStatus = p2.status; } if (p1.type == DeepPoint.PointType.Intersection) { //break when we get back to start if (currentShape.Count > 0 && currentShape[0].p == p1.p) { complete = true; break; } //we must add point 1 since it's on the border currentShape.Add(p1); //if it was going inwards if (p1.tempStatus == DeepPoint.PointStatus.In) { //go to shapePoints loop and start from after point1 goToIndex = (clipIntersectionToShapeIndex[p1.p] + 1) % deepShape.Count; break; } } else //p1 is normal { if (p1.tempStatus == DeepPoint.PointStatus.In) { //break when we get back to start if (currentShape.Count > 0 && currentShape[0].p == p1.p) { complete = true; break; } //we must add point 1 since it's on the border currentShape.Add(p1); } } } //end deepClip for } //end while loop output.Add(currentShape); currentShape = new List <DeepPoint>(); }//end main for loop //remove duplicate points for (int iOutput = 0; iOutput < output.Count; iOutput++) { List <DeepPoint> points = output[iOutput]; for (int i = 0; i < points.Count; i++) { //remove duplicates if (points[i].p == points.NextAfter(i).p) { //remove current points.RemoveAt(i); i--; } } if (points.Count < 3) { output.Remove(points); iOutput--; } } pen.Width = 5; DrawLines(Array.ConvertAll(output[0].ToArray(), p => p.p), Color.Green); pen.Width = 2; } //end doClip
public static bool Intersection(DeepPoint start1, DeepPoint end1, DeepPoint start2, DeepPoint end2, out List <PointF> output) { output = new List <PointF>(); float det; float A1, B1, C1; float A2, B2, C2; //https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/#line_line_intersection A1 = end1.p.Y - start1.p.Y; B1 = start1.p.X - end1.p.X; C1 = A1 * start1.p.X + B1 * start1.p.Y; A2 = end2.p.Y - start2.p.Y; B2 = start2.p.X - end2.p.X; C2 = A2 * start2.p.X + B2 * start2.p.Y; det = A1 * B2 - A2 * B1; if (det == 0) { float mTop = end1.p.Y - start1.p.Y; float mBot = end1.p.X - start1.p.X; float b1 = end1.p.Y - (mTop * end1.p.X) / mBot; float b2 = end2.p.Y - (mTop * end2.p.X) / mBot; if (b1 != b2) { return(false); } bool overlap = false; //there will possibly be two points float cxMin = Math.Min(start2.p.X, end2.p.X); float cxMax = Math.Max(start2.p.X, end2.p.X); float cyMin = Math.Min(start2.p.Y, end2.p.Y); float cyMax = Math.Max(start2.p.Y, end2.p.Y); if (cxMin <= start1.p.X && start1.p.X <= cxMax && cyMin <= start1.p.Y && start1.p.Y <= cyMax) { //use start1 output.Add(start1.p); overlap = true; } else { //use start2 output.Add(start2.p); } if (cxMin <= end1.p.X && end1.p.X <= cxMax && cyMin <= end1.p.Y && end1.p.Y <= cyMax) { //use end1 output.Add(end1.p); overlap = true; } else { //use end2 output.Add(end2.p); } //lines are parallel and possibly overlap Console.WriteLine("Parallel Lines"); return(overlap); } PointF intersect = new PointF(B2 * C1 - B1 * C2, A1 * C2 - A2 * C1); //would normally be /det intersect.X /= det; intersect.Y /= det; //using multiplication instead since it's less likely to have precision errors float xMin = Math.Min(start2.p.X, end2.p.X); float xMax = Math.Max(start2.p.X, end2.p.X); float yMin = Math.Min(start2.p.Y, end2.p.Y); float yMax = Math.Max(start2.p.Y, end2.p.Y); float xMin2 = Math.Min(start1.p.X, end1.p.X); float xMax2 = Math.Max(start1.p.X, end1.p.X); float yMin2 = Math.Min(start1.p.Y, end1.p.Y); float yMax2 = Math.Max(start1.p.Y, end1.p.Y); if (xMin <= intersect.X && intersect.X <= xMax && yMin <= intersect.Y && intersect.Y <= yMax && xMin2 <= intersect.X && intersect.X <= xMax2 && yMin2 <= intersect.Y && intersect.Y <= yMax2) { output.Add(intersect); return(true); } else { return(false); } }
//Distance Squared public static float DistanceSquared(DeepPoint p1, DeepPoint p2) { return((p2.p.X - p1.p.X) * (p2.p.X - p1.p.X) + (p2.p.Y - p1.p.Y) * (p2.p.Y - p1.p.Y)); }
public static DeepPoint.PointStatus InOrOut(this PointF point, PointF[] shape) { //a point that we can guarantee is outside of our shape PointF outside = new PointF(float.MaxValue, float.MaxValue); //find the leftmost and topmost bounds for (int i = 0; i < shape.Length; i++) { PointF p = shape[i]; if (p.X < outside.X) { outside.X = p.X; } if (p.Y < outside.Y) { outside.Y = p.Y; } } outside.X -= 7; outside.Y -= 13; HashSet <PointF> intersections = new HashSet <PointF>(); int intersectionCount = 0; for (int i = 0; i < shape.Length; i++) { PointF c1 = shape[i]; PointF c2 = shape[(i + 1) % shape.Length]; //if our point is the same as one of the shape points, we consider it inside the shape //if(point == c1 || point == c2) return DeepPoint.PointStatus.In; Console.WriteLine("{0} == {1}", DeepPoint.Distance(point, c1) + DeepPoint.Distance(point, c2), DeepPoint.Distance(c1, c2)); if (DeepPoint.Distance(point, c1) + DeepPoint.Distance(point, c2) == DeepPoint.Distance(c1, c2)) { return(DeepPoint.PointStatus.In); //TODO: mark it as border } float det; float A1, B1, C1; float A2, B2, C2; //https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/#line_line_intersection A1 = c2.Y - c1.Y; B1 = c1.X - c2.X; C1 = A1 * c1.X + B1 * c1.Y; A2 = outside.Y - point.Y; B2 = point.X - outside.X; C2 = A2 * point.X + B2 * point.Y; det = A1 * B2 - A2 * B1; if (det == 0) { throw new Exception("PARALLEL"); } PointF intersect = new PointF(B2 * C1 - B1 * C2, A1 * C2 - A2 * C1); //would normally be /det intersect.X /= det; intersect.Y /= det; //using multiplication instead since it's less likely to have precision errors float xMin = Math.Min(c1.X, c2.X); float xMax = Math.Max(c1.X, c2.X); float yMin = Math.Min(c1.Y, c2.Y); float yMax = Math.Max(c1.Y, c2.Y); float xMin2 = Math.Min(point.X, outside.X); float xMax2 = Math.Max(point.X, outside.X); float yMin2 = Math.Min(point.Y, outside.Y); float yMax2 = Math.Max(point.Y, outside.Y); if (xMin <= intersect.X && intersect.X <= xMax && yMin <= intersect.Y && intersect.Y <= yMax && xMin2 <= intersect.X && intersect.X <= xMax2 && yMin2 <= intersect.Y && intersect.Y <= yMax2 && !intersections.Contains(intersect)) { intersectionCount++; intersections.Add(intersect); } } //covers 0 + evens if (intersectionCount % 2 == 0) { return(DeepPoint.PointStatus.Out); } else { return(DeepPoint.PointStatus.In); //odds > 0 } }