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 } |