void ScanIntersect(ScanSegment hSeg) { // Find the VSeg in the scanline with the lowest X-intersection with HSeg, then iterate // all VSegs in the scan line after that until we leave the HSeg range. // We only use FindFirstHSeg in this routine, to find the first satisfying node, // so we don't care that we leave leftovers in it. findFirstHSeg = hSeg; RBNode <ScanSegment> segNode = verticalSegmentsScanLine.FindFirst(findFirstPred); for (; null != segNode; segNode = verticalSegmentsScanLine.Next(segNode)) { ScanSegment vSeg = segNode.Item; if (1 == PointComparer.Compare(vSeg.Start.X, hSeg.End.X)) { break; // Out of HSeg range } VisibilityVertex newVertex = visGraph.AddVertex(new Point(vSeg.Start.X, hSeg.Start.Y)); // HSeg has just opened so if we are overlapped and newVertex already existed, // it was because we just closed a previous HSeg or VSeg and are now opening one // whose Start is the same as previous. So we may be appending a vertex that // is already the *Seg.HighestVisibilityVertex, which will be a no-op. Otherwise // this will add a (possibly Overlapped)VisibilityEdge in the *Seg direction. hSeg.AppendVisibilityVertex(visGraph, newVertex); vSeg.AppendVisibilityVertex(visGraph, newVertex); } // endforeach scanline VSeg in range OnSegmentClose(hSeg); }
/// <summary> /// For ordering V segments in the scanline by X. /// </summary> /// <param name="first"></param> /// <param name="second"></param> /// <returns></returns> public int Compare(ScanSegment first, ScanSegment second) { if (first == second) { return(0); } if (first == null) { return(-1); } if (second == null) { return(1); } // Note: Unlike the ScanSegment-generating scanline, this scanline has no slope // calculations so no additional rounding error is introduced. int cmp = PointComparer.Compare(first.Start.X, second.Start.X); // Separate segments may join at Start and End due to overlap, so compare the Y positions; // the Close (lowest Y) comes before the Open. if (0 == cmp) { cmp = PointComparer.Compare(first.Start.Y, second.Start.Y); } return(cmp); }
static internal bool RectangleInteriorsIntersect(Rectangle a, Rectangle b) { return((PointComparer.Compare(a.Bottom, b.Top) < 0) && (PointComparer.Compare(b.Bottom, a.Top) < 0) && (PointComparer.Compare(a.Left, b.Right) < 0) && (PointComparer.Compare(b.Left, a.Right) < 0)); }
internal void MergeFrom(PointAndCrossingsList other) { Reset(); if ((null == other) || (0 == other.ListOfPointsAndCrossings.Count)) { return; } if (0 == this.ListOfPointsAndCrossings.Count) { this.ListOfPointsAndCrossings.AddRange(other.ListOfPointsAndCrossings); } if (null == this.ListOfPointsAndCrossings) { this.ListOfPointsAndCrossings = new List <PointAndCrossings>(other.ListOfPointsAndCrossings); return; } // Do the usual sorted-list merge. int thisIndex = 0, thisMax = this.ListOfPointsAndCrossings.Count; int otherIndex = 0, otherMax = other.ListOfPointsAndCrossings.Count; var newCrossingsList = new List <PointAndCrossings>(this.ListOfPointsAndCrossings.Count); while ((thisIndex < thisMax) || (otherIndex < otherMax)) { if (thisIndex >= thisMax) { newCrossingsList.Add(other.ListOfPointsAndCrossings[otherIndex++]); continue; } if (otherIndex >= otherMax) { newCrossingsList.Add(this.ListOfPointsAndCrossings[thisIndex++]); continue; } PointAndCrossings thisPac = this.ListOfPointsAndCrossings[thisIndex]; PointAndCrossings otherPac = other.ListOfPointsAndCrossings[otherIndex]; int cmp = PointComparer.Compare(thisPac.Location, otherPac.Location); if (0 == cmp) { // No duplicates newCrossingsList.Add(thisPac); ++thisIndex; ++otherIndex; } else if (-1 == cmp) { newCrossingsList.Add(thisPac); ++thisIndex; } else { newCrossingsList.Add(otherPac); ++otherIndex; } } this.ListOfPointsAndCrossings = newCrossingsList; }
static internal bool PointIsInRectangleInterior(Point point, Rectangle rect) { return((PointComparer.Compare(point.Y, rect.Top) < 0) && (PointComparer.Compare(rect.Bottom, point.Y) < 0) && (PointComparer.Compare(point.X, rect.Right) < 0) && (PointComparer.Compare(rect.Left, point.X) < 0)); }
/// <summary> /// Move along the linked list until we hit the ScanSegment that contains the point. /// </summary> internal bool TraverseToSegmentContainingPoint(Point point) { // This is not a simple Next() because scan segments are extended "through" obstacles // (intermixing overlapped and non-overlapped) and thus a ScanSegment's Start and End // may not be in the vertexPoints collection and the ScanSegment must be skipped. if (this.CurrentSegment.ContainsPoint(point)) { return(true); } var pointCoord = this.IsHorizontal ? point.Y : point.X; if (!PointComparer.Equal(this.Coord, pointCoord)) { Debug.Assert(PointComparer.Compare(this.Coord, pointCoord) == -1, "point is before current Coord"); while (this.MoveNext()) { // Skip to the end of the linked list if this point is not on the same coordinate. } return(false); } for (;;) { // In the event of mismatched rounding on horizontal versus vertical intersections // with a sloped obstacle side, we may have a point that is just before or just // after the current segment. If the point is in some space that doesn't have a // scansegment, and if we are "close enough" to one end or the other of a scansegment, // then grow the scansegment enough to include the new point. if ((null == this.CurrentSegment.NextSegment) || PointComparer.GetDirections(this.CurrentSegment.End, point) == PointComparer.GetDirections(point, this.CurrentSegment.NextSegment.Start)) { if (ApproximateComparer.CloseIntersections(this.CurrentSegment.End, point)) { this.CurrentSegment.Update(this.CurrentSegment.Start, point); return(true); } } if (!this.MoveNext()) { return(false); } if (this.CurrentSegment.ContainsPoint(point)) { return(true); } // This is likely the reverse of the above; the point rounding mismatched to just before // rather than just after the current segment. if (PointComparer.IsPureLower(point, this.CurrentSegment.Start)) { Debug.Assert(ApproximateComparer.CloseIntersections(this.CurrentSegment.Start, point), "Skipped over the point in the ScanSegment linked list"); this.CurrentSegment.Update(point, this.CurrentSegment.End); return(true); } } }
internal PointAndCrossingsList GetOrderedListBetween(Point start, Point end) { if (0 == pointCrossingMap.Count) { return(null); } if (PointComparer.Compare(start, end) > 0) { Point temp = start; start = end; end = temp; } // Start and end are inclusive. pointList.Clear(); foreach (var intersection in pointCrossingMap.Keys) { if ((PointComparer.Compare(intersection, start) >= 0) && (PointComparer.Compare(intersection, end) <= 0)) { pointList.Add(intersection); } } pointList.Sort(); var pointAndCrossingList = new PointAndCrossingsList(); var numCrossings = pointList.Count; for (int ii = 0; ii < numCrossings; ++ii) { Point intersect = pointList[ii]; pointAndCrossingList.Add(intersect, pointCrossingMap[intersect]); } return(pointAndCrossingList); }
internal bool CurrentIsBeforeOrAt(Point comparand) { if (index >= ListOfPointsAndCrossings.Count) { return(false); } return(PointComparer.Compare(ListOfPointsAndCrossings[index].Location, comparand) <= 0); }
private void SpliceGroupBoundaryCrossings(PointAndCrossingsList crossingList, VisibilityVertex startVertex, LineSegment maxSegment) { if ((null == crossingList) || (0 == crossingList.Count)) { return; } crossingList.Reset(); var start = maxSegment.Start; var end = maxSegment.End; var dir = PointComparer.GetPureDirection(start, end); // Make sure we are going in the ascending direction. if (!StaticGraphUtility.IsAscending(dir)) { start = maxSegment.End; end = maxSegment.Start; dir = CompassVector.OppositeDir(dir); } // We need to back up to handle group crossings that are between a VisibilityBorderIntersect on a sloped border and the // incoming startVertex (which is on the first ScanSegment in Perpendicular(dir) that is outside that padded border). startVertex = TraverseToFirstVertexAtOrAbove(startVertex, start, CompassVector.OppositeDir(dir)); // Splice into the Vertices between and including the start/end points. for (var currentVertex = startVertex; null != currentVertex; currentVertex = StaticGraphUtility.FindNextVertex(currentVertex, dir)) { bool isFinalVertex = (PointComparer.Compare(currentVertex.Point, end) >= 0); while (crossingList.CurrentIsBeforeOrAt(currentVertex.Point)) { PointAndCrossings pac = crossingList.Pop(); // If it's past the start and at or before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) > 0) { if (PointComparer.Compare(pac.Location, end) <= 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, CompassVector.OppositeDir(dir)); } } // If it's at or past the start and before the end, splice in the crossings in the descending direction. if (PointComparer.Compare(pac.Location, startVertex.Point) >= 0) { if (PointComparer.Compare(pac.Location, end) < 0) { SpliceGroupBoundaryCrossing(currentVertex, pac, dir); } } } if (isFinalVertex) { break; } } }
internal void Trim(Point start, Point end) { Reset(); if ((null == ListOfPointsAndCrossings) || (0 == ListOfPointsAndCrossings.Count)) { return; } ListOfPointsAndCrossings = new List <PointAndCrossings>(ListOfPointsAndCrossings.Where( pair => (PointComparer.Compare(pair.Location, start) >= 0) && (PointComparer.Compare(pair.Location, end) <= 0))); }
private bool AppendGroupCrossingsThroughPoint(VisibilityGraph vg, Point lastPoint) { if (null == GroupBoundaryPointAndCrossingsList) { return(false); } bool found = false; while (GroupBoundaryPointAndCrossingsList.CurrentIsBeforeOrAt(lastPoint)) { // We will only create crossing Edges that the segment actually crosses, not those it ends before crossing. // For those terminal crossings, the adjacent segment creates the interior vertex and crossing edge. PointAndCrossings pac = GroupBoundaryPointAndCrossingsList.Pop(); GroupBoundaryCrossing[] lowDirCrossings = null; GroupBoundaryCrossing[] highDirCrossings = null; if (PointComparer.Compare(pac.Location, Start) > 0) { lowDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, ScanDirection.OppositeDirection); } if (PointComparer.Compare(pac.Location, End) < 0) { highDirCrossings = PointAndCrossingsList.ToCrossingArray(pac.Crossings, ScanDirection.Direction); } found = true; VisibilityVertex crossingVertex = vg.FindVertex(pac.Location) ?? vg.AddVertex(pac.Location); if ((null != lowDirCrossings) || (null != highDirCrossings)) { AddLowCrossings(vg, crossingVertex, lowDirCrossings); AddHighCrossings(vg, crossingVertex, highDirCrossings); } else { // This is at this.Start with only lower-direction toward group interior(s), or at this.End with only // higher-direction toward group interior(s). Therefore an adjacent ScanSegment will create the crossing // edge, so create the crossing vertex here and we'll link to it. if (null == LowestVisibilityVertex) { SetInitialVisibilityVertex(crossingVertex); } else { Debug.Assert(PointComparer.Equal(End, crossingVertex.Point), "Expected this.End crossingVertex"); AppendHighestVisibilityVertex(crossingVertex); } } } return(found); }
bool IsVSegInHSegRange(ScanSegment v) { return(PointComparer.Compare(v.Start.X, findFirstHSeg.Start.X) >= 0); }
// end ScanIntersect() #endregion // Scanline utilities #region IComparer<SegEvent> /// <summary> /// For ordering events first by Y, then X, then by whether it's an H or V seg. /// </summary> /// <param name="first"></param> /// <param name="second"></param> /// <returns></returns> public int Compare(SegEvent first, SegEvent second) { if (first == second) { return(0); } if (first == null) { return(-1); } if (second == null) { return(1); } // Unlike the ScanSegment-generating scanline in VisibilityGraphGenerator, this scanline has no slope // calculations so no additional rounding error is introduced. int cmp = PointComparer.Compare(first.Site.Y, second.Site.Y); if (0 != cmp) { return(cmp); } // Both are at same Y so we must ensure that for equivalent Y, VClose comes after // HOpen which comes after VOpen, thus make sure VOpen comes before VClose. if (first.IsVertical && second.IsVertical) { // Separate segments may join at Start and End due to overlap. Debug.Assert(!StaticGraphUtility.IntervalsOverlap(first.Segment, second.Segment) || (0 == PointComparer.Compare(first.Segment.Start, second.Segment.End)) || (0 == PointComparer.Compare(first.Segment.End, second.Segment.Start)) , "V subsumption failure detected in SegEvent comparison"); if (0 == cmp) { // false is < true. cmp = (SegEventType.VClose == first.EventType).CompareTo(SegEventType.VClose == second.EventType); } return(cmp); } // If both are H segs, then sub-order by X. if (!first.IsVertical && !second.IsVertical) { // Separate segments may join at Start and End due to overlap, so compare by Start.X; // the ending segment (lowest Start.X) comes before the Open (higher Start.X). Debug.Assert(!StaticGraphUtility.IntervalsOverlap(first.Segment, second.Segment) || (0 == PointComparer.Compare(first.Segment.Start, second.Segment.End)) || (0 == PointComparer.Compare(first.Segment.End, second.Segment.Start)) , "H subsumption failure detected in SegEvent comparison"); cmp = PointComparer.Compare(first.Site.X, second.Site.X); return(cmp); } // One is Vertical and one is Horizontal; we are only interested in the vertical at this point. SegEvent vEvent = first.IsVertical ? first : second; // Make sure that we have opened all V segs before and closed them after opening // an H seg at the same Y coord. Otherwise we'll miss "T" or "corner" intersections. // (RectilinearTests.Connected_Vertical_Segments_Are_Intersected tests that we get the expected count here.) // Start assuming Vevent is 'first' and it's VOpen, which should come before HOpen. cmp = -1; // Start with first == VOpen if (SegEventType.VClose == vEvent.EventType) { cmp = 1; // change to first == VClose } if (vEvent != first) { cmp *= -1; // undo the swap. } return(cmp); }
static internal bool IntervalsOverlap(Point start1, Point end1, Point start2, Point end2) { return(IntervalsAreCollinear(start1, end1, start2, end2) && PointComparer.Compare(start1, end2) != PointComparer.Compare(end1, start2)); }
internal int ComparePerpCoord(Point lhs, Point rhs) { return(PointComparer.Compare((lhs - rhs) * PerpDirectionAsPoint, 0.0)); }