-
Notifications
You must be signed in to change notification settings - Fork 0
/
Point.py
executable file
·290 lines (239 loc) · 8.83 KB
/
Point.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
"""
A Point class is defined here, which stores point coordinates, color and corresponding texture coordinates.
First version Created on 09/23/2018
:author: micou(Zezhou Sun)
:version: 2021.1.3
"""
import copy
import time
import math
import numpy as np
from ColorType import ColorType
class Point:
"""
Properties:
coords: List<Integer>
color: ColorType
texture: List<Float>
Desciption:
Invisible Variables:
coords is used to describe coordinates of a point, only integers allowed
color is used to describe color of a point, must be ColorType Object
texture is used to describe corresponding coordinates in texture, can be float or double
"""
# Enforce type checking for all variables, set them invisible
coords = None
color = None
texture = None
def __init__(self, coords=None, color=None, textureCoords=None):
"""
init Point by using coords, __color, textureCoords or an existing point
any missing argument will be set to all zero
coords: list<int> or tuple<int>.
color: list or int or ColorType.
textureCoords: list or tuple.
"""
# Be careful, Default dimension & dimensionT is 2
self.setCoords(coords)
self.setColor(color)
self.setTextureCoords(textureCoords)
def __repr__(self):
return "p:" + str(self.getCoords()) + \
" c:" + str(self.getColor()) + \
" t:" + str(self.getTextureCoords())
def __hash__(self):
coords = self.coords
if self.coords is None:
coords = (None,)
texture = self.texture
if self.texture is None:
texture = (None, )
return hash((tuple(coords), self.color, tuple(texture)))
def __eq__(self, other):
if not isinstance(other, type(self)):
return False
else:
return (self.coords == other.getCoords()).all() and \
self.texture == other.getTextureCoords() and \
self.color == other.getColor()
def __iter__(self):
return iter(self.coords)
def __len__(self):
return len(self.coords)
def __getitem__(self, i):
return self.coords[i]
def __mul__(self, coefficient):
return Point([coefficient * i for i in self.coords], self.color, self.texture)
def __rmul__(self, coefficient):
return self.__mul__(coefficient)
def __add__(self, anotherPoint):
return Point([i + j for i, j in zip(self.coords, anotherPoint.coords)], self.color, self.texture)
def __sub__(self, anotherPoint):
return Point([i - j for i, j in zip(self.coords, anotherPoint.coords)], self.color, self.texture)
################# Start of basic functions
def normalize(self):
"""
Normalize current point's coords, return a new Point object
:rtype: Point
"""
norm = np.linalg.norm(self.coords)
if norm == 0:
# if the coords is not set or when it is all zero, keep it as original and return
return self.copy()
coords = self.coords / norm
return Point(coords)
def norm(self):
"""
get the norm of this Point's coords
:rtype: float
"""
if self.coords is not None:
return np.linalg.norm(self.coords)
else:
return 0.0
def dot(self, pt):
"""
get the dot product between this Point and another Point
:rtype: float
"""
if (self.coords is None) or (pt.coords is None):
raise Exception("Cannot do dot product between empty Points")
if len(self.coords) != len(pt.coords):
raise Exception("Cannot do dot product between Points with different size")
# this float conversion is necessary, otherwise result will have type np.float32/np.int/np.float64
# any other iterable variable multiplied with these types will be forced to convert to np.array type
return float(np.dot(self.coords, pt.coords))
def distance(self, pt):
"""
get the distance between this Point and another Point
:rtype: float
"""
if (self.coords is None) or (pt.coords is None):
raise Exception("Cannot calculate distance between empty Points")
if len(self.coords) != len(pt.coords):
raise Exception("Cannot calculate distance between Points with different size")
diff = self - pt
# this float conversion is necessary, otherwise result will have type np.float32/np.int/np.float64
# any other iterable variable multiplied with these types will be forced to convert to np.array type
return float(np.sqrt(np.dot(diff.coords, diff.coords)))
def reflect(self, normal):
"""
reflect the vector from origin to self.coords, normalPoint's coords is the normal of the plane that vector
reflect with
:param normal: contains the surface normal which self.coords reflect with
:type normal: Point
"""
n = copy.deepcopy(normal).normalize()
if len(n.coords) != len(self.coords):
raise Exception("Cannot reflect vector with normal which have different size")
ndp = 2 * self.dot(n)
return self - ndp * n
def cross3d(self, anotherVector) -> "Point":
"""
cross product the vector with another vector
"""
if (self.coords is None) or (anotherVector.coords is None) or \
(len(self.coords) != 3) or (len(anotherVector.coords) != 3):
raise Exception("Error input argument for cross product 3D. Only accept 3 dimension Point")
s = self.coords
d = anotherVector.coords
return Point((s[1]*d[2]-s[2]*d[1], s[2]*d[0]-s[0]*d[2], s[0]*d[1]-s[1]*d[0]))
def angleWith(self, anotherVector) -> float:
"""
get the angle between two vectors
"""
if (self.coords is None) or (anotherVector.coords is None):
raise Exception("Error input argument for angle calculation. Only accept 3 dimension Point")
return np.arccos(self.dot(anotherVector) / (self.norm() * anotherVector.norm()))
def setColor(self, color):
"""
Set point color
:param color: Point's color
:type color: ColorType
:return: None
"""
self.color = copy.deepcopy(color)
def setColor_r(self, r):
self.color.r = r
def setColor_g(self, g):
self.color.g = g
def setColor_b(self, b):
self.color.b = b
def getDim(self):
"""
get point coordinates dimension
:return: point coordinates dimension, which is a non-negative integer
"""
if self.coords is not None:
return len(self.coords)
else:
return 0
def getDimT(self):
"""
get point texture coordinates dimension
:return: point texture coordinates dimension, which is a non-negative integer
"""
if self.texture is not None:
return len(self.texture)
else:
return 0
def getCoords(self):
return self.coords
def getTextureCoords(self):
return self.texture
def getColor(self):
return self.color
def setCoords(self, coords):
"""Use a tuple/list to set all values in coords"""
if coords is not None:
self.coords = np.array(coords)
else:
self.coords = None
def setTextureCoords(self, textureCoords):
"""Use a tuple/list of coords to set all values in textureCoords"""
if textureCoords is not None:
self.texture = np.array(textureCoords)
else:
self.texture = None
def copy(self):
newPoint = Point(copy.deepcopy(self.coords), copy.deepcopy(self.color), copy.deepcopy(self.texture))
return newPoint
################# End of basic functions
if __name__ == "__main__":
a = Point((1, 2))
print(a)
a.setColor(ColorType(0.5, 0.2, 0.3))
print(a)
a.setCoords([3, 4])
print(a)
a.setTextureCoords((2.22, 3.33))
print("Point a: ", a)
b = a.copy()
print("Point copied from point a: ", b)
try:
print("Test for illegal input")
c = Point((1.5, 4))
except:
print("Get Error")
# Test for list<Point>
pl = [Point((1, 3)), Point((2, 3)),
Point((3, 5))]
print(pl)
# Test for set<Point>
ps = set(pl)
print(ps)
ps.add(Point((1, 3), ColorType(1, 0, 1)))
print(ps)
ps.add(Point((1, 3), ColorType(0, 0, 0)))
print(ps)
t1 = time.time()
[Point() for _ in range(500 * 500)]
print(time.time() - t1)
t1 = time.time()
for _ in range(500 * 500):
a = Point()
print(time.time() - t1)
t1 = time.time()
for _ in range(500 * 500):
a = ColorType()
print(time.time() - t1)