1
0
Files
tmi-practicum-2018-2019/TMI-practicum/SweepLineEffAlgorithm.cs
2019-05-18 19:57:22 +02:00

184 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Medallion.Collections;
namespace TMI_practicum
{
public static class SweepLineEffAlgorithm
{
public static IEnumerable<Intersection> Solve(IEnumerable<Circle> circles)
{
var intersections = new Stack<Intersection>();
var events = new PriorityQueue<Event>();
var upperActive = new CircleTree<SegmentKey, Circle>();
var bottomActive = new CircleTree<SegmentKey, Circle>();
foreach (var circle in circles)
{
events.Enqueue(new Event(Event.EventType.Start, circle.X - circle.R, circle));
events.Enqueue(new Event(Event.EventType.End, circle.X + circle.R, circle));
}
while (events.Count != 0)
{
Event e = events.Dequeue();
int hash = e.GetHashCode();
switch (e.Type)
{
case Event.EventType.Start:
upperActive.Add(
new SegmentKey(e.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash), e.Circle);
bottomActive.Add(
new SegmentKey(e.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash),
e.Circle);
CheckIntersections(upperActive, e, intersections, true);
CheckIntersections(bottomActive, e, intersections, false);
break;
case Event.EventType.End:
upperActive.Remove(new SegmentKey(e.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper,
hash));
bottomActive.Remove(new SegmentKey(e.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom,
hash));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return intersections;
}
private static void CheckIntersections(CircleTree<SegmentKey, Circle> active, Event e,
Stack<Intersection> intersections, bool upper)
{
Circle other = null;
try
{
other = upper
? active.FindSuccessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Upper,
e.GetHashCode()))
: active.FindPredecessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Bottom,
e.GetHashCode()));
}
catch (KeyNotFoundException)
{
}
if (other != null)
{
var intersects = e.Circle.FindIntersections(other);
if (intersects != null)
{
foreach (var intersection in intersects)
{
if (upper && e.Circle.Y == intersection.Y) continue;
intersections.Push(intersection);
}
}
}
}
private class CircleTree<TKey, TValue> : SortedDictionary<TKey, TValue>
{
public TValue FindSuccessor(TKey key)
{
var other = this.Keys.Where(s => this.Comparer.Compare(s, key) > 0).MinOrDefault();
return this[other];
}
public TValue FindPredecessor(TKey key)
{
var other = this.Keys.Where(s => this.Comparer.Compare(s, key) < 0).MaxOrDefault();
return this[other];
}
}
private struct Event : IComparable<Event>
{
public EventType Type { get; }
public Circle Circle { get; }
public double X { get; }
public enum EventType
{
Start,
End
}
public Event(EventType type, double x, Circle circle)
{
Type = type;
X = x;
Circle = circle;
}
public int CompareTo(Event other)
{
return X.CompareTo(other.X);
}
}
private struct SegmentKey : IComparable<SegmentKey>
{
private readonly double _x;
private readonly double? _y;
public 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
}
}
}
public static class EnumerableExtensions
{
public static T MinOrDefault<T>(this IEnumerable<T> sequence)
{
return sequence.Any() ? sequence.Min() : default;
}
public static T MaxOrDefault<T>(this IEnumerable<T> sequence)
{
return sequence.Any() ? sequence.Max() : default;
}
}
}