tests/quadrilaterals.py

Mon, 24 Jun 2019 19:21:49 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Mon, 24 Jun 2019 19:21:49 +0300
changeset 95
a3536e51f6bc
parent 90
6ae063fd27dc
child 103
662de6b8cfc2
permissions
-rw-r--r--

fixed the cyclical dependency handling

from math import radians
from testsuite import problem_type, report_problem
from geometry import *

def sign_consistency(container):
    # Returns whether all elements in container have the same sign
    return min(container) * max(container) >= 0

@problem_type('concave', severity = 'hold', message = 'concave quadrilateral')
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 report_problem('concave', bad_object = quadrilateral)

@problem_type('skew-major',
    severity = 'hold',
    message = lambda skew_angle:
        str.format('skew quadrilateral (plane angle {})',
            degree_rep(skew_angle),
        ),
)
@problem_type('skew-minor',
    severity = 'warning',
    message = lambda skew_angle:
        str.format('slightly skew quadrilateral (plane angle {})',
            degree_rep(skew_angle),
        ),
)
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 report_problem(
                    'skew-major',
                    bad_object = quadrilateral,
                    skew_angle = skew_angle,
                )
                break
            elif skew_angle > radians(1.0):
                yield report_problem(
                    'skew-minor',
                    bad_object = quadrilateral,
                    skew_angle = skew_angle,
                )
                break

@problem_type('self-intersecting',
    severity = 'hold',
    message = 'self-intersecting quadrilateral',
)
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 report_problem(
                    'self-intersecting',
                    bad_object = quadrilateral,
                )
                break

@problem_type('collinear', severity = 'hold', message = 'collinear polygon')
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 report_problem('collinear', bad_object = element)
                    break

@problem_type('hairline-polygon',
    severity = 'warning',
    message = lambda smallest_angle: str.format(
        'hairline polygon (smallest angle {})',
        degree_rep(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 report_problem(
                    'hairline-polygon',
                    bad_object = element,
                    smallest_angle = smallest_angle,
                )

manifest = {
    'tests': [
        skew_test,
        concave_test,
        bowtie_test,
        collinear_test,
        hairline_test,
    ],
}

mercurial