ldraw.py

Sun, 10 Dec 2017 15:45:50 +0200

author
Santeri Piippo
date
Sun, 10 Dec 2017 15:45:50 +0200
changeset 1
5411a25cfca7
parent 0
55b4c97d44c5
child 2
50d3086070df
permissions
-rwxr-xr-x

Parsing function complete

#!/usr/bin/env python3
import linetypes
import re
from geometry import *

class BadLdrawLine(Exception):
    pass

def parse_ldraw_code(line):
    line = line.strip()
    if not line:
        return linetypes.EmptyLine()
    elif line == '0':
        return linetypes.Comment('')
    elif line.startswith('0 '):
        return linetypes.Comment(line[2:].strip())
    elif line.startswith('1 '):
        return parse_ldraw_subfile_reference(line)
    elif line.startswith('2 '):
        return parse_ldraw_line(line)
    elif line.startswith('3 '):
        return parse_ldraw_triangle(line)
    elif line.startswith('4 '):
        return parse_ldraw_quadrilateral(line)
    elif line.startswith('5 '):
        return parse_ldraw_contour(line)
    else:
        raise BadLdrawLine('unknown line type')

def parse_ldraw_subfile_reference(line):
    pattern = r'^1\s+(\d+)' + r'\s+([^ ]+)' * (3 + 9 + 1) + r'\s*$'
    match = re.search(pattern, line)
    if not match:
        raise BadLdrawLine('unable to parse')
    groups = list(match.groups())
    indices = {
        'color': 0,
        'anchor': slice(1, 4),
        'matrix': slice(4, 13),
        'subfile_path': 13
    }
    try:
        color = int(groups[indices['color']])
        vertex_values = [float(x) for x in groups[indices['anchor']]]
        matrix_values = [float(x) for x in groups[indices['matrix']]]
    except ValueError:
        raise BadLdrawLine('bad numeric values')
    return linetypes.SubfileReference(
        color = color,
        anchor = Vertex(*vertex_values),
        matrix = TransformationMatrix(matrix_values),
        subfile_path = groups[indices['subfile_path']]
    )

def generic_parse_polygon(line, *, type_code, vertex_count):
    pattern = r'^' \
        + str(type_code) \
        + '\s+(\d+)' \
        + r'\s+([^ ]+)' * (vertex_count * 3) \
        + r'\s*$'
    match = re.search(pattern, line)
    if not match:
        raise BadLdrawLine(str.format('cannot parse type-{} line', type_code))
    vertices = []
    for vertex_index in range(vertex_count):
        slice_begin = 1 + vertex_index * 3
        slice_end = 1 + (vertex_index + 1) * 3
        coordinates = match.groups()[slice_begin:slice_end]
        assert(len(coordinates) == 3)
        try:
            coordinates = [float(x) for x in coordinates]
        except ValueError:
            raise BadLdrawLine('bad numeric values')
        vertices.append(Vertex(*coordinates))
    return {
        'color': int(match.group(1)),
        'vertices': vertices,
    }

def parse_ldraw_line(line):
    parse_result = generic_parse_polygon(line, type_code = 2, vertex_count = 2)
    return linetypes.LineSegment(
        color = parse_result['color'],
        geometry = LineSegment(*parse_result['vertices']),
    )

def parse_ldraw_triangle(line):
    parse_result = generic_parse_polygon(line, type_code = 3, vertex_count = 3)
    return linetypes.Triangle(
        color = parse_result['color'],
        geometry = Polygon(parse_result['vertices']),
    )

def parse_ldraw_quadrilateral(line):
    parse_result = generic_parse_polygon(line, type_code = 4, vertex_count = 4)
    return linetypes.Quadrilateral(
        color = parse_result['color'],
        geometry = Polygon(parse_result['vertices']),
    )

def parse_ldraw_contour(line):
    parse_result = generic_parse_polygon(line, type_code = 5, vertex_count = 4)
    return linetypes.Contour(
        color = parse_result['color'],
        geometry = LineSegment(*parse_result['vertices'][0:2]),
        control_points = parse_result['vertices'][2:],
    )

def read_ldraw(file, *, libraries):
    result = list()
    for line in file:
        result.append(parse_ldraw_code(line))
    return result

if __name__ == '__main__':
    from sys import argv
    libraries = [{'path': '/home/teemu/ldraw', 'role': 'official'}]
    with open(argv[1], 'r') as file:
        model = read_ldraw(file, libraries = libraries)
        from pprint import pprint
        pprint(model)

mercurial