1# Copyright (c) 2018-2021 Manfred Moitzi
2# License: MIT License
3from ezdxf.render.forms import (
4    circle, close_polygon, cube, extrude, cylinder,
5    cone, square, box, ngon,
6)
7from ezdxf.render.forms import open_arrow, arrow2
8from ezdxf.render.forms import (
9    spline_interpolation,
10    spline_interpolated_profiles,
11)
12from ezdxf.render.forms import from_profiles_linear, from_profiles_spline
13from ezdxf.render.forms import rotation_form, ngon_to_triangles
14from ezdxf.render.forms import translate, rotate, scale
15from ezdxf.math import Vec3, close_vectors
16
17
18def test_circle_open():
19    c = list(circle(8))
20    assert len(c) == 8
21
22
23def test_circle_closed():
24    c = list(circle(8, close=True))
25    assert len(c) == 9
26
27
28def test_close_polygon():
29    p = list(close_polygon([(1, 0), (2, 0), (3, 0), (4, 0)]))
30    assert len(p) == 5
31    assert p[4] == (1, 0)
32
33
34def test_close_polygon_without_doublets():
35    p = list(close_polygon([(1, 0), (2, 0), (3, 0), (4, 0), (1, 0)]))
36    assert len(p) == 5
37
38
39def test_close_circle():
40    assert len(list(circle(8, close=True))) == 9
41    assert len(list(close_polygon(circle(8, close=True)))) == 9
42    assert len(list(close_polygon(circle(8, close=False)))) == 9
43
44
45def test_square():
46    sq = square(2)
47    assert len(sq) == 4
48    assert close_vectors(sq, [(0, 0), (2, 0), (2, 2), (0, 2)])
49
50
51def test_box():
52    b = box(3, 2)
53    assert len(b) == 4
54    assert close_vectors(b, [(0, 0), (3, 0), (3, 2), (0, 2)])
55
56
57def test_open_arrow():
58    a = open_arrow(3, 60)
59    assert len(a) == 3
60    assert close_vectors(a, [(-3, 1.5), (0, 0), (-3, -1.5)])
61
62
63def test_closed_arrow():
64    a = arrow2(3, 60, 45)
65    assert len(a) == 4
66    assert close_vectors(a, [(-3, 1.5), (0, 0), (-3, -1.5), (-1.5, 0)])
67
68
69def test_cube():
70    c = cube(center=True)
71    assert len(c.vertices) == 8
72    assert len(c.faces) == 6
73
74    c = cube(center=False)
75    assert len(c.vertices) == 8
76    assert len(c.faces) == 6
77
78
79def test_extrude():
80    profile = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
81    path = ((0, 0, 0), (0, 0, 1))
82    mesh = extrude(profile, path, close=True)
83    assert len(mesh.vertices) == 8
84    assert len(mesh.faces) == 4
85
86
87def test_from_profiles_linear():
88    bottom = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
89    top = [(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
90    mesh = from_profiles_linear([bottom, top], close=True, caps=True)
91    assert len(mesh.vertices) == 8
92    assert len(mesh.faces) == 6
93
94    mesh = from_profiles_linear([bottom, top], close=True, caps=False)
95    assert len(mesh.vertices) == 8
96    assert len(mesh.faces) == 4
97
98
99def in_vertices(v, vertices):
100    v = Vec3(v)
101    return any(v.isclose(v2) for v2 in vertices)
102
103
104def test_cylinder():
105    mesh = cylinder(12)
106    assert len(mesh.faces) == 14  # 1x bottom, 1x top, 12x side
107    assert len(mesh.vertices) == 24  # 12x bottom, 12x top
108
109    mesh = cylinder(count=12, radius=3, top_radius=2, top_center=(1, 0, 3),
110                    caps=False)
111    assert len(mesh.faces) == 12
112    assert len(mesh.vertices) == 24
113    assert in_vertices((3, 0, 3), mesh.vertices)
114    assert in_vertices((-1, 0, 3), mesh.vertices)
115
116
117def test_spline_interpolation():
118    vertices = [(0., 0.), (1., 2.), (3., 1.), (5., 3.)]
119    result = spline_interpolation(vertices, method='uniform', subdivide=4)
120    assert len(result) == 13  # (len-1) * subdivide + 1
121    assert Vec3(0, 0, 0).isclose(result[0]), 'expected start point'
122    assert Vec3(5, 3, 0).isclose(result[-1]), 'expected end point'
123    assert Vec3(1, 2, 0).isclose(result[4]), 'expected 2. fit point'
124    assert Vec3(3, 1, 0).isclose(result[8]), 'expected 3. fit point'
125
126
127def test_spline_interpolated_profiles():
128    p1 = circle(12, radius=2, elevation=0, close=True)
129    p2 = circle(12, radius=3, elevation=2, close=True)
130    p3 = circle(12, radius=1, elevation=4, close=True)
131    p4 = circle(12, radius=2, elevation=6, close=True)
132    profiles = list(spline_interpolated_profiles([p1, p2, p3, p4], subdivide=4))
133    assert len(profiles) == 13  # 3*4 + 1
134
135
136def test_from_profiles_splines():
137    p1 = circle(12, radius=2, elevation=0, close=True)
138    p2 = circle(12, radius=3, elevation=2, close=True)
139    p3 = circle(12, radius=1, elevation=4, close=True)
140    p4 = circle(12, radius=2, elevation=6, close=True)
141    mesh = from_profiles_spline([p1, p2, p3, p4], subdivide=4, caps=True)
142    assert len(mesh.vertices) == 156  # 12 (circle) * 13 (profiles)
143    assert len(mesh.faces) == 146  # 12 (circle) * 12 + 2
144
145
146def test_cone():
147    mesh = cone(12, 2, apex=(0, 0, 3))
148    assert len(mesh.vertices) == 13
149    assert len(mesh.faces) == 13
150
151
152def test_rotation_form():
153    profile = [(0, 0.1), (1, 1), (3, 1.5), (5, 3)]  # in xy-plane
154    mesh = rotation_form(count=16, profile=profile,
155                         axis=(1, 0, 0))  # rotation axis is the x-axis
156    assert len(mesh.vertices) == 16 * 4
157    assert len(mesh.faces) == 16 * 3
158
159
160def test_translate():
161    p = [(1, 2, 3), (4, 5, 6)]
162    r = list(translate(p, (3, 2, 1)))
163    assert r[0].isclose((4, 4, 4))
164    assert r[1].isclose((7, 7, 7))
165
166
167def test_scale():
168    p = [(1, 2, 3), (4, 5, 6)]
169    r = list(scale(p, (3, 2, 1)))
170    assert r[0].isclose((3, 4, 3))
171    assert r[1].isclose((12, 10, 6))
172
173
174def test_rotate():
175    p = [(1, 0, 3), (0, 1, 6)]
176    r = list(rotate(p, 90, deg=True))
177    assert r[0].isclose((0, 1, 3))
178    assert r[1].isclose((-1, 0, 6))
179
180
181def test_square_by_radius():
182    corners = list(ngon(4, radius=1))
183    assert len(corners) == 4
184    assert corners[0].isclose((1, 0, 0))
185    assert corners[1].isclose((0, 1, 0))
186    assert corners[2].isclose((-1, 0, 0))
187    assert corners[3].isclose((0, -1, 0))
188
189
190def test_heptagon_by_edge_length():
191    corners = list(ngon(7, length=10))
192    assert len(corners) == 7
193    assert corners[0].isclose((11.523824354812433, 0, 0))
194    assert corners[1].isclose((7.184986963636852, 9.009688679024192, 0))
195    assert corners[2].isclose((-2.564292158181384, 11.234898018587335, 0))
196    assert corners[3].isclose((-10.382606982861683, 5, 0))
197    assert corners[4].isclose((-10.382606982861683, -5, 0))
198    assert corners[5].isclose((-2.564292158181387, -11.234898018587335, 0))
199    assert corners[6].isclose((7.18498696363685, -9.009688679024192, 0))
200
201
202def test_ngons_to_triangles():
203    open_square = square()
204    r = list(ngon_to_triangles(open_square))
205    assert len(r) == 4
206    center = r[0][2]
207    assert center == (0.5, 0.5, 0)
208
209    closed_square = list(circle(4, elevation=2, close=True))
210    assert len(closed_square) == 5
211    r = list(ngon_to_triangles(closed_square))
212    assert len(r) == 4
213    center = r[0][2]
214    assert center.isclose((0, 0, 2))
215
216    # also subdivide triangles
217    r = list(ngon_to_triangles([(0, 0), (1, 0), (1, 1)]))
218    assert len(r) == 3
219