proteusPy.turtle3D

Implementation of a 3D 'Turtle' in Python.

Part of the program proteusPy, https://github.com/suchanek/proteusPy, a Python packages for the manipulation and analysis of macromolecules. Based on the C implementation originally authored by Eric G. Suchanek PhD, 1990.

  1"""
  2Implementation of a 3D 'Turtle' in Python.
  3
  4Part of the program proteusPy, https://github.com/suchanek/proteusPy, 
  5a Python packages for the manipulation and analysis of macromolecules. 
  6Based on the C implementation originally authored by Eric G. Suchanek PhD, 1990.
  7
  8"""
  9
 10# Last modification 3/9/24 -egs-
 11
 12__pdoc__ = {"__all__": True}
 13
 14import math
 15
 16import numpy
 17
 18numpy.set_printoptions(suppress=True)
 19
 20from Bio.PDB.vectors import Vector, calc_angle, calc_dihedral
 21
 22_DOWN_ = -1
 23_UP_ = 1
 24_ORIENTATION_INIT = -1
 25
 26ORIENT_BACKBONE = 2
 27ORIENT_SIDECHAIN = 1
 28
 29
 30# Class Definition begins
 31class Turtle3D:
 32    """3D Turtle."""
 33
 34    def __init__(self, name="3D_Turtle"):
 35        # internal rep is arrays
 36        self._position = numpy.array((0.0, 0.0, 0.0), "d")
 37        self._heading = numpy.array((1.0, 0.0, 0.0), "d")
 38        self._left = numpy.array((0.0, 1.0, 0.0), "d")
 39        self._up = numpy.array((0.0, 0.0, 1.0), "d")
 40
 41        # expose these as Vectors
 42        self.pos = Vector(0.0, 0.0, 0.0)
 43        self.h = Vector(1.0, 0.0, 0.0)
 44        self.l = Vector(0.0, 1.0, 0.0)
 45        self.u = Vector(0.0, 0.0, 1.0)
 46
 47        self._name = name
 48        self._pen = _UP_
 49        self._orientation = _ORIENTATION_INIT  # will be set to 1 or 2 later when used for residue building
 50        self._recording = False
 51        self._tape = []
 52
 53    def new(
 54        self,
 55        name: str,
 56        pos: Vector,
 57        head: Vector = Vector(1.0, 0.0, 0.0),
 58        left: Vector = Vector(0.0, 1.0, 0.0),
 59        up: Vector = Vector(0.0, 0.0, 1.0),
 60        recording=False,
 61    ) -> None:
 62        """
 63        Initialize a Turtle with a name, position, optionally heading and left.
 64
 65        :param name: Turtle's Name
 66        :param pos: Turtle's Position
 67        :param head: Turtle's Heading vector, defaults to Vector(1,0,0)
 68        :param left: Turtle's Left vector, defaults to Vector(0,1,0)
 69        :param up: Turtle's Up vector, defaults to Vector(0,0,1)
 70        :param pen: Pen state, defaults to 'up'
 71        :param recording: _description_, defaults to False
 72        """
 73        self._name = name
 74        self.pos = pos
 75        self.h = head.normalized()
 76        self.l = left.normalized()
 77        self.u = up.normalized()
 78
 79        self._position = pos._ar
 80        self._left = left.normalized()._ar
 81        self._heading = head.normalized()._ar
 82        self._up = up.normalized()._ar
 83
 84        self._recording = recording
 85
 86    def copy_coords(self, source) -> None:
 87        """
 88        Copy the Position, Heading, Left and Up coordinate system from
 89        the input source into self. Argument: source: Turtle3D
 90        Returns: None
 91        """
 92
 93        # copy the Arrays
 94        self._position = source._position.copy()
 95        self._heading = source._heading.copy()
 96        self._left = source._left.copy()
 97        self._up = source._up.copy()
 98
 99        # copy the Vectors - create new ones from the source arrays
100        self.pos = Vector(source._position)
101        self.h = Vector(source._heading)
102        self.l = Vector(source._left)
103        self.u = Vector(source._up)
104
105        self._orientation = source._orientation
106
107    def reset(self) -> None:
108        """
109        Reset the Turtle to be at the Origin, with correct Heading, Left and Up vectors.
110        Arguments: None
111        Returns: None
112        """
113        self.__init__()
114
115    @property
116    def Orientation(self) -> numpy.array:
117        return self._orientation
118
119    @Orientation.setter
120    def Orientation(self, orientation):
121        assert (
122            orientation == ORIENT_BACKBONE or orientation == ORIENT_SIDECHAIN
123        ), f"Orientation must be {ORIENT_BACKBONE} or {ORIENT_SIDECHAIN}"
124        self._orientation = orientation
125
126    @property
127    def Pen(self) -> str:
128        if self._pen == _UP_:
129            return "up"
130        else:
131            return "down"
132
133    @Pen.setter
134    def Pen(self, pen) -> None:
135        if pen == "up":
136            self._pen = _UP_
137        elif pen == "down":
138            self._pen = _DOWN_
139        else:
140            self._pen = _DOWN_
141
142    @property
143    def Recording(self) -> bool:
144        return self._recording
145
146    @Recording.setter
147    def Recording(self, recording: bool):
148        self._recording = recording
149
150    def ResetTape(self) -> None:
151        self.Recording(False)
152        self._tape = []
153
154    @property
155    def Position(self) -> Vector:
156        """
157        The Turtle's Position
158
159        :return: Position
160        :rtype: Vector
161        """
162        return self.pos
163
164    @Position.setter
165    def Position(self, x, y=None, z=None) -> None:
166        """
167        Set's the Turtle's Position
168
169        :param x: X coordinate
170        :type x: float
171        :param y: Y coordinate, defaults to None
172        :type y: float, optional
173        :param z: Z coordinate, defaults to None
174        :type z: float, optional
175        :raises ValueError: Type error
176        """
177
178        if y is None and z is None:
179            # Vector, Array, list, tuple...
180            if isinstance(x, Vector):
181                self.pos = x
182                self._position = x.get_array()
183
184            elif len(x) != 3:
185                raise ValueError(
186                    "Turtle3D: x is not a vector list/tuple/array of 3 numbers"
187                )
188            else:
189                self._position = numpy.array(x, "d")
190        else:
191            # Three numbers
192            self._position = numpy.array((x, y, z), "d")
193
194        self.pos = Vector(self._position)
195        return
196
197    @property
198    def Heading(self) -> Vector:
199        """
200        Get the Turtle's Heading
201
202        :return: Heading
203        :rtype: Vector
204        """
205        return self.h
206
207    @Heading.setter
208    def Heading(self, x, y=None, z=None) -> None:
209        """
210        Set the turtle's Heading direction vector
211
212        :param x: X coordinate
213        :type x: float
214        :param y: Y coordinate, defaults to None
215        :type y: float, optional
216        :param z: Z coordinate, defaults to None
217        :type z: float, optional
218        :raises ValueError: illegal value
219        """
220
221        if isinstance(x, Vector):
222            self.h = x
223            self._heading = x.get_array()
224
225        elif y is None and z is None:
226            # Array, list, tuple...
227            if len(x) != 3:
228                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
229            self._heading = numpy.array(x, "d")
230        else:
231            # Three numbers
232            self._heading = numpy.array((x, y, z), "d")
233        self.h = Vector(self._heading)
234        return
235
236    @property
237    def Left(self) -> Vector:
238        """
239        Get the Turtle's Left direction vector
240
241        :return: Left
242        :rtype: Vector
243        """
244
245        return self.l
246
247    @Left.setter
248    def Left(self, x, y=None, z=None):
249        """
250        Set the turtle's Left direction vector
251
252        :param x: X coordinate
253        :type x: float
254        :param y: Y coordinate, defaults to None
255        :type y: float, optional
256        :param z: Z coordinate, defaults to None
257        :type z: float, optional
258        :raises ValueError: illegal value
259        """
260        if isinstance(x, Vector):
261            self.l = x
262            self._left = x.get_array()
263
264        elif y is None and z is None:
265            # Array, list, tuple...
266            if len(x) != 3:
267                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
268            self._left = numpy.array(x, "d")
269        else:
270            # Three numbers
271            self._left = numpy.array((x, y, z), "d")
272        self.l = Vector(self._left)
273        return
274
275    @property
276    def Up(self) -> Vector:
277        """
278        The Turtle's Up direction vector
279
280        :return: Up
281        :rtype: Vector
282        """
283
284        return self.u
285
286    @Up.setter
287    def Up(self, x, y=None, z=None) -> None:
288        """
289        Set the turtle's Up direction vector
290
291        :param x: X coordinate
292        :type x: float
293        :param y: Y coordinate, defaults to None
294        :type y: float, optional
295        :param z: Z coordinate, defaults to None
296        :type z: float, optional
297        :raises ValueError: illegal value
298        """
299        if isinstance(x, Vector):
300            self.u = x
301            self._up = x.get_array()
302
303        elif y is None and z is None:
304            # Array, list, tuple...
305            if len(x) != 3:
306                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
307            self._up = numpy.array(x, "d")
308        else:
309            # Three numbers
310            self._up = numpy.array((x, y, z), "d")
311        self.u = Vector(self._up)
312        return
313
314    @property
315    def Name(self) -> str:
316        """
317        Return the Turtle's Name.
318        """
319        return self._name
320
321    @Name.setter
322    def Name(self, name) -> None:
323        """
324        Set the Turtle's name.
325
326        """
327        self._name = name
328
329    def move(self, distance: float) -> None:
330        """
331        Move the Turtle distance (Ã…), in direction of Heading
332
333        :param distance: Amount to move (Ã…)
334        :type distance: float
335        """
336        self._position = self._position + self._heading * distance
337        self.pos = Vector(self._position)
338
339    def roll(self, angle) -> None:
340        """
341        Roll the Turtle about the heading vector angle degrees
342
343        :param angle: roll angle, degrees -180 -> 180
344        :type angle: float
345        """
346
347        ang = angle * math.pi / 180.0
348        cosang = numpy.cos(ang)
349        sinang = numpy.sin(ang)
350
351        lold = self._left.copy()
352        uold = self._up.copy()
353
354        self._up[0] = cosang * uold[0] - sinang * lold[0]
355        self._up[1] = cosang * uold[1] - sinang * lold[1]
356        self._up[2] = cosang * uold[2] - sinang * lold[2]
357        self._up = self.unit(self._up)
358
359        self.u = Vector(self._up)
360
361        self._left[0] = cosang * lold[0] + sinang * uold[0]
362        self._left[1] = cosang * lold[1] + sinang * uold[1]
363        self._left[2] = cosang * lold[2] + sinang * uold[2]
364        self._left = self.unit(self._left)
365
366        self.l = Vector(self._left)
367
368    def yaw(self, angle) -> None:
369        """
370        Yaw the Turtle about the up vector (180 - angle) degrees.
371        This is used when building molecules
372
373        :param angle: Yaw angle, degrees -180 -> 180
374        :type angle: float
375        """
376
377        ang = ((180 - angle) * math.pi) / 180.0
378        cosang = numpy.cos(ang)
379        sinang = numpy.sin(ang)
380
381        lold = self._left.copy()
382        hold = self._heading.copy()
383
384        self._heading[0] = cosang * hold[0] + sinang * lold[0]
385        self._heading[1] = cosang * hold[1] + sinang * lold[1]
386        self._heading[2] = cosang * hold[2] + sinang * lold[2]
387        self._heading = self.unit(self._heading)
388        self.h = Vector(self._heading)
389
390        self._left[0] = cosang * lold[0] - sinang * hold[0]
391        self._left[1] = cosang * lold[1] - sinang * hold[1]
392        self._left[2] = cosang * lold[2] - sinang * hold[2]
393        self._left = self.unit(self._left)
394        self.l = Vector(self._left)
395
396    def turn(self, angle) -> None:
397        """
398        Turn the Turtle about the up vector angle degrees.
399
400        :param angle: Turn angle, degrees
401        :type angle: float
402        """
403
404        ang = (angle * math.pi) / 180.0
405
406        cosang = numpy.cos(ang)
407        sinang = numpy.sin(ang)
408
409        heading = self._heading.copy()
410        left = self._left.copy()
411
412        self._heading[0] = cosang * heading[0] + sinang * left[0]
413        self._heading[1] = cosang * heading[1] + sinang * left[1]
414        self._heading[2] = cosang * heading[2] + sinang * left[2]
415
416        self._heading = self.unit(self._heading)
417        self.h = Vector(self._heading)
418
419        self._left[0] = cosang * left[0] - sinang * heading[0]
420        self._left[1] = cosang * left[1] - sinang * heading[1]
421        self._left[2] = cosang * left[2] - sinang * heading[2]
422        self._left = self.unit(self._left)
423        self.l = Vector(self._left)
424
425    def pitch(self, angle) -> None:
426        """
427        pitch the Turtle about the left vector angle degrees
428
429        :param angle: Pitch angle, degrees -180 -> 180
430        :type angle: float
431        """
432
433        up = self._up.copy()
434        heading = self._heading.copy()
435
436        ang = angle * math.pi / 180.0
437        cosang = numpy.cos(ang)
438        sinang = numpy.sin(ang)
439
440        self._heading[0] = heading[0] * cosang - up[0] * sinang
441        self._heading[1] = heading[1] * cosang - up[1] * sinang
442        self._heading[2] = heading[2] * cosang - up[2] * sinang
443        self._heading = self.unit(self._heading)
444        self.h = Vector(self._heading)
445
446        self._up[0] = up[0] * cosang + heading[0] * sinang
447        self._up[1] = up[1] * cosang + heading[1] * sinang
448        self._up[2] = up[2] * cosang + heading[2] * sinang
449        self._up = self.unit(self._up)
450        self.u = Vector(self._up)
451
452    def unit(self, v) -> Vector:
453        """
454        Return a unit vector for the input vector.
455
456        :param v: Input Vector
457        :return: Unit Vector
458        """
459        norm = numpy.linalg.norm(v)
460        if norm == 0:
461            return v
462        return v / norm
463
464    def orient(
465        self, position: numpy.array, heading: numpy.array, left: numpy.array
466    ) -> None:
467        """
468        Orients the turtle with Position at p1, Heading at p2 and Left at p3
469
470        :param position: Position
471        :type position: numpy.array
472        :param heading: Heading direction vector
473        :type heading: numpy.array
474        :param left: Left direction vector
475        :type left: numpy.array
476        """
477
478        self._position = position
479
480        temp = heading - position
481        self._heading = self.unit(temp)
482        self.h = Vector(self._heading)
483
484        temp = left - position
485        self._left = self.unit(temp)
486
487        temp = numpy.cross(self._heading, self._left)
488        self._up = self.unit(temp)
489        self.u = Vector(self._up)
490
491        # fix left to be orthogonal
492        temp = numpy.cross(self._up, self._heading)
493        self._left = self.unit(temp)
494        self.l = Vector(self._left)
495        return
496
497    def orient_at_residue(self, chain, resnumb, orientation) -> None:
498        """
499        Orient the turtle at the specified residue from the input Chain in
500        either orientation 1 or 2.
501
502        :param chain: list of Residues in the model, eg: chain = model['A']
503        :type chain: str
504        :param resnumb: residue number
505        :type resnumb: int
506        :param orientation: 1 - at Ca heading towards Cb with N at the left or
507            2 - at Ca heading towards C with N at the left
508        :type orientation: int
509        """
510
511        assert (
512            self._orientation == 1 or self._orientation == 2
513        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
514
515        residue = chain[resnumb]
516        assert (
517            residue is not None
518        ), f"get_backbone_from_sidechain() requires valid residue number"
519
520        # by this point I'm pretty confident I have coordinates
521        # we pull the actual numpy.array from the coordinates since that's what the
522        # Turtle3D expects
523
524        n = residue["N"].get_vector().get_array()
525        ca = residue["CA"].get_vector().get_array()
526        cb = residue["CB"].get_vector().get_array()
527        c = residue["C"].get_vector().get_array()
528
529        if orientation == ORIENT_SIDECHAIN:
530            self.orient(ca, cb, n)
531            self.set_orientation(ORIENT_SIDECHAIN)
532        elif orientation == ORIENT_BACKBONE:
533            self.orient(ca, c, n)
534            self.set_orientation(ORIENT_BACKBONE)
535        return
536
537    def orient_from_backbone(
538        self,
539        n: numpy.array,
540        ca: numpy.array,
541        cb: numpy.array,
542        c: numpy.array,
543        orientation,
544    ) -> None:
545        """
546        Orient the turtle at the specified residue from the input Chain in
547        either orientation 1 or 2.
548
549        Arguments:
550            turtle: input Turtle3D object
551            n: position of n atom
552            ca: position of ca atom
553            c: position of c atom
554            orientation:
555            1 - at Ca heading towards Cb with N at the left
556            2 - at Ca heading towards C with N at the left
557        Returns: None. Turtle internal state is modified
558        """
559
560        assert (
561            orientation == 1 or orientation == 2
562        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
563
564        _n = n.copy()
565        _ca = ca.copy()
566        _cb = cb.copy()
567        _c = c.copy()
568
569        if orientation == ORIENT_SIDECHAIN:
570            self.orient(_ca, _cb, _n)
571            self.Orientation = ORIENT_SIDECHAIN
572        elif orientation == ORIENT_BACKBONE:
573            self.orient(_ca, _c, _n)
574            self.Orientation = ORIENT_BACKBONE
575        return
576
577    def to_local(self, global_vec) -> numpy.array:
578        """
579        Returns the Turtle-centered local coordinates for input Global vector (3d)
580        """
581
582        newpos = global_vec - self._position
583        dp1 = numpy.dot(self._heading, newpos)
584        dp2 = numpy.dot(self._left, newpos)
585        dp3 = numpy.dot(self._up, newpos)
586
587        result = numpy.array((dp1, dp2, dp3), "d")
588        return result
589
590    def to_localVec(self, global_vec) -> Vector:
591        """
592        Returns the Turtle-centered local coordinates for input Global vector (3d)
593        """
594
595        res = self.to_local(global_vec)
596        return Vector(res)
597
598    def to_global(self, local) -> numpy.array:
599        """
600        Returns the global coordinates for input local vector (3d)
601        """
602
603        p1 = (
604            self._position[0]
605            + self._heading[0] * local[0]
606            + self._left[0] * local[1]
607            + self._up[0] * local[2]
608        )
609        p2 = (
610            self._position[1]
611            + self._heading[1] * local[0]
612            + self._left[1] * local[1]
613            + self._up[1] * local[2]
614        )
615        p3 = (
616            self._position[2]
617            + self._heading[2] * local[0]
618            + self._left[2] * local[1] * self._up[2] * local[2]
619        )
620
621        return numpy.array((p1, p2, p3), "d")
622
623    def to_globalVec(self, local) -> Vector:
624        """
625        Returns the global coordinates for input local vector (3d)
626        """
627        res = self.to_global(local)
628        return Vector(res)
629
630    def __repr__(self):
631        """Return Turtle 3D coordinates."""
632        return f"<Turtle: {self._name}\n Position: {self._position},\n Heading: {self._heading} \n Left: {self._left} \n Up: {self._up}\n Orientation: {self._orientation}\n Pen: {self._pen} \n Recording: {self._recording}>"
633
634    def bbone_to_schain(self) -> None:
635        """
636        Function requires turtle to be in orientation #2 (at alpha carbon,
637        headed towards carbonyl, with nitrogen on left) and converts to orientation #1
638        (at alpha c, headed to beta carbon, with nitrogen on left.
639
640        Arguments:
641            turtle: Turtle3D object in orientation #2
642
643        Returns: modified Turtle3D
644        """
645
646        # assert self._orientation == 2, f'bbone_to_schain() requires Turtle3D to be in orientation #2'
647
648        self.roll(240.0)
649        self.pitch(180.0)
650        self.yaw(110.0)
651        self.roll(240.0)
652        self.Orientation = 1  # sets the orientation flag
653
654    def schain_to_bbone(self) -> None:
655        """
656        Function requires turtle to be in orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left)
657        and converts to orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left).
658
659        Arguments:
660            None
661        Returns: modified Turtle3D
662        """
663
664        # assert self._orientation == 1, f'schain_to_bbone() requires Turtle3D to be in orientation #1'
665
666        self.pitch(180.0)
667        self.roll(240.0)
668        self.yaw(110.0)
669        self.roll(120.0)
670        self.Orientation = 2  # sets the orientation flag
671        return
672
673
674# class definition ends
675
676if __name__ == "__main__":
677    import doctest
678
679    doctest.testmod()
680
681# End of file
ORIENT_BACKBONE = 2
ORIENT_SIDECHAIN = 1
class Turtle3D:
 32class Turtle3D:
 33    """3D Turtle."""
 34
 35    def __init__(self, name="3D_Turtle"):
 36        # internal rep is arrays
 37        self._position = numpy.array((0.0, 0.0, 0.0), "d")
 38        self._heading = numpy.array((1.0, 0.0, 0.0), "d")
 39        self._left = numpy.array((0.0, 1.0, 0.0), "d")
 40        self._up = numpy.array((0.0, 0.0, 1.0), "d")
 41
 42        # expose these as Vectors
 43        self.pos = Vector(0.0, 0.0, 0.0)
 44        self.h = Vector(1.0, 0.0, 0.0)
 45        self.l = Vector(0.0, 1.0, 0.0)
 46        self.u = Vector(0.0, 0.0, 1.0)
 47
 48        self._name = name
 49        self._pen = _UP_
 50        self._orientation = _ORIENTATION_INIT  # will be set to 1 or 2 later when used for residue building
 51        self._recording = False
 52        self._tape = []
 53
 54    def new(
 55        self,
 56        name: str,
 57        pos: Vector,
 58        head: Vector = Vector(1.0, 0.0, 0.0),
 59        left: Vector = Vector(0.0, 1.0, 0.0),
 60        up: Vector = Vector(0.0, 0.0, 1.0),
 61        recording=False,
 62    ) -> None:
 63        """
 64        Initialize a Turtle with a name, position, optionally heading and left.
 65
 66        :param name: Turtle's Name
 67        :param pos: Turtle's Position
 68        :param head: Turtle's Heading vector, defaults to Vector(1,0,0)
 69        :param left: Turtle's Left vector, defaults to Vector(0,1,0)
 70        :param up: Turtle's Up vector, defaults to Vector(0,0,1)
 71        :param pen: Pen state, defaults to 'up'
 72        :param recording: _description_, defaults to False
 73        """
 74        self._name = name
 75        self.pos = pos
 76        self.h = head.normalized()
 77        self.l = left.normalized()
 78        self.u = up.normalized()
 79
 80        self._position = pos._ar
 81        self._left = left.normalized()._ar
 82        self._heading = head.normalized()._ar
 83        self._up = up.normalized()._ar
 84
 85        self._recording = recording
 86
 87    def copy_coords(self, source) -> None:
 88        """
 89        Copy the Position, Heading, Left and Up coordinate system from
 90        the input source into self. Argument: source: Turtle3D
 91        Returns: None
 92        """
 93
 94        # copy the Arrays
 95        self._position = source._position.copy()
 96        self._heading = source._heading.copy()
 97        self._left = source._left.copy()
 98        self._up = source._up.copy()
 99
100        # copy the Vectors - create new ones from the source arrays
101        self.pos = Vector(source._position)
102        self.h = Vector(source._heading)
103        self.l = Vector(source._left)
104        self.u = Vector(source._up)
105
106        self._orientation = source._orientation
107
108    def reset(self) -> None:
109        """
110        Reset the Turtle to be at the Origin, with correct Heading, Left and Up vectors.
111        Arguments: None
112        Returns: None
113        """
114        self.__init__()
115
116    @property
117    def Orientation(self) -> numpy.array:
118        return self._orientation
119
120    @Orientation.setter
121    def Orientation(self, orientation):
122        assert (
123            orientation == ORIENT_BACKBONE or orientation == ORIENT_SIDECHAIN
124        ), f"Orientation must be {ORIENT_BACKBONE} or {ORIENT_SIDECHAIN}"
125        self._orientation = orientation
126
127    @property
128    def Pen(self) -> str:
129        if self._pen == _UP_:
130            return "up"
131        else:
132            return "down"
133
134    @Pen.setter
135    def Pen(self, pen) -> None:
136        if pen == "up":
137            self._pen = _UP_
138        elif pen == "down":
139            self._pen = _DOWN_
140        else:
141            self._pen = _DOWN_
142
143    @property
144    def Recording(self) -> bool:
145        return self._recording
146
147    @Recording.setter
148    def Recording(self, recording: bool):
149        self._recording = recording
150
151    def ResetTape(self) -> None:
152        self.Recording(False)
153        self._tape = []
154
155    @property
156    def Position(self) -> Vector:
157        """
158        The Turtle's Position
159
160        :return: Position
161        :rtype: Vector
162        """
163        return self.pos
164
165    @Position.setter
166    def Position(self, x, y=None, z=None) -> None:
167        """
168        Set's the Turtle's Position
169
170        :param x: X coordinate
171        :type x: float
172        :param y: Y coordinate, defaults to None
173        :type y: float, optional
174        :param z: Z coordinate, defaults to None
175        :type z: float, optional
176        :raises ValueError: Type error
177        """
178
179        if y is None and z is None:
180            # Vector, Array, list, tuple...
181            if isinstance(x, Vector):
182                self.pos = x
183                self._position = x.get_array()
184
185            elif len(x) != 3:
186                raise ValueError(
187                    "Turtle3D: x is not a vector list/tuple/array of 3 numbers"
188                )
189            else:
190                self._position = numpy.array(x, "d")
191        else:
192            # Three numbers
193            self._position = numpy.array((x, y, z), "d")
194
195        self.pos = Vector(self._position)
196        return
197
198    @property
199    def Heading(self) -> Vector:
200        """
201        Get the Turtle's Heading
202
203        :return: Heading
204        :rtype: Vector
205        """
206        return self.h
207
208    @Heading.setter
209    def Heading(self, x, y=None, z=None) -> None:
210        """
211        Set the turtle's Heading direction vector
212
213        :param x: X coordinate
214        :type x: float
215        :param y: Y coordinate, defaults to None
216        :type y: float, optional
217        :param z: Z coordinate, defaults to None
218        :type z: float, optional
219        :raises ValueError: illegal value
220        """
221
222        if isinstance(x, Vector):
223            self.h = x
224            self._heading = x.get_array()
225
226        elif y is None and z is None:
227            # Array, list, tuple...
228            if len(x) != 3:
229                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
230            self._heading = numpy.array(x, "d")
231        else:
232            # Three numbers
233            self._heading = numpy.array((x, y, z), "d")
234        self.h = Vector(self._heading)
235        return
236
237    @property
238    def Left(self) -> Vector:
239        """
240        Get the Turtle's Left direction vector
241
242        :return: Left
243        :rtype: Vector
244        """
245
246        return self.l
247
248    @Left.setter
249    def Left(self, x, y=None, z=None):
250        """
251        Set the turtle's Left direction vector
252
253        :param x: X coordinate
254        :type x: float
255        :param y: Y coordinate, defaults to None
256        :type y: float, optional
257        :param z: Z coordinate, defaults to None
258        :type z: float, optional
259        :raises ValueError: illegal value
260        """
261        if isinstance(x, Vector):
262            self.l = x
263            self._left = x.get_array()
264
265        elif y is None and z is None:
266            # Array, list, tuple...
267            if len(x) != 3:
268                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
269            self._left = numpy.array(x, "d")
270        else:
271            # Three numbers
272            self._left = numpy.array((x, y, z), "d")
273        self.l = Vector(self._left)
274        return
275
276    @property
277    def Up(self) -> Vector:
278        """
279        The Turtle's Up direction vector
280
281        :return: Up
282        :rtype: Vector
283        """
284
285        return self.u
286
287    @Up.setter
288    def Up(self, x, y=None, z=None) -> None:
289        """
290        Set the turtle's Up direction vector
291
292        :param x: X coordinate
293        :type x: float
294        :param y: Y coordinate, defaults to None
295        :type y: float, optional
296        :param z: Z coordinate, defaults to None
297        :type z: float, optional
298        :raises ValueError: illegal value
299        """
300        if isinstance(x, Vector):
301            self.u = x
302            self._up = x.get_array()
303
304        elif y is None and z is None:
305            # Array, list, tuple...
306            if len(x) != 3:
307                raise ValueError("Turtle3D: x is not a list/tuple/array of 3 numbers")
308            self._up = numpy.array(x, "d")
309        else:
310            # Three numbers
311            self._up = numpy.array((x, y, z), "d")
312        self.u = Vector(self._up)
313        return
314
315    @property
316    def Name(self) -> str:
317        """
318        Return the Turtle's Name.
319        """
320        return self._name
321
322    @Name.setter
323    def Name(self, name) -> None:
324        """
325        Set the Turtle's name.
326
327        """
328        self._name = name
329
330    def move(self, distance: float) -> None:
331        """
332        Move the Turtle distance (Ã…), in direction of Heading
333
334        :param distance: Amount to move (Ã…)
335        :type distance: float
336        """
337        self._position = self._position + self._heading * distance
338        self.pos = Vector(self._position)
339
340    def roll(self, angle) -> None:
341        """
342        Roll the Turtle about the heading vector angle degrees
343
344        :param angle: roll angle, degrees -180 -> 180
345        :type angle: float
346        """
347
348        ang = angle * math.pi / 180.0
349        cosang = numpy.cos(ang)
350        sinang = numpy.sin(ang)
351
352        lold = self._left.copy()
353        uold = self._up.copy()
354
355        self._up[0] = cosang * uold[0] - sinang * lold[0]
356        self._up[1] = cosang * uold[1] - sinang * lold[1]
357        self._up[2] = cosang * uold[2] - sinang * lold[2]
358        self._up = self.unit(self._up)
359
360        self.u = Vector(self._up)
361
362        self._left[0] = cosang * lold[0] + sinang * uold[0]
363        self._left[1] = cosang * lold[1] + sinang * uold[1]
364        self._left[2] = cosang * lold[2] + sinang * uold[2]
365        self._left = self.unit(self._left)
366
367        self.l = Vector(self._left)
368
369    def yaw(self, angle) -> None:
370        """
371        Yaw the Turtle about the up vector (180 - angle) degrees.
372        This is used when building molecules
373
374        :param angle: Yaw angle, degrees -180 -> 180
375        :type angle: float
376        """
377
378        ang = ((180 - angle) * math.pi) / 180.0
379        cosang = numpy.cos(ang)
380        sinang = numpy.sin(ang)
381
382        lold = self._left.copy()
383        hold = self._heading.copy()
384
385        self._heading[0] = cosang * hold[0] + sinang * lold[0]
386        self._heading[1] = cosang * hold[1] + sinang * lold[1]
387        self._heading[2] = cosang * hold[2] + sinang * lold[2]
388        self._heading = self.unit(self._heading)
389        self.h = Vector(self._heading)
390
391        self._left[0] = cosang * lold[0] - sinang * hold[0]
392        self._left[1] = cosang * lold[1] - sinang * hold[1]
393        self._left[2] = cosang * lold[2] - sinang * hold[2]
394        self._left = self.unit(self._left)
395        self.l = Vector(self._left)
396
397    def turn(self, angle) -> None:
398        """
399        Turn the Turtle about the up vector angle degrees.
400
401        :param angle: Turn angle, degrees
402        :type angle: float
403        """
404
405        ang = (angle * math.pi) / 180.0
406
407        cosang = numpy.cos(ang)
408        sinang = numpy.sin(ang)
409
410        heading = self._heading.copy()
411        left = self._left.copy()
412
413        self._heading[0] = cosang * heading[0] + sinang * left[0]
414        self._heading[1] = cosang * heading[1] + sinang * left[1]
415        self._heading[2] = cosang * heading[2] + sinang * left[2]
416
417        self._heading = self.unit(self._heading)
418        self.h = Vector(self._heading)
419
420        self._left[0] = cosang * left[0] - sinang * heading[0]
421        self._left[1] = cosang * left[1] - sinang * heading[1]
422        self._left[2] = cosang * left[2] - sinang * heading[2]
423        self._left = self.unit(self._left)
424        self.l = Vector(self._left)
425
426    def pitch(self, angle) -> None:
427        """
428        pitch the Turtle about the left vector angle degrees
429
430        :param angle: Pitch angle, degrees -180 -> 180
431        :type angle: float
432        """
433
434        up = self._up.copy()
435        heading = self._heading.copy()
436
437        ang = angle * math.pi / 180.0
438        cosang = numpy.cos(ang)
439        sinang = numpy.sin(ang)
440
441        self._heading[0] = heading[0] * cosang - up[0] * sinang
442        self._heading[1] = heading[1] * cosang - up[1] * sinang
443        self._heading[2] = heading[2] * cosang - up[2] * sinang
444        self._heading = self.unit(self._heading)
445        self.h = Vector(self._heading)
446
447        self._up[0] = up[0] * cosang + heading[0] * sinang
448        self._up[1] = up[1] * cosang + heading[1] * sinang
449        self._up[2] = up[2] * cosang + heading[2] * sinang
450        self._up = self.unit(self._up)
451        self.u = Vector(self._up)
452
453    def unit(self, v) -> Vector:
454        """
455        Return a unit vector for the input vector.
456
457        :param v: Input Vector
458        :return: Unit Vector
459        """
460        norm = numpy.linalg.norm(v)
461        if norm == 0:
462            return v
463        return v / norm
464
465    def orient(
466        self, position: numpy.array, heading: numpy.array, left: numpy.array
467    ) -> None:
468        """
469        Orients the turtle with Position at p1, Heading at p2 and Left at p3
470
471        :param position: Position
472        :type position: numpy.array
473        :param heading: Heading direction vector
474        :type heading: numpy.array
475        :param left: Left direction vector
476        :type left: numpy.array
477        """
478
479        self._position = position
480
481        temp = heading - position
482        self._heading = self.unit(temp)
483        self.h = Vector(self._heading)
484
485        temp = left - position
486        self._left = self.unit(temp)
487
488        temp = numpy.cross(self._heading, self._left)
489        self._up = self.unit(temp)
490        self.u = Vector(self._up)
491
492        # fix left to be orthogonal
493        temp = numpy.cross(self._up, self._heading)
494        self._left = self.unit(temp)
495        self.l = Vector(self._left)
496        return
497
498    def orient_at_residue(self, chain, resnumb, orientation) -> None:
499        """
500        Orient the turtle at the specified residue from the input Chain in
501        either orientation 1 or 2.
502
503        :param chain: list of Residues in the model, eg: chain = model['A']
504        :type chain: str
505        :param resnumb: residue number
506        :type resnumb: int
507        :param orientation: 1 - at Ca heading towards Cb with N at the left or
508            2 - at Ca heading towards C with N at the left
509        :type orientation: int
510        """
511
512        assert (
513            self._orientation == 1 or self._orientation == 2
514        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
515
516        residue = chain[resnumb]
517        assert (
518            residue is not None
519        ), f"get_backbone_from_sidechain() requires valid residue number"
520
521        # by this point I'm pretty confident I have coordinates
522        # we pull the actual numpy.array from the coordinates since that's what the
523        # Turtle3D expects
524
525        n = residue["N"].get_vector().get_array()
526        ca = residue["CA"].get_vector().get_array()
527        cb = residue["CB"].get_vector().get_array()
528        c = residue["C"].get_vector().get_array()
529
530        if orientation == ORIENT_SIDECHAIN:
531            self.orient(ca, cb, n)
532            self.set_orientation(ORIENT_SIDECHAIN)
533        elif orientation == ORIENT_BACKBONE:
534            self.orient(ca, c, n)
535            self.set_orientation(ORIENT_BACKBONE)
536        return
537
538    def orient_from_backbone(
539        self,
540        n: numpy.array,
541        ca: numpy.array,
542        cb: numpy.array,
543        c: numpy.array,
544        orientation,
545    ) -> None:
546        """
547        Orient the turtle at the specified residue from the input Chain in
548        either orientation 1 or 2.
549
550        Arguments:
551            turtle: input Turtle3D object
552            n: position of n atom
553            ca: position of ca atom
554            c: position of c atom
555            orientation:
556            1 - at Ca heading towards Cb with N at the left
557            2 - at Ca heading towards C with N at the left
558        Returns: None. Turtle internal state is modified
559        """
560
561        assert (
562            orientation == 1 or orientation == 2
563        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
564
565        _n = n.copy()
566        _ca = ca.copy()
567        _cb = cb.copy()
568        _c = c.copy()
569
570        if orientation == ORIENT_SIDECHAIN:
571            self.orient(_ca, _cb, _n)
572            self.Orientation = ORIENT_SIDECHAIN
573        elif orientation == ORIENT_BACKBONE:
574            self.orient(_ca, _c, _n)
575            self.Orientation = ORIENT_BACKBONE
576        return
577
578    def to_local(self, global_vec) -> numpy.array:
579        """
580        Returns the Turtle-centered local coordinates for input Global vector (3d)
581        """
582
583        newpos = global_vec - self._position
584        dp1 = numpy.dot(self._heading, newpos)
585        dp2 = numpy.dot(self._left, newpos)
586        dp3 = numpy.dot(self._up, newpos)
587
588        result = numpy.array((dp1, dp2, dp3), "d")
589        return result
590
591    def to_localVec(self, global_vec) -> Vector:
592        """
593        Returns the Turtle-centered local coordinates for input Global vector (3d)
594        """
595
596        res = self.to_local(global_vec)
597        return Vector(res)
598
599    def to_global(self, local) -> numpy.array:
600        """
601        Returns the global coordinates for input local vector (3d)
602        """
603
604        p1 = (
605            self._position[0]
606            + self._heading[0] * local[0]
607            + self._left[0] * local[1]
608            + self._up[0] * local[2]
609        )
610        p2 = (
611            self._position[1]
612            + self._heading[1] * local[0]
613            + self._left[1] * local[1]
614            + self._up[1] * local[2]
615        )
616        p3 = (
617            self._position[2]
618            + self._heading[2] * local[0]
619            + self._left[2] * local[1] * self._up[2] * local[2]
620        )
621
622        return numpy.array((p1, p2, p3), "d")
623
624    def to_globalVec(self, local) -> Vector:
625        """
626        Returns the global coordinates for input local vector (3d)
627        """
628        res = self.to_global(local)
629        return Vector(res)
630
631    def __repr__(self):
632        """Return Turtle 3D coordinates."""
633        return f"<Turtle: {self._name}\n Position: {self._position},\n Heading: {self._heading} \n Left: {self._left} \n Up: {self._up}\n Orientation: {self._orientation}\n Pen: {self._pen} \n Recording: {self._recording}>"
634
635    def bbone_to_schain(self) -> None:
636        """
637        Function requires turtle to be in orientation #2 (at alpha carbon,
638        headed towards carbonyl, with nitrogen on left) and converts to orientation #1
639        (at alpha c, headed to beta carbon, with nitrogen on left.
640
641        Arguments:
642            turtle: Turtle3D object in orientation #2
643
644        Returns: modified Turtle3D
645        """
646
647        # assert self._orientation == 2, f'bbone_to_schain() requires Turtle3D to be in orientation #2'
648
649        self.roll(240.0)
650        self.pitch(180.0)
651        self.yaw(110.0)
652        self.roll(240.0)
653        self.Orientation = 1  # sets the orientation flag
654
655    def schain_to_bbone(self) -> None:
656        """
657        Function requires turtle to be in orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left)
658        and converts to orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left).
659
660        Arguments:
661            None
662        Returns: modified Turtle3D
663        """
664
665        # assert self._orientation == 1, f'schain_to_bbone() requires Turtle3D to be in orientation #1'
666
667        self.pitch(180.0)
668        self.roll(240.0)
669        self.yaw(110.0)
670        self.roll(120.0)
671        self.Orientation = 2  # sets the orientation flag
672        return

3D Turtle.

Turtle3D(name='3D_Turtle')
35    def __init__(self, name="3D_Turtle"):
36        # internal rep is arrays
37        self._position = numpy.array((0.0, 0.0, 0.0), "d")
38        self._heading = numpy.array((1.0, 0.0, 0.0), "d")
39        self._left = numpy.array((0.0, 1.0, 0.0), "d")
40        self._up = numpy.array((0.0, 0.0, 1.0), "d")
41
42        # expose these as Vectors
43        self.pos = Vector(0.0, 0.0, 0.0)
44        self.h = Vector(1.0, 0.0, 0.0)
45        self.l = Vector(0.0, 1.0, 0.0)
46        self.u = Vector(0.0, 0.0, 1.0)
47
48        self._name = name
49        self._pen = _UP_
50        self._orientation = _ORIENTATION_INIT  # will be set to 1 or 2 later when used for residue building
51        self._recording = False
52        self._tape = []
pos
h
l
u
def new( self, name: str, pos: Bio.PDB.vectors.Vector, head: Bio.PDB.vectors.Vector = <Vector 1.00, 0.00, 0.00>, left: Bio.PDB.vectors.Vector = <Vector 0.00, 1.00, 0.00>, up: Bio.PDB.vectors.Vector = <Vector 0.00, 0.00, 1.00>, recording=False) -> None:
54    def new(
55        self,
56        name: str,
57        pos: Vector,
58        head: Vector = Vector(1.0, 0.0, 0.0),
59        left: Vector = Vector(0.0, 1.0, 0.0),
60        up: Vector = Vector(0.0, 0.0, 1.0),
61        recording=False,
62    ) -> None:
63        """
64        Initialize a Turtle with a name, position, optionally heading and left.
65
66        :param name: Turtle's Name
67        :param pos: Turtle's Position
68        :param head: Turtle's Heading vector, defaults to Vector(1,0,0)
69        :param left: Turtle's Left vector, defaults to Vector(0,1,0)
70        :param up: Turtle's Up vector, defaults to Vector(0,0,1)
71        :param pen: Pen state, defaults to 'up'
72        :param recording: _description_, defaults to False
73        """
74        self._name = name
75        self.pos = pos
76        self.h = head.normalized()
77        self.l = left.normalized()
78        self.u = up.normalized()
79
80        self._position = pos._ar
81        self._left = left.normalized()._ar
82        self._heading = head.normalized()._ar
83        self._up = up.normalized()._ar
84
85        self._recording = recording

Initialize a Turtle with a name, position, optionally heading and left.

Parameters
  • name: Turtle's Name
  • pos: Turtle's Position
  • head: Turtle's Heading vector, defaults to Vector(1,0,0)
  • left: Turtle's Left vector, defaults to Vector(0,1,0)
  • up: Turtle's Up vector, defaults to Vector(0,0,1)
  • pen: Pen state, defaults to 'up'
  • recording: _description_, defaults to False
def copy_coords(self, source) -> None:
 87    def copy_coords(self, source) -> None:
 88        """
 89        Copy the Position, Heading, Left and Up coordinate system from
 90        the input source into self. Argument: source: Turtle3D
 91        Returns: None
 92        """
 93
 94        # copy the Arrays
 95        self._position = source._position.copy()
 96        self._heading = source._heading.copy()
 97        self._left = source._left.copy()
 98        self._up = source._up.copy()
 99
100        # copy the Vectors - create new ones from the source arrays
101        self.pos = Vector(source._position)
102        self.h = Vector(source._heading)
103        self.l = Vector(source._left)
104        self.u = Vector(source._up)
105
106        self._orientation = source._orientation

Copy the Position, Heading, Left and Up coordinate system from the input source into self. Argument: source: Turtle3D Returns: None

def reset(self) -> None:
108    def reset(self) -> None:
109        """
110        Reset the Turtle to be at the Origin, with correct Heading, Left and Up vectors.
111        Arguments: None
112        Returns: None
113        """
114        self.__init__()

Reset the Turtle to be at the Origin, with correct Heading, Left and Up vectors. Arguments: None Returns: None

Orientation: <built-in function array>
116    @property
117    def Orientation(self) -> numpy.array:
118        return self._orientation
Pen: str
127    @property
128    def Pen(self) -> str:
129        if self._pen == _UP_:
130            return "up"
131        else:
132            return "down"
Recording: bool
143    @property
144    def Recording(self) -> bool:
145        return self._recording
def ResetTape(self) -> None:
151    def ResetTape(self) -> None:
152        self.Recording(False)
153        self._tape = []
Position: Bio.PDB.vectors.Vector
155    @property
156    def Position(self) -> Vector:
157        """
158        The Turtle's Position
159
160        :return: Position
161        :rtype: Vector
162        """
163        return self.pos

The Turtle's Position

Returns

Position

Heading: Bio.PDB.vectors.Vector
198    @property
199    def Heading(self) -> Vector:
200        """
201        Get the Turtle's Heading
202
203        :return: Heading
204        :rtype: Vector
205        """
206        return self.h

Get the Turtle's Heading

Returns

Heading

Left: Bio.PDB.vectors.Vector
237    @property
238    def Left(self) -> Vector:
239        """
240        Get the Turtle's Left direction vector
241
242        :return: Left
243        :rtype: Vector
244        """
245
246        return self.l

Get the Turtle's Left direction vector

Returns

Left

Up: Bio.PDB.vectors.Vector
276    @property
277    def Up(self) -> Vector:
278        """
279        The Turtle's Up direction vector
280
281        :return: Up
282        :rtype: Vector
283        """
284
285        return self.u

The Turtle's Up direction vector

Returns

Up

Name: str
315    @property
316    def Name(self) -> str:
317        """
318        Return the Turtle's Name.
319        """
320        return self._name

Return the Turtle's Name.

def move(self, distance: float) -> None:
330    def move(self, distance: float) -> None:
331        """
332        Move the Turtle distance (Ã…), in direction of Heading
333
334        :param distance: Amount to move (Ã…)
335        :type distance: float
336        """
337        self._position = self._position + self._heading * distance
338        self.pos = Vector(self._position)

Move the Turtle distance (Ã…), in direction of Heading

Parameters
  • distance: Amount to move (Ã…)
def roll(self, angle) -> None:
340    def roll(self, angle) -> None:
341        """
342        Roll the Turtle about the heading vector angle degrees
343
344        :param angle: roll angle, degrees -180 -> 180
345        :type angle: float
346        """
347
348        ang = angle * math.pi / 180.0
349        cosang = numpy.cos(ang)
350        sinang = numpy.sin(ang)
351
352        lold = self._left.copy()
353        uold = self._up.copy()
354
355        self._up[0] = cosang * uold[0] - sinang * lold[0]
356        self._up[1] = cosang * uold[1] - sinang * lold[1]
357        self._up[2] = cosang * uold[2] - sinang * lold[2]
358        self._up = self.unit(self._up)
359
360        self.u = Vector(self._up)
361
362        self._left[0] = cosang * lold[0] + sinang * uold[0]
363        self._left[1] = cosang * lold[1] + sinang * uold[1]
364        self._left[2] = cosang * lold[2] + sinang * uold[2]
365        self._left = self.unit(self._left)
366
367        self.l = Vector(self._left)

Roll the Turtle about the heading vector angle degrees

Parameters
  • angle: roll angle, degrees -180 -> 180
def yaw(self, angle) -> None:
369    def yaw(self, angle) -> None:
370        """
371        Yaw the Turtle about the up vector (180 - angle) degrees.
372        This is used when building molecules
373
374        :param angle: Yaw angle, degrees -180 -> 180
375        :type angle: float
376        """
377
378        ang = ((180 - angle) * math.pi) / 180.0
379        cosang = numpy.cos(ang)
380        sinang = numpy.sin(ang)
381
382        lold = self._left.copy()
383        hold = self._heading.copy()
384
385        self._heading[0] = cosang * hold[0] + sinang * lold[0]
386        self._heading[1] = cosang * hold[1] + sinang * lold[1]
387        self._heading[2] = cosang * hold[2] + sinang * lold[2]
388        self._heading = self.unit(self._heading)
389        self.h = Vector(self._heading)
390
391        self._left[0] = cosang * lold[0] - sinang * hold[0]
392        self._left[1] = cosang * lold[1] - sinang * hold[1]
393        self._left[2] = cosang * lold[2] - sinang * hold[2]
394        self._left = self.unit(self._left)
395        self.l = Vector(self._left)

Yaw the Turtle about the up vector (180 - angle) degrees. This is used when building molecules

Parameters
  • angle: Yaw angle, degrees -180 -> 180
def turn(self, angle) -> None:
397    def turn(self, angle) -> None:
398        """
399        Turn the Turtle about the up vector angle degrees.
400
401        :param angle: Turn angle, degrees
402        :type angle: float
403        """
404
405        ang = (angle * math.pi) / 180.0
406
407        cosang = numpy.cos(ang)
408        sinang = numpy.sin(ang)
409
410        heading = self._heading.copy()
411        left = self._left.copy()
412
413        self._heading[0] = cosang * heading[0] + sinang * left[0]
414        self._heading[1] = cosang * heading[1] + sinang * left[1]
415        self._heading[2] = cosang * heading[2] + sinang * left[2]
416
417        self._heading = self.unit(self._heading)
418        self.h = Vector(self._heading)
419
420        self._left[0] = cosang * left[0] - sinang * heading[0]
421        self._left[1] = cosang * left[1] - sinang * heading[1]
422        self._left[2] = cosang * left[2] - sinang * heading[2]
423        self._left = self.unit(self._left)
424        self.l = Vector(self._left)

Turn the Turtle about the up vector angle degrees.

Parameters
  • angle: Turn angle, degrees
def pitch(self, angle) -> None:
426    def pitch(self, angle) -> None:
427        """
428        pitch the Turtle about the left vector angle degrees
429
430        :param angle: Pitch angle, degrees -180 -> 180
431        :type angle: float
432        """
433
434        up = self._up.copy()
435        heading = self._heading.copy()
436
437        ang = angle * math.pi / 180.0
438        cosang = numpy.cos(ang)
439        sinang = numpy.sin(ang)
440
441        self._heading[0] = heading[0] * cosang - up[0] * sinang
442        self._heading[1] = heading[1] * cosang - up[1] * sinang
443        self._heading[2] = heading[2] * cosang - up[2] * sinang
444        self._heading = self.unit(self._heading)
445        self.h = Vector(self._heading)
446
447        self._up[0] = up[0] * cosang + heading[0] * sinang
448        self._up[1] = up[1] * cosang + heading[1] * sinang
449        self._up[2] = up[2] * cosang + heading[2] * sinang
450        self._up = self.unit(self._up)
451        self.u = Vector(self._up)

pitch the Turtle about the left vector angle degrees

Parameters
  • angle: Pitch angle, degrees -180 -> 180
def unit(self, v) -> Bio.PDB.vectors.Vector:
453    def unit(self, v) -> Vector:
454        """
455        Return a unit vector for the input vector.
456
457        :param v: Input Vector
458        :return: Unit Vector
459        """
460        norm = numpy.linalg.norm(v)
461        if norm == 0:
462            return v
463        return v / norm

Return a unit vector for the input vector.

Parameters
  • v: Input Vector
Returns

Unit Vector

def orient( self, position: <built-in function array>, heading: <built-in function array>, left: <built-in function array>) -> None:
465    def orient(
466        self, position: numpy.array, heading: numpy.array, left: numpy.array
467    ) -> None:
468        """
469        Orients the turtle with Position at p1, Heading at p2 and Left at p3
470
471        :param position: Position
472        :type position: numpy.array
473        :param heading: Heading direction vector
474        :type heading: numpy.array
475        :param left: Left direction vector
476        :type left: numpy.array
477        """
478
479        self._position = position
480
481        temp = heading - position
482        self._heading = self.unit(temp)
483        self.h = Vector(self._heading)
484
485        temp = left - position
486        self._left = self.unit(temp)
487
488        temp = numpy.cross(self._heading, self._left)
489        self._up = self.unit(temp)
490        self.u = Vector(self._up)
491
492        # fix left to be orthogonal
493        temp = numpy.cross(self._up, self._heading)
494        self._left = self.unit(temp)
495        self.l = Vector(self._left)
496        return

Orients the turtle with Position at p1, Heading at p2 and Left at p3

Parameters
  • position: Position
  • heading: Heading direction vector
  • left: Left direction vector
def orient_at_residue(self, chain, resnumb, orientation) -> None:
498    def orient_at_residue(self, chain, resnumb, orientation) -> None:
499        """
500        Orient the turtle at the specified residue from the input Chain in
501        either orientation 1 or 2.
502
503        :param chain: list of Residues in the model, eg: chain = model['A']
504        :type chain: str
505        :param resnumb: residue number
506        :type resnumb: int
507        :param orientation: 1 - at Ca heading towards Cb with N at the left or
508            2 - at Ca heading towards C with N at the left
509        :type orientation: int
510        """
511
512        assert (
513            self._orientation == 1 or self._orientation == 2
514        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
515
516        residue = chain[resnumb]
517        assert (
518            residue is not None
519        ), f"get_backbone_from_sidechain() requires valid residue number"
520
521        # by this point I'm pretty confident I have coordinates
522        # we pull the actual numpy.array from the coordinates since that's what the
523        # Turtle3D expects
524
525        n = residue["N"].get_vector().get_array()
526        ca = residue["CA"].get_vector().get_array()
527        cb = residue["CB"].get_vector().get_array()
528        c = residue["C"].get_vector().get_array()
529
530        if orientation == ORIENT_SIDECHAIN:
531            self.orient(ca, cb, n)
532            self.set_orientation(ORIENT_SIDECHAIN)
533        elif orientation == ORIENT_BACKBONE:
534            self.orient(ca, c, n)
535            self.set_orientation(ORIENT_BACKBONE)
536        return

Orient the turtle at the specified residue from the input Chain in either orientation 1 or 2.

Parameters
  • chain: list of Residues in the model, eg: chain = model['A']
  • resnumb: residue number
  • orientation: 1 - at Ca heading towards Cb with N at the left or 2 - at Ca heading towards C with N at the left
def orient_from_backbone( self, n: <built-in function array>, ca: <built-in function array>, cb: <built-in function array>, c: <built-in function array>, orientation) -> None:
538    def orient_from_backbone(
539        self,
540        n: numpy.array,
541        ca: numpy.array,
542        cb: numpy.array,
543        c: numpy.array,
544        orientation,
545    ) -> None:
546        """
547        Orient the turtle at the specified residue from the input Chain in
548        either orientation 1 or 2.
549
550        Arguments:
551            turtle: input Turtle3D object
552            n: position of n atom
553            ca: position of ca atom
554            c: position of c atom
555            orientation:
556            1 - at Ca heading towards Cb with N at the left
557            2 - at Ca heading towards C with N at the left
558        Returns: None. Turtle internal state is modified
559        """
560
561        assert (
562            orientation == 1 or orientation == 2
563        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
564
565        _n = n.copy()
566        _ca = ca.copy()
567        _cb = cb.copy()
568        _c = c.copy()
569
570        if orientation == ORIENT_SIDECHAIN:
571            self.orient(_ca, _cb, _n)
572            self.Orientation = ORIENT_SIDECHAIN
573        elif orientation == ORIENT_BACKBONE:
574            self.orient(_ca, _c, _n)
575            self.Orientation = ORIENT_BACKBONE
576        return

Orient the turtle at the specified residue from the input Chain in either orientation 1 or 2.

Arguments: turtle: input Turtle3D object n: position of n atom ca: position of ca atom c: position of c atom orientation: 1 - at Ca heading towards Cb with N at the left 2 - at Ca heading towards C with N at the left Returns: None. Turtle internal state is modified

def to_local(self, global_vec) -> <built-in function array>:
578    def to_local(self, global_vec) -> numpy.array:
579        """
580        Returns the Turtle-centered local coordinates for input Global vector (3d)
581        """
582
583        newpos = global_vec - self._position
584        dp1 = numpy.dot(self._heading, newpos)
585        dp2 = numpy.dot(self._left, newpos)
586        dp3 = numpy.dot(self._up, newpos)
587
588        result = numpy.array((dp1, dp2, dp3), "d")
589        return result

Returns the Turtle-centered local coordinates for input Global vector (3d)

def to_localVec(self, global_vec) -> Bio.PDB.vectors.Vector:
591    def to_localVec(self, global_vec) -> Vector:
592        """
593        Returns the Turtle-centered local coordinates for input Global vector (3d)
594        """
595
596        res = self.to_local(global_vec)
597        return Vector(res)

Returns the Turtle-centered local coordinates for input Global vector (3d)

def to_global(self, local) -> <built-in function array>:
599    def to_global(self, local) -> numpy.array:
600        """
601        Returns the global coordinates for input local vector (3d)
602        """
603
604        p1 = (
605            self._position[0]
606            + self._heading[0] * local[0]
607            + self._left[0] * local[1]
608            + self._up[0] * local[2]
609        )
610        p2 = (
611            self._position[1]
612            + self._heading[1] * local[0]
613            + self._left[1] * local[1]
614            + self._up[1] * local[2]
615        )
616        p3 = (
617            self._position[2]
618            + self._heading[2] * local[0]
619            + self._left[2] * local[1] * self._up[2] * local[2]
620        )
621
622        return numpy.array((p1, p2, p3), "d")

Returns the global coordinates for input local vector (3d)

def to_globalVec(self, local) -> Bio.PDB.vectors.Vector:
624    def to_globalVec(self, local) -> Vector:
625        """
626        Returns the global coordinates for input local vector (3d)
627        """
628        res = self.to_global(local)
629        return Vector(res)

Returns the global coordinates for input local vector (3d)

def bbone_to_schain(self) -> None:
635    def bbone_to_schain(self) -> None:
636        """
637        Function requires turtle to be in orientation #2 (at alpha carbon,
638        headed towards carbonyl, with nitrogen on left) and converts to orientation #1
639        (at alpha c, headed to beta carbon, with nitrogen on left.
640
641        Arguments:
642            turtle: Turtle3D object in orientation #2
643
644        Returns: modified Turtle3D
645        """
646
647        # assert self._orientation == 2, f'bbone_to_schain() requires Turtle3D to be in orientation #2'
648
649        self.roll(240.0)
650        self.pitch(180.0)
651        self.yaw(110.0)
652        self.roll(240.0)
653        self.Orientation = 1  # sets the orientation flag

Function requires turtle to be in orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left) and converts to orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left.

Arguments: turtle: Turtle3D object in orientation #2

Returns: modified Turtle3D

def schain_to_bbone(self) -> None:
655    def schain_to_bbone(self) -> None:
656        """
657        Function requires turtle to be in orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left)
658        and converts to orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left).
659
660        Arguments:
661            None
662        Returns: modified Turtle3D
663        """
664
665        # assert self._orientation == 1, f'schain_to_bbone() requires Turtle3D to be in orientation #1'
666
667        self.pitch(180.0)
668        self.roll(240.0)
669        self.yaw(110.0)
670        self.roll(120.0)
671        self.Orientation = 2  # sets the orientation flag
672        return

Function requires turtle to be in orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left) and converts to orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left).

Arguments: None Returns: modified Turtle3D