Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions 22_segment_intersect/Bentley_Ottmann_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from entities import Point


def test(id) :
return {
1: [2,
[Point(0, 0), Point(0, 24),
Point(-5, -25), Point(40, 40)],
[]],
2: [2,
[Point(0, 0), Point(3, 6),
Point(6, 6), Point(37, 37)],
[]],
3: [2,
[Point(0, 0), Point(2, 27),
Point(27, 27), Point(34, 34)],
[]],
4: [2,
[Point(0, 0), Point(2, 30),
Point(30, 30), Point(28, 28)],
[]],
5: [4,
[Point(10, 20), Point(50, 60),
Point(40, 30), Point(30, 60),
Point(50, 60), Point(50, 40),
Point(30, 20), Point(50, 40)],
[Point(35, 45),
Point(50, 60),
Point(50, 40),
Point(40, 30)]],
6: [7,
[Point(4, 5), Point(1, 3),
Point(3, 3), Point(4, 5),
Point(4, 3), Point(4, 5),
Point(4, 5), Point(5, 3),
Point(4, 1), Point(3, 3),
Point(5, 3), Point(4, 1),
Point(4, 1), Point(4, 3)],
[Point(4, 5),
Point(3, 3),
Point(4, 3),
Point(5, 3),
Point(4, 1)]],
7: [14,
[Point(-300, 0), Point(-300, 100),
Point(-300, 100), Point(-200, 200),
Point(-200, 200), Point(-100, 200),
Point(-100, 200), Point(0, 100),
Point(0, 100), Point(100, 200),
Point(100, 200), Point(200, 200),
Point(200, 200), Point(300, 100),
Point(300, 100), Point(300, 0),
Point(300, 0), Point(200, -100),
Point(200, -100), Point(100, -200),
Point(100, -200), Point(0, -300),
Point(0, -300), Point(-100, -200),
Point(-100, -200), Point(-200, -100),
Point(-200, -100), Point(-300, 0)],
[Point(-300, 100),
Point(-200, 200),
Point(-100, 200),
Point(0, 100),
Point(100, 200),
Point(200, 200),
Point(300, 100),
Point(300, 0),
Point(200, -100),
Point(100, -200),
Point(0, -300),
Point(-100, -200),
Point(-200, -100),
Point(-300, 0)]],
8: [8,
[Point(-20, 0), Point(-20, 39),
Point(-20, 40), Point(29, 40),
Point(30, 40), Point(30, 11),
Point(30, 10), Point(-9, 10),
Point(-10, 10), Point(-10, 29),
Point(-10, 30), Point(19, 30),
Point(20, 30), Point(20, 21),
Point(20, 20), Point(0, 20)],
[]],
9: [4,
[Point(12, 455), Point(35, -61),
Point(123, 46), Point(-166, -181),
Point(-100, -13), Point(56, 700),
Point(-100, 1), Point(80, -1)],
[Point(63397847386456, -815531637627, 1000000000000),
Point(-48472157447999, 483023971644, 500000000000),
Point(32301958334230, -470021759269, 1000000000000),
Point(6673742589653, -4880485924390, 200000000000)]],
10: [9,
[Point(-230, 48), Point(-84, 116),
Point(-137, 18), Point(-94, 342),
Point(-3000000000000000, 2704532564500349, 10000000000000), Point(-31, 0),
Point(-200, 200), Point(600, 500),
Point(300, 200), Point(500, 500),
Point(400, 200), Point(400, 500),
Point(500, 200), Point(300, 500),
Point(600, 200), Point(200, 500),
Point(200, 350), Point(600, 350)],
[Point(400, 350),
Point(-126628841820639, 96145470932852, 1000000000000)]]
}.get(id)
419 changes: 419 additions & 0 deletions 22_segment_intersect/Line_segment_intersection.ipynb

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions 22_segment_intersect/checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import numpy as np
from entities import Point, Segment

import exercise_1_tests
import Bentley_Ottmann_tests


def check_first_exercise(plt, get_intersection_point):
test = exercise_1_tests.test

def calculate_det(a, b):
return a.x * b.y - b.x * a.y

def bounding_box(p, a, b):
if a > b:
a, b = b, a
return a <= p <= b

def check_overlap(p, a, b):
orientation = np.sign(calculate_det(b - a, p - a))
if orientation != 0:
return False
return bounding_box(p.x, a.x, b.x) and bounding_box(p.y, a.y, b.y)

def drow_points(axis, points, color):
for point in points:
axis.scatter(point.x, point.y, c=color, s=40)

def drow_segments(axis, a, b, color):
axis.plot([a.x, b.x], [a.y, b.y], c=color)

f, axes = plt.subplots(2, 4, figsize=(11, 6))
print("Part 1. Intersection tests")
for i, axis in zip(range(1, 9), axes.reshape(8)):
axis.set_title("Test " + str(i))
a, b, c, d, answ = test(i)
output = get_intersection_point(a, b, c, d)
drow_points(axis, [a, b], 'r')
drow_segments(axis, a, b, 'r')
drow_points(axis, [c, d], 'b')
drow_segments(axis, c, d, 'b')
if output == answ:
print("Test", i, "Ok")
else:
print("Test", i, "Failed:")
print("\tx={}, y={}, det={}".format(answ.x, answ.y, answ.det), "expected but\n",
"\tx={}, y={}, det={}".format(output.x, output.y, output.det), "found")
print("Part 2. Overlap tests")
for i in range(9, 21):
a, b, c, d = test(i)
output = get_intersection_point(a, b, c, d)
if check_overlap(output, a, b) and check_overlap(output, c, d):
print("Test", i, "Ok")
else:
print("Test", i, "Fail")
print("\ta={}, b={}, c={}, d={}".format(a, b, c, d))
print("\tYour answer:", output)

plt.show()


def check_Bentley_Ottmann_algorithm(plt, find_intersections):
test = Bentley_Ottmann_tests.test

f, axes = plt.subplots(2, 5, figsize=(12, 6))
for i, axis in zip(range(1, 11), axes.reshape(10)):
axis.set_title("Test " + str(i))
input = test(i)
n = input[0] # number of segments
segments = []
for j in range(0, 2 * n, 2):
p1, p2 = input[1][j], input[1][j + 1]
s = Segment(p1, p2, j)
segments.append(s)
axis.plot([p1.x, p2.x], [p1.y, p2.y], c='black')
axis.scatter(p1.x, p1.y, c='black', s=30)
axis.scatter(p2.x, p2.y, c='black', s=30)
intersection_points = find_intersections(segments)
intersection_points_answ = set()
intersection_points_answ.update(input[2])
if intersection_points == intersection_points_answ:
print("Test", i, "Ok")
else:
print("Test", i, "Fail:")
print("\t", intersection_points_answ, "expected but")
print("\t", intersection_points if len(intersection_points) > 0 else "{}", "found")

plt.show()
113 changes: 113 additions & 0 deletions 22_segment_intersect/entities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from fractions import *
from enum import Enum
Type = Enum('Type', 'left right inter')


class Point:
__slots__ = ("x", "y", "det")

def __init__(self, x=0, y=0, det=1):
self.x = x
self.y = y
self.det = det

def __add__(self, p):
return Point(Fraction(self.x, self.det) + Fraction(p.x, p.det),
Fraction(self.y, self.det) + Fraction(p.y, p.det))

def __sub__(self, p):
return Point(Fraction(self.x, self.det) - Fraction(p.x, p.det),
Fraction(self.y, self.det) - Fraction(p.y, p.det))

def __neg__(self):
return Point(-self.x, -self.y, self.det)

def __lt__(self, p):
return Fraction(self.x, self.det) < Fraction(p.x, p.det) or \
Fraction(self.x, self.det) == Fraction(p.x, p.det) and Fraction(self.y, self.det) < Fraction(p.y, p.det)

def __eq__(self, p):
return Fraction(self.x, self.det) == Fraction(p.x, p.det) and \
Fraction(self.y, self.det) == Fraction(p.y, p.det)

def __gt__(self, p):
return not (self < p or self == p)

def __hash__(self):
return hash(tuple([Fraction(self.x, self.det), Fraction(self.y, self.det)]))

def __repr__(self):
return "(%r, %r)" % (str(Fraction(self.x, self.det)), str(Fraction(self.y, self.det)))


class Segment:
__slots__ = ("a", "b", "id")

def __init__(self, a, b, id):
self.a = min(a, b)
self.b = max(a, b)
self.id = id

def __lt__(self, s):
if s is None:
return False
if self.id == s.id:
return False
if self.a == s.a:
return self.b < s.b
return self.a < s.a

def __eq__(self, s):
return self.id == s.id

def __gt__(self, p):
return not (self < p or self == p)

def __repr__(self):
return "[%r, %r]" % (self.a, self.b)


class Type(Enum):
left = 0
right = 1
inter = 2

def __lt__(self, other):
return self.value < other.value


class Event:
__slots__ = ("segment", "t", "addit_segment", "inter_point") # addit_segment & inter_point are used only for type = inter

def __init__(self, segment, t, addit_segment = None, inter_point = None):
self.segment = segment
self.t = t
self.addit_segment = addit_segment
self.inter_point = inter_point

def __lt__(self, e):
a = self.segment.a if self.t == Type.left else \
self.segment.b if self.t == Type.right else self.inter_point
b = e.segment.a if e.t == Type.left else \
e.segment.b if e.t == Type.right else e.inter_point
if a == b:
if self.t == e.t:
if self.segment == e.segment:
return self.addit_segment < e.addit_segment
return self.segment < e.segment
return self.t < e.t
return a < b

def __eq__(self, e):
return self.t == e.t and \
self.segment.id == e.segment.id and \
(self.addit_segment is None and e.addit_segment is None or
self.addit_segment is not None and e.addit_segment is None and self.addit_segment == e.addit_segment)

def __gt__(self, e):
return not (self == e or self < e)

def __repr__(self):
if type != Type.inter:
return "Event [%r, type = %r]" % (self.segment, self.t)
return "Event inter [%r, %r, point = %r]" % (self.segment, self.addit_segment, self.inter_point)
27 changes: 27 additions & 0 deletions 22_segment_intersect/exercise_1_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from entities import Point


def test(id):
return {
1: [Point(1, 1), Point(3, 3), Point(3, 3), Point(3, 2), Point(-6, -6, -2)],
2: [Point(1, 1), Point(4, 5), Point(2, 1), Point(2, 5), Point(24, 28, 12)],
3: [Point(-18, -34), Point(62, 15), Point(83, 7), Point(-8, 16), Point(277978, 51274, 5179)],
4: [Point(0, 0), Point(1, 4), Point(2, 2), Point(0, 3), Point(6, 24, 9)],
5: [Point(0, 0), Point(7, 1), Point(0, 1), Point(7, 0), Point(-49, -7, -14)],
6: [Point(1, 2), Point(4, 2), Point(4, 2), Point(7, 2), Point(4, 2, 1)],
7: [Point(0, 0), Point(5, 2), Point(3, 0), Point(2, 2), Point(30, 12, 12)],
8: [Point(10, 1), Point(100, 80), Point(30, 2), Point(-60, 95), Point(330300, 169530, 15480)],

9: [Point(-1, 1), Point(3, 1), Point(1, 1), Point(5, 1)],
10: [Point(-2, -2), Point(-2, 6), Point(-2, 3), Point(-2, 10)],
11: [Point(1, 1), Point(5, 5), Point(3, 3), Point(7, 7)],
12: [Point(10, -10), Point(1, -1), Point(-5, 5), Point(5, -5)],
13: [Point(-80, -80), Point(30, -80), Point(30, -80), Point(-80, -80)],
14: [Point(8, -70), Point(8, 70), Point(8, -70), Point(8, 70)],
15: [Point(-15, -15), Point(32, 32), Point(32, 32), Point(-15, -15)],
16: [Point(-15, 132), Point(14, 14), Point(14, 14), Point(-15, 132)],
17: [Point(4, 18), Point(4, 46), Point(4, 46), Point(4, 43)],
18: [Point(6, -17), Point(8, -17), Point(33, -17), Point(6, -17)],
19: [Point(3, 18), Point(15, 1), Point(27, -16), Point(3, 18)],
20: [Point(-1, -1), Point(8, 8), Point(-1, -1), Point(101, 101)]
}.get(id)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 22_segment_intersect/images/lower_endpoint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 22_segment_intersect/images/skew.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 22_segment_intersect/images/sweep_line.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 22_segment_intersect/images/upper_endpoint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions 22_segment_intersect/segments_set.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
9
1 3 1 -2
6 1 1 0
3 3 1 1
5 -2 1 3
8 3 4 9
2 1 0 6
2 3 3 2
-1 3 5 6
1 1 8 8
37 changes: 37 additions & 0 deletions 22_segment_intersect/solutions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import numpy as np
from entities import Point


def point_belongs_to_segment(p, a, b): # as we know both segments lay on the same line
left, right, p_ = 0, 0, 0
if a.x == b.x:
left, right, p_ = a.y, b.y, p.y
else:
left, right, p_ = a.x, b.x, p.x
if left > right:
left, right = right, left
return left <= p_ <= right


def get_intersection_point(a, b, c, d):
left = [[d.x - c.x, a.x - b.x],
[d.y - c.y, a.y - b.y]]
right = [[b.x * a.y - b.y * a.x],
[d.x * c.y - d.y * c.x]]
numerator = np.matmul(left, right)
denominator = (a.y - b.y) * (d.x - c.x) - (b.x - a.x) * (c.y - d.y)
if denominator != 0:
return Point(numerator[0][0], numerator[1][0], denominator)
p = None
if point_belongs_to_segment(a, c, d):
p = a
elif point_belongs_to_segment(b, c, d):
p = b
elif point_belongs_to_segment(c, a, b):
p = c
elif point_belongs_to_segment(d, a, b):
p = d
if p is None:
raise Exception('Incorrect test!')
return p