PyMOL cartoons and surfaces¶
quiltwright.pymol
¶
Molecular cartoons and surfaces, via headless PyMOL¶
The representation :mod:pypdb2pov cannot write. pdb2pov has emitted
atoms and bonds since 1993 -- spheres at van der Waals, covalent or CPK radii,
optionally with bond cylinders -- and nothing else. There is no ribbon in it
and never was.
Yet pov-scenes/porin/3porin.inc is a Richardson cartoon, because it was
never pdb2pov output: its header says converted to POVRay V2.2 by
WCVT2POV V2.7, so the ribbons came from some 1993-94 mesh exporter whose
output survives in no repository. The most striking image in the archive is
therefore the one representation the pipeline could not regenerate. This
module regenerates it, from a PDB ID and a command line.
The contract is pdb2pov -o's, deliberately. What comes out is an
object-only .inc: geometry centred on the origin, a
#declare <name>_enclosing_radius beside it, no camera and no lights. That
is the same shape :mod:pypdb2pov writes, which is what makes a cartoon drop
into an existing scene unchanged::
#include "ompf_cartoon.inc"
Vitrine_Mount(ompf_cartoon, ompf_cartoon_enclosing_radius)
Vitrine_Mount asks nothing about how the geometry was produced, and
neither :func:~quiltwright.povray.render_pov_quilt nor
:class:~quiltwright.povray.PovCamera needs any change to render it.
Why the export needs post-processing. cmd.get_povray() emits one
mesh2 object per triangle, each carrying its own three-entry vertex,
normal and texture list. Nothing is shared. A GFP cartoon arrives as 17,140
single-face meshes and 9.3 MB; an OmpF porin trimer as 75,792 and 41.3 MB; an
alpha-hemolysin heptamer as 152,596 and 83.1 MB -- and a quilt re-parses that
48 times. :func:~quiltwright.povgen.coalesce_mesh2 merges them into one
mesh with shared lists, which is most of what this module does with what
PyMOL hands it.
PyMOL is optional, and stays optional. Open-source PyMOL is not
OSI-licensed -- Homebrew declares it LicenseRef-Homebrew-cannot-represent
-- while quiltwright is BSD-3, so it can never be a hard dependency. It is
also awkward to reach: PyPI has only ever published alphas of
pymol-open-source, and the Homebrew build bundles its own interpreter that
no project virtualenv can import from. So this module drives PyMOL
in-process when it can import it and by subprocess when it cannot, chooses
automatically, and reports which through :func:available.
Typical use::
from quiltwright.pymol import cartoon_inc
result = cartoon_inc("2omf.cif.gz", "ompf_cartoon.inc")
print(result.enclosing_radius, result.faces)
CartoonMeshResult(path, enclosing_radius, vertices, faces, backend)
dataclass
¶
What :func:cartoon_obj wrote.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The |
required |
enclosing_radius
|
float
|
Radius of the sphere about the origin that
contains the geometry, in angstroms -- identical to what
:func: |
required |
vertices
|
int
|
Vertices in the emitted mesh, after deduplication. |
required |
faces
|
int
|
Triangles in the emitted mesh. |
required |
backend
|
str
|
|
required |
CartoonResult(path, identifier, enclosing_radius, centre, vertices, faces, rep, backend)
dataclass
¶
What :func:cartoon_inc wrote.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The |
required |
identifier
|
str
|
The POV-Ray identifier declared in it. |
required |
enclosing_radius
|
float
|
Radius of the sphere about the origin that
contains the geometry, in angstroms. Also declared in the file as
|
required |
centre
|
tuple[float, float, float]
|
The model-space point moved to the origin, in angstroms. |
required |
vertices
|
int
|
Vertices in the emitted mesh, after deduplication. |
required |
faces
|
int
|
Triangles in the emitted mesh. |
required |
rep
|
str
|
The representation exported. |
required |
backend
|
str
|
|
required |
PyMolNotAvailable
¶
Bases: RuntimeError
Raised when neither an importable PyMOL nor a pymol binary exists.
available()
¶
How PyMOL can be reached from here, if at all.
Returns:
| Type | Description |
|---|---|
str | None
|
|
Source code in src/quiltwright/pymol.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
cartoon_inc(source, out, *, rep='cartoon', color='spectrum', finish='normal', selection='polymer', assembly='1', transparency=0.0, surface_quality=None, coalesce=True, name=None)
¶
Export a structure as a POV-Ray object-only include.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str | Path
|
Anything PyMOL can load -- |
required |
out
|
str | Path
|
Path of the |
required |
rep
|
str
|
One of :data: |
'cartoon'
|
color
|
str | None
|
|
'spectrum'
|
finish
|
str
|
|
'normal'
|
selection
|
str
|
PyMOL selection to show; |
'polymer'
|
assembly
|
str
|
Biological assembly to load. |
'1'
|
transparency
|
float
|
0 to 1. Exports as POV-Ray |
0.0
|
surface_quality
|
int | None
|
PyMOL's |
None
|
coalesce
|
bool
|
Merge the per-triangle meshes. Leave it on unless you are comparing against the raw export. |
True
|
name
|
str | None
|
POV-Ray identifier to declare. Defaults to out's stem, put
through :func: |
None
|
Returns:
| Type | Description |
|---|---|
CartoonResult
|
A :class: |
Raises:
| Type | Description |
|---|---|
PyMolNotAvailable
|
If PyMOL cannot be reached at all. |
ValueError
|
If rep or finish is not a recognised value. |
Source code in src/quiltwright/pymol.py
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 | |
cartoon_obj(source, out, *, rep='cartoon', color=None, roughness=0.3, selection='polymer', assembly='1', surface_quality=None)
¶
Export a structure as a Wavefront OBJ -- the mesh twin of :func:cartoon_inc.
Runs the identical PyMOL export (same representation, selection and
assembly) and coalesces it the same way, but writes the triangles as a
plain .obj instead of a POV-Ray include, for feeding the same
subject to :mod:quiltwright.cycles instead of :mod:quiltwright.povray.
This is what makes a "which backend wins on a mesh this size" comparison
honest: both start from the exact same PyMOL triangulation, not two
independently-modelled scenes.
Geometry is centred on the origin exactly as :func:cartoon_inc centres
it (undoing PyMOL's camera pull-back, then the same bounding-box
recentring), so the enclosing radius here matches what that function
would report for the same export.
No coordinate flip. With the identity view the export script sets,
cmd.get_povray() emits the model's own right-handed coordinates,
offset only by the view centre and the camera pull-back -- PyMOL's POV
header compensates with a right-handed camera rather than by reflecting
the geometry. :mod:quiltwright.cycles is right-handed too, so the OBJ
is the model, recentred, with PyMOL's winding kept. (It is
:func:cartoon_inc, bound for left-handed POV-Ray, that reflects z.)
An earlier version negated z here on the belief that the export was
already left-handed, and so rendered every structure as its enantiomer;
tests/test_pymol.py now checks the mesh against the structure's own
CA atoms. Also see quiltwright.cycles's obj importer, which must
be told the file is already in this package's +z-up convention, or
Blender's default Y-up-to-Z-up remap for Wavefront files turns this mesh
90 degrees against a camera framed for it.
Colour. Plain OBJ carries none, so by default (color=None) this
writes geometry only, exactly as before. Passing a colour bakes it into
a companion .mtl instead: color="ss" runs cmd.dss() and
splits the mesh into the three flat colours of :data:SS_COLORS (helix,
strand, loop -- the conventional three-colour cartoon), and any other
PyMOL colour name or "#rrggbb" paints the whole thing one flat
colour. Either way the colour PyMOL baked into the raw mesh is read back
out of its own texture list rather than recomputed, so it is exactly what
PyMOL applied. color="spectrum" is rejected: a rainbow ramp is one
colour per residue, which would mean one OBJ material per residue --
unworkable, and exactly the case :func:cartoon_inc and the POV-Ray
backend exist for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str | Path
|
Anything PyMOL can load -- |
required |
out
|
str | Path
|
Path of the |
required |
rep
|
str
|
One of :data: |
'cartoon'
|
color
|
str | None
|
|
None
|
roughness
|
float
|
Blender |
0.3
|
selection
|
str
|
PyMOL selection to show; |
'polymer'
|
assembly
|
str
|
Biological assembly to load; |
'1'
|
surface_quality
|
int | None
|
PyMOL's |
None
|
Returns:
| Type | Description |
|---|---|
CartoonMeshResult
|
A :class: |
Raises:
| Type | Description |
|---|---|
PyMolNotAvailable
|
If PyMOL cannot be reached at all. |
ValueError
|
If rep is not a known representation, color is
|
Source code in src/quiltwright/pymol.py
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 | |
pov_identifier(stem, fallback='molecule')
¶
Derive a legal POV-Ray identifier from an output path.
POV identifiers are alphanumerics and underscores and may not begin with a
digit, so 2omf -- the obvious name for a structure -- becomes
_2omf. Kept deliberately identical to pypdb2pov's function of the
same name, so a cartoon and an atom scene of the same structure declare
the same identifier and a host scene can swap one for the other.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stem
|
str
|
Output path or bare stem. |
required |
fallback
|
str
|
Used when stem has no usable characters. |
'molecule'
|
Returns:
| Type | Description |
|---|---|
str
|
A legal identifier. |
Source code in src/quiltwright/pymol.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |