249 lines
9.1 KiB
C#
249 lines
9.1 KiB
C#
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<Intersection> Solve(IEnumerable<Circle> circles)
|
|
{
|
|
var intersections = new System.Collections.Generic.HashSet<Intersection>();
|
|
var events = new PriorityQueue<Event>();
|
|
var active = new C5.TreeDictionary<SegmentKey, Circle>();
|
|
|
|
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<SegmentKey, Circle> neighbour, Circle circle,
|
|
ISet<Intersection> intersections, bool upper, PriorityQueue<Event> 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<SegmentKey, Circle>(segment, circle), neighbour}));
|
|
intersections.Add(intersection);
|
|
}
|
|
}
|
|
|
|
private struct Event : IComparable<Event>
|
|
{
|
|
public EventType Type { get; }
|
|
public Circle Circle { get; }
|
|
public double X { get; }
|
|
public double Y { get; }
|
|
|
|
public C5.KeyValuePair<SegmentKey, Circle>[] Segments { get; }
|
|
|
|
public enum EventType
|
|
{
|
|
Start,
|
|
Intersection,
|
|
End
|
|
}
|
|
|
|
public Event(EventType type, double x, Circle circle, double y,
|
|
C5.KeyValuePair<SegmentKey, Circle>[] 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<SegmentKey>
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
} |