/
line.py
132 lines (100 loc) · 3.71 KB
/
line.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-
from decimal import Decimal
from typing import Union
from tools import is_zero, first_nonzero_index, to_decimal
from vector import Vector
class GeometryException(Exception):
""""""
class LinesParallelException(GeometryException):
""""""
class LinesEqualException(GeometryException):
""""""
class Line:
precision = None
def __init__(self,
normal_vector: Vector,
constant_term: Union[int, float, str, Decimal]):
self.dimension = 2
self.normal_vector: Vector = normal_vector
self.constant_term = to_decimal(constant_term)
self.base_point = None
base_point_coords = ['0'] * self.dimension
initial_index = first_nonzero_index(self.normal_vector)
if initial_index is not None:
initial_coefficient = self.normal_vector[initial_index]
base_point_coords[initial_index] = \
self.constant_term / initial_coefficient
self.base_point = Vector(*base_point_coords)
def is_parallel(self, other: 'Line') -> bool:
"""
>>> Line(Vector(2, 3), 5).is_parallel(Line(Vector(4, 6), 3))
True
>>> Line(Vector(2, 3), 5).is_parallel(Line(Vector(4, 5), 3))
False
"""
return self.normal_vector.is_parallel(other.normal_vector)
def __eq__(self, other: 'Line') -> bool:
"""
>>> Line(Vector(2, 3), 5) == Line(Vector(4, 6), 10)
True
>>> Line(Vector(2, 3), 5) == Line(Vector(4, 5), 10)
False
"""
if not self.normal_vector:
if other.normal_vector:
return False
else:
diff = self.constant_term - other.constant_term
return is_zero(diff)
elif not other.normal_vector:
return False
if not self.is_parallel(other):
return False
middle_vector = self.base_point - other.base_point
return self.normal_vector.is_orthogonal(middle_vector)
def get_intersection(self, other: 'Line') -> Union['Line', Vector, None]:
"""
>>> Line(Vector(3, 3), 6).get_intersection(Line(Vector(3, -3), 3))
Vector(1.5, 0.5)
>>> Line(Vector(2, 3), 5).get_intersection(Line(Vector(4, 6), 10))
Line(2x_0+3x_1=5)
>>> Line(Vector(2, 3), 5).get_intersection(Line(Vector(4, 6), 3))
"""
if self == other:
return self
if self.is_parallel(other):
return None
a, b = self.normal_vector
c, d = other.normal_vector
k1, k2 = self.constant_term, other.constant_term
return Vector((d * k1 - b * k2) / (a * d - b * c),
(a * k2 - c * k1) / (a * d - b * c))
def __str__(self):
"""
>>> Line(Vector(2, 3), 1)
Line(2x_0+3x_1=1)
"""
class_name = self.__class__.__name__
initial_index = first_nonzero_index(self.normal_vector)
def to_coeff(value, index: int):
value = round(value, self.precision)
if not value % 1:
value = int(value)
if not value:
return ''
result = f'{value:+}x_{index}'
return result
if initial_index is None:
output = '0'
else:
output = ''.join(to_coeff(v, i) for i, v in
enumerate(self.normal_vector))
if output.startswith('+'):
output = output[1:]
constant = round(self.constant_term)
if constant % 1 == 0:
constant = int(constant)
output += f'={constant}'
return f'{class_name}({output})'
__repr__ = __str__