tests/subfiles.py

Wed, 29 May 2019 16:36:23 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 29 May 2019 16:36:23 +0300
changeset 53
0cc196c634f1
parent 37
e46fa477007b
child 54
0c686d10eb49
permissions
-rw-r--r--

fixed the formatting of the license

from testsuite import warning, error
from geometry import *
from os.path import dirname
from pathlib import Path
from configparser import ConfigParser
ini_path = Path(dirname(__file__)) / 'library-standards.ini'
library_standards = ConfigParser()

with ini_path.open() as file:
    library_standards.read_file(file)

def determinant_test(model):
    '''
        Checks all subfile references for matrices with rows or columns all
        zero.
    '''
    yield from (
        error(subfile_reference, 'zero-determinant')
        for subfile_reference in model.subfile_references
        if abs(subfile_reference.matrix.determinant() - 0) < 1e-15
    )

def scaling_description(scaling, axes = 'xyz'):
    '''
        Returns a pretty description of a scaling vector. The axes parameter
        controls what axes are printed and can be used to filter away
        uninteresting values.
    '''
    return ', '.join(
        str.format('{} = {}', letter, getattr(scaling, letter))
        for letter in axes
    )

def check_scaling(scaling, axes):
    ''' Returns whether all given axes on the given scaling vector are 1. '''
    return all(
        abs(getattr(scaling, axis) - 1) < 1e-5
        for axis in axes
    )

# Restriction to checking function mapping.
restriction_tests = {
    'no scaling': lambda scaling: check_scaling(scaling, 'xyz'),
    'y-scaling only': lambda scaling: check_scaling(scaling, 'xz'),
    'stud3-like scaling': lambda scaling: all([
        check_scaling(scaling, 'xz'),
        # check if y-scaling is 1 or -1
        abs(abs(scaling.y) - 1) < 1e-5,
    ]),
}

def scaling_legality_test(model):
    '''
        Checks the part against primitive references with bad scaling. Some
        primitives (e.g. pegs) are not allowed to be scaled in the
        X or Z directions. Some (e.g. most studs) are not allowed to be scaled
        in the Y direction either.
    '''
    from fnmatch import fnmatch
    scaling_restrictions = library_standards['scaling restrictions']
    for subfile_reference in model.subfile_references:
        primitive = subfile_reference.subfile_path.lower()
        scaling = subfile_reference.matrix.scaling_vector()
        # Find all restrictions that apply to this subfile reference.
        restrictions = {
            restriction
            for pattern, restriction in scaling_restrictions.items()
            if fnmatch(primitive, pattern)
        }
        # Check restrictions against the subfile. If any restrictions were
        # found then the scaling vector must pass at least one of them.
        if restrictions and not any(
            restriction_tests[restriction](scaling)
            for restriction in restrictions
        ):
            interesting_axes = ''.join(
                axis
                for axis in 'xyz'
                if abs(getattr(scaling, axis) - 1) > 1e-5
            )
            yield warning(subfile_reference, 'illegal-scaling',
                primitive = primitive,
                axes = interesting_axes,
                scaling = scaling)

manifest = {
    'tests': {
        'determinant': determinant_test,
        'scaling-legality': scaling_legality_test,
    },
    'messages': {
        'zero-determinant': 'matrix determinant is zero '
            '(row or column all zero)',
        'illegal-scaling': lambda primitive, scaling, axes:
            str.format('scaling of unscalable primitive {} ({})',
                primitive,
                scaling_description(scaling, axes),
            ),
    },
}

mercurial