Skip to content

Commit 7a13d05

Browse files
committed
Implement Vector2
* docs/Geometry.rst - fix outdated quick start guide * pygorithm/geometry/vector2.py - implement * test/test_geometry.py - fix incorrect tests
1 parent 47aeb4e commit 7a13d05

File tree

3 files changed

+94
-27
lines changed

3 files changed

+94
-27
lines changed

docs/Geometry.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Quick Start Guide
1717
poly1 = polygon2.Polygon2.from_regular(5, 5)
1818
1919
# create a polygon from tuple (x, y) - note that the polygon must be convex
20+
# and the points must be clockwise
2021
poly2 = polygon2.Polygon2(points=[ (0, 0), (1, 0), (1, 1), (0, 1) ])
2122
2223
# create a polygon from vector2s.
@@ -39,9 +40,6 @@ Quick Start Guide
3940
else:
4041
print('No intersection')
4142
42-
# check complete overlap
43-
overlap = polygon2.Polygon2.contains_polygon(poly1, poly2, (0, 2), (3, 1))
44-
4543
Features
4644
--------
4745

pygorithm/geometry/vector2.py

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
Defines a simple two-dimensional, mutable vector.
77
"""
88

9+
import math
10+
911
class Vector2(object):
1012
"""
1113
Define a simple two-dimensional, mutable vector.
@@ -49,7 +51,21 @@ def __init__(self, *args, **kwargs):
4951
:param kwargs: named arguments (purpose known by name)
5052
"""
5153

52-
pass
54+
if len(args) == 2:
55+
self.x = args[0]
56+
self.y = args[1]
57+
elif len(args) == 1:
58+
if type(args[0]) == tuple:
59+
self.x = args[0][0]
60+
self.y = args[0][1]
61+
else:
62+
self.x = args[0].x
63+
self.y = args[0].y
64+
else:
65+
assert(len(args) == 0)
66+
67+
self.x = kwargs['x']
68+
self.y = kwargs['y']
5369

5470
def __add__(self, other):
5571
"""
@@ -75,7 +91,7 @@ def __add__(self, other):
7591
:rtype: :class:`pygorithm.geometry.vector2.Vector2`
7692
"""
7793

78-
pass
94+
return Vector2(self.x + other.x, self.y + other.y)
7995

8096
def __sub__(self, other):
8197
"""
@@ -105,7 +121,7 @@ def __sub__(self, other):
105121
:rtype: :class:`pygorithm.geometry.vector2.Vector2`
106122
"""
107123

108-
pass
124+
return Vector2(self.x - other.x, self.y - other.y)
109125

110126
def __mul__(self, scale_factor):
111127
"""
@@ -136,7 +152,10 @@ def __mul__(self, scale_factor):
136152
:raises TypeError: if scale_factor is a Vector2
137153
"""
138154

139-
pass
155+
if type(scale_factor) == Vector2:
156+
raise TypeError('scale_factor cannot be a Vector2 (use dot!)')
157+
158+
return Vector2(self.x * scale_factor, self.y * scale_factor)
140159

141160
def __rmul__(self, scale_factor):
142161
"""
@@ -167,7 +186,10 @@ def __rmul__(self, scale_factor):
167186
:raises TypeError: if scale_factor is a Vector2
168187
"""
169188

170-
pass
189+
if type(scale_factor) == Vector2:
190+
raise TypeError('scale_factor cannot be a Vector2 (use dot!)')
191+
192+
return Vector2(self.x * scale_factor, self.y * scale_factor)
171193

172194
def __repr__(self):
173195
"""
@@ -188,11 +210,13 @@ def __repr__(self):
188210
:rtype: string
189211
"""
190212

191-
pass
213+
return "vector2(x={}, y={})".format(self.x, self.y)
192214

193215
def __str__(self):
194216
"""
195-
Create a human-readable representation of this vector
217+
Create a human-readable representation of this vector.
218+
219+
Rounds to 3 decimal places if there are more.
196220
197221
Example:
198222
@@ -212,7 +236,15 @@ def __str__(self):
212236
:rtype: string
213237
"""
214238

215-
pass
239+
pretty_x = round(self.x * 1000) / 1000
240+
if pretty_x == math.floor(pretty_x):
241+
pretty_x = math.floor(pretty_x)
242+
243+
pretty_y = round(self.y * 1000) / 1000
244+
if pretty_y == math.floor(pretty_y):
245+
pretty_y = math.floor(pretty_y)
246+
247+
return "<{}, {}>".format(pretty_x, pretty_y)
216248

217249
def dot(self, other):
218250
"""
@@ -245,7 +277,7 @@ def dot(self, other):
245277
:rtype: :class:`numbers.Number`
246278
"""
247279

248-
pass
280+
return self.x * other.x + self.y * other.y
249281

250282
def rotate(self, *args, **kwargs):
251283
"""
@@ -294,7 +326,42 @@ def rotate(self, *args, **kwargs):
294326
:rtype: :class:`pygorithm.geometry.vector2.Vector2`
295327
"""
296328

297-
pass
329+
args_counter = 0
330+
deg_rads = None
331+
about = None
332+
333+
if 'radians' in kwargs:
334+
deg_rads = kwargs['radians']
335+
elif 'degrees' in kwargs:
336+
deg_rads = kwargs['degrees'] * math.pi / 180
337+
else:
338+
deg_rads = args[args_counter]
339+
args_counter = args_counter + 1
340+
341+
if 'about' in kwargs:
342+
about = kwargs['about']
343+
else:
344+
if len(args) > args_counter:
345+
about = args[args_counter]
346+
347+
fixed_x = self.x
348+
fixed_y = self.y
349+
350+
if about is not None:
351+
fixed_x -= about.x
352+
fixed_y -= about.y
353+
354+
rotated_x = fixed_x * math.cos(deg_rads) - fixed_y * math.sin(deg_rads)
355+
rotated_y = fixed_y * math.cos(deg_rads) + fixed_x * math.sin(deg_rads)
356+
357+
final_x = rotated_x
358+
final_y = rotated_y
359+
360+
if about is not None:
361+
final_x += about.x
362+
final_y += about.y
363+
364+
return Vector2(final_x, final_y)
298365

299366
def normalize(self):
300367
"""
@@ -325,7 +392,7 @@ def normalize(self):
325392
:rtype: :class:`pygorithm.geometry.vector2.Vector2`
326393
"""
327394

328-
pass
395+
return self * (1 / self.magnitude())
329396

330397
def magnitude_squared(self):
331398
"""
@@ -346,7 +413,8 @@ def magnitude_squared(self):
346413
:returns: square of the magnitude of this vector
347414
:rtype: :class:`numbers.Number`
348415
"""
349-
pass
416+
417+
return self.x * self.x + self.y * self.y
350418

351419
def magnitude(self):
352420
"""
@@ -372,4 +440,5 @@ def magnitude(self):
372440
:returns: magnitude of this vector
373441
:rtype: :class:`numbers.Number`
374442
"""
375-
pass
443+
444+
return math.sqrt(self.magnitude_squared())

tests/test_geometry.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_repr(self):
113113

114114
vec_repr = repr(vec)
115115

116-
self.assertEqual('vector2(x=7, y=1)', vec_repr)
116+
self.assertEqual('vector2(x=7, y=11)', vec_repr)
117117

118118
def test_str(self):
119119
vec = vector2.Vector2(7, 11)
@@ -158,20 +158,20 @@ def test_rotate(self):
158158
self.assertAlmostEqual(0.70710678118, vec3.y)
159159

160160
vec4 = vec1.rotate(math.pi, vector2.Vector2(1, 1))
161-
self.assertEqual(1, vec4.x)
162-
self.assertEqual(2, vec4.y)
161+
self.assertAlmostEqual(1, vec4.x)
162+
self.assertAlmostEqual(2, vec4.y)
163163

164164
vec5 = vec1.rotate(radians = math.pi, about = vector2.Vector2(1, 1))
165-
self.assertEqual(1, vec5.x)
166-
self.assertEqual(2, vec5.y)
165+
self.assertAlmostEqual(1, vec5.x)
166+
self.assertAlmostEqual(2, vec5.y)
167167

168168
vec6 = vec1.rotate(degrees = 180, about = vector2.Vector2(1, 1))
169-
self.assertEqual(1, vec6.x)
170-
self.assertEqual(2, vec6.y)
169+
self.assertAlmostEqual(1, vec6.x)
170+
self.assertAlmostEqual(2, vec6.y)
171171

172172
vec7 = vec1.rotate(vector2.Vector2(1, 1), degrees = 180)
173-
self.assertEqual(1, vec7.x)
174-
self.assertEqual(2, vec7.y)
173+
self.assertAlmostEqual(1, vec7.x)
174+
self.assertAlmostEqual(2, vec7.y)
175175

176176
def test_normalize(self):
177177
vec1 = vector2.Vector2(2, 0)
@@ -567,8 +567,8 @@ def test_constructor_standard(self):
567567
self.assertEqual(0, poly.lines[3].end.x)
568568
self.assertEqual(1, poly.lines[3].end.y)
569569

570-
self.assertIsNotNone(next(vec for vec in poly.normals if vec.horizontal, None))
571-
self.assertIsNotNone(next(vec for vec in poly.normals if vec.vertical, None))
570+
self.assertIsNotNone(next((vec for vec in poly.normals if vec.horizontal), None))
571+
self.assertIsNotNone(next((vec for vec in poly.normals if vec.vertical), None))
572572

573573
self.assertAlmostEqual(0.5, poly.center.x)
574574
self.assertAlmostEqual(0.5, poly.center.y)

0 commit comments

Comments
 (0)