Wed, 29 May 2019 16:36:23 +0300
fixed the formatting of the license
from math import radians from testsuite import warning, error, notice from geometry import * def sign_consistency(container): # Returns whether all elements in container have the same sign return min(container) * max(container) >= 0 def concave_test(model): ''' Checks that all quadrilaterals are convex. ''' for quadrilateral in model.quadrilaterals: # Rotate the polygon into the XY plane. Then z is facing # away from the quadrilateral. geometry = transform_to_xy(quadrilateral.geometry) # Now do a 2D concavity test: # https://math.stackexchange.com/a/1745427 z_scores = [ cross_product(v2 - v1, v3 - v1).z for v1, v2, v3 in pairs(geometry.vertices, count = 3) ] if not sign_consistency(z_scores): yield error(quadrilateral, 'concave-error') def skew_test(model): ''' Checks that all quadrilaterals are coplanar. ''' for quadrilateral in model.quadrilaterals: for triangles in split_quadrilateral(quadrilateral.geometry): plane_1 = triangle_plane_normal(triangles[0]) plane_2 = triangle_plane_normal(triangles[1]) skew_angle = vector_angle(plane_1, plane_2, normalized = True) if skew_angle > radians(3.0): yield error(quadrilateral, 'skew-error', skew_angle = skew_angle, ) break elif skew_angle > radians(1.0): yield warning(quadrilateral, 'skew-warning', skew_angle = skew_angle, ) break def bowtie_test(model): for quadrilateral in model.quadrilaterals: geometry = transform_to_xy(quadrilateral.geometry) vertices = IndexRing(geometry.vertices) for i in (0, 1): line_1 = LineSegment(vertices[0 + i], vertices[1 + i]) line_2 = LineSegment(vertices[2 + i], vertices[3 + i]) intersection = line_segment_intersection_xy(line_1, line_2) if intersection: yield error(quadrilateral, 'self-intersecting') break def collinear_test(model): 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 error(element, 'collinearity-error') def hairline_score(smallest_angle): from math import log10 return max(0, -log10(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): yield notice(element, 'hairline-warning', smallest_angle = smallest_angle, ) manifest = { 'tests': { 'skew': skew_test, 'concave': concave_test, 'bowtie': bowtie_test, 'collinearity': collinear_test, 'hairline': hairline_test, }, 'messages': { 'skew-error': lambda skew_angle: str.format('skew quadrilateral (plane angle {})', degree_rep(skew_angle), ), 'skew-warning': lambda skew_angle: str.format('slightly skew quadrilateral (plane angle {})', degree_rep(skew_angle), ), 'concave-error': 'concave quadrilateral', 'self-intersecting': 'bowtie quadrilateral', 'collinearity-error': 'collinear polygon', 'hairline-warning': lambda smallest_angle: str.format('hairline polygon (smallest angle {})', degree_rep(smallest_angle), ), }, }