Got the skew test working

Sun, 21 Jan 2018 15:03:38 +0200

author
Santeri Piippo
date
Sun, 21 Jan 2018 15:03:38 +0200
changeset 13
12d4ddc4bfd8
parent 12
eb74680a5e43
child 14
d383f319f35b

Got the skew test working

geometry.py file | annotate | diff | comparison | revisions
ldcheck.py file | annotate | diff | comparison | revisions
reference/quadrilaterals.ldr file | annotate | diff | comparison | revisions
tests/concave.py file | annotate | diff | comparison | revisions
testsuite.py file | annotate | diff | comparison | revisions
--- a/geometry.py	Fri Jan 19 13:41:23 2018 +0200
+++ b/geometry.py	Sun Jan 21 15:03:38 2018 +0200
@@ -67,7 +67,12 @@
     def __matmul__(self, transformation_matrix):
         return transform(self, transformation_matrix)
     def __eq__(self, other):
-        return self.coordinates == other.coordinates
+        return all(
+            abs(a - b) < 1e-8
+            for a, b in zip(self.coordinates, other.coordinates)
+        )
+    def __ne__(self, other):
+        return not self.__eq__(other)
     def __lt__(self, other):
         return self.coordinates < other.coordinates
     def __hash__(self):
@@ -146,6 +151,9 @@
     ]).determinant()
     return Vector(x, y, z).normalized()
 
+class NoIntersection(Exception):
+    pass
+
 def pairs(iterable, *, count = 2):
     '''
         Iterates the given iterable and returns tuples containing `count`
--- a/ldcheck.py	Fri Jan 19 13:41:23 2018 +0200
+++ b/ldcheck.py	Sun Jan 21 15:03:38 2018 +0200
@@ -7,6 +7,7 @@
 from colours import load_colours
 from geometry import *
 from pathlib import Path
+import linetypes
 
 from os.path import realpath
 script_directory = Path(realpath(__file__)).parent
@@ -47,6 +48,15 @@
     from math import log10
     return max(0, -log10(smallest_angle))
 
+class Model:
+	def __init__(self, body):
+		self.body = body
+	@property
+	def quadrilaterals(self):
+		yield from filter(
+			lambda element: isinstance(element, linetypes.Quadrilateral),
+			self.body)
+
 if __name__ == '__main__':
     from sys import argv
     config = load_config('ldcheck.cfg')
@@ -54,8 +64,9 @@
         with ldconfig_ldr_path.open() as ldconfig_ldr:
             load_colours(ldconfig_ldr)
     with open(argv[1], 'r') as file:
-        model = read_ldraw(file, config = config)
-        for line_number, entry in enumerate(model, 1):
+        model_body = read_ldraw(file, config = config)
+        model = Model(body = model_body)
+        for line_number, entry in enumerate(model_body, 1):
             if hasattr(entry, 'colour'):
                 print(repr(entry.colour))
             if hasattr(entry, 'geometry') and len(entry.geometry) >= 3:
@@ -66,4 +77,4 @@
                         line_number = line_number,
                     ))
                     print(entry.textual_representation())
-                    print('-' * 25)
\ No newline at end of file
+                    print('-' * 25)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/reference/quadrilaterals.ldr	Sun Jan 21 15:03:38 2018 +0200
@@ -0,0 +1,19 @@
+0 // square
+4 16 -20 0 10 -20 0 20 -10 0 20 -10 0 10
+0 // concave
+4 16 10 0 10 5 0 25 10 0 20 15 0 25
+0 // kite
+4 16 -15 0 -15 -20 0 0 -15 0 5 -10 0 0
+0 // bowtie
+4 16 5 0 -5 5 0 -15 15 0 -5 15 0 -15
+0 // trapezium
+4 16 25 0 10 30 0 20 35 0 20 40 0 10
+0 // skew
+4 16 25 0 -5 25 0 -15 35 0 -15 35 5 -5
+4 16 40 0 -5 40 0 -15 50 5 -15 50 0 -5
+0 // parallelogram
+4 16 25 0 25 30 0 35 40 0 35 35 0 25
+0 // rhombus
+4 16 -5 0 40 -15 0 35 -5 0 30 5 0 35
+0 // collinear
+4 16 20 0 40 30 0 45 30 0 50 30 0 55
--- a/tests/concave.py	Fri Jan 19 13:41:23 2018 +0200
+++ b/tests/concave.py	Sun Jan 21 15:03:38 2018 +0200
@@ -1,5 +1,5 @@
-from math import degrees, pi as π
-from testsuite import warning
+from math import acos, degrees, radians, pi as π
+from testsuite import warning, error
 from geometry import *
 
 def sign_consistency(container):
@@ -11,7 +11,7 @@
     
 
 def concave_test(model):
-    for quadrilateral in model:
+    for quadrilateral in model.quadrilaterals:
         print([cross_product(v2 - v1, v3 - v1) for v1, v2, v3 in pairs(quadrilateral.geometry.vertices, count = 3)])
         z_scores = [
             cross_product(v2 - v1, v3 - v1).z
@@ -19,4 +19,44 @@
         ]
         print(z_scores)
         if not sign_consistency(z_scores):
-            yield warning(quadrilateral, 'Concave quadrilateral')
\ No newline at end of file
+            yield warning(quadrilateral, 'Concave quadrilateral')
+
+def bowtie_quadrilateral_test(model):
+    for quadrilateral in model.quadrilaterals:
+        vertices = IndexRing(quadrilateral.geometry.vertices)
+        for i in (0, 1):
+            line1 = LineSegment(vertices[0 + i], vertices[1 + i])
+            line2 = LineSegment(vertices[2 + i], vertices[3 + i])
+            try:
+                line_intersection(line1, line2)
+            except NoIntersection:
+                pass
+            else:
+                yield error(quadrilateral, 'Bowtie quadrilateral')
+                break
+
+def vector_angle(vec_1, vec_2, normalized = False):
+    cosine = dot_product(vec_1, vec_2)
+    try:
+        cosine /= vec_1.length() * vec_2.length()
+    except ZeroDivisionError:
+        return 0
+    angle = acos(cosine)
+    if normalized and angle > π / 2:
+        angle = π - angle
+    return angle
+
+def skew_test(model):
+    for quadrilateral in model.quadrilaterals:
+        vertices = IndexRing(quadrilateral.geometry.vertices)
+        for i in (0, 1):
+            a, b = vertices[0 + i], vertices[1 + i]
+            c, d = vertices[2 + i], vertices[3 + i]
+            plane_1 = cross_product(b - a, d - a)
+            plane_2 = cross_product(d - c, b - c)
+            angle = vector_angle(plane_1, plane_2, normalized = True)
+            if angle > radians(0.1):
+                yield error(quadrilateral,
+                    'Skew quadrilateral (plane angle {}°)',
+                    '%.3f' % degrees(angle))
+                break
--- a/testsuite.py	Fri Jan 19 13:41:23 2018 +0200
+++ b/testsuite.py	Sun Jan 21 15:03:38 2018 +0200
@@ -3,4 +3,11 @@
         'type': 'warning',
         'object': object,
         'message': str.format(*message),
-    }
\ No newline at end of file
+    }
+
+def error(object, *message):
+    return {
+        'type': 'error',
+        'object': object,
+        'message': str.format(*message),
+    }

mercurial