using System; using System.Collections.Generic; using Medallion.Collections; namespace TMI_practicum { public static class SweepLineEffAlgorithm { public static IEnumerable Solve(IEnumerable circles) { var intersections = new HashSet(); var events = new PriorityQueue(); var active = new C5.TreeDictionary(); foreach (var circle in circles) { events.Enqueue(new Event(Event.EventType.Start, circle.X - circle.R, circle, circle.Y)); events.Enqueue(new Event(Event.EventType.End, circle.X + circle.R, circle, circle.Y)); } while (events.Count != 0) { Event e = events.Dequeue(); int hash = e.GetHashCode(); switch (e.Type) { case Event.EventType.Start: active.Add( new SegmentKey(e.Circle.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash), e.Circle); active.Add( new SegmentKey(e.Circle.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash), e.Circle); CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); break; case Event.EventType.End: CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); active.Remove(new SegmentKey(e.Circle.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash)); active.Remove(new SegmentKey(e.Circle.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash)); break; case Event.EventType.Intersection: CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); break; default: throw new ArgumentOutOfRangeException(); } } return intersections; } private static void CheckIntersections(C5.TreeDictionary active, Event e, HashSet intersections, bool upper, PriorityQueue events) { C5.KeyValuePair other; if (upper) { if (!active.TrySuccessor(new SegmentKey(e.X, e.Y, SegmentKey.SegmentType.Upper, e.GetHashCode()), out other)) return; } else { if (!active.TryPredecessor( new SegmentKey(e.X, e.Y, SegmentKey.SegmentType.Bottom, e.GetHashCode()), out other)) { return; } } var intersects = e.Circle.FindIntersections(other.Value); if (intersects == null) return; foreach (var intersection in intersects) { if (intersections.Contains(intersection)) continue; events.Add(new Event(Event.EventType.Intersection, intersection.X, e.Circle, intersection.Y)); intersections.Add(intersection); } } private struct Event : IComparable { public EventType Type { get; } public Circle Circle { get; } public double X { get; } public double Y { get; } public enum EventType { Start, Intersection, End } public Event(EventType type, double x, Circle circle, double y) { Type = type; X = x; Circle = circle; Y = y; } public int CompareTo(Event other) { return X.CompareTo(other.X); } public override int GetHashCode() { unchecked // Overflow is fine, just wrap { int hash = 17; // Suitable nullity checks etc, of course :) hash = hash * 23 + Type.GetHashCode(); hash = hash * 23 + X.GetHashCode(); hash = hash * 23 + Y.GetHashCode(); return hash; } } } private struct SegmentKey : IComparable { private readonly double _x; private readonly double? _y; private double Y => _y ?? double.MinValue; private readonly SegmentType _type; private readonly int _identifier; public SegmentKey(double x, double y, SegmentType type, int identifier) { _y = y; _type = type; _identifier = identifier; _x = x; } public int CompareTo(SegmentKey other) { if (_identifier.Equals(other._identifier)) return _type.CompareTo(other._type); if (!_y.Equals(other._y)) return Y.CompareTo(other.Y); return _type.Equals(other._type) ? _x.CompareTo(other._x) : _type.CompareTo(other._type); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) return false; SegmentKey r = (SegmentKey) obj; return _identifier.Equals(r._identifier) && _type.Equals(r._type); } public override int GetHashCode() { return _identifier.GetHashCode() ^ _type.GetHashCode(); } public enum SegmentType { Bottom, Upper } } } }