23 yield report_problem('concave', bad_object = quadrilateral) |
23 yield report_problem('concave', bad_object = quadrilateral) |
24 |
24 |
25 @problem_type('skew-major', |
25 @problem_type('skew-major', |
26 severity = 'hold', |
26 severity = 'hold', |
27 message = lambda skew_angle: |
27 message = lambda skew_angle: |
28 str.format('skew quadrilateral (plane angle {})', |
28 str.format('skew (non-coplanar) quadrilateral (plane angle {})', |
29 degree_rep(skew_angle), |
29 degree_rep(skew_angle), |
30 ), |
30 ), |
31 ) |
31 ) |
32 @problem_type('skew-minor', |
32 @problem_type('skew-minor', |
33 severity = 'warning', |
33 severity = 'warning', |
34 message = lambda skew_angle: |
34 message = lambda skew_angle: |
35 str.format('slightly skew quadrilateral (plane angle {})', |
35 str.format('slightly skew (non-coplanar) quadrilateral (plane angle {})', |
36 degree_rep(skew_angle), |
36 degree_rep(skew_angle), |
37 ), |
37 ), |
38 ) |
38 ) |
39 def skew_test(model): |
39 def skew_test(model): |
40 ''' Checks that all quadrilaterals are coplanar. ''' |
40 ''' Checks that all quadrilaterals are coplanar. ''' |
58 ) |
58 ) |
59 break |
59 break |
60 |
60 |
61 @problem_type('self-intersecting', |
61 @problem_type('self-intersecting', |
62 severity = 'hold', |
62 severity = 'hold', |
63 message = 'self-intersecting quadrilateral', |
63 message = 'bowtie (self-intersecting) quadrilateral', |
64 ) |
64 ) |
65 def bowtie_test(model): |
65 def bowtie_test(model): |
66 for quadrilateral in model.quadrilaterals: |
66 for quadrilateral in model.quadrilaterals: |
67 geometry = transform_to_xy(quadrilateral.geometry) |
67 geometry = transform_to_xy(quadrilateral.geometry) |
68 vertices = IndexRing(geometry.vertices) |
68 vertices = IndexRing(geometry.vertices) |
75 'self-intersecting', |
75 'self-intersecting', |
76 bad_object = quadrilateral, |
76 bad_object = quadrilateral, |
77 ) |
77 ) |
78 break |
78 break |
79 |
79 |
80 @problem_type('collinear', severity = 'hold', message = 'collinear polygon') |
80 @problem_type('collinear', |
|
81 severity = 'hold', |
|
82 message = lambda min_angle, max_angle: str.format( |
|
83 'collinear polygon (smallest interior angle {min_angle}, largest {max_angle})', |
|
84 min_angle = degree_rep(min_angle), |
|
85 max_angle = degree_rep(max_angle), |
|
86 ), |
|
87 ) |
81 def collinear_test(model): |
88 def collinear_test(model): |
|
89 from math import radians, cos, acos |
|
90 # according to the LDraw spec, all interior angles must be |
|
91 # between 0.025 degrees and 179.9 degrees. Note that between 0 and pi, |
|
92 # cos(x) is a downwards curve, so the minimum cosine is for the maximum |
|
93 # angle. |
|
94 min_cosine = cos(radians(179.9)) |
|
95 max_cosine = cos(radians(0.025)) |
82 for element in model.body: |
96 for element in model.body: |
83 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: |
97 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: |
84 for a, b, c in pairs(element.geometry.vertices, count = 3): |
98 cosines = list(element.geometry.angle_cosines()) |
85 if cross_product(b - a, c - a).length() < 1e-5: |
99 if any( |
86 yield report_problem('collinear', bad_object = element) |
100 not(min_cosine < cosine < max_cosine) |
87 break |
101 for cosine in cosines |
88 |
102 ): |
89 @problem_type('hairline-polygon', |
|
90 severity = 'warning', |
|
91 message = lambda smallest_angle: str.format( |
|
92 'hairline polygon (smallest angle {})', |
|
93 degree_rep(smallest_angle), |
|
94 ), |
|
95 ) |
|
96 def hairline_test(model): |
|
97 for element in model.body: |
|
98 if hasattr(element, 'geometry') and len(element.geometry.vertices) >= 3: |
|
99 smallest_angle = element.geometry.smallest_angle |
|
100 if smallest_angle < radians(0.5): |
|
101 yield report_problem( |
103 yield report_problem( |
102 'hairline-polygon', |
104 'collinear', |
103 bad_object = element, |
105 bad_object = element, |
104 smallest_angle = smallest_angle, |
106 min_angle = min(map(acos, cosines)), |
|
107 max_angle = max(map(acos, cosines)), |
105 ) |
108 ) |
106 |
109 |
107 manifest = { |
110 manifest = { |
108 'tests': [ |
111 'tests': [ |
109 skew_test, |
112 skew_test, |
110 concave_test, |
113 concave_test, |
111 bowtie_test, |
114 bowtie_test, |
112 collinear_test, |
115 collinear_test, |
113 hairline_test, |
|
114 ], |
116 ], |
115 } |
117 } |