181 lines
6.2 KiB
C#
181 lines
6.2 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;
|
|
Circle other2 = 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()));
|
|
other2 = upper? active.FindPredecessor(new SegmentKey(e.X, e.Circle.Y, SegmentKey.SegmentType.Upper, e.GetHashCode()))
|
|
:
|
|
active.FindSuccessor(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(T);
|
|
}
|
|
public static T MaxOrDefault<T>(this IEnumerable<T> sequence)
|
|
{
|
|
return sequence.Any() ? sequence.Max() : default(T);
|
|
}
|
|
}
|
|
} |