--- a/tests/quadrilaterals.py Thu Aug 26 19:16:25 2021 +0300 +++ b/tests/quadrilaterals.py Thu Aug 26 19:36:44 2021 +0300 @@ -25,14 +25,14 @@ @problem_type('skew-major', severity = 'hold', message = lambda skew_angle: - str.format('skew quadrilateral (plane angle {})', + str.format('skew (non-coplanar) quadrilateral (plane angle {})', degree_rep(skew_angle), ), ) @problem_type('skew-minor', severity = 'warning', message = lambda skew_angle: - str.format('slightly skew quadrilateral (plane angle {})', + str.format('slightly skew (non-coplanar) quadrilateral (plane angle {})', degree_rep(skew_angle), ), ) @@ -60,7 +60,7 @@ @problem_type('self-intersecting', severity = 'hold', - message = 'self-intersecting quadrilateral', + message = 'bowtie (self-intersecting) quadrilateral', ) def bowtie_test(model): for quadrilateral in model.quadrilaterals: @@ -77,31 +77,34 @@ ) break -@problem_type('collinear', severity = 'hold', message = 'collinear polygon') +@problem_type('collinear', + severity = 'hold', + message = lambda min_angle, max_angle: str.format( + 'collinear polygon (smallest interior angle {min_angle}, largest {max_angle})', + min_angle = degree_rep(min_angle), + max_angle = degree_rep(max_angle), + ), +) def collinear_test(model): + from math import radians, cos, acos + # according to the LDraw spec, all interior angles must be + # between 0.025 degrees and 179.9 degrees. Note that between 0 and pi, + # cos(x) is a downwards curve, so the minimum cosine is for the maximum + # angle. + min_cosine = cos(radians(179.9)) + max_cosine = cos(radians(0.025)) for element in model.body: if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: - for a, b, c in pairs(element.geometry.vertices, count = 3): - if cross_product(b - a, c - a).length() < 1e-5: - yield report_problem('collinear', bad_object = element) - break - -@problem_type('hairline-polygon', - severity = 'warning', - message = lambda smallest_angle: str.format( - 'hairline polygon (smallest angle {})', - degree_rep(smallest_angle), - ), -) -def hairline_test(model): - for element in model.body: - if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: - smallest_angle = element.geometry.smallest_angle - if smallest_angle < radians(0.5): + cosines = list(element.geometry.angle_cosines()) + if any( + not(min_cosine < cosine < max_cosine) + for cosine in cosines + ): yield report_problem( - 'hairline-polygon', + 'collinear', bad_object = element, - smallest_angle = smallest_angle, + min_angle = min(map(acos, cosines)), + max_angle = max(map(acos, cosines)), ) manifest = { @@ -110,6 +113,5 @@ concave_test, bowtie_test, collinear_test, - hairline_test, ], }