using System; using System.Collections.Generic; using C5; using Medallion.Collections; namespace TMI_practicum { public static class SweepLineEffAlgorithm { private static double _sweepline = 0; public static IEnumerable Solve(IEnumerable circles) { var intersections = new System.Collections.Generic.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.GetIdentifier(); _sweepline = e.X; switch (e.Type) { case Event.EventType.Start: var upper = new SegmentKey(SegmentKey.SegmentType.Upper, hash, e.Circle); var lower = new SegmentKey(SegmentKey.SegmentType.Bottom, hash, e.Circle); active.Add(upper, e.Circle); active.Add(lower, e.Circle); if (active.TrySuccessor(upper, out var other)) CheckIntersections(upper, other, e.Circle, intersections, true, events); if (active.TryPredecessor(lower, out other)) CheckIntersections(lower, other, e.Circle, intersections, false, events); break; case Event.EventType.End: upper = new SegmentKey(SegmentKey.SegmentType.Upper, hash, e.Circle); lower = new SegmentKey(SegmentKey.SegmentType.Bottom, hash, e.Circle); if (active.TrySuccessor(upper, out other)) CheckIntersections(upper, other, e.Circle, intersections, true, events); if (active.TryPredecessor(lower, out other)) CheckIntersections(lower, other, e.Circle, intersections, false, events); active.Remove(upper); active.Remove(lower); break; case Event.EventType.Intersection: var s1 = e.Segments[0]; var s2 = e.Segments[1]; _sweepline -= 10e-12; active.Remove(s1.Key); active.Remove(s2.Key); _sweepline += 20e-12; try { active.Add(s2.Key, e.Segments[1].Value); active.Add(s1.Key, e.Segments[0].Value); } catch (DuplicateNotAllowedException) { } if (s1.Key.Type == SegmentKey.SegmentType.Upper) { if (active.TrySuccessor(s1.Key, out other)) CheckIntersections(s1.Key, other, s1.Value, intersections, true, events); } else { if (active.TryPredecessor(s1.Key, out other)) CheckIntersections(s1.Key, other, s1.Value, intersections, true, events); } if (s2.Key.Type == SegmentKey.SegmentType.Upper) { if (active.TrySuccessor(s2.Key, out other)) CheckIntersections(s2.Key, other, s2.Value, intersections, true, events); } else { if (active.TryPredecessor(s2.Key, out other)) CheckIntersections(s2.Key, other, s2.Value, intersections, true, events); } break; default: throw new ArgumentOutOfRangeException(); } } return intersections; } private static void CheckIntersections(SegmentKey segment, C5.KeyValuePair neighbour, Circle circle, ISet intersections, bool upper, PriorityQueue events) { var intersects = circle.FindIntersections(neighbour.Value); if (intersects == null) return; foreach (var intersection in intersects) { if (intersections.Contains(intersection)) continue; events.Add(new Event(Event.EventType.Intersection, intersection.X, circle, intersection.Y, new[] {new C5.KeyValuePair(segment, circle), neighbour})); intersections.Add(intersection); } } private struct Event : IComparable { public EventType Type { get; } public Circle Circle { get; } public double X { get; } public double Y { get; } public C5.KeyValuePair[] Segments { get; } public enum EventType { Start, Intersection, End } public Event(EventType type, double x, Circle circle, double y, C5.KeyValuePair[] segments = null) { Type = type; X = x; Circle = circle; Y = y; Segments = segments; } public int CompareTo(Event other) { return X.CompareTo(other.X); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + Type.GetHashCode(); hash = hash * 23 + Circle.X.GetHashCode(); hash = hash * 23 + Circle.Y.GetHashCode(); return hash; } } public int GetIdentifier() { unchecked { int hash = 17; hash = hash * 23 + EventType.Start.GetHashCode(); hash = hash * 23 + Circle.X.GetHashCode(); hash = hash * 23 + Circle.Y.GetHashCode(); return hash; } } } private struct SegmentKey : IComparable { private readonly Circle _circle; private double _lastX; private double _lastY; private double Y { get { if (_lastX == _sweepline) return _lastY; _lastX = _sweepline; _lastY = CalculateY(_circle, Type); return _lastY; } } public SegmentType Type { get; } private readonly int _identifier; public SegmentKey(SegmentType type, int identifier, Circle circle) { Type = type; _identifier = identifier; _circle = circle; _lastX = _sweepline; _lastY = CalculateY(_circle, Type); } public int CompareTo(SegmentKey other) { if (_identifier == other._identifier) return Type.CompareTo(other.Type); if (Y != other.Y) return Y.CompareTo(other.Y); return Type == other.Type ? _identifier.CompareTo(other._identifier) : Type.CompareTo(other.Type); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) return false; SegmentKey r = (SegmentKey) obj; return _identifier == r._identifier && Type == r.Type; } public override int GetHashCode() { return _identifier.GetHashCode() ^ Type.GetHashCode(); } public enum SegmentType { Bottom, Upper } } private static double CalculateY(Circle circle, SegmentKey.SegmentType type) { if (_sweepline >= circle.X + circle.R || _sweepline <= circle.X - circle.R) return circle.Y; return type == SegmentKey.SegmentType.Upper ? Math.Round(Math.Sqrt(circle.R * circle.R - Math.Pow(_sweepline - circle.X, 2)) + circle.Y, 14) : Math.Round(-Math.Sqrt(circle.R * circle.R - Math.Pow(_sweepline - circle.X, 2)) + circle.Y, 14); } } }