1# Copyright (c) 2021, Manfred Moitzi 2# License: MIT License 3 4import pytest 5import ezdxf 6from ezdxf.lldxf import const 7from ezdxf.math import ( 8 required_fit_points, required_control_points, 9 required_knot_values, uniform_knot_vector, 10) 11from ezdxf.audit import Auditor, AuditError 12 13 14@pytest.fixture(scope='module') 15def doc(): 16 return ezdxf.new() 17 18 19@pytest.fixture 20def auditor(doc): 21 return Auditor(doc) 22 23 24@pytest.fixture 25def spline(doc): 26 msp = doc.modelspace() 27 return msp.add_spline() 28 29 30@pytest.mark.parametrize('order, expected', [ 31 (0, 2), (1, 2), (2, 2), (3, 3), (4, 4), (5, 5) 32]) 33def test_required_fit_points_without_given_end_tangents(order, expected): 34 assert required_fit_points(order, tangents=False) == expected 35 36 37@pytest.mark.parametrize('order, expected', [ 38 (0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 3) 39]) 40def test_required_fit_points_with_given_end_tangents(order, expected): 41 assert required_fit_points(order, tangents=True) == expected 42 43 44@pytest.mark.parametrize('order, expected', [ 45 (0, 2), (1, 2), (2, 2), (3, 3), (4, 4), 46]) 47def test_required_control_points_calculation(order, expected): 48 assert required_control_points(order) == expected 49 50 51def test_any_points_present(auditor, spline): 52 spline.audit(auditor) 53 assert len(auditor.fixes) == 1 54 assert auditor.fixes[0].code == AuditError.INVALID_SPLINE_DEFINITION 55 56 # Test if invalid spline will be destroyed: 57 auditor.empty_trashcan() 58 assert spline.is_alive is False 59 60 61def test_degree_of_spline(auditor, spline): 62 # Attribute validator prevents setting an invalid degree: 63 with pytest.raises(const.DXFValueError): 64 spline.dxf.degree = 0 65 66 # But could be loaded from DXF file: 67 # Hack to test entity validator for degree < 1 68 spline.dxf.__dict__['degree'] = 0 69 spline.audit(auditor) 70 assert len(auditor.fixes) == 1 71 assert auditor.fixes[0].code == AuditError.INVALID_SPLINE_DEFINITION 72 73 # Test if invalid spline will be destroyed: 74 auditor.empty_trashcan() 75 assert spline.is_alive is False 76 77 78def add_n_fit_points(s, count: int): 79 for x in range(count): 80 s.fit_points.append([x, 0, 0]) 81 82 83class TestFitPoints: 84 85 @pytest.mark.parametrize('degree', [1, 2, 3, 4]) 86 def test_if_fit_point_count_is_valid(self, auditor, spline, degree): 87 order = degree + 1 88 spline.dxf.degree = degree 89 add_n_fit_points(spline, required_fit_points(order, tangents=True) - 1) 90 91 spline.audit(auditor) 92 assert len(auditor.fixes) == 1 93 assert auditor.fixes[ 94 0].code == AuditError.INVALID_SPLINE_FIT_POINT_COUNT 95 96 # Test if invalid spline will be destroyed: 97 auditor.empty_trashcan() 98 assert spline.is_alive is False 99 100 def test_remove_unused_knot_values(self, auditor, spline): 101 add_n_fit_points(spline, 4) 102 # Add arbitrary knot values -- have no meaning for splines defined 103 # by fit points: 104 spline.knots = [1, 2, 3, 4] 105 spline.audit(auditor) 106 assert len(auditor.fixes) == 1 107 assert auditor.fixes[0].code == \ 108 AuditError.INVALID_SPLINE_KNOT_VALUE_COUNT 109 assert len(spline.knots) == 0 110 111 # Spline is usable, test if spline will not be destroyed: 112 auditor.empty_trashcan() 113 assert spline.is_alive is True 114 115 def test_remove_unused_weights(self, auditor, spline): 116 add_n_fit_points(spline, 4) 117 # Add arbitrary weights -- have no meaning for splines defined by fit 118 # points: 119 spline.weights = [1, 2, 2, 1] 120 spline.audit(auditor) 121 assert len(auditor.fixes) == 1 122 assert auditor.fixes[0].code == AuditError.INVALID_SPLINE_WEIGHT_COUNT 123 assert len(spline.weights) == 0 124 125 # Spline is usable, test if spline will not be destroyed: 126 auditor.empty_trashcan() 127 assert spline.is_alive is True 128 129 130def add_n_control_points(s, count: int): 131 for x in range(count): 132 s.control_points.append([x, 0, 0]) 133 134 135class TestControlPoints: 136 @pytest.mark.parametrize('degree', [1, 2, 3, 4]) 137 def test_auditing_control_point_count(self, auditor, spline, degree): 138 order = degree + 1 139 spline.dxf.degree = degree 140 add_n_control_points(spline, required_control_points(order) - 1) 141 142 spline.audit(auditor) 143 assert len(auditor.fixes) == 1 144 assert auditor.fixes[0].code == \ 145 AuditError.INVALID_SPLINE_CONTROL_POINT_COUNT 146 147 # Test if invalid spline will be destroyed: 148 auditor.empty_trashcan() 149 assert spline.is_alive is False 150 151 @pytest.mark.parametrize('degree', [1, 2, 3, 4]) 152 def test_auditing_knot_value_count(self, auditor, spline, degree): 153 order = degree + 1 154 spline.dxf.degree = degree 155 add_n_control_points(spline, required_control_points(order)) 156 spline.knots = [1, 2, 3] 157 158 spline.audit(auditor) 159 assert len(auditor.fixes) == 1 160 assert auditor.fixes[0].code == \ 161 AuditError.INVALID_SPLINE_KNOT_VALUE_COUNT 162 163 # Test if invalid spline will be destroyed: 164 auditor.empty_trashcan() 165 assert spline.is_alive is False 166 167 @pytest.mark.parametrize('degree', [1, 2, 3, 4]) 168 def test_auditing_weight_count(self, auditor, spline, degree): 169 order = degree + 1 170 spline.dxf.degree = degree 171 count = required_control_points(order) 172 add_n_control_points(spline, count) 173 spline.knots = uniform_knot_vector(count, order) 174 spline.weights = range(count - 1) 175 176 spline.audit(auditor) 177 assert len(auditor.fixes) == 1 178 assert auditor.fixes[0].code == \ 179 AuditError.INVALID_SPLINE_WEIGHT_COUNT 180 181 # Test if invalid spline will be destroyed: 182 auditor.empty_trashcan() 183 assert spline.is_alive is False 184 185 186if __name__ == '__main__': 187 pytest.main([__file__]) 188