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
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.
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 = []
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
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
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
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
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
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
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
315 @property 316 def Name(self) -> str: 317 """ 318 Return the Turtle's Name. 319 """ 320 return self._name
Return the Turtle's Name.
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 (Ã…)
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
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
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
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
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
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
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
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
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)
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)
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)
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)
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
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