1
0
Files
tmi-practicum-2018-2019/TMI-practicum/SweepLineEffAlgorithm.cs
2019-05-21 17:50:08 +02:00

254 lines
9.2 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)
{
Console.WriteLine("error");
}
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 (upper)
// {
// if (intersection.Y < circle.Y) continue;
// }
// else if (intersection.Y > circle.Y) continue;
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.Equals(_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.Equals(other._identifier)) return Type.CompareTo(other.Type);
if (!Y.Equals(other.Y)) return Y.CompareTo(other.Y);
return Type.Equals(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.Equals(r._identifier) && Type.Equals(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)
{
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);
}
}
}