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