// Scan segments with no visibility will usually be internal to an overlap clump, // but may be in an external "corner" of intersecting sides for a small enough span // that no other segment crosses them. In that case we don't need them and they // would require extra handling later. internal void RemoveSegmentsWithNoVisibility(ScanSegmentTree horizontalScanSegments, ScanSegmentTree verticalScanSegments) { foreach (ScanSegment seg in segmentsWithoutVisibility) { (seg.IsVertical ? verticalScanSegments : horizontalScanSegments).Remove(seg); } }
static internal void Test_DumpScanSegments(ObstacleTree obstacleTree, ScanSegmentTree hSegs, ScanSegmentTree vSegs) { var debugCurves = Test_GetObstacleDebugCurves(obstacleTree); debugCurves.AddRange(Test_GetScanSegmentCurves(hSegs)); debugCurves.AddRange(Test_GetScanSegmentCurves(vSegs)); DebugCurveCollection.WriteToFile(debugCurves, GetDumpFileName("ScanSegments")); }
static internal void Test_ShowScanSegments(ObstacleTree obstacleTree, ScanSegmentTree hSegs, ScanSegmentTree vSegs) { #if TEST_MSAGL var debugCurves = Test_GetObstacleDebugCurves(obstacleTree); debugCurves.AddRange(Test_GetScanSegmentCurves(hSegs)); debugCurves.AddRange(Test_GetScanSegmentCurves(vSegs)); LayoutAlgorithmSettings.ShowDebugCurvesEnumeration(debugCurves); #endif // TEST }
private static void CreateScanSegmentTree(ScanSegmentVector segmentVector, ScanSegmentTree segmentTree) { foreach (var item in segmentVector.Items) { for (var segment = item.FirstSegment; segment != null; segment = segment.NextSegment) { if (segment.HasVisibility()) { segmentTree.InsertUnique(segment); } } } }
static internal List <DebugCurve> Test_GetScanSegmentCurves(ScanSegmentTree segTree) { return(segTree.Segments.Select(seg => new DebugCurve(0.2, seg.IsOverlapped ? "Aqua" : (seg.IsReflection ? "LightGreen" : "DarkGreen"), new LineSegment(seg.Start, seg.End))).ToList()); }
// If we have collinear segments, then we may be able to just update the previous one // instead of growing the ScanSegmentTree. // - For multiple collinear OpenVertexEvents, neighbors to the high side have not yet // been seen, so a segment is created that spans the lowest and highest neighbors. // A subsequent collinear OpenVertexEvent will be to the high side and will add a // subsegment of that segment, so we subsume it into LastAddedSegment. // - For multiple collinear CloseVertexEvents, closing neighbors to the high side are // still open, so a segment is created from the lowest neighbor to the next-highest // collinear obstacle to be closed. When that next-highest CloseVertexEvent is // encountered, it will extend LastAddedSegment. // - For multiple collinear mixed Open and Close events, we'll do all Opens first, // followed by all closes (per EventQueue opening), so we may add multiple discrete // segments, which ScanSegmentTree will merge. internal static bool Subsume(ref ScanSegment seg, Point newStart, Point newEnd, double weight, PointAndCrossingsList gbcList, ScanDirection scanDir, ScanSegmentTree tree, out bool extendStart, out bool extendEnd) { // Initialize these to the non-subsumed state; the endpoints were extended (or on a // different line). extendStart = true; extendEnd = true; if (null == seg) { return(false); } // If they don't overlap (including touching at an endpoint), we don't subsume. if (!StaticGraphUtility.IntervalsOverlap(seg.Start, seg.End, newStart, newEnd)) { return(false); } // If the overlapped-ness isn't the same, we don't subsume. ScanSegmentTree::MergeSegments // will mark that the low-to-high direction needs a VisibilityVertex to link the two segments. // These may differ by more than Curve.DistanceEpsilon in the case of reflection lookahead // segments collinear with vertex-derived segments, so have a looser tolerance here and we'll // adjust the segments in ScanSegmentTree.MergeSegments. if (seg.Weight != weight) { if ((seg.Start == newStart) && (seg.End == newEnd)) { // This is probably because of a rounding difference by one DistanceEpsilon reporting being // inside an obstacle vs. the scanline intersection calculation side-ordering. // Test is RectilinearFileTests.Overlap_Rounding_Vertex_Intersects_Side. seg.Weight = Math.Min(seg.Weight, weight); return(true); } // In the case of groups, we go through the group boundary; this may coincide with a // reflection segment. RectilinearFileTests.ReflectionSubsumedBySegmentExitingGroup. Debug.Assert((seg.Weight == OverlappedWeight) == (weight == OverlappedWeight) || ApproximateComparer.CloseIntersections(seg.End, newStart) || ApproximateComparer.CloseIntersections(seg.Start, newEnd) , "non-equal overlap-mismatched ScanSegments overlap by more than just Start/End"); return(false); } // Subsume the input segment. Return whether the start/end points were extended (newStart // is before this.Start, or newEnd is after this.End), so the caller can generate reflections // and so we can merge group border crossings. extendStart = (-1 == scanDir.CompareScanCoord(newStart, seg.Start)); extendEnd = (1 == scanDir.CompareScanCoord(newEnd, seg.End)); if (extendStart || extendEnd) { // We order by start and end so need to replace this in the tree regardless of which end changes. tree.Remove(seg); seg.startPoint = scanDir.Min(seg.Start, newStart); seg.endPoint = scanDir.Max(seg.End, newEnd); seg = tree.InsertUnique(seg).Item; seg.MergeGroupBoundaryCrossingList(gbcList); } return(true); }