ldcheck.py

changeset 54
0c686d10eb49
parent 47
4da025d0b283
child 62
f0a6bf48b05e
equal deleted inserted replaced
53:0cc196c634f1 54:0c686d10eb49
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))

mercurial