diff --git a/TMI-practicum/Circle.cs b/TMI-practicum/Circle.cs index 7c71853..7c6f20f 100644 --- a/TMI-practicum/Circle.cs +++ b/TMI-practicum/Circle.cs @@ -23,26 +23,27 @@ namespace TMI_practicum public IEnumerable FindIntersections(Circle c1) { + IList intersections; double d = Distance(c1); if (d > R + c1.R || d < Math.Abs(R - c1.R) || (d == 0.0 && R - c1.R == 0.0)) return null; - double a = Math.Round((R * R - c1.R * c1.R + d * d) / (2.0*d), 15); - double px = Math.Round(X + a * (c1.X - X) / d, 15); - double py = Math.Round(Y + a * (c1.Y - Y) / d, 15); + double a = (R * R - c1.R * c1.R + d * d) / (2.0*d); + double px = X + a * (c1.X - X) / d; + double py = Y + a * (c1.Y - Y) / d; double htemp = Math.Round(R * R - a * a, 15); if (htemp == 0) { intersections = new Intersection[1]; - intersections[0] = new Intersection(px, py); + intersections[0] = new Intersection(Math.Round(px, 15), Math.Round(py, 15)); } else { intersections = new Intersection[2]; double h = Math.Round(Math.Sqrt(htemp), 15); - intersections[0] = new Intersection(px + h * (c1.Y - Y) / d, py - h * (c1.X - X) / d); - intersections[1] = new Intersection(px - h * (c1.Y - Y) / d, py + h * (c1.X - X) / d); + intersections[0] = new Intersection(Math.Round(px + h * (c1.Y - Y) / d, 15), Math.Round(py - h * (c1.X - X) / d, 15)); + intersections[1] = new Intersection(Math.Round(px - h * (c1.Y - Y) / d, 15), Math.Round(py + h * (c1.X - X) / d, 15)); } return intersections; diff --git a/TMI-practicum/Intersection.cs b/TMI-practicum/Intersection.cs index 19662fe..01eedac 100644 --- a/TMI-practicum/Intersection.cs +++ b/TMI-practicum/Intersection.cs @@ -18,7 +18,7 @@ namespace TMI_practicum public override bool Equals(object obj) { - return obj is Intersection && this == (Intersection) obj; + return obj is Intersection intersection && this == intersection; } public static bool operator ==(Intersection x, Intersection y) { diff --git a/TMI-practicum/Program.cs b/TMI-practicum/Program.cs index 908942e..7a8e72a 100644 --- a/TMI-practicum/Program.cs +++ b/TMI-practicum/Program.cs @@ -10,7 +10,8 @@ namespace TMI_practicum { public static void Main(string[] args) { - GetChartData(); + CheckAlgo(); + //GetChartData(); if (args.Length < 1) { Console.WriteLine("Please enter a file."); @@ -51,6 +52,62 @@ namespace TMI_practicum } + private static void CheckAlgo() + { + bool failed = false; + + int i = 0; + while (!failed) + { + Console.WriteLine("start check {0}", i++); + var circles = CreateRandomCircles(5); + + var correctSol = SweepLineAlgorithm.Solve(circles); + var solved = SweepLineEffAlgorithm.Solve(circles); + + foreach (var intersection in correctSol) + { + if (solved.Contains(intersection)) continue; + + if (solved.Contains(new Intersection(intersection.X + 10e-15, intersection.Y)) || + solved.Contains(new Intersection(intersection.X - 10e-15, intersection.Y)) || + solved.Contains(new Intersection(intersection.X + 10e-15, intersection.Y + 10e-15)) || + solved.Contains(new Intersection(intersection.X + 10e-15, intersection.Y - 10e-15)) || + solved.Contains(new Intersection(intersection.X - 10e-15, intersection.Y - 10e-15)) || + solved.Contains(new Intersection(intersection.X - 10e-15, intersection.Y - 10e-15)) || + solved.Contains(new Intersection(intersection.X, intersection.Y + 10e-15)) || + solved.Contains(new Intersection(intersection.X, intersection.Y - 10e-15)) + ) continue; + + failed = true; + Console.WriteLine("Not found: {0}\t{1}", intersection.X, intersection.Y); + Console.WriteLine("corrrect"); + foreach (var intersection1 in correctSol) + { + Console.WriteLine("{0}\t{1}", intersection1.X, intersection1.Y); + } + Console.WriteLine("wrong"); + foreach (var intersection1 in solved) + { + Console.WriteLine("{0}\t{1}", intersection1.X, intersection1.Y); + } + + Console.WriteLine("Cirkels"); + foreach (var circle in circles) + { + Console.WriteLine("(x-{0})^2 + (y-{1})^2 = {2}^2", circle.X, circle.Y, circle.R); + } + + foreach (var circle in circles) + { + Console.WriteLine("{0} {1} {2}", circle.X, circle.Y, circle.R); + } + break; + } + + } + } + private static List CreateRandomCircles(double amount) { var circles = new List(); @@ -59,7 +116,7 @@ namespace TMI_practicum for (int i = 0; i < amount; i++) { circles.Add(new Circle(Math.Round(rnd.NextDouble() * 1.0, 15), Math.Round(rnd.NextDouble() * 1.0, 15), - Math.Round(rnd.NextDouble() * 0.01, 15))); + Math.Round(rnd.NextDouble() * 0.90 + 0.05, 15))); } return circles; diff --git a/TMI-practicum/SweepLineEffAlgorithm.cs b/TMI-practicum/SweepLineEffAlgorithm.cs index bd43a5f..4158289 100644 --- a/TMI-practicum/SweepLineEffAlgorithm.cs +++ b/TMI-practicum/SweepLineEffAlgorithm.cs @@ -1,14 +1,17 @@ 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 HashSet(); + var intersections = new System.Collections.Generic.HashSet(); var events = new PriorityQueue(); var active = new C5.TreeDictionary(); @@ -23,27 +26,46 @@ namespace TMI_practicum { Event e = events.Dequeue(); int hash = e.GetHashCode(); + _sweepline = e.X; + switch (e.Type) { case Event.EventType.Start: active.Add( - new SegmentKey(e.Circle.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, hash), e.Circle); - active.Add( - new SegmentKey(e.Circle.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, hash), + new SegmentKey(SegmentKey.SegmentType.Upper, hash, e.Circle), e.Circle); - + active.Add( + new SegmentKey(SegmentKey.SegmentType.Bottom, hash, e.Circle), + e.Circle); + CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); break; case Event.EventType.End: CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); - active.Remove(new SegmentKey(e.Circle.X, e.Circle.Y + e.Circle.R, SegmentKey.SegmentType.Upper, - hash)); - active.Remove(new SegmentKey(e.Circle.X, e.Circle.Y - e.Circle.R, SegmentKey.SegmentType.Bottom, - hash)); + active.Remove(new SegmentKey(SegmentKey.SegmentType.Upper, + hash, e.Circle)); + active.Remove(new SegmentKey(SegmentKey.SegmentType.Bottom, + hash, e.Circle)); break; case Event.EventType.Intersection: + var s1 = e.Segments[0].Key; + var s2 = e.Segments[1].Key; + + active.Remove(s1); + active.Remove(s2); + + try + { + active.Add(s2, e.Segments[1].Value); + active.Add(s1, e.Segments[0].Value); + } + catch (DuplicateNotAllowedException) + { + Console.WriteLine("error"); + } + CheckIntersections(active, e, intersections, true, events); CheckIntersections(active, e, intersections, false, events); break; @@ -51,37 +73,38 @@ namespace TMI_practicum throw new ArgumentOutOfRangeException(); } } - + return intersections; } - private static void CheckIntersections(C5.TreeDictionary active, Event e, - HashSet intersections, bool upper, PriorityQueue events) + private static void CheckIntersections(C5.TreeDictionary active, Event e, System.Collections.Generic.HashSet intersections, bool upper, PriorityQueue events) { - C5.KeyValuePair other; + SegmentKey s; if (upper) { - if (!active.TrySuccessor(new SegmentKey(e.X, e.Y, SegmentKey.SegmentType.Upper, - e.GetHashCode()), out other)) + s = new SegmentKey(SegmentKey.SegmentType.Upper, + e.GetHashCode(), e.Circle); + if (!active.TrySuccessor(s, out other)) return; } else { + s = new SegmentKey(SegmentKey.SegmentType.Bottom, e.GetHashCode(), e.Circle); if (!active.TryPredecessor( - new SegmentKey(e.X, e.Y, SegmentKey.SegmentType.Bottom, e.GetHashCode()), out other)) + s, out other)) { return; } } - + var intersects = e.Circle.FindIntersections(other.Value); if (intersects == null) return; foreach (var intersection in intersects) { if (intersections.Contains(intersection)) continue; - events.Add(new Event(Event.EventType.Intersection, intersection.X, e.Circle, intersection.Y)); + events.Add(new Event(Event.EventType.Intersection, intersection.X, e.Circle, intersection.Y, new []{new C5.KeyValuePair(s, e.Circle), other})); intersections.Add(intersection); } } @@ -93,6 +116,8 @@ namespace TMI_practicum public double X { get; } public double Y { get; } + public C5.KeyValuePair[] Segments { get; } + public enum EventType { Start, @@ -100,14 +125,17 @@ namespace TMI_practicum End } - public Event(EventType type, double x, Circle circle, double y) + 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); @@ -115,10 +143,9 @@ namespace TMI_practicum public override int GetHashCode() { - unchecked // Overflow is fine, just wrap + unchecked { int hash = 17; - // Suitable nullity checks etc, of course :) hash = hash * 23 + Type.GetHashCode(); hash = hash * 23 + X.GetHashCode(); hash = hash * 23 + Y.GetHashCode(); @@ -129,26 +156,40 @@ namespace TMI_practicum private struct SegmentKey : IComparable { - private readonly double _x; - private readonly double? _y; - private double Y => _y ?? double.MinValue; - private readonly SegmentType _type; + 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; + } + } + + private SegmentType Type { get; } private readonly int _identifier; - public SegmentKey(double x, double y, SegmentType type, int identifier) + public SegmentKey(SegmentType type, int identifier, Circle circle) { - _y = y; - _type = type; + Type = type; _identifier = identifier; - _x = x; + _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); + 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); + return Type.Equals(other.Type) ? _identifier.CompareTo(other._identifier) : Type.CompareTo(other.Type); } public override bool Equals(object obj) @@ -156,12 +197,12 @@ namespace TMI_practicum if (obj == null || GetType() != obj.GetType()) return false; SegmentKey r = (SegmentKey) obj; - return _identifier.Equals(r._identifier) && _type.Equals(r._type); + return _identifier.Equals(r._identifier) && Type.Equals(r.Type); } public override int GetHashCode() { - return _identifier.GetHashCode() ^ _type.GetHashCode(); + return _identifier.GetHashCode() ^ Type.GetHashCode(); } public enum SegmentType @@ -170,5 +211,12 @@ namespace TMI_practicum 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); + } } } \ No newline at end of file