tests/quadrilaterals.py

changeset 147
bec55b021ae7
parent 106
d93ef722bba6
--- 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,
     ],
 }

mercurial