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

3D Turtle.

Turtle3D(name='3D_Turtle')
37    def __init__(self, name="3D_Turtle"):
38        # internal rep is arrays
39        self._position = numpy.array((0.0, 0.0, 0.0), "d")
40        self._heading = numpy.array((1.0, 0.0, 0.0), "d")
41        self._left = numpy.array((0.0, 1.0, 0.0), "d")
42        self._up = numpy.array((0.0, 0.0, 1.0), "d")
43
44        # expose these as Vectors
45        self.pos = Vector(0.0, 0.0, 0.0)
46        self.h = Vector(1.0, 0.0, 0.0)
47        self.l = Vector(0.0, 1.0, 0.0)
48        self.u = Vector(0.0, 0.0, 1.0)
49
50        self._name = name
51        self._pen = _UP_
52        self._orientation = _ORIENTATION_INIT  # will be set to 1 or 2 later when used for residue building
53        self._recording = False
54        self._tape = []
pos
h
l
u
def new( self, name: str, pos: proteusPy.vector3D.Vector3D, head: proteusPy.vector3D.Vector3D = <Vector3D (1.00, 0.00, 0.00)>, left: proteusPy.vector3D.Vector3D = <Vector3D (0.00, 1.00, 0.00)>, up: proteusPy.vector3D.Vector3D = <Vector3D (0.00, 0.00, 1.00)>, recording=False) -> None:
56    def new(
57        self,
58        name: str,
59        pos: Vector,
60        head: Vector = Vector(1.0, 0.0, 0.0),
61        left: Vector = Vector(0.0, 1.0, 0.0),
62        up: Vector = Vector(0.0, 0.0, 1.0),
63        recording=False,
64    ) -> None:
65        """
66        Initialize a Turtle with a name, position, optionally heading and left.
67
68        :param name: Turtle's Name
69        :param pos: Turtle's Position
70        :param head: Turtle's Heading vector, defaults to Vector(1,0,0)
71        :param left: Turtle's Left vector, defaults to Vector(0,1,0)
72        :param up: Turtle's Up vector, defaults to Vector(0,0,1)
73        :param pen: Pen state, defaults to 'up'
74        :param recording: _description_, defaults to False
75        """
76        self._name = name
77        self.pos = pos
78        self.h = head.normalized()
79        self.l = left.normalized()
80        self.u = up.normalized()
81
82        self._position = pos.get_array()
83        self._left = left.normalized().get_array()
84        self._heading = head.normalized().get_array()
85        self._up = up.normalized().get_array()
86
87        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:
 89    def copy_coords(self, source) -> None:
 90        """
 91        Copy the Position, Heading, Left and Up coordinate system from
 92        the input source into self. Argument: source: Turtle3D
 93        Returns: None
 94        """
 95
 96        # copy the Arrays
 97        self._position = source._position.copy()
 98        self._heading = source._heading.copy()
 99        self._left = source._left.copy()
100        self._up = source._up.copy()
101
102        # copy the Vectors - create new ones from the source arrays
103        self.pos = Vector(source._position)
104        self.h = Vector(source._heading)
105        self.l = Vector(source._left)
106        self.u = Vector(source._up)
107
108        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:
110    def reset(self) -> None:
111        """
112        Reset the Turtle to be at the Origin, with correct Heading, Left and Up vectors.
113        Arguments: None
114        Returns: None
115        """
116        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>
118    @property
119    def Orientation(self) -> numpy.array:
120        return self._orientation
Pen: str
129    @property
130    def Pen(self) -> str:
131        if self._pen == _UP_:
132            return "up"
133        else:
134            return "down"
Recording: bool
145    @property
146    def Recording(self) -> bool:
147        return self._recording
def ResetTape(self) -> None:
153    def ResetTape(self) -> None:
154        self.Recording(False)
155        self._tape = []
Position: proteusPy.vector3D.Vector3D
157    @property
158    def Position(self) -> Vector:
159        """
160        The Turtle's Position
161
162        :return: Position
163        :rtype: Vector
164        """
165        return self.pos

The Turtle's Position

Returns

Position

Heading: proteusPy.vector3D.Vector3D
200    @property
201    def Heading(self) -> Vector:
202        """
203        Get the Turtle's Heading
204
205        :return: Heading
206        :rtype: Vector
207        """
208        return self.h

Get the Turtle's Heading

Returns

Heading

Left: proteusPy.vector3D.Vector3D
239    @property
240    def Left(self) -> Vector:
241        """
242        Get the Turtle's Left direction vector
243
244        :return: Left
245        :rtype: Vector
246        """
247
248        return self.l

Get the Turtle's Left direction vector

Returns

Left

Up: proteusPy.vector3D.Vector3D
278    @property
279    def Up(self) -> Vector:
280        """
281        The Turtle's Up direction vector
282
283        :return: Up
284        :rtype: Vector
285        """
286
287        return self.u

The Turtle's Up direction vector

Returns

Up

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

Return the Turtle's Name.

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

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

Parameters
  • distance: Amount to move (Ã…)
def roll(self, angle) -> None:
342    def roll(self, angle) -> None:
343        """
344        Roll the Turtle about the heading vector angle degrees
345
346        :param angle: roll angle, degrees -180 -> 180
347        :type angle: float
348        """
349
350        ang = angle * math.pi / 180.0
351        cosang = numpy.cos(ang)
352        sinang = numpy.sin(ang)
353
354        lold = self._left.copy()
355        uold = self._up.copy()
356
357        self._up[0] = cosang * uold[0] - sinang * lold[0]
358        self._up[1] = cosang * uold[1] - sinang * lold[1]
359        self._up[2] = cosang * uold[2] - sinang * lold[2]
360        self._up = self.unit(self._up)
361
362        self.u = Vector(self._up)
363
364        self._left[0] = cosang * lold[0] + sinang * uold[0]
365        self._left[1] = cosang * lold[1] + sinang * uold[1]
366        self._left[2] = cosang * lold[2] + sinang * uold[2]
367        self._left = self.unit(self._left)
368
369        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:
371    def yaw(self, angle) -> None:
372        """
373        Yaw the Turtle about the up vector (180 - angle) degrees.
374        This is used when building molecules
375
376        :param angle: Yaw angle, degrees -180 -> 180
377        :type angle: float
378        """
379
380        ang = ((180 - angle) * math.pi) / 180.0
381        cosang = numpy.cos(ang)
382        sinang = numpy.sin(ang)
383
384        lold = self._left.copy()
385        hold = self._heading.copy()
386
387        self._heading[0] = cosang * hold[0] + sinang * lold[0]
388        self._heading[1] = cosang * hold[1] + sinang * lold[1]
389        self._heading[2] = cosang * hold[2] + sinang * lold[2]
390        self._heading = self.unit(self._heading)
391        self.h = Vector(self._heading)
392
393        self._left[0] = cosang * lold[0] - sinang * hold[0]
394        self._left[1] = cosang * lold[1] - sinang * hold[1]
395        self._left[2] = cosang * lold[2] - sinang * hold[2]
396        self._left = self.unit(self._left)
397        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:
399    def turn(self, angle) -> None:
400        """
401        Turn the Turtle about the up vector angle degrees.
402
403        :param angle: Turn angle, degrees
404        :type angle: float
405        """
406
407        ang = (angle * math.pi) / 180.0
408
409        cosang = numpy.cos(ang)
410        sinang = numpy.sin(ang)
411
412        heading = self._heading.copy()
413        left = self._left.copy()
414
415        self._heading[0] = cosang * heading[0] + sinang * left[0]
416        self._heading[1] = cosang * heading[1] + sinang * left[1]
417        self._heading[2] = cosang * heading[2] + sinang * left[2]
418
419        self._heading = self.unit(self._heading)
420        self.h = Vector(self._heading)
421
422        self._left[0] = cosang * left[0] - sinang * heading[0]
423        self._left[1] = cosang * left[1] - sinang * heading[1]
424        self._left[2] = cosang * left[2] - sinang * heading[2]
425        self._left = self.unit(self._left)
426        self.l = Vector(self._left)

Turn the Turtle about the up vector angle degrees.

Parameters
  • angle: Turn angle, degrees
def pitch(self, angle) -> None:
428    def pitch(self, angle) -> None:
429        """
430        pitch the Turtle about the left vector angle degrees
431
432        :param angle: Pitch angle, degrees -180 -> 180
433        :type angle: float
434        """
435
436        up = self._up.copy()
437        heading = self._heading.copy()
438
439        ang = angle * math.pi / 180.0
440        cosang = numpy.cos(ang)
441        sinang = numpy.sin(ang)
442
443        self._heading[0] = heading[0] * cosang - up[0] * sinang
444        self._heading[1] = heading[1] * cosang - up[1] * sinang
445        self._heading[2] = heading[2] * cosang - up[2] * sinang
446        self._heading = self.unit(self._heading)
447        self.h = Vector(self._heading)
448
449        self._up[0] = up[0] * cosang + heading[0] * sinang
450        self._up[1] = up[1] * cosang + heading[1] * sinang
451        self._up[2] = up[2] * cosang + heading[2] * sinang
452        self._up = self.unit(self._up)
453        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) -> proteusPy.vector3D.Vector3D:
455    def unit(self, v) -> Vector:
456        """
457        Return a unit vector for the input vector.
458
459        :param v: Input Vector
460        :return: Unit Vector
461        """
462        norm = numpy.linalg.norm(v)
463        if norm == 0:
464            return v
465        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:
467    def orient(
468        self, position: numpy.array, heading: numpy.array, left: numpy.array
469    ) -> None:
470        """
471        Orients the turtle with Position at p1, Heading at p2 and Left at p3
472
473        :param position: Position
474        :type position: numpy.array
475        :param heading: Heading direction vector
476        :type heading: numpy.array
477        :param left: Left direction vector
478        :type left: numpy.array
479        """
480
481        self._position = position
482
483        temp = heading - position
484        self._heading = self.unit(temp)
485        self.h = Vector(self._heading)
486
487        temp = left - position
488        self._left = self.unit(temp)
489
490        temp = numpy.cross(self._heading, self._left)
491        self._up = self.unit(temp)
492        self.u = Vector(self._up)
493
494        # fix left to be orthogonal
495        temp = numpy.cross(self._up, self._heading)
496        self._left = self.unit(temp)
497        self.l = Vector(self._left)
498        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:
500    def orient_at_residue(self, chain, resnumb, orientation) -> None:
501        """
502        Orient the turtle at the specified residue from the input Chain in
503        either orientation 1 or 2.
504
505        :param chain: list of Residues in the model, eg: chain = model['A']
506        :type chain: str
507        :param resnumb: residue number
508        :type resnumb: int
509        :param orientation: 1 - at Ca heading towards Cb with N at the left or
510            2 - at Ca heading towards C with N at the left
511        :type orientation: int
512        """
513
514        assert (
515            self._orientation == 1 or self._orientation == 2
516        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
517
518        residue = chain[resnumb]
519        assert (
520            residue is not None
521        ), f"get_backbone_from_sidechain() requires valid residue number"
522
523        # by this point I'm pretty confident I have coordinates
524        # we pull the actual numpy.array from the coordinates since that's what the
525        # Turtle3D expects
526
527        n = residue["N"].get_vector().get_array()
528        ca = residue["CA"].get_vector().get_array()
529        cb = residue["CB"].get_vector().get_array()
530        c = residue["C"].get_vector().get_array()
531
532        if orientation == ORIENT_SIDECHAIN:
533            self.orient(ca, cb, n)
534            self.set_orientation(ORIENT_SIDECHAIN)
535        elif orientation == ORIENT_BACKBONE:
536            self.orient(ca, c, n)
537            self.set_orientation(ORIENT_BACKBONE)
538        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:
540    def orient_from_backbone(
541        self,
542        n: numpy.array,
543        ca: numpy.array,
544        cb: numpy.array,
545        c: numpy.array,
546        orientation,
547    ) -> None:
548        """
549        Orient the turtle at the specified residue from the input Chain in
550        either orientation 1 or 2.
551
552        Arguments:
553            turtle: input Turtle3D object
554            n: position of n atom
555            ca: position of ca atom
556            c: position of c atom
557            orientation:
558            1 - at Ca heading towards Cb with N at the left
559            2 - at Ca heading towards C with N at the left
560        Returns: None. Turtle internal state is modified
561        """
562
563        assert (
564            orientation == 1 or orientation == 2
565        ), f"orient_at_residue() requires Turtle3D to be #1 or #2"
566
567        _n = n.copy()
568        _ca = ca.copy()
569        _cb = cb.copy()
570        _c = c.copy()
571
572        if orientation == ORIENT_SIDECHAIN:
573            self.orient(_ca, _cb, _n)
574            self.Orientation = ORIENT_SIDECHAIN
575        elif orientation == ORIENT_BACKBONE:
576            self.orient(_ca, _c, _n)
577            self.Orientation = ORIENT_BACKBONE
578        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>:
580    def to_local(self, global_vec) -> numpy.array:
581        """
582        Returns the Turtle-centered local coordinates for input Global vector (3d)
583        """
584
585        newpos = global_vec - self._position
586        dp1 = numpy.dot(self._heading, newpos)
587        dp2 = numpy.dot(self._left, newpos)
588        dp3 = numpy.dot(self._up, newpos)
589
590        result = numpy.array((dp1, dp2, dp3), "d")
591        return result

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

def to_localVec(self, global_vec) -> proteusPy.vector3D.Vector3D:
593    def to_localVec(self, global_vec) -> Vector:
594        """
595        Returns the Turtle-centered local coordinates for input Global vector (3d)
596        """
597
598        res = self.to_local(global_vec)
599        return Vector(res)

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

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

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

def to_globalVec(self, local) -> proteusPy.vector3D.Vector3D:
626    def to_globalVec(self, local) -> Vector:
627        """
628        Returns the global coordinates for input local vector (3d)
629        """
630        res = self.to_global(local)
631        return Vector(res)

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

def bbone_to_schain(self) -> None:
637    def bbone_to_schain(self) -> None:
638        """
639        Function requires turtle to be in orientation #2 (at alpha carbon,
640        headed towards carbonyl, with nitrogen on left) and converts to orientation #1
641        (at alpha c, headed to beta carbon, with nitrogen on left.
642
643        Arguments:
644            turtle: Turtle3D object in orientation #2
645
646        Returns: modified Turtle3D
647        """
648
649        # assert self._orientation == 2, f'bbone_to_schain() requires Turtle3D to be in orientation #2'
650
651        self.roll(240.0)
652        self.pitch(180.0)
653        self.yaw(110.0)
654        self.roll(240.0)
655        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:
657    def schain_to_bbone(self) -> None:
658        """
659        Function requires turtle to be in orientation #1 (at alpha c, headed to beta carbon, with nitrogen on left)
660        and converts to orientation #2 (at alpha carbon, headed towards carbonyl, with nitrogen on left).
661
662        Arguments:
663            None
664        Returns: modified Turtle3D
665        """
666
667        # assert self._orientation == 1, f'schain_to_bbone() requires Turtle3D to be in orientation #1'
668
669        self.pitch(180.0)
670        self.roll(240.0)
671        self.yaw(110.0)
672        self.roll(120.0)
673        self.Orientation = 2  # sets the orientation flag
674        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