tests/quadrilaterals.py

changeset 62
f0a6bf48b05e
parent 32
75f44d3063da
child 64
1c0884f5506e
equal deleted inserted replaced
61:15c95d3fcfd8 62:f0a6bf48b05e
1 from math import radians 1 from math import radians
2 from testsuite import warning, error, notice 2 from testsuite import problem_type, report_problem
3 from geometry import * 3 from geometry import *
4 4
5 def sign_consistency(container): 5 def sign_consistency(container):
6 # Returns whether all elements in container have the same sign 6 # Returns whether all elements in container have the same sign
7 return min(container) * max(container) >= 0 7 return min(container) * max(container) >= 0
8 8
9 @problem_type('concave', severity = 'error', message = 'concave quadrilateral')
9 def concave_test(model): 10 def concave_test(model):
10 ''' Checks that all quadrilaterals are convex. ''' 11 ''' Checks that all quadrilaterals are convex. '''
11 for quadrilateral in model.quadrilaterals: 12 for quadrilateral in model.quadrilaterals:
12 # Rotate the polygon into the XY plane. Then z is facing 13 # Rotate the polygon into the XY plane. Then z is facing
13 # away from the quadrilateral. 14 # away from the quadrilateral.
17 z_scores = [ 18 z_scores = [
18 cross_product(v2 - v1, v3 - v1).z 19 cross_product(v2 - v1, v3 - v1).z
19 for v1, v2, v3 in pairs(geometry.vertices, count = 3) 20 for v1, v2, v3 in pairs(geometry.vertices, count = 3)
20 ] 21 ]
21 if not sign_consistency(z_scores): 22 if not sign_consistency(z_scores):
22 yield error(quadrilateral, 'concave-error') 23 yield report_problem('concave', bad_object = quadrilateral)
23 24
25 @problem_type('skew-major',
26 severity = 'error',
27 message = lambda skew_angle:
28 str.format('skew quadrilateral (plane angle {})',
29 degree_rep(skew_angle),
30 ),
31 )
32 @problem_type('skew-minor',
33 severity = 'notice',
34 message = lambda skew_angle:
35 str.format('slightly skew quadrilateral (plane angle {})',
36 degree_rep(skew_angle),
37 ),
38 )
24 def skew_test(model): 39 def skew_test(model):
25 ''' Checks that all quadrilaterals are coplanar. ''' 40 ''' Checks that all quadrilaterals are coplanar. '''
26 for quadrilateral in model.quadrilaterals: 41 for quadrilateral in model.quadrilaterals:
27 for triangles in split_quadrilateral(quadrilateral.geometry): 42 for triangles in split_quadrilateral(quadrilateral.geometry):
28 plane_1 = triangle_plane_normal(triangles[0]) 43 plane_1 = triangle_plane_normal(triangles[0])
29 plane_2 = triangle_plane_normal(triangles[1]) 44 plane_2 = triangle_plane_normal(triangles[1])
30 skew_angle = vector_angle(plane_1, plane_2, normalized = True) 45 skew_angle = vector_angle(plane_1, plane_2, normalized = True)
31 if skew_angle > radians(3.0): 46 if skew_angle > radians(3.0):
32 yield error(quadrilateral, 'skew-error', 47 yield report_problem(
48 'skew-major',
49 bad_object = quadrilateral,
33 skew_angle = skew_angle, 50 skew_angle = skew_angle,
34 ) 51 )
35 break 52 break
36 elif skew_angle > radians(1.0): 53 elif skew_angle > radians(1.0):
37 yield warning(quadrilateral, 'skew-warning', 54 yield report_problem(
55 'skew-minor',
56 bad_object = quadrilateral,
38 skew_angle = skew_angle, 57 skew_angle = skew_angle,
39 ) 58 )
40 break 59 break
41 60
61 @problem_type('self-intersecting',
62 severity = 'error',
63 message = 'self-intersecting quadrilateral',
64 )
42 def bowtie_test(model): 65 def bowtie_test(model):
43 for quadrilateral in model.quadrilaterals: 66 for quadrilateral in model.quadrilaterals:
44 geometry = transform_to_xy(quadrilateral.geometry) 67 geometry = transform_to_xy(quadrilateral.geometry)
45 vertices = IndexRing(geometry.vertices) 68 vertices = IndexRing(geometry.vertices)
46 for i in (0, 1): 69 for i in (0, 1):
47 line_1 = LineSegment(vertices[0 + i], vertices[1 + i]) 70 line_1 = LineSegment(vertices[0 + i], vertices[1 + i])
48 line_2 = LineSegment(vertices[2 + i], vertices[3 + i]) 71 line_2 = LineSegment(vertices[2 + i], vertices[3 + i])
49 intersection = line_segment_intersection_xy(line_1, line_2) 72 intersection = line_segment_intersection_xy(line_1, line_2)
50 if intersection: 73 if intersection:
51 yield error(quadrilateral, 'self-intersecting') 74 yield report_problem(
75 'self-intersecting',
76 bad_object = quadrilateral,
77 )
52 break 78 break
53 79
80 @problem_type('collinear', severity = 'error', message = 'collinear polygon')
54 def collinear_test(model): 81 def collinear_test(model):
55 for element in model.body: 82 for element in model.body:
56 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: 83 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3:
57 for a, b, c in pairs(element.geometry.vertices, count = 3): 84 for a, b, c in pairs(element.geometry.vertices, count = 3):
58 if cross_product(b - a, c - a).length() < 1e-5: 85 if cross_product(b - a, c - a).length() < 1e-5:
59 yield error(element, 'collinearity-error') 86 yield report_problem('collinear', bad_object = element)
60 87
61 def hairline_score(smallest_angle): 88 @problem_type('hairline-polygon',
62 from math import log10 89 severity = 'notice',
63 return max(0, -log10(smallest_angle)) 90 message = lambda smallest_angle: str.format(
64 91 'hairline polygon (smallest angle {})',
92 degree_rep(smallest_angle),
93 ),
94 )
65 def hairline_test(model): 95 def hairline_test(model):
66 for element in model.body: 96 for element in model.body:
67 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: 97 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3:
68 smallest_angle = element.geometry.smallest_angle 98 smallest_angle = element.geometry.smallest_angle
69 if smallest_angle < radians(0.5): 99 if smallest_angle < radians(0.5):
70 yield notice(element, 'hairline-warning', 100 yield report_problem(
101 'hairline-polygon',
102 bad_object = element,
71 smallest_angle = smallest_angle, 103 smallest_angle = smallest_angle,
72 ) 104 )
73 105
74 manifest = { 106 manifest = {
75 'tests': { 107 'tests': [
76 'skew': skew_test, 108 skew_test,
77 'concave': concave_test, 109 concave_test,
78 'bowtie': bowtie_test, 110 bowtie_test,
79 'collinearity': collinear_test, 111 collinear_test,
80 'hairline': hairline_test, 112 hairline_test,
81 }, 113 ],
82 'messages': {
83 'skew-error': lambda skew_angle:
84 str.format('skew quadrilateral (plane angle {})',
85 degree_rep(skew_angle),
86 ),
87 'skew-warning': lambda skew_angle:
88 str.format('slightly skew quadrilateral (plane angle {})',
89 degree_rep(skew_angle),
90 ),
91 'concave-error': 'concave quadrilateral',
92 'self-intersecting': 'bowtie quadrilateral',
93 'collinearity-error': 'collinear polygon',
94 'hairline-warning': lambda smallest_angle:
95 str.format('hairline polygon (smallest angle {})',
96 degree_rep(smallest_angle),
97 ),
98 },
99 } 114 }

mercurial