From 6a5b1b6dc3988059193312eb239bed6b1681d088 Mon Sep 17 00:00:00 2001 From: Debesh Mandal Date: Thu, 12 Nov 2020 11:48:00 +0000 Subject: [PATCH 1/4] added nucleotide transformation methods --- drawNA/oxdna/nucleotide.py | 61 +++++++++++++++++++++++++++++- drawNA/polygons.py | 3 +- test/test_oxdna/test_nucleotide.py | 29 ++++++++++++++ test/test_polygons.py | 2 +- 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/drawNA/oxdna/nucleotide.py b/drawNA/oxdna/nucleotide.py index 9aaf4ea..e9a00c2 100644 --- a/drawNA/oxdna/nucleotide.py +++ b/drawNA/oxdna/nucleotide.py @@ -3,8 +3,9 @@ """ import numpy as np import pandas as pd +from scipy.spatial.transform import Rotation -from typing import List +from typing import List, Union import warnings from .utils import get_rotation_matrix @@ -304,4 +305,62 @@ def make_across(self) -> "Nucleotide": self.across = nucleotide return nucleotide + def transform(self, matrix: np.ndarray): + vecs = [self.pos_com, self._a1, self._a3, self._v, self._L] + + if matrix.shape == (3, 3): + for vec in vecs: + vec = np.dot(matrix, vec.T) + + + elif matrix.shape == (3, 4): + for vec in vecs: + vec = np.dot(matrix, np.hstack([vec, np.ones((3, 1))]).T) + + else: + raise TypeError( + f'Transformation Matrix must have shape (3, 3) ' + f'or (3, 4) but has shape {matrix.shape}' + ) + + return + + def translate(self, translation_vector: np.ndarray): + + if isinstance(translation_vector, list): + translation_vector = np.array(translation_vector) + + if not (translation_vector.shape == (1, 3) or + translation_vector.shape == (3, )): + raise TypeError( + f'Translation vector has the wrong shape ' + f'({translation_vector.shape})' + ) + self.pos_com += translation_vector + return + + def rotate(self, rotator: np.ndarray): + + if isinstance(rotator, list): + rotator = np.array(rotator) + + # if 1d array with length 3 -> euler angles + if rotator.shape == (1, 3) or rotator.shape == (3, ): + matrix = Rotation.from_euler('xyz', rotator).as_matrix() + + # if 1d array with length 4 -> quaternion + elif rotator.shape == (1, 4) or rotator.shape == (4, ): + matrix = Rotation(rotator).as_matrix() + + # if 2d array use transform + elif rotator.shape == (3, 3): + matrix = rotator + + else: + raise TypeError( + f'Rotator was passed that is not valid:\n{rotator}' + ) + + self.transform(matrix) + return diff --git a/drawNA/polygons.py b/drawNA/polygons.py index d4f59de..dea668a 100644 --- a/drawNA/polygons.py +++ b/drawNA/polygons.py @@ -2,7 +2,6 @@ from typing import List import numpy as np -import meshio import matplotlib.pyplot as plt import matplotlib.tri as tri @@ -89,7 +88,7 @@ def write_STL(self, fout: str): triangulation = tri.Triangulation(self.vertices[:, 0], self.vertices[:, 1]) triangles = triangulation.get_masked_triangles() cells = [("triangle", triangles)] - meshio.write_points_cells(fout, self.vertices, cells) + #meshio.write_points_cells(fout, self.vertices, cells) def write_PLY(self, fout: str, comments: List[str] = []): """ diff --git a/test/test_oxdna/test_nucleotide.py b/test/test_oxdna/test_nucleotide.py index cf2f0ea..4f8bfe0 100644 --- a/test/test_oxdna/test_nucleotide.py +++ b/test/test_oxdna/test_nucleotide.py @@ -51,6 +51,35 @@ def test_Nucleotide_across(): new_2.index = 1 assert nucleotide.across == new_2.index assert new_2.across == nucleotide.index + +def test_Nucleotide_transform(): + nucleotide = Nucleotide( + "A", + np.array([1.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0.0, 0.0, 1.0]), + ) + + nucleotide.translate(np.array([10., 0., 0.])) + assert all(nucleotide.pos_com == [11., 0., 0.]) + + nucleotide.rotate([0., 0., 0., 1.]) + nucleotide.rotate([0., 0., 0.]) + nucleotide.rotate( + np.array([ + [1., 0., 0.], + [0., 1., 0.], + [0., 0., 0.], + ]) + ) + + nucleotide.transform(np.array([ + [1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + ])) + + return if __name__ == "__main__": test_Nucleotide() diff --git a/test/test_polygons.py b/test/test_polygons.py index 91c0cfd..c87346c 100644 --- a/test/test_polygons.py +++ b/test/test_polygons.py @@ -34,7 +34,7 @@ def test_BoundaryPolygon(): assert isinstance(square.edges[0], Edge) # square.plot2D(show=False) - square.write_STL(f"{ROOT}/square.stl") + # square.write_STL(f"{ROOT}/square.stl") square.write_PLY(f"{ROOT}/square.ply") From 0471304a65ec9e95f8c369a673f64ecb45e2fd99 Mon Sep 17 00:00:00 2001 From: Debesh Mandal Date: Thu, 12 Nov 2020 12:01:44 +0000 Subject: [PATCH 2/4] updated transformations for strands and systems --- .github/workflows/coverage.yml | 5 +---- drawNA/oxdna/strand.py | 15 +++++++++++++++ drawNA/oxdna/system.py | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 54cdb2a..bb0b363 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -2,10 +2,7 @@ name: Coverage -on: - push: - branches: - [master] +on: push jobs: coverage: diff --git a/drawNA/oxdna/strand.py b/drawNA/oxdna/strand.py index f4a6430..49e2553 100644 --- a/drawNA/oxdna/strand.py +++ b/drawNA/oxdna/strand.py @@ -153,6 +153,21 @@ def sequence(self, seq: str): def copy(self): return deepcopy(Strand(self._nucleotides)) + def transform(self, matrix: np.ndarray): + for nucleotide in self._nucleotides: + nucleotide.transform(matrix) + return + + def translate(self, translation_vector: np.ndarray): + for nucleotide in self._nucleotides: + nucleotide.translate(translation_vector) + return + + def rotate(self, rotator: np.ndarray): + for nucleotide in self._nucleotides: + nucleotide.rotate(rotator) + return + def generate_helix( n: int = None, diff --git a/drawNA/oxdna/system.py b/drawNA/oxdna/system.py index 7784a41..16bda1f 100644 --- a/drawNA/oxdna/system.py +++ b/drawNA/oxdna/system.py @@ -265,3 +265,21 @@ def add_strands(self, strand_obj: (list or dict) = None, index: int = None): raise TypeError( "add_strands() requires ONE of a list or dictionary of strands" ) + + def transform(self, matrix: np.ndarray): + for strand in self._strands: + for nucleotide in strand._nucleotides: + nucleotide.transform(matrix) + return + + def translate(self, translation_vector: np.ndarray): + for strand in self._strands: + for nucleotide in strand._nucleotides: + nucleotide.translate(translation_vector) + return + + def rotate(self, rotator: np.ndarray): + for strand in self._strands: + for nucleotide in strand._nucleotides: + nucleotide.rotate(rotator) + return From bf2265f6a4ebe2a5551b067b12c8f6f3084f4b8e Mon Sep 17 00:00:00 2001 From: Debesh Mandal Date: Thu, 12 Nov 2020 12:21:56 +0000 Subject: [PATCH 3/4] updated transformations to work now --- drawNA/oxdna/nucleotide.py | 2 +- test/test_oxdna/test_strand.py | 61 ++++++++++++++++++++++++++++++++++ test/test_oxdna/test_system.py | 19 +++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/drawNA/oxdna/nucleotide.py b/drawNA/oxdna/nucleotide.py index e9a00c2..5b07952 100644 --- a/drawNA/oxdna/nucleotide.py +++ b/drawNA/oxdna/nucleotide.py @@ -316,7 +316,7 @@ def transform(self, matrix: np.ndarray): elif matrix.shape == (3, 4): for vec in vecs: - vec = np.dot(matrix, np.hstack([vec, np.ones((3, 1))]).T) + vec = np.dot(matrix, np.hstack([vec, [1]])) else: raise TypeError( diff --git a/test/test_oxdna/test_strand.py b/test/test_oxdna/test_strand.py index 70dac40..4a90d5d 100644 --- a/test/test_oxdna/test_strand.py +++ b/test/test_oxdna/test_strand.py @@ -106,6 +106,67 @@ def test_generate_helix_seq(): assert len(strand.nucleotides) == 10 assert strand.sequence[0:11] == long_seq[0:10] +def test_strand_transform(): + strand = Strand( + [ + Nucleotide( + "A", + np.array([1.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + Nucleotide( + "A", + np.array([2.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + Nucleotide( + "A", + np.array([3.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + Nucleotide( + "A", + np.array([4.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + Nucleotide( + "A", + np.array([5.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + Nucleotide( + "A", + np.array([6.0, 0.0, 0.0]), + np.array([1.0, 0.0, 0.0]), + np.array([0, 0.0, 1.0]), + ), + ] + ) + strand.translate(np.array([10., 0., 0.])) + + + strand.rotate([0., 0., 0., 1.]) + strand.rotate([0., 0., 0.]) + strand.rotate( + np.array([ + [1., 0., 0.], + [0., 1., 0.], + [0., 0., 0.], + ]) + ) + + strand.transform(np.array([ + [1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + ])) + return + if __name__ == "__main__": diff --git a/test/test_oxdna/test_system.py b/test/test_oxdna/test_system.py index d98fef8..e83004e 100644 --- a/test/test_oxdna/test_system.py +++ b/test/test_oxdna/test_system.py @@ -125,6 +125,25 @@ def test_System(): print(system) print(system.dataframe) # system.write_oxDNA() + + system.translate(np.array([10., 0., 0.])) + + + system.rotate([0., 0., 0., 1.]) + system.rotate([0., 0., 0.]) + system.rotate( + np.array([ + [1., 0., 0.], + [0., 1., 0.], + [0., 0., 0.], + ]) + ) + + system.transform(np.array([ + [1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + ])) return From 534e46b3bb4bd9ebaed244b30d4f804fbe347a81 Mon Sep 17 00:00:00 2001 From: Debesh Mandal Date: Thu, 12 Nov 2020 12:30:06 +0000 Subject: [PATCH 4/4] changed version to v1.0.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 85c7d94..aa5037b 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="drawNA", - version="1.1.0", + version="1.0.1", author="Shanil Panara & Debesh Mandal", description="Package for creating origami", long_description=long_description,