File: test_603_vec2.py

package info (click to toggle)
ezdxf 1.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 104,528 kB
  • sloc: python: 182,341; makefile: 116; lisp: 20; ansic: 4
file content (410 lines) | stat: -rw-r--r-- 8,546 bytes parent folder | download | duplicates (2)
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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# Copyright (c) 2019-2020 Manfred Moitzi
# License: MIT License
import pytest
import math
import pickle

# Import from 'ezdxf.math._vector' to test Python implementation
from ezdxf.math._vector import Vec2, Vec3
from ezdxf.acc import USE_C_EXT

all_vec_classes = [Vec2, Vec3]
vec2_only = [Vec2]

if USE_C_EXT:
    from ezdxf.acc.vector import Vec2 as CVec2

    all_vec_classes.append(CVec2)
    vec2_only.append(CVec2)


# Vec2 is a sub set of Vec3, Vec3 can do everything Vec2 can do, but not every
# operation has the same result for 2D and 3D.
@pytest.fixture(params=all_vec_classes)
def vcls(request):
    return request.param


@pytest.fixture(params=vec2_only)
def vec2(request):
    return request.param


def test_init_tuple(vcls):
    v = vcls((2, 3))
    assert v.x == 2
    assert v.y == 3


def test_empty_init(vcls):
    v = vcls()
    assert v.x == 0.
    assert v.y == 0.


def test_init_vec2(vcls):
    v = Vec2(vcls(2, 3))
    assert v.x == 2
    assert v.y == 3


def test_compatible_to_vector():
    v = Vec3(Vec2(1, 2))
    assert v == (1, 2, 0)

    v = Vec2(Vec3(1, 2, 3))
    assert v.x == 1
    assert v.y == 2


def test_vec3(vec2):
    v = vec2(1, 2)
    assert len(v) == 2
    v3 = v.vec3
    assert len(v3) == 3
    assert v3 == (1, 2, 0)


def test_round(vec2):
    v = vec2(1.123, 2.123)
    v2 = v.round(1)
    assert v2 == (1.1, 2.1)


def test_from_angle(vcls):
    angle = math.radians(50)
    length = 3.0
    assert vcls.from_angle(angle, length) == vcls(
        (math.cos(angle) * length, math.sin(angle) * length)
    )


def test_vec2_as_tuple(vec2):
    v = vec2(1, 2)
    assert v[0] == 1
    assert v[1] == 2

    with pytest.raises(IndexError):
        _ = v[2]
    # negative indices not supported
    with pytest.raises(IndexError):
        _ = v[-1]


def test_iter(vcls):
    assert sum(vcls(1, 2)) == 3


def test_deep_copy():
    import copy

    v = Vec2(1, 2)
    l1 = [v, v, v]
    l2 = copy.copy(l1)
    assert l2[0] is l2[1]
    assert l2[1] is l2[2]
    assert l2[0] is v

    # Vec3, CVec2 and CVec3 are immutable and do not create copies of itself!
    l3 = copy.deepcopy(l1)
    assert l3[0] is l3[1]
    assert l3[1] is l3[2]
    assert l3[0] is not v


def test_get_angle(vcls):
    v = vcls(3, 3)
    assert math.isclose(v.angle_deg, 45)
    assert math.isclose(v.angle, math.radians(45))


def test_compare_vectors(vcls):
    v1 = vcls(1, 2)
    assert v1 == v1

    v2 = vcls(2, 3)
    assert v2 > v1
    assert v1 < v2


def test_is_close(vcls):
    v1 = vcls(421846.9857097387, -36908.41493252139)
    v2 = vcls(421846.9857097387, -36908.41493252141)
    assert v1.isclose(v2) is True


def test_is_null(vcls):
    v = vcls(0, 0)
    assert v.is_null is True

    v1 = vcls(23.56678, 56678.56778) * (1.0 / 14.5667)
    v2 = vcls(23.56678, 56678.56778) / 14.5667
    assert (v2 - v1).is_null


def test_is_not_null_default_abs_tol(vcls):
    assert vcls(1e-11, 0).is_null is False


def test_is_null_default_abs_tol(vcls):
    assert vcls(1e-12, 0).is_null is True


def test_bool(vcls):
    v = vcls((0, 0))
    assert bool(v) is False

    v1 = vcls(23.56678, 56678.56778) * (1.0 / 14.5667)
    v2 = vcls(23.56678, 56678.56778) / 14.5667
    result = v2 - v1
    assert bool(result) is False
    # current rel_tol=1e-9
    assert not vcls(1e-8, 0).is_null


def test_magnitude(vcls):
    v = vcls(3, 4)
    assert math.isclose(abs(v), 5)
    assert math.isclose(v.magnitude, 5)


def test_normalize(vcls):
    v = vcls(2, 0)
    assert v.normalize() == (1, 0)


def test_normalize_to_length(vcls):
    v = vcls(2, 0)
    assert v.normalize(4) == (4, 0)


def test_orthogonal_ccw(vcls):
    v = vcls(3, 4)
    assert v.orthogonal() == (-4, 3)


def test_orthogonal_cw(vcls):
    v = vcls(3, 4)
    assert v.orthogonal(False) == (4, -3)


def test_negative(vcls):
    v = vcls(2, 3)
    assert -v == (-2, -3)


def test_add_vector(vcls):
    assert vcls(2, 3) + vcls(7, 7) == (9, 10)


def test_add_vec3(vec2):
    assert vec2(2, 3) + Vec3(7, 7) == (9, 10)


def test_iadd_vector(vec2):
    v = Vec2(2, 3)
    v += Vec2(7, 7)
    assert v == (9, 10)


def test_inplace_operations_do_not_mutate_vec2_inplace():
    v = Vec2(2, 3)
    v_check = v
    v += Vec2(7, 7)
    assert v_check is not v, "__iadd__ should not operate inplace"

    v_check = v
    v -= Vec2(7, 7)
    assert v_check is not v, "__isub__ should not operate inplace"

    v_check = v
    v *= 1
    assert v_check is not v, "__imul__ should not operate inplace"

    v_check = v
    v /= 1
    assert v_check is not v, "__itruediv__ should not operate inplace"


def test_add_scalar_type_erorr(vcls):
    with pytest.raises(TypeError):
        vcls(1, 1) + 1


def test_iadd_scalar_type_error(vcls):
    v = vcls(2, 3)
    with pytest.raises(TypeError):
        v += 1


def test_radd_scalar_type_error(vcls):
    with pytest.raises(TypeError):
        1 + vcls(1, 1)


def test_radd_tuple_type_error(vec2):
    with pytest.raises(TypeError):
        (1, 1) + vec2(1, 1)


def test_sub_vector(vcls):
    assert vcls(2, 3) - vcls(7, 7) == (-5, -4)


def test_isub_vector(vec2):
    v = Vec2(2, 3)
    v -= Vec2(7, 7)
    assert v == (-5, -4)


def test_sub_vec3(vec2):
    assert vec2(2, 3) - Vec3(7, 7) == (-5, -4)


def test_sub_scalar_type_error(vcls):
    with pytest.raises(TypeError):
        vcls(1, 1) - 1


def test_isub_scalar_type_erorr(vcls):
    v = vcls(2, 3)
    with pytest.raises(TypeError):
        v -= 1


def test_rsub_tuple(vec2):
    with pytest.raises(TypeError):
        (2, 3) - vec2(7, 7)


def test_rsub_scalar_type_error(vcls):
    with pytest.raises(TypeError):
        1 - vcls(1, 1)


def test_mul_scalar(vcls):
    v = vcls(2, 3)
    assert v * 2 == (4, 6)


def test_imul_scalar(vcls):
    v = vcls(2, 3)
    v *= 2
    assert v == (4, 6)


def test_rmul_scalar(vcls):
    assert 2 * vcls(2, 3) == (4, 6)


def test_mul_tuple_type_error(vcls):
    with pytest.raises(TypeError):
        vcls(2, 3) * (2, 2)


def test_rmul_tuple_type_error(vcls):
    with pytest.raises(TypeError):
        (2, 2) * vcls(2, 3)


def test_imul_tuple_type_error(vcls):
    v = vcls(2, 3)
    with pytest.raises(TypeError):
        v *= (2, 2)


def test_div_scalar(vcls):
    v = vcls(2, 3)
    assert v / 2 == (1, 1.5)


def test_idiv_scalar(vcls):
    v = vcls(2, 3)
    v /= 2
    assert v == (1, 1.5)


def test_dot_product(vcls):
    v1 = vcls(2, 7)
    v2 = vcls(3, 9)
    assert math.isclose(v1.dot(v2), 69)


def test_angle_deg(vcls):
    assert math.isclose(vcls((0, 1)).angle_deg, 90)
    assert math.isclose(vcls((0, -1)).angle_deg, -90)
    assert math.isclose(vcls((1, 1)).angle_deg, 45)
    assert math.isclose(vcls((-1, 1)).angle_deg, 135)


def test_angle_between(vcls):
    v1 = vcls(0, 1)
    v2 = vcls(1, 1)
    angle = v1.angle_between(v2)
    assert math.isclose(angle, math.pi / 4)
    # reverse order, same result
    angle = v2.angle_between(v1)
    assert math.isclose(angle, math.pi / 4)


@pytest.mark.parametrize(
    "v1, v2",
    [
        [(1, 0), (0, 0)],
        [(0, 0), (1, 0)],
        [(0, 0), (0, 0)],
    ],
)
def test_angle_between_null_vector(vcls, v1, v2):
    with pytest.raises(ZeroDivisionError):
        vcls(v1).angle_between(vcls(v2))


def test_angle_between_outside_domain():
    v1 = Vec3(721.046967113573, 721.0469671135688, 0.0)
    v2 = Vec3(-721.0469671135725, -721.0469671135688, 0.0)
    angle = v1.angle_between(v2)
    assert math.isclose(angle, math.pi)
    # reverse order, same result
    angle = v2.angle_between(v1)
    assert math.isclose(angle, math.pi)


def test_rotate(vcls):
    assert vcls(2, 2).rotate_deg(90).isclose(vcls(-2, 2))


def test_lerp(vcls):
    v1 = vcls(1, 1)
    v2 = vcls(4, 4)
    assert v1.lerp(v2, 0.5) == (2.5, 2.5)
    assert v1.lerp(v2, 0) == (1, 1)
    assert v1.lerp(v2, 1) == (4, 4)


def test_project(vcls):
    v = vcls(10, 0)
    assert v.project(vcls(5, 0)) == (5, 0)
    assert v.project(vcls(5, 5)) == (5, 0)
    assert v.project(vcls(5, 5)) == (5, 0)

    v = vcls(10, 10)
    assert v.project(vcls(10, 0)).isclose(vcls(5, 5))


def test_det(vec2):
    assert vec2(1, 0).det(vec2(0, 1)) == 1
    assert vec2(0, 1).det(vec2(1, 0)) == -1


def test_sum(vcls):
    assert vcls.sum([]).is_null is True
    assert vcls.sum([vcls(1, 1)]) == (1, 1)
    assert vcls.sum([vcls(1, 1), vcls(2, 2)]) == (3, 3)


def test_picklable(vec2):
    for v in [vec2((1, 2.5)), vec2(1, 2.5)]:
        pickled_v = pickle.loads(pickle.dumps(v))
        assert v == pickled_v
        assert type(v) is type(pickled_v)