private bool IsStreetInsidePolygon(Street street, Point[] polygon) { //var oxHalfLine = new Street(point, new Point(int.MaxValue, point.y)); //int intersectionsCount = 0; var edges = new List <Street>(); for (int i = 0; i < polygon.Length - 1; i++) { edges.Add(new Street(polygon[i], polygon[i + 1])); } edges.Add(new Street(polygon.Last(), polygon[0])); foreach (var edge in edges) { int intersectionResult = CheckIntersection(edge, street); if (intersectionResult == 1) { return(true); } } return(false); }
//Consider intersections with end points private bool IsPointInsidePolygon(Point point, Point[] polygon) { var oxHalfLine = new Street(point, new Point(int.MaxValue, point.y)); int intersectionsCount = 0; var edges = new List <Street>(); for (int i = 0; i < polygon.Length - 1; i++) { edges.Add(new Street(polygon[i], polygon[i + 1])); } edges.Add(new Street(polygon.Last(), polygon[0])); foreach (var edge in edges) { if (IsPointOnLine(point, edge)) { return(true); } int intersectionResult = CheckIntersection(oxHalfLine, edge); if (intersectionResult == 1) { intersectionsCount++; } //else if (intersectionResult == int.MaxValue) // intersectionsCount++; } if (intersectionsCount % 2 == 1) { return(true); } else { return(false); } }
private bool IsPointOnLine(Point p, Street street) { var p1 = street.p1; var p2 = street.p2; bool isVertical = TryGetLinearFunction(p1, p2, out double slope, out double valueIn0); if (isVertical) { double x = p2.x; if (p.x != x) { return(false); } if (p.y < Math.Min(p1.y, p2.y)) { return(false); } if (p.y > Math.Max(p1.y, p2.y)) { return(false); } else { return(true); } } double resolution = 1; if (Math.Abs(p.y - (slope * p.x + valueIn0)) < resolution) { return(true); } else { return(false); } }
/// <summary> /// Zwraca punkt przecięcia odcinków s1 i s2. /// W przypadku gdy nie ma jednoznacznego takiego punktu rzuć wyjątek ArgumentException /// </summary> public Point GetIntersectionPoint(Street s1, Street s2) { //znajdź współczynniki a i b prostych y=ax+b zawierających odcinki s1 i s2 //uwaga na proste równoległe do osi y //uwaga na odcinki równoległe o wspólnych końcu //porównaj równania prostych, aby znaleźć ich punkt wspólny if (CheckIntersection(s1, s2) != 1) { throw new ArgumentException(); } if (s1.p1.x == s1.p2.x && s2.p1.x == s2.p2.x) { if (s1.p1.y == s2.p1.y) { return(s1.p1); } if (s1.p1.y == s2.p2.y) { return(s1.p1); } if (s1.p2.y == s2.p1.y) { return(s1.p2); } return(s1.p2); } else if (s1.p1.x == s1.p2.x) { double a = (s2.p1.y - s2.p2.y) / (s2.p1.x - s2.p2.x); double b = s2.p2.y - a * s2.p2.x; double x = s1.p1.x; Func <double, double> f = (x1) => (a * x1 + b); return(new Point(x, f(x))); } else if (s2.p1.x == s2.p2.x) { double c = (s1.p1.y - s1.p2.y) / (s1.p1.x - s1.p2.x); double d = s1.p2.y - c * s1.p2.x; double x = s2.p1.x; Func <double, double> f = (x1) => (c * x1 + d); return(new Point(x, f(x))); } else { double a = (s1.p1.y - s1.p2.y) / (s1.p1.x - s1.p2.x); double b = s1.p2.y - a * s1.p2.x; double c = (s2.p1.y - s2.p2.y) / (s2.p1.x - s2.p2.x); double d = s2.p2.y - c * s2.p2.x; if (a == c) { if (s1.p1 == s2.p1) { return(s1.p1); } if (s1.p1 == s2.p2) { return(s1.p1); } if (s1.p2 == s2.p1) { return(s1.p2); } if (s1.p2 == s2.p2) { return(s1.p2); } } double x = (d - b) / (a - c); Func <double, double> f = (x1) => (a * x1 + b); return(new Point(x, f(x))); } }
/// <summary> /// Sprawdza przecięcie zadanych ulic-odcinków. Zwraca liczbę punktów wspólnych. /// </summary> /// <returns>0 - odcinki rozłączne, /// 1 - dokładnie jeden punkt wspólny, /// int.MaxValue - odcinki częściowo pokrywają się (więcej niż 1 punkt wspólny)</returns> public int CheckIntersection(Street s1, Street s2) { return(SegmentIntersection(s1.p1, s1.p2, s2.p1, s2.p2)); }
List <Street[]> CreateCities() { Street[] c0 = new Street[] { //miasto z nieprawidłowymi ulicami (pokrywającymi się) new Street(new Point(-1, -1), new Point(1, 1)), new Street(new Point(0, 0), new Point(5, 5)), new Street(new Point(1, 1), new Point(3, 3)) }; Street[] c1 = new Street[] { //wszystkie ulice współlilniowe, pierwsza i ostatnia oddzielone od pozostałych new Street(new Point(-11, -11), new Point(-100, -100)), new Street(new Point(0, 0), new Point(1, 1)), new Street(new Point(1, 1), new Point(3, 3)), new Street(new Point(0, 0), new Point(-10, -10)), new Street(new Point(5, 5), new Point(3, 3)), new Street(new Point(6, 6), new Point(5, 5)), new Street(new Point(7, 7), new Point(6.5, 6.5)) }; Street[] c2 = new Street[] { //wszystkie ulice rozłączne, ułożone w sposób losowy new Street(new Point(0, 0), new Point(0, 0.5)), new Street(new Point(5, 5), new Point(6, 6)), new Street(new Point(-1, 0), new Point(-1, -10)), new Street(new Point(4, 5), new Point(5, 6)), new Street(new Point(100, 100), new Point(100, -10)), new Street(new Point(0.6, 0.6), new Point(0.7, 0.8)) }; Street[] c3 = new Street[] { //dwie długie równoległe ulice z "odnogami" new Street(new Point(0, 0), new Point(60, 0)), new Street(new Point(0, 10), new Point(60, 10)), new Street(new Point(0, 5), new Point(0, 0)), new Street(new Point(10, 5), new Point(10, 0)), new Street(new Point(20, 6), new Point(20, -6)), new Street(new Point(30, 9), new Point(30, -1)), new Street(new Point(40, 10), new Point(40, 11)), new Street(new Point(50, 15), new Point(50, 5)), new Street(new Point(60, 5), new Point(60, 10)), }; Street[] c4 = new Street[] { //3 rozłączne kwadraty o wspólnym środku new Street(new Point(-1, -1), new Point(-1, 1)), new Street(new Point(-1, 1), new Point(1, 1)), new Street(new Point(1, -1), new Point(1, 1)), new Street(new Point(1, -1), new Point(-1, -1)), new Street(new Point(-2, -2), new Point(-2, 2)), new Street(new Point(-2, 2), new Point(2, 2)), new Street(new Point(2, -2), new Point(2, 2)), new Street(new Point(2, -2), new Point(-2, -2)), new Street(new Point(-3, -3), new Point(-3, 3)), new Street(new Point(-3, 3), new Point(3, 3)), new Street(new Point(3, 3), new Point(3, -3)), new Street(new Point(3, -3), new Point(-3, -3)) }; Street[] c5 = new Street[] { //tylko jedna ulica new Street(new Point(-5, -6), new Point(7, 10)) }; Street[] c6 = new Street[] { //dwie rozłączne nieregularne gwiazdy new Street(new Point(0, 0), new Point(5, 5)), new Street(new Point(0, 0), new Point(0, 3)), new Street(new Point(0, 0), new Point(2, 5)), new Street(new Point(0, 0), new Point(-1, 5)), new Street(new Point(0, 0), new Point(1, -3)), new Street(new Point(2, 1), new Point(0, 0)), new Street(new Point(10, 10), new Point(12, 12)), new Street(new Point(11, 11), new Point(13, 14)), new Street(new Point(11, 11), new Point(10, 9)), new Street(new Point(11, 8), new Point(11, 15)) }; Street[] c7 = new Street[] { //4 kwadraty, 3 z nich połączone new Street(new Point(0, 0), new Point(1, 0)), new Street(new Point(1, 0), new Point(1, 1)), new Street(new Point(1, 1), new Point(0, 1)), new Street(new Point(0, 1), new Point(0, 0)), new Street(new Point(5, 5), new Point(6, 5)), new Street(new Point(6, 5), new Point(6, 6)), new Street(new Point(6, 6), new Point(5, 6)), new Street(new Point(5, 6), new Point(5, 5)), new Street(new Point(0, 5), new Point(1, 5)), new Street(new Point(1, 5), new Point(1, 6)), new Street(new Point(1, 6), new Point(0, 6)), new Street(new Point(0, 6), new Point(0, 5)), new Street(new Point(5, 0), new Point(6, 0)), new Street(new Point(6, 0), new Point(6, 1)), new Street(new Point(6, 1), new Point(5, 1)), new Street(new Point(5, 1), new Point(5, 0)), //łączenia new Street(new Point(1, 1), new Point(5, 5)), new Street(new Point(1, 0), new Point(0, 5)) }; //duży test wydajnościowy, 2000 rozłącznych ulic Street[] c8 = new Street[2000]; for (int i = 0; i < 2000; i++) { c8[i] = new Street(new Point(i, 5), new Point(i, -5)); } //duży test wydajnościowy, 500 połączonych ulic + 500 połączonych ulic List <Street> c9 = new List <Street>(); for (int i = 0; i < 1000; i++) { c9.Add(new Street(new Point(i, i), new Point(i + 1, i + 1))); c9.Add(new Street(new Point(-i, -5), new Point(-i - 1, -5))); } return(new List <Street[]>() { c0, c1, c2, c3, c4, c5, c6, c7, c8, c9.ToArray() }); }
public override void VerifyTestCase(out Result resultCode, out string message, object settings) { double EPS = 0.00001; if (result != expectedResult) { message = $"Zły wynik, oczekiwano {expectedResult}, zwrócono {result}"; resultCode = Result.BadResult; return; } if (path == null || intersections == null) { message = $"Wynik OK, pusta ścieżka lub lista przecięć"; resultCode = Result.BadResult; return; } if (path.Count != expectedPathLength) { message = $"Wynik OK, nieprawidłowa długość znalezionej ścieżki: oczekiwano {expectedPathLength}, zwrócono {path.Count}"; resultCode = Result.BadResult; return; } if (path.Count > 0 && path.Count != intersections.Count + 1) { message = $"Wynik OK, nieprawidłowa długość listy przecięć"; resultCode = Result.BadResult; return; } //sprawdzenie początku i końca ścieżki (czy są w odpowiednich dzielnicach) if (path.Count > 0 && !streetsToDistrict1.Contains(path[0])) { message = $"Wynik OK, pierwszą ulicą zwróconej ścieżki ({path[0]}) nie można dojechać do pierwszej dzielnicy"; resultCode = Result.BadResult; return; } if (path.Count > 0 && !streetsToDistrict2.Contains(path[path.Count - 1])) { message = $"Wynik OK, ostatnią ulicą zwróconej ścieżki ({path[path.Count - 1]}) nie można dojechać do drugiej dzielnicy"; resultCode = Result.BadResult; return; } //sprawdzenie czy kolejne odcinki zawierają punkty przecięć for (int i = 0; i < path.Count - 1; i++) { Street s = streets[path[i]]; if (Point.CrossProduct(s.p2 - s.p1, intersections[i] - s.p1) > EPS || !(Math.Min(s.p1.x, s.p2.x) <= intersections[i].x + EPS && intersections[i].x <= Math.Max(s.p1.x, s.p2.x) + EPS && Math.Min(s.p1.y, s.p2.y) <= intersections[i].y + EPS && intersections[i].y <= Math.Max(s.p1.y, s.p2.y) + EPS) ) { message = $"Wynik OK, {i}-ty punkt przecięcia nie leży na {i}-tym odcinku {s.p1} ++ {s.p2} != {intersections[i]}"; resultCode = Result.BadResult; return; } s = streets[path[i + 1]]; if (Point.CrossProduct(s.p2 - s.p1, intersections[i] - s.p1) > EPS || !(Math.Min(s.p1.x, s.p2.x) <= intersections[i].x + EPS && intersections[i].x <= Math.Max(s.p1.x, s.p2.x) + EPS && Math.Min(s.p1.y, s.p2.y) <= intersections[i].y + EPS && intersections[i].y <= Math.Max(s.p1.y, s.p2.y) + EPS) ) { message = $"Wynik OK, {i}-ty punkt przecięcia nie leży na {i + 1}-tym odcinku"; resultCode = Result.BadResult; return; } } message = $"OK (czas:{PerformanceTime,6:#0.000} jednostek)"; resultCode = Result.Success; }
public CheckIntersectionTestCase(double timeLimit, Exception expectedException, string description, Street s1, Street s2, int expectedResult) : base(timeLimit, expectedException, description) { this.s1 = s1; this.s2 = s2; this.expectedResult = expectedResult; }
/// <summary> /// Zwraca punkt przecięcia odcinków s1 i s2. /// W przypadku gdy nie ma jednoznacznego takiego punktu rzuć wyjątek ArgumentException /// </summary> // etap 3 public Point GetIntersectionPoint(Street s1, Street s2) { //znajdź współczynniki a i b prostych y=ax+b zawierających odcinki s1 i s2 //uwaga na proste równoległe do osi y //uwaga na odcinki równoległe o wspólnych końcu //porównaj równania prostych, aby znaleźć ich punkt wspólny if (CheckIntersection(s1, s2) != 1) { throw new ArgumentException(); } Point p1 = min(s1); // max zwroci zawsze inny punkt niz min Point p2 = max(s1); Point p3 = min(s2); Point p4 = max(s2); // odcinki stykaja sie (tez dwa pionowe, dwa poziome, bo wtedy musza sie stykac jesli sie przeciely) if (p1 == p3) { return(new Point(p1.x, p1.y)); } if (p2 == p4) { return(new Point(p2.x, p2.y)); } if (p1 == p4) { return(new Point(p1.x, p1.y)); } if (p2 == p3) { return(new Point(p2.x, p2.y)); } double a1 = (p2.y - p1.y) / (p2.x - p1.x); double b1 = p1.y - a1 * p1.x; double a2 = (p4.y - p3.y) / (p4.x - p3.x); double b2 = p3.y - a2 * p3.x; // jeden odcinek pionowy if (p1.x == p2.x) { return(new Point(p1.x, a2 * p1.x + b2)); } if (p3.x == p4.x) { return(new Point(p3.x, a1 * p3.x + b1)); } // jeden odcinek poziomy if (p1.y == p2.y) { return(new Point((p1.y - b2) / a2, p1.y)); } if (p3.y == p4.y) { return(new Point((p3.y - b1) / a1, p3.y)); } //double py = (b2 - b1) / (a1 - a2); //double px = (py - b2) / a2; double px = (b2 - b1) / (a1 - a2); double py = a1 * px + b1; return(new Point(px, py)); }
/// <summary> /// Sprawdza przecięcie zadanych ulic-odcinków. Zwraca liczbę punktów wspólnych. /// </summary> /// <returns>0 - odcinki rozłączne, /// 1 - dokładnie jeden punkt wspólny, /// int.MaxValue - odcinki częściowo pokrywają się (więcej niż 1 punkt wspólny)</returns> public int CheckIntersection(Street s1, Street s2) { Point p1 = s1.p1; Point p2 = s1.p2; Point p3 = s2.p1; Point p4 = s2.p2; double d1 = Point.CrossProduct((p4 - p3), (p1 - p3)); double d2 = Point.CrossProduct((p4 - p3), (p2 - p3)); double d3 = Point.CrossProduct((p2 - p1), (p3 - p1)); double d4 = Point.CrossProduct((p2 - p1), (p4 - p1)); double d12 = d1 * d2; double d34 = d3 * d4; if (d12 > 0 || d34 > 0) { return(0); } if (d12 < 0 || d34 < 0) { return(1); } if ((p1 == p3 || p1 == p4) && !OnRectangle(p2, p3, p4)) { return(1); } if ((p2 == p3 || p2 == p4) && !OnRectangle(p1, p3, p4)) { return(1); } if ((p3 == p1 || p3 == p2) && !OnRectangle(p4, p1, p2)) { return(1); } if ((p4 == p1 || p4 == p2) && !OnRectangle(p3, p1, p2)) { return(1); } if (OnRectangle(p1, p3, p4) || OnRectangle(p2, p3, p4) || OnRectangle(p3, p1, p2) || OnRectangle(p4, p1, p2)) { return(int.MaxValue); } if (!OnRectangle(p1, p3, p4) && !OnRectangle(p2, p3, p4)) { return(0); } if (!OnRectangle(p3, p1, p2) && !OnRectangle(p4, p1, p2)) { return(0); } return(1); }
/// <summary> /// Zwraca punkt przecięcia odcinków s1 i s2. /// W przypadku gdy nie ma jednoznacznego takiego punktu rzuć wyjątek ArgumentException /// </summary> public Point GetIntersectionPoint(Street s1, Street s2) { //znajdź współczynniki a i b prostych y=ax+b zawierających odcinki s1 i s2 //uwaga na proste równoległe do osi y //uwaga na odcinki równoległe o wspólnych końcu //porównaj równania prostych, aby znaleźć ich punkt wspólny if (CheckIntersection(s1, s2) != 1) { throw new ArgumentException(); } Point p1 = s1.p1; Point p2 = s1.p2; Point p3 = s2.p1; Point p4 = s2.p2; double x = 0; double y = 0; if (p1 == p3 || p1 == p4) // wspolny koniec { return(p1); } if (p2 == p3 || p2 == p4) { return(p2); } (double a1, double b1, double c1) = GetLine(p1, p2); (double a2, double b2, double c2) = GetLine(p3, p4); if (a1 != 0 && b1 != 0 && a2 != 0 && b2 != 0) { double W = a1 * b2 - a2 * b1; double Wx = (-c1) * b2 - (-c2) * b1; double Wy = a1 * (-c2) - a2 * (-c1); x = Wx / W; y = Wy / W; } else if ((a1 == 0) && (a2 != 0 && b2 != 0)) { y = -c1; x = (-c2 - b2 * y) / a2; } else if ((a2 == 0) && (a1 != 0 && b1 != 0)) { y = -c2; x = (-c1 - b1 * y) / a1; } else if ((b1 == 0) && (a2 != 0 && b2 != 0)) { x = -c1; y = (-c2 - a2 * x) / b2; } else if ((b2 == 0) && (a1 != 0 && b1 != 0)) { x = -c2; y = (-c1 - a1 * x) / b1; } else if ((a1 == 0) && (b2 == 0)) { y = -c1; x = -c2; } else if ((a2 == 0) && (b1 == 0)) { y = -c2; x = -c1; } return(new Point(x, y)); }
/// <summary> /// Sprawdza przecięcie zadanych ulic-odcinków. Zwraca liczbę punktów wspólnych. /// </summary> /// <returns>0 - odcinki rozłączne, /// 1 - dokładnie jeden punkt wspólny, /// int.MaxValue - odcinki częściowo pokrywają się (więcej niż 1 punkt wspólny)</returns> public int CheckIntersection(Street s1, Street s2) { return(0); }