Added matrix code, moved library paths to ldcheck.cfg.

Fri, 22 Dec 2017 15:13:43 +0200

author
Santeri Piippo
date
Fri, 22 Dec 2017 15:13:43 +0200
changeset 9
fea8e9ae6f29
parent 8
303c51137cb2
child 10
ccc33237949e

Added matrix code, moved library paths to ldcheck.cfg.

geometry.py file | annotate | diff | comparison | revisions
ldcheck.py file | annotate | diff | comparison | revisions
ldverify.py file | annotate | diff | comparison | revisions
--- a/geometry.py	Thu Dec 21 12:27:16 2017 +0200
+++ b/geometry.py	Fri Dec 22 15:13:43 2017 +0200
@@ -205,28 +205,107 @@
 def is_real(number):
     return isinstance(number, int) or isinstance(number, float)
 
+def matrix_indices():
+    '''
+        Yields index pairs for matrix iteration
+    '''
+    from itertools import product
+    yield from product(range(3), range(3))
+
 class Matrix3x3:
     '''
         A 3×3 matrix forming the top-left portion of a full 4×4 transformation
         matrix.
     '''
-    def __init__(self, values):
-        assert(all(is_real(x) for x in values))
-        assert len(values) == 9
-        self.values = values
+    class Row:
+        def __init__(self, matrix_ref, i1, i2, i3):
+            self.matrix_ref = matrix_ref
+            self.matrix_indices = i1, i2, i3
+        def __getitem__(self, row_index):
+            return self.matrix_ref[self.matrix_indices[row_index]]
+        def __setitem__(self, row_index, value):
+            self.matrix_ref[self.matrix_indices[row_index]] = value
+    def __init__(self, values = None):
+        if values is not None:
+            assert(all(is_real(x) for x in values))
+            assert len(values) == 9
+            self.values = values
+        else:
+            self.values = [1, 0, 0, 0, 1, 0, 0, 0, 1]
     def __repr__(self):
-        return str.format('Matrix3x3({!r})', self.values)
+        if self != Matrix3x3():
+            return str.format('Matrix3x3({!r})', self.values)
+        else:
+            return 'Matrix3x3()'
     def __getitem__(self, index):
-        return self.values[index]
+        if isinstance(index, tuple):
+            assert all(k in [0, 1, 2] for k in index)
+            return self.values[index[0] * 3 + index[1]]
+        else:
+            return Matrix3x3.Row(self, index * 3, index * 3 + 1, index * 3 + 2)
     def determinant(self):
-        v = self.values
         return sum([
-            +(v[0] * v[4] * v[8]),
-            +(v[1] * v[5] * v[6]),
-            +(v[2] * v[3] * v[7]),
-            -(v[2] * v[4] * v[6]),
-            -(v[1] * v[3] * v[8]),
-            -(v[0] * v[5] * v[7]),
+            +(self[0, 0] * self[1, 1] * self[2, 2]),
+            +(self[0, 1] * self[1, 2] * self[2, 0]),
+            +(self[0, 2] * self[1, 0] * self[2, 1]),
+            -(self[0, 2] * self[1, 1] * self[2, 0]),
+            -(self[0, 1] * self[1, 0] * self[2, 2]),
+            -(self[0, 0] * self[1, 2] * self[2, 1]),
+        ])
+    @property
+    def scaling_vector(self):
+        '''
+            Extracts scaling factors from this transformation matrix.
+        '''
+        from math import sqrt
+        return Vector(
+            x = sqrt(self[0, 0] ** 2 + self[1, 0] ** 2 + self[2, 0] ** 2),
+            y = sqrt(self[0, 1] ** 2 + self[1, 1] ** 2 + self[2, 1] ** 2),
+            z = sqrt(self[0, 2] ** 2 + self[1, 2] ** 2 + self[2, 2] ** 2),
+        )
+    @property
+    def rotation_component(self):
+        '''
+            Extracts rotation from this matrix.
+        '''
+        return self.split()[0]
+    def split(self):
+        '''
+            Extracts the rotation matrix and scaling vector.
+        '''
+        vec = self.scaling_vector
+        return Matrix3x3([
+            self[i, j] / vec.coordinates[j]
+            for i, j in matrix_indices()
+        ]), vec
+    @property
+    def contains_scaling(self):
+        '''
+            Returns whether this matrix contains scaling factors.
+        '''
+        vec = self.scaling_vector
+        return abs((vec.x * vec.y * vec.z) - 1) >= 1e-10
+    @property
+    def contains_rotation(self):
+        '''
+            Returns whether this matrix contains rotation factors.
+        '''
+        return self.rotation_component != Matrix3x3()
+    def __eq__(self, other):
+        '''
+            Returns whether two matrices are equivalent.
+        '''
+        return all(
+            abs(self[(i, j)] - other[(i, j)]) < 1e-10
+            for i, j in matrix_indices()
+        )
+    def __matmul__(self, other):
+        '''
+            Computes the matrix multiplication self × other.
+        '''
+        return Matrix3x3([
+            sum(self[i, k] * other[k, j] for k in range(3))
+            for i, j in matrix_indices()
         ])
 
 def complete_matrix(matrix, anchor):
@@ -235,9 +314,9 @@
         transformation matrix.
     '''
     return [
-        matrix[0], matrix[1], matrix[2], anchor.x,
-        matrix[3], matrix[4], matrix[5], anchor.y,
-        matrix[6], matrix[7], matrix[8], anchor.z,
+        matrix[0, 0], matrix[0, 1], matrix[0, 2], anchor.x,
+        matrix[1, 0], matrix[1, 1], matrix[1, 2], anchor.y,
+        matrix[2, 0], matrix[2, 1], matrix[2, 2], anchor.z,
         0, 0, 0, 1,
     ]
 
@@ -245,16 +324,17 @@
     '''
         Transforms a vertex by a 4×4 transformation matrix.
     '''
-    u = transformation_matrix[0] * vertex.x \
-        + transformation_matrix[1] * vertex.y \
-        + transformation_matrix[2] * vertex.z \
-        + transformation_matrix[3]
-    v = transformation_matrix[4] * vertex.x \
-        + transformation_matrix[5] * vertex.y \
-        + transformation_matrix[6] * vertex.z \
-        + transformation_matrix[7]
-    w = transformation_matrix[8] * vertex.x \
-        + transformation_matrix[9] * vertex.y \
-        + transformation_matrix[10] * vertex.z \
-        + transformation_matrix[11]
-    return Vertex(u, v, w)
+    return Vertex(
+        x = transformation_matrix[0] * vertex.x \
+            + transformation_matrix[1] * vertex.y \
+            + transformation_matrix[2] * vertex.z \
+            + transformation_matrix[3],
+        y = transformation_matrix[4] * vertex.x \
+            + transformation_matrix[5] * vertex.y \
+            + transformation_matrix[6] * vertex.z \
+            + transformation_matrix[7],
+        z = transformation_matrix[8] * vertex.x \
+            + transformation_matrix[9] * vertex.y \
+            + transformation_matrix[10] * vertex.z \
+            + transformation_matrix[11],
+    )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ldcheck.py	Fri Dec 22 15:13:43 2017 +0200
@@ -0,0 +1,69 @@
+#!/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
+
+from os.path import realpath
+script_directory = Path(realpath(__file__)).parent
+
+def load_config(filename):
+    from configobj import ConfigObj
+    from copy import deepcopy
+    config = ConfigObj(filename, encoding = 'UTF8')
+    read_config = deepcopy(config)
+    if 'libraries' not in config:
+        config['libraries'] = ['/path/to/ldraw']
+    if config != read_config:
+        config.write()
+    return config
+
+def read_ldraw(file, *, config):
+    result = list()
+    for line in file:
+        result.append(parse_ldraw_code(line))
+    return result
+
+def library_paths(config):
+    for library_path_string in config['libraries']:
+        yield Path(library_path_string).expanduser()
+
+def find_ldconfig_ldr_paths(config):
+    for library_path in library_paths(config):
+        ldconfig_paths = [
+            library_path / 'LDConfig.ldr',
+            library_path / 'ldconfig.ldr',
+        ]
+        for path in ldconfig_paths:
+            print(path)
+            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
+    config = load_config('ldcheck.cfg')
+    for ldconfig_ldr_path in find_ldconfig_ldr_paths(config):
+        with ldconfig_ldr_path.open() as ldconfig_ldr:
+            load_colours(ldconfig_ldr)
+    with open(argv[1], 'r') as file:
+        model = read_ldraw(file, config = config)
+        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(
+                        'Hairline {type} at line {line_number}',
+                        type = entry.typename(),
+                        line_number = line_number,
+                    ))
+                    print(entry.textual_representation())
+                    print('-' * 25)
\ No newline at end of file
--- a/ldverify.py	Thu Dec 21 12:27:16 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#!/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()
-    for line in file:
-        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': 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)
-        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(
-                        'Hairline {type} at line {line_number}',
-                        type = entry.typename(),
-                        line_number = line_number,
-                    ))
-                    print(entry.textual_representation())
-                    print('-' * 25)
\ No newline at end of file

mercurial