using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.Msagl.Core.DataStructures; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Routing.Visibility; namespace Microsoft.Msagl.Routing.Rectilinear { internal partial class SparseVisibilityGraphGenerator : VisibilityGraphGenerator { /// /// This forms the vector of ScanSegments for the sparse VisibilityGraph. /// internal class ScanSegmentVector { private readonly ScanSegmentVectorItem[] vector; internal ScanSegmentVector(Set coordMap, bool isHorizontal) { this.vector = new ScanSegmentVectorItem[coordMap.Count]; this.IsHorizontal = isHorizontal; var coords = coordMap.OrderBy(coord => coord).GetEnumerator(); coords.MoveNext(); for (int ii = 0; ii < this.vector.Length; ++ii) { this.vector[ii] = new ScanSegmentVectorItem(coords.Current); coords.MoveNext(); } } /// /// The index of the scan segment vector we're appending to on the ScanSegment-generation sweep. /// internal int CurrentSlotIndex { get; set; } /// /// The number of slots. /// internal int Length { get { return this.vector.Length; } } /// /// The item at the index of the scan segment vector we're appending to on the ScanSegment-generation sweep. /// internal ScanSegmentVectorItem CurrentSlot { get { return this.vector[this.CurrentSlotIndex]; } } /// /// The indexed item in the vector. /// internal ScanSegmentVectorItem this[int slot] { get { return this.vector[slot]; } } /// /// Appends a ScanSegment to the linked list in the "Current" slot. /// internal void CreateScanSegment(Point start, Point end, double weight, PointAndCrossingsList gbcList) { this.CurrentSlot.AppendScanSegment(new ScanSegment(start, end, weight, gbcList)); } internal void ScanSegmentsCompleteForCurrentSlot() { ++this.CurrentSlotIndex; } internal void ScanSegmentsComplete() { foreach (var item in this.vector) { item.AddPendingPerpendicularCoordsToScanSegments(); } } /// /// Returns an enumeration of the vector of ScanSegmentVectorItems. /// internal IEnumerable Items { get { return this.vector; } } /// /// Reset vector state between passes. /// internal void ResetForIntersections() { foreach (ScanSegmentVectorItem t in this.vector) { t.ResetForIntersections(); } } /// /// Indicates if this contains horizontal or vertical ScanSegments. /// private bool IsHorizontal { get; set; } /// /// Search the vector for the nearest slot in the specified direction. /// internal int FindNearest(double coord, int directionIfMiss) { // Array.BinarySearch doesn't allow mapping from ScanSegmentVectorItem to its Coord. int low = 0; int high = this.vector.Length - 1; if (coord <= this.vector[low].Coord) { return low; } if (coord >= this.vector[high].Coord) { return high; } while ((high - low) > 2) { var mid = low + ((high - low)/2); var item = this.vector[mid]; if (coord < item.Coord) { high = mid; continue; } if (coord > item.Coord) { low = mid; continue; } // TODOsparse - profile - see if I really need the perpCoordMap Debug.Assert(false, "Should not be here if coord is in the vector"); return mid; } // We know the value is between low and high, non-inclusive. for (++low; low <= high; ++low) { var item = this.vector[low]; if (coord < item.Coord) { return (directionIfMiss > 0) ? low : low - 1; } if (coord == item.Coord) { break; } } // TODOsparse - profile - see if I really need the perpCoordMap Debug.Assert(false, "Should not be here if coord is in the vector"); return low; } internal void CreateSparseVerticesAndEdges(VisibilityGraph vg) { foreach (var item in this.vector) { item.ResetForIntersections(); for (var segment = item.FirstSegment; segment != null; segment = segment.NextSegment) { segment.CreateSparseVerticesAndEdges(vg); } } } // Get the coordinate that remains constant along a segment in this vector. internal double GetParallelCoord(Point site) { return this.IsHorizontal ? site.Y : site.X; } // Get the coordinate that changes along a segment in this vector (and is thus the parallel // coord of an intersecting segment). internal double GetPerpendicularCoord(Point site) { return this.IsHorizontal ? site.X : site.Y; } internal void ConnectAdjoiningSegmentEndpoints() { // Make sure that any series of segments (of different overlappedness) that have points in the // graph are connected at adjoining starts/ends and ends/starts (these adjoining points may not be // Steiner points in the graph if they are on indirect segments. foreach (var item in this.vector) { item.ResetForIntersections(); var prevSegment = item.FirstSegment; for (var segment = prevSegment.NextSegment; segment != null; segment = segment.NextSegment) { if (segment.HasSparsePerpendicularCoords && prevSegment.HasSparsePerpendicularCoords) { if (segment.Start == prevSegment.End) { double perpCoord = this.GetPerpendicularCoord(segment.Start); prevSegment.AddSparseEndpoint(perpCoord); segment.AddSparseEndpoint(perpCoord); } } prevSegment = segment; } } } /// /// /// public override string ToString() { return (this.IsHorizontal ? "(H) count == " : "(V) count == ") + this.vector.Count(); } } } }