parse.py

changeset 2
50d3086070df
child 3
1dc58f44d556
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parse.py	Sun Dec 10 15:46:47 2017 +0200
@@ -0,0 +1,106 @@
+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:],
+    )

mercurial