|
1 import linetypes |
|
2 import re |
|
3 from geometry import * |
|
4 |
|
5 class BadLdrawLine(Exception): |
|
6 pass |
|
7 |
|
8 def parse_ldraw_code(line): |
|
9 line = line.strip() |
|
10 if not line: |
|
11 return linetypes.EmptyLine() |
|
12 elif line == '0': |
|
13 return linetypes.Comment('') |
|
14 elif line.startswith('0 '): |
|
15 return linetypes.Comment(line[2:].strip()) |
|
16 elif line.startswith('1 '): |
|
17 return parse_ldraw_subfile_reference(line) |
|
18 elif line.startswith('2 '): |
|
19 return parse_ldraw_line(line) |
|
20 elif line.startswith('3 '): |
|
21 return parse_ldraw_triangle(line) |
|
22 elif line.startswith('4 '): |
|
23 return parse_ldraw_quadrilateral(line) |
|
24 elif line.startswith('5 '): |
|
25 return parse_ldraw_contour(line) |
|
26 else: |
|
27 raise BadLdrawLine('unknown line type') |
|
28 |
|
29 def parse_ldraw_subfile_reference(line): |
|
30 pattern = r'^1\s+(\d+)' + r'\s+([^ ]+)' * (3 + 9 + 1) + r'\s*$' |
|
31 match = re.search(pattern, line) |
|
32 if not match: |
|
33 raise BadLdrawLine('unable to parse') |
|
34 groups = list(match.groups()) |
|
35 indices = { |
|
36 'color': 0, |
|
37 'anchor': slice(1, 4), |
|
38 'matrix': slice(4, 13), |
|
39 'subfile_path': 13 |
|
40 } |
|
41 try: |
|
42 color = int(groups[indices['color']]) |
|
43 vertex_values = [float(x) for x in groups[indices['anchor']]] |
|
44 matrix_values = [float(x) for x in groups[indices['matrix']]] |
|
45 except ValueError: |
|
46 raise BadLdrawLine('bad numeric values') |
|
47 return linetypes.SubfileReference( |
|
48 color = color, |
|
49 anchor = Vertex(*vertex_values), |
|
50 matrix = TransformationMatrix(matrix_values), |
|
51 subfile_path = groups[indices['subfile_path']] |
|
52 ) |
|
53 |
|
54 def generic_parse_polygon(line, *, type_code, vertex_count): |
|
55 pattern = r'^' \ |
|
56 + str(type_code) \ |
|
57 + '\s+(\d+)' \ |
|
58 + r'\s+([^ ]+)' * (vertex_count * 3) \ |
|
59 + r'\s*$' |
|
60 match = re.search(pattern, line) |
|
61 if not match: |
|
62 raise BadLdrawLine(str.format('cannot parse type-{} line', type_code)) |
|
63 vertices = [] |
|
64 for vertex_index in range(vertex_count): |
|
65 slice_begin = 1 + vertex_index * 3 |
|
66 slice_end = 1 + (vertex_index + 1) * 3 |
|
67 coordinates = match.groups()[slice_begin:slice_end] |
|
68 assert(len(coordinates) == 3) |
|
69 try: |
|
70 coordinates = [float(x) for x in coordinates] |
|
71 except ValueError: |
|
72 raise BadLdrawLine('bad numeric values') |
|
73 vertices.append(Vertex(*coordinates)) |
|
74 return { |
|
75 'color': int(match.group(1)), |
|
76 'vertices': vertices, |
|
77 } |
|
78 |
|
79 def parse_ldraw_line(line): |
|
80 parse_result = generic_parse_polygon(line, type_code = 2, vertex_count = 2) |
|
81 return linetypes.LineSegment( |
|
82 color = parse_result['color'], |
|
83 geometry = LineSegment(*parse_result['vertices']), |
|
84 ) |
|
85 |
|
86 def parse_ldraw_triangle(line): |
|
87 parse_result = generic_parse_polygon(line, type_code = 3, vertex_count = 3) |
|
88 return linetypes.Triangle( |
|
89 color = parse_result['color'], |
|
90 geometry = Polygon(parse_result['vertices']), |
|
91 ) |
|
92 |
|
93 def parse_ldraw_quadrilateral(line): |
|
94 parse_result = generic_parse_polygon(line, type_code = 4, vertex_count = 4) |
|
95 return linetypes.Quadrilateral( |
|
96 color = parse_result['color'], |
|
97 geometry = Polygon(parse_result['vertices']), |
|
98 ) |
|
99 |
|
100 def parse_ldraw_contour(line): |
|
101 parse_result = generic_parse_polygon(line, type_code = 5, vertex_count = 4) |
|
102 return linetypes.Contour( |
|
103 color = parse_result['color'], |
|
104 geometry = LineSegment(*parse_result['vertices'][0:2]), |
|
105 control_points = parse_result['vertices'][2:], |
|
106 ) |