Added ldconfig.ldr support

Thu, 21 Dec 2017 12:27:16 +0200

author
Santeri Piippo
date
Thu, 21 Dec 2017 12:27:16 +0200
changeset 8
303c51137cb2
parent 7
0ab0d61ccee8
child 9
fea8e9ae6f29

Added ldconfig.ldr support

colours.py file | annotate | diff | comparison | revisions
ldraw.py file | annotate | diff | comparison | revisions
ldverify.py file | annotate | diff | comparison | revisions
parse.py file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/colours.py	Thu Dec 21 12:27:16 2017 +0200
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+class Colour:
+    '''
+        Colour interface. Supports LDConfig colours and direct colours.
+        For LDConfig colours to work, load_colours must be called first.
+    '''
+    def __init__(self, index):
+        if not isinstance(index, int):
+            index = int(index, 0)
+        self.index = index
+    def __str__(self):
+        if self.is_direct_colour:
+            # write direct colours with hex codes
+            return '0x%07X' % self.index
+        else:
+            return str(self.index)
+    def __repr__(self):
+        try:
+            return 'colours.' + colours_inverse_dict[self.index]
+        except KeyError:
+            return str.format('Colour({!r})', self.index)
+    @property
+    def is_direct_colour(self):
+        return self.index >= 0x2000000
+    @property
+    def is_ldconfig_colour(self):
+        return self.index in ldconfig_colour_data
+    @property
+    def name(self):
+        if self.is_ldconfig_colour:
+            return ldconfig_colour_data[self.index]['name']
+        else:
+            return str(self)
+    @property
+    def face_colour(self):
+        if self.is_ldconfig_colour:
+            return ldconfig_colour_data[self.index]['value']
+        elif self.is_direct_colour:
+            return '#%06X' % (self.index & 0xffffff)
+        else:
+            return '#000000'
+    @property
+    def is_valid(self):
+        return self.is_ldconfig_colour or self.is_direct_colour
+    def __eq__(self, other):
+        return self.index == other.index
+    def __lt__(self, other):
+        return self.index < other.index
+    def __le__(self, other):
+        return self.index <= other.index
+    def __gt__(self, other):
+        return self.index > other.index
+    def __ge__(self, other):
+        return self.index >= other.index
+
+def parse_ldconfig_ldr_line(line):
+    '''
+        Parses a single LDConfig.ldr line.
+
+        Returns:
+            · a dict for a successful parsed colour.
+            · None for empty lines and comments.
+
+        Raises an error for invalid lines.
+    '''
+    line = line.strip()
+    import re
+    def parse_tag(tag):
+        match = re.search(tag + r'\s+([^ ]+)', line)
+        if match:
+            return match.group(1)
+        else:
+            raise KeyError(str.format('Tag {} not found', tag))
+    # parse 0 !COLOUR and get the name
+    match = re.search(r'^0\s+!COLOUR\s([^ ]+)', line)
+    if not match:
+        # failed, check if it's an empty line or comment:
+        if not line or line.startswith('0'):
+            return None
+        else:
+            # failed too, thus the line is bad
+            raise ValueError('Bad LDConfig.ldr line')
+    # replace underscores for readability
+    name = match.group(1).replace('_', ' ')
+    # parse tags
+    code = int(parse_tag('CODE'))
+    value = parse_tag('VALUE')
+    edge = parse_tag('EDGE')
+    return {
+        'name': name,
+        'code': code,
+        'value': value,
+        'edge': edge,
+    }
+
+def parse_ldconfig_ldr(ldconfig_ldr):
+    '''
+        Parses LDConfig.ldr and returns pairs
+    '''
+    for line in ldconfig_ldr:
+        colour = parse_ldconfig_ldr_line(line)
+        if colour:
+            yield (colour['code'], colour)
+
+class colours:
+    '''
+        LDConfig colour namespace, exists for interactive mode and for
+        Colour.__repr__ to return something pretty.
+    '''
+    pass
+
+# LDConfig lookup tables
+colours_inverse_dict = {}
+ldconfig_colour_data = {}
+
+def load_colours(ldconfig_ldr):
+    '''
+        Loads colours. Expects a file pointer to LDConfig.ldr as the parameter.
+    '''
+    global ldconfig_colour_data
+    ldconfig_colour_data = dict(parse_ldconfig_ldr(ldconfig_ldr))
+    for index, colour in ldconfig_colour_data.items():
+        identifier = colour['name'].replace(' ', '_').lower()
+        setattr(colours, identifier, Colour(index))
+        colours_inverse_dict[index] = identifier
+
+# Interactive mode support (pass LDConfig.ldr path as a command-line argument)
+if __name__ == '__main__':
+    from sys import argv
+    with open(argv[1]) as ldconfig_ldr:
+        load_colours(ldconfig_ldr)
\ No newline at end of file
--- a/ldraw.py	Thu Dec 21 10:46:41 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-class Colour:
-    def __init__(self, index):
-        self.index = int(index, 0)
-    def __str__(self):
-        if self.is_direct_colour():
-            # write direct colours with hex codes
-            return '0x%07X' % self.index
-        else:
-            return str(self.index)
-    def is_direct_colour(self):
-        return self.index >= 0x2000000
--- a/ldverify.py	Thu Dec 21 10:46:41 2017 +0200
+++ b/ldverify.py	Thu Dec 21 12:27:16 2017 +0200
@@ -1,6 +1,12 @@
 #!/usr/bin/env python3
+from sys import version_info
+if version_info < (3, 4):
+    raise RuntimeError('Python 3.4 or newer required')
+
 from parse import parse_ldraw_code
+from colours import load_colours
 from geometry import *
+from pathlib import Path
 
 def read_ldraw(file, *, libraries):
     result = list()
@@ -8,17 +14,31 @@
         result.append(parse_ldraw_code(line))
     return result
 
+def find_ldconfig_ldr_paths(libraries):
+    for library in libraries:
+        ldconfig_paths = [
+            library['path'] / 'LDConfig.ldr',
+            library['path'] / 'ldconfig.ldr',
+        ]
+        for path in ldconfig_paths:
+            if path.is_file():
+                yield path
+
 def hairline_score(smallest_angle):
     from math import log10
     return max(0, -log10(smallest_angle))
 
 if __name__ == '__main__':
     from sys import argv
-    libraries = [{'path': '/home/teemu/ldraw', 'role': 'official'}]
+    libraries = [{'path': Path('~/ldraw').expanduser(), 'role': 'official'}]
+    for ldconfig_ldr_path in find_ldconfig_ldr_paths(libraries):
+        with ldconfig_ldr_path.open() as ldconfig_ldr:
+            load_colours(ldconfig_ldr)
     with open(argv[1], 'r') as file:
         model = read_ldraw(file, libraries = libraries)
-        min_angle_tup = (1e12,)
         for line_number, entry in enumerate(model, 1):
+            if hasattr(entry, 'colour'):
+                print(repr(entry.colour))
             if hasattr(entry, 'geometry') and len(entry.geometry) >= 3:
                 if hairline_score(entry.geometry.smallest_angle) >= 2.0:
                     print(str.format(
--- a/parse.py	Thu Dec 21 10:46:41 2017 +0200
+++ b/parse.py	Thu Dec 21 12:27:16 2017 +0200
@@ -1,7 +1,7 @@
 import linetypes
 import re
 from geometry import *
-from ldraw import Colour
+from colours import Colour
 
 class BadLdrawLine(Exception):
     pass

mercurial