Skip to content

Commit

Permalink
Remake transformation and drop matrices/geometrica dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
samirelanduk committed Nov 1, 2017
1 parent 9a9c700 commit 1e9e227
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 48 deletions.
23 changes: 23 additions & 0 deletions atomium/structures/atoms.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ def location(self):
return (self._x, self._y, self._z)


def translate(self, dx, dy, dz):
"""Translates an atom in 3D space.
:param float dx: The distance to move in the x direction.
:param float dy: The distance to move in the y direction.
:param float dz: The distance to move in the z direction."""

self._x += dx
self._y += dy
self._z += dz


def rotate(self, angle, axis):
"""Rotates an atom in 3D space.
:param float angle: Angle in radians.
:param str axis: The axis to rotate around."""

vector = Vector(self._x, self._y, self._z)
vector.rotate(angle, axis)
self._x, self._y, self._z = vector.values()


def atom_id(self):
"""Returns the atom's unique integer ID.
Expand Down
21 changes: 8 additions & 13 deletions atomium/structures/molecules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from collections import Counter
import weakref
from math import sqrt
from geometrica import translate, rotate
from .atoms import Atom

class AtomicStructure:
Expand Down Expand Up @@ -125,7 +124,7 @@ def pairwise_atoms(self):
the number of returned pairs will be a triangle number.
:rtype: ``list``"""

atoms = list(self.atoms())
for a_index in range(len(atoms) - 1):
for o_index in range(a_index + 1, len(atoms)):
Expand Down Expand Up @@ -166,23 +165,19 @@ def translate(self, dx, dy, dz):
:param Number dy: The distance to move in the y direction.
:param Number dz: The distance to move in the z direction."""

atoms = list(self.atoms())
points = translate(atoms, dx, dy, dz)
for index, atom in enumerate(atoms):
atom._x, atom._y, atom._z = points[index]
for atom in self.atoms():
atom.translate(dx, dy, dz)


def rotate(self, axis, angle):
def rotate(self, angle, axis):
"""Rotates the structure about an axis, updating all atom coordinates
accordingly.
:param str axis: The axis to rotate around. Can only be 'x', 'y' or 'z'.
:param Number angle: The angle in degrees. Rotation is right handed."""
:param Number angle: The angle in radians.
:param str axis: The axis to rotate around. Can only be 'x', 'y' or 'z'."""

atoms = list(self.atoms())
points = rotate(atoms, axis, angle)
for index, atom in enumerate(atoms):
atom._x, atom._y, atom._z = points[index]
for atom in self.atoms():
atom.rotate(angle, axis)


def center_of_mass(self):
Expand Down
4 changes: 1 addition & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
geometrica
matrices
points
points==0.3
requests
python-coveralls
3 changes: 2 additions & 1 deletion tests/integration/test_structures.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from tests.integration.base import IntegratedTest
from atomium.structures import Model, Atom, Residue, Chain, Molecule
import atomium
Expand Down Expand Up @@ -56,7 +57,7 @@ def test_can_manipulate_model(self):
model.translate(-12, -11.5, -1.5)
self.assertEqual((atom1.x(), atom1.y(), atom1.z()), (0, 0, 0))
self.assertEqual((atom2.x(), atom2.y(), atom2.z()), (0.5, -1.5, 0.5))
model.rotate("x", 180)
model.rotate(math.pi, "x")
self.assertEqual((atom1.x(), atom1.y(), atom1.z()), (0, 0, 0))

# Atoms can be removed
Expand Down
42 changes: 11 additions & 31 deletions tests/unit/structure_tests/test_atomic_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,44 +243,24 @@ def test_can_get_formula(self, mock_atoms):
class AtomicStructureTranslationTests(AtomicStructureTest):

@patch("atomium.structures.molecules.AtomicStructure.atoms")
@patch("atomium.structures.molecules.translate")
def test_translation_uses_geometrica(self, mock_translate, mock_atoms):
mock_atoms.return_value = [self.atoms[0]] + [self.atoms[-1]]
structure = AtomicStructure(self.atom1, self.atom3)
mock_translate.return_value = [(6, 6, 1), (9, 9, 4)]
self.atom1._x, self.atom1._y, self.atom1._z = 1, 2, 3
self.atom3._x, self.atom3._y, self.atom3._z = 4, 5, 6
def test_structure_translation(self, mock_atoms):
mock_atoms.return_value = set(self.atoms)
structure = AtomicStructure(self.atom1, self.atom2, self.atom3)
structure.translate(5, 4, -2)
mock_translate.assert_called_with([self.atoms[0]] + [self.atoms[-1]], 5, 4, -2)
self.assertEqual(
set([
(self.atom1._x, self.atom1._y, self.atom1._z),
(self.atom3._x, self.atom3._y, self.atom3._z)
]),
set([(6, 6, 1), (9, 9, 4)])
)
for atom in self.atoms:
atom.translate.assert_called_with(5, 4, -2)



class AtomicStructureRotationTests(AtomicStructureTest):

@patch("atomium.structures.molecules.AtomicStructure.atoms")
@patch("atomium.structures.molecules.rotate")
def test_rotation_uses_geometrica(self, mock_rotate, mock_atoms):
mock_atoms.return_value = [self.atoms[0]] + [self.atoms[-1]]
structure = AtomicStructure(self.atom1, self.atom3)
mock_rotate.return_value = [(6, 6, 1), (9, 9, 4)]
self.atom1._x, self.atom1._y, self.atom1._z = 1, 2, 3
self.atom3._x, self.atom3._y, self.atom3._z = 4, 5, 6
structure.rotate("x", 90)
mock_rotate.assert_called_with([self.atoms[0]] + [self.atoms[-1]], "x", 90)
self.assertEqual(
set([
(self.atom1._x, self.atom1._y, self.atom1._z),
(self.atom3._x, self.atom3._y, self.atom3._z)
]),
set([(6, 6, 1), (9, 9, 4)])
)
def test_structure_rotation(self, mock_atoms):
mock_atoms.return_value = set(self.atoms)
structure = AtomicStructure(self.atom1, self.atom2, self.atom3)
structure.rotate(0.1, "z")
for atom in self.atoms:
atom.rotate.assert_called_with(0.1, "z")



Expand Down
29 changes: 29 additions & 0 deletions tests/unit/structure_tests/test_atoms.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,35 @@ def test_atom_location(self):



class AtomTranslationTests(TestCase):

def test_can_translate_atoms(self):
atom = Atom("C", 20, 30, 50)
atom.translate(5, -4, 12)
self.assertEqual(atom._x, 25)
self.assertEqual(atom._y, 26)
self.assertEqual(atom._z, 62)



class AtomRotationTEsts(TestCase):

@patch("atomium.structures.atoms.Vector")
def test_can_rotate_atoms(self, mock_vector):
v = Mock()
mock_vector.return_value = v
v.values.return_value = (10, 20, 30)
atom = Atom("C", 20, 30, 50)
atom.rotate(0.5, "y")
mock_vector.assert_called_with(20, 30, 50)
v.rotate.assert_called_with(0.5, "y")
self.assertEqual(atom._x, 10)
self.assertEqual(atom._y, 20)
self.assertEqual(atom._z, 30)




class AtomIdTests(TestCase):

def test_id_property(self):
Expand Down

0 comments on commit 1e9e227

Please sign in to comment.