1 #!/usr/bin/env python3 |
1 #!/usr/bin/env python3 |
2 from sys import version_info |
2 from sys import version_info |
3 if version_info < (3, 4): |
3 if version_info < (3, 4): |
4 raise RuntimeError('Python 3.4 or newer required') |
4 raise RuntimeError('Python 3.4 or newer required') |
5 |
5 |
6 from parse import parse_ldraw_code |
|
7 from colours import load_colours |
6 from colours import load_colours |
8 from geometry import * |
7 from geometry import * |
9 from pathlib import Path |
8 from pathlib import Path |
10 import linetypes |
9 import linetypes |
11 import header |
10 import header |
|
11 import parse |
12 |
12 |
13 from os.path import realpath |
13 from os.path import realpath |
14 script_directory = Path(realpath(__file__)).parent |
14 script_directory = Path(realpath(__file__)).parent |
15 |
15 |
16 def load_config(filename): |
16 def load_config(filename): |
22 config['libraries'] = ['/path/to/ldraw'] |
22 config['libraries'] = ['/path/to/ldraw'] |
23 if config != read_config: |
23 if config != read_config: |
24 config.write() |
24 config.write() |
25 check_library_paths(config) |
25 check_library_paths(config) |
26 return config |
26 return config |
27 |
|
28 def read_ldraw(file, *, name = '', config): |
|
29 model_body = [ |
|
30 parse_ldraw_code(line) |
|
31 for line in file |
|
32 ] |
|
33 headerparser = header.HeaderParser() |
|
34 try: |
|
35 header_parse_result = headerparser.parse(model_body) |
|
36 header_object = header_parse_result['header'] |
|
37 end = header_parse_result['end-index'] |
|
38 except header.HeaderError as error: |
|
39 header_object = header.BadHeader(error.index, error.reason) |
|
40 end = 0 |
|
41 model = Model( |
|
42 header = header_object, |
|
43 body = model_body, |
|
44 header_size = end) |
|
45 model.name = name |
|
46 return model |
|
47 |
27 |
48 def library_paths(config): |
28 def library_paths(config): |
49 for library_path_string in config['libraries']: |
29 for library_path_string in config['libraries']: |
50 yield Path(library_path_string).expanduser() |
30 yield Path(library_path_string).expanduser() |
51 |
31 |
80 library_path / path |
60 library_path / path |
81 for path in ['LDConfig.ldr', 'ldconfig.ldr'] |
61 for path in ['LDConfig.ldr', 'ldconfig.ldr'] |
82 if (library_path / path).is_file() |
62 if (library_path / path).is_file() |
83 ] |
63 ] |
84 |
64 |
85 class Model: |
|
86 def __init__(self, header, body, *, header_size = 0): |
|
87 self.header = header |
|
88 self.body = body |
|
89 self.header_size = header_size |
|
90 def filter_by_type(self, type): |
|
91 yield from [ |
|
92 element |
|
93 for element in self.body |
|
94 if isinstance(element, type) |
|
95 ] |
|
96 @property |
|
97 def subfile_references(self): |
|
98 yield from self.filter_by_type(linetypes.SubfileReference) |
|
99 @property |
|
100 def line_segments(self): |
|
101 yield from self.filter_by_type(linetypes.LineSegment) |
|
102 @property |
|
103 def triangles(self): |
|
104 yield from self.filter_by_type(linetypes.Triangle) |
|
105 @property |
|
106 def quadrilaterals(self): |
|
107 yield from self.filter_by_type(linetypes.Quadrilateral) |
|
108 @property |
|
109 def has_header(self): |
|
110 return self.header and not isinstance(self.header, header.BadHeader) |
|
111 |
|
112 import argparse |
65 import argparse |
113 class ListTestSuiteAction(argparse.Action): |
66 class ListTestSuiteAction(argparse.Action): |
114 def __init__(self, option_strings, dest, nargs = None, **kwargs): |
67 def __init__(self, option_strings, dest, nargs = None, **kwargs): |
115 super().__init__(option_strings, dest, nargs = 0, **kwargs) |
68 super().__init__(option_strings, dest, nargs = 0, **kwargs) |
116 def __call__(self, *args, **kwargs): |
69 def __call__(self, *args, **kwargs): |
132 action = ListTestSuiteAction, |
85 action = ListTestSuiteAction, |
133 help = 'Lists all possible checks and exit', |
86 help = 'Lists all possible checks and exit', |
134 ) |
87 ) |
135 parser.add_argument('--dump-structure', action = 'store_true') |
88 parser.add_argument('--dump-structure', action = 'store_true') |
136 parser.add_argument('--rebuild', action = 'store_true') |
89 parser.add_argument('--rebuild', action = 'store_true') |
|
90 parser.add_argument('--flatness', action = 'store_true') |
137 args = parser.parse_args() |
91 args = parser.parse_args() |
138 config = load_config('ldcheck.cfg') |
92 config = load_config('ldcheck.cfg') |
139 for ldconfig_ldr_path in find_ldconfig_ldr_paths(config): |
93 for ldconfig_ldr_path in find_ldconfig_ldr_paths(config): |
140 with ldconfig_ldr_path.open() as ldconfig_ldr: |
94 with ldconfig_ldr_path.open() as ldconfig_ldr: |
141 load_colours(ldconfig_ldr) |
95 load_colours(ldconfig_ldr) |
142 with open(args.filename) as file: |
96 if args.flatness: |
143 from os.path import basename |
97 import filecache |
144 model = read_ldraw( |
98 cache = filecache.SubfileCache( |
145 file, |
99 ldraw_directories = config['libraries'], |
146 name = basename(args.filename), |
|
147 config = config, |
|
148 ) |
100 ) |
149 if args.dump_structure: |
101 subfile = cache.prepare_file(args.filename) |
150 print('header: ' + type(model.header).__name__) |
102 if not subfile.valid: |
151 for key in sorted(dir(model.header)): |
103 print(subfile.problem) |
152 if not key.startswith('__'): |
|
153 print('\t' + key + ': ' + repr(getattr(model.header, key))) |
|
154 for entry in model.body: |
|
155 print(entry) |
|
156 elif args.rebuild: |
|
157 for entry in model.body: |
|
158 print(entry.textual_representation(), end = '\r\n') |
|
159 else: |
104 else: |
160 from testsuite import load_tests, check_model, format_report |
105 if subfile.flatness: |
161 test_suite = load_tests() |
106 print(str.format( |
162 report = check_model(model, test_suite) |
107 'Flatness: {}', |
163 print(format_report(report, model, test_suite)) |
108 ', '.join(subfile.flatness), |
|
109 )) |
|
110 else: |
|
111 print('File is not flat in any dimensions') |
|
112 else: |
|
113 with open(args.filename) as file: |
|
114 from os.path import basename |
|
115 model = parse.read_ldraw( |
|
116 file, |
|
117 name = basename(args.filename), |
|
118 ldraw_directories = config['libraries']) |
|
119 if args.dump_structure: |
|
120 print('header: ' + type(model.header).__name__) |
|
121 for key in sorted(dir(model.header)): |
|
122 if not key.startswith('__'): |
|
123 print('\t' + key + ': ' + repr(getattr(model.header, key))) |
|
124 for entry in model.body: |
|
125 print(entry) |
|
126 elif args.rebuild: |
|
127 for entry in model.body: |
|
128 print(entry.textual_representation(), end = '\r\n') |
|
129 else: |
|
130 from testsuite import load_tests, check_model, format_report |
|
131 test_suite = load_tests() |
|
132 report = check_model(model, test_suite) |
|
133 print(format_report(report, model, test_suite)) |