File: test_644_construction_circle.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 (333 lines) | stat: -rw-r--r-- 11,228 bytes parent folder | download
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
# Copyright (c) 2009-2021, Manfred Moitzi
# License: MIT License
import math
from math import isclose

import pytest

from ezdxf.math import (
    ConstructionRay,
    ConstructionLine,
    ConstructionCircle,
    Vec2,
    UVec,
)

HALF_PI = math.pi / 2.0


def test_init_circle():
    circle = ConstructionCircle((0.0, 0.0), 5)
    point = circle.point_at(HALF_PI)
    assert isclose(point[0], 0.0, abs_tol=1e-4)
    assert isclose(point[1], 5.0, abs_tol=1e-4)
    point = circle.point_at(HALF_PI / 2)
    assert isclose(point[0], 3.5355, abs_tol=1e-4)
    assert isclose(point[1], 3.5355, abs_tol=1e-4)


def test_within():
    circle = ConstructionCircle((0.0, 0.0), 5)
    p1 = (3.0, 2.0)
    p2 = (4.0, 5.0)
    assert circle.inside(p1) is True
    assert circle.inside(p2) is False


def test_tangent():
    circle = ConstructionCircle((0.0, 0.0), 5.0)
    tangent = circle.tangent(HALF_PI / 2)
    assert isclose(tangent._slope, -1, abs_tol=1e-4)
    tangent = circle.tangent(-HALF_PI / 2)
    assert isclose(tangent._slope, 1, abs_tol=1e-4)
    tangent = circle.tangent(0)
    assert tangent._is_vertical is True
    tangent = circle.tangent(HALF_PI)
    assert tangent._is_horizontal is True


def test_intersect_ray_pass():
    circle = ConstructionCircle((10.0, 10.0), 3)
    ray1_hor = ConstructionRay((10.0, 15.0), angle=0)
    ray2_hor = ConstructionRay((10.0, 5.0), angle=0)
    ray1_vert = ConstructionRay((5.0, 10.0), angle=HALF_PI)
    ray2_vert = ConstructionRay((15.0, 10.0), angle=-HALF_PI)
    ray3 = ConstructionRay((13.24, 14.95), angle=0.3992)
    assert len(circle.intersect_ray(ray1_hor)) == 0
    assert len(circle.intersect_ray(ray2_hor)) == 0
    assert len(circle.intersect_ray(ray1_vert)) == 0
    assert len(circle.intersect_ray(ray2_vert)) == 0
    assert len(circle.intersect_ray(ray3)) == 0


def test_intersect_ray_touch():
    def test_touch(testnum, x, y, _angle, abs_tol=1e-6):
        result = True
        ray = ConstructionRay((x, y), angle=_angle)
        points = circle.intersect_ray(ray, abs_tol=abs_tol)
        if len(points) != 1:
            result = False
        else:
            point = points[0]
            # print ("{0}: x= {1:.{places}f} y= {2:.{places}f} : x'= {3:.{places}f} y' = {4:.{places}f}".format(testnum, x, y, point[0], point[1], places=places))
            if not isclose(point[0], x, abs_tol=abs_tol):
                result = False
            if not isclose(point[1], y, abs_tol=abs_tol):
                result = False
        return result

    circle = ConstructionCircle((10.0, 10.0), 3)
    assert test_touch(1, 10.0, 13.0, 0) is True
    assert test_touch(2, 10.0, 7.0, 0) is True
    assert test_touch(3, 7.0, 10.0, HALF_PI) is True
    assert test_touch(4, 13.0, 10.0, -HALF_PI) is True
    assert test_touch(5, 8.8341, 12.7642, 0.3991568, abs_tol=1e-4) is True


class TestCircleInterectRay:
    @pytest.fixture
    def circle(self):
        return ConstructionCircle((10.0, 10.0), 3)

    def test_vertical_ray(self, circle):
        ray_vert = ConstructionRay((8.5, 10.0), angle=HALF_PI)
        cross_points = circle.intersect_ray(ray_vert)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[1] > p2[1]:
            p1, p2 = p2, p1
        assert p1.isclose((8.5, 7.4019), abs_tol=1e-4)
        assert p2.isclose((8.5, 12.5981), abs_tol=1e-4)

    def test_horizontal_ray(self, circle):
        ray_hor = ConstructionRay((10, 8.5), angle=0.0)
        cross_points = circle.intersect_ray(ray_hor)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[0] > p2[0]:
            p1, p2 = p2, p1
        assert p1.isclose((7.4019, 8.5), abs_tol=1e-4)
        assert p2.isclose((12.5981, 8.5), abs_tol=1e-4)

    def test_diagonal_ray(self, circle):
        ray_slope = ConstructionRay((5, 5), (16, 12))
        cross_points = circle.intersect_ray(ray_slope)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[0] > p2[0]:
            p1, p2 = p2, p1
        assert p1.isclose((8.64840, 7.3217), abs_tol=1e-4)
        assert p2.isclose((12.9986, 10.0900), abs_tol=1e-4)

    def test_diagonal_ray_through_mid_point(self, circle):
        ray_slope = ConstructionRay((10, 10), angle=HALF_PI / 2)
        cross_points = circle.intersect_ray(ray_slope)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[0] > p2[0]:
            p1, p2 = p2, p1
        # print (p1[0], p1[1], p2[0], p2[1])
        assert p1.isclose((7.8787, 7.8787), abs_tol=1e-4)
        assert p2.isclose((12.1213, 12.1213), abs_tol=1e-4)

    def test_horizontal_ray_through_mid_point(self, circle):
        ray_hor = ConstructionRay((10, 10), angle=0)
        cross_points = circle.intersect_ray(ray_hor)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[0] > p2[0]:
            p1, p2 = p2, p1
        # print (p1[0], p1[1], p2[0], p2[1])
        assert p1.isclose((7, 10), abs_tol=1e-5)
        assert p2.isclose((13, 10), abs_tol=1e-5)

    def test_vertical_ray_through_mid_point(self, circle):
        ray_vert = ConstructionRay((10, 10), angle=HALF_PI)
        cross_points = circle.intersect_ray(ray_vert)
        assert len(cross_points) == 2
        p1, p2 = cross_points
        if p1[1] > p2[1]:
            p1, p2 = p2, p1
        # print (p1[0], p1[1], p2[0], p2[1])
        assert p1.isclose((10, 7), abs_tol=1e-5)
        assert p2.isclose((10, 13), abs_tol=1e-5)


def test_circles_do_not_intersect():
    M1 = (30, 30)
    M2 = (40, 40)
    M3 = (30.3, 30.3)
    circle1 = ConstructionCircle(M1, 5)
    circle2 = ConstructionCircle(M1, 3)
    circle3 = ConstructionCircle(M2, 3)
    circle4 = ConstructionCircle(M3, 3)

    cross_points = circle1.intersect_circle(circle2)
    assert len(cross_points) == 0
    cross_points = circle2.intersect_circle(circle3)
    assert len(cross_points) == 0
    cross_points = circle1.intersect_circle(circle4)
    assert len(cross_points) == 0


@pytest.mark.parametrize(
    "center, point",
    [
        ((26.5, 20.0), (25.0, 20.0)),
        ((20.0, 26.5), (20.0, 25.0)),
        ((13.5, 20.0), (15.0, 20.0)),
        ((20.0, 13.5), (20.0, 15.0)),
        ((23.5, 20.0), (25.0, 20.0)),
        ((20.0, 23.5), (20.0, 25.0)),
        ((16.5, 20.0), (15.0, 20.0)),
        ((20.0, 16.5), (20.0, 15.0)),
    ],
)
def test_two_circles_touching_at_one_point(center, point):
    circle1 = ConstructionCircle((20, 20), 5)
    circle2 = ConstructionCircle(center, 1.5)
    points = circle1.intersect_circle(circle2)
    assert len(points) == 1
    assert points[0].isclose(point, abs_tol=1e-9) is True


def test_intersect_circle_intersect():
    def check_intersection(m, p1, p2, abs_tol=1e-4):
        p1 = Vec2(p1)
        p2 = Vec2(p2)
        circle2 = ConstructionCircle(m, 1.5)
        points = circle1.intersect_circle(circle2, abs_tol=abs_tol)
        assert len(points) == 2
        a, b = points

        result1 = a.isclose(p1, abs_tol=abs_tol) and b.isclose(p2, abs_tol=abs_tol)
        result2 = a.isclose(p2, abs_tol=abs_tol) and b.isclose(p1, abs_tol=abs_tol)
        return result1 or result2

    circle1 = ConstructionCircle((40, 20), 5)
    assert (
        check_intersection((46.0, 20.0), (44.8958, 21.0153), (44.8958, 18.9847)) is True
    )
    assert (
        check_intersection((44.0, 20.0), (44.8438, 21.2402), (44.8438, 18.7598)) is True
    )
    assert (
        check_intersection((40.0, 26.0), (38.9847, 24.8958), (41.0153, 24.8958)) is True
    )
    assert (
        check_intersection((40.0, 24.0), (38.7598, 24.8438), (41.2402, 24.8438)) is True
    )
    assert (
        check_intersection((34.0, 20.0), (35.1042, 18.9847), (35.1042, 21.0153)) is True
    )
    # assert check_intersection( (36.,20.),  (35.1563, 18.7598),  (35.1563, 21.2402)))
    assert (
        check_intersection((40.0, 14.0), (38.9847, 15.1042), (41.0153, 15.1042)) is True
    )
    assert (
        check_intersection((40.0, 14.0), (38.9847, 15.1042), (41.0153, 15.1042)) is True
    )
    assert (
        check_intersection((36.8824, 17.4939), (35.4478, 17.9319), (37.0018, 15.9987))
        is True
    )
    assert (
        check_intersection((35.3236, 16.2408), (35.5481, 17.7239), (36.8203, 16.1413))
        is True
    )


@pytest.mark.parametrize(
    "center, expected",
    [
        [(1, 0), (-1, 0)],  # right of inner circle
        [(-1, 0), (1, 0)],  # left of inner circle
        [(0, 1), (0, -1)],  # above of inner circle
        [(0, -1), (0, 1)],  # below of inner circle
    ],
    ids=[
        "outer circle right of inner circle",
        "outer circle left of inner circle",
        "outer circle above of inner circle",
        "outer circle below of inner circle",
    ],
)
def test_inner_circle_touches_outer_circle_issue_982(center: UVec, expected: UVec):
    inner_circle = ConstructionCircle((0, 0), 1)
    outer_circle = ConstructionCircle(center, 2)
    pnt = inner_circle.intersect_circle(outer_circle)[0]
    assert pnt.isclose(expected)


def test_vertices():
    circle = ConstructionCircle((0, 0), 1.0)
    vertices = list(circle.vertices([0, math.pi * 0.5, math.pi, math.pi * 1.5]))
    assert vertices[0].isclose((1, 0))
    assert vertices[1].isclose((0, 1))
    assert vertices[2].isclose((-1, 0))
    assert vertices[3].isclose((0, -1))


def test_flattening():
    circle = ConstructionCircle((0, 0), 1.0)
    vertices = list(circle.flattening(0.01))
    assert len(vertices) == 24
    assert vertices[0].isclose(vertices[-1]), "expected closed polygon"


def test_create_3P():
    p1 = (3.0, 3.0)
    p2 = (5.0, 7.0)
    p3 = (12.0, 5.0)
    circle = ConstructionCircle.from_3p(p1, p2, p3)
    assert isclose(circle.center[0], 7.6875, abs_tol=1e-4)
    assert isclose(circle.center[1], 3.15625, abs_tol=1e-4)
    assert isclose(circle.radius, 4.6901, abs_tol=1e-4)


@pytest.mark.parametrize(
    "start,end",
    [
        [(0.5, 2.0), (1.5, 2.0)],
        [(0.5, -2.0), (1.5, -2.0)],
        [(2.0, -2.0), (2.0, 2.0)],
        [(-2.0, -2.0), (-2.0, 2.0)],
    ],
)
def test_intersect_line_in_no_point(start, end):
    """The intersection calculation itself is based on intersect_ray() and is
    already tested.
    """
    circle = ConstructionCircle((0, 0), 1.0)
    assert len(circle.intersect_line(ConstructionLine(start, end))) == 0


@pytest.mark.parametrize(
    "start,end",
    [
        [(0.5, 0.5), (1.5, 1.5)],
        [(-0.5, -0.5), (-1.5, -1.5)],
        [(0.0, 1.0), (0.5, 1.0)],  # touches the circle at one point
    ],
)
def test_intersect_line_in_one_point(start, end):
    """The intersection calculation itself is based on intersect_ray() and is
    already tested.
    """
    circle = ConstructionCircle((0, 0), 1.0)
    assert len(circle.intersect_line(ConstructionLine(start, end))) == 1


@pytest.mark.parametrize(
    "start,end",
    [
        [(0.0, -2.0), (0.0, 2.0)],
        [(0.5, -2.0), (0.5, 2.0)],
        [(-2.0, 0.0), (2.0, 0.0)],
        [(-2.0, 0.5), (2.0, 0.5)],
        [(-2.0, -2.0), (2.0, 2.0)],
    ],
)
def test_intersect_line_in_two_points(start, end):
    circle = ConstructionCircle((0, 0), 1.0)
    assert len(circle.intersect_line(ConstructionLine(start, end))) == 2