Shape Properties#

Shape properties are measurements of the shape of a protein when it is viewed from a specific orientation. Examples include the projected area, perimeter, bounding-box size, and shape moments.

Before a shape property can be calculated, the corresponding shape must already have been calculated by a ShapeCalculator, as described in Shapes.

Using Shape Properties#

Before calculating a shape property, we first need to load a protein and calculate a shape for it. Here, we use the 8JXW structure, as described in Loading Protein, and calculate an alpha shape for each sampled orientation. See Shapes for more details on shape calculation.

Load Protein and Calculate Alpha Shape#
import ProteoTwirl.ProteinRotation as pt

protein_structure = pt.ProteinRotation(
    protein             = "Test_Data/8JXW_H.pdb",
    selection           = "protein and segid E and not element H",
    base_rotation       = "principle",
    center_selection    = True,
)

protein_structure.add_property(
    property        = "Alphashape",
    alpha           = 0.5,
    hole_fill       = 0.1,
    rotation_res    = 64,
    use_default     = False,
)

# Added Alpha_Shape_50_HF10

To calculate a shape property, specify the name of the previously calculated shape using shape_prop_name. The rotation settings should match those used for the shape calculation, including rotation_res, rotation_range, and related options, so that each property value corresponds to the same sampled orientation.

Calculate Shape Area#
protein_structure.add_property(
    property        = "shape.area",
    shape_prop_name = "Alpha_Shape_50_HF10",
    rotation_res    = 64,
    use_default     = False,
)

# Added Alpha_Shape_50_HF10_area

The first shape added to a ProteinRotation object is set as the default shape. If a shape property should be calculated for this default shape, shape_prop_name does not need to be specified. See Default Shape for more information on default shapes.

Available Shape Properties#

ProteoTwirl includes several built-in shape properties. These are listed below.

Shape Properties#

Property

Name

Return Type

Description

Area

shape.area

float

Calculates the area of the shape.

Perimeter

shape.perimeter

float

Calculates the perimeter length of the shape.

Bounding X Length

shape.bounds_xspan

float

Calculates the x-axis span of the smallest bounding box containing the shape.

Bounding Y Length

shape.bounds_yspan

float

Calculates the y-axis span of the smallest bounding box containing the shape.

Number of Components

shape.component_count

int

Counts how many disconnected polygons the shape consists of.

First Hu moment

shape.hu1

float

Calculates the first Hu moment of the shape.

Second Hu moment

shape.hu2

float

Calculates the second Hu moment of the shape.

Third Hu moment

shape.hu3

float

Calculates the third Hu moment of the shape.

Fourth Hu moment

shape.hu4

float

Calculates the fourth Hu moment of the shape.

Fifth Hu moment

shape.hu5

float

Calculates the fifth Hu moment of the shape.

Sixth Hu moment

shape.hu6

float

Calculates the sixth Hu moment of the shape.

Seventh Hu moment

shape.hu7

float

Calculates the seventh Hu moment of the shape.

All Hu moments

shape.hu_moments

list[float]

Calculates all seven Hu moments of the shape.

Custom Shape Property#

You can define a custom shape property in ProteoTwirl by defining a function that takes a Shapely geometry object as input and returns a value.

Here, we implement a simple circularity measure:

\[\text{circularity} = \frac{4\pi \cdot \text{Area}}{\text{Perimeter}^2}\]

A perfect circle has a circularity of 1. Less circular shapes have lower values.

Define a Circularity Function#
import numpy as np
from shapely.geometry import MultiPolygon

def circularity(shape, **kwargs):
    polygon = shape

    # If the shape contains multiple disconnected polygons,
    # calculate circularity for the largest component.
    if isinstance(shape, MultiPolygon):
        polygon = max(shape.geoms, key=lambda geom: geom.area)

    # Avoid division by zero for empty or degenerate shapes.
    if polygon.is_empty or polygon.length == 0:
        return np.nan

    return (4 * np.pi * polygon.area) / (polygon.length ** 2)

The easiest way to use this function as a shape property is to register it as a shape feature.

Register the Circularity Shape Property#
from ProteoTwirl.Properties.ShapeProperty import _register_shape_feature

_register_shape_feature("circularity", circularity)

The new property can then be calculated like any other shape property.

Calculate the Circularity Shape Property#
protein_structure.add_property(
    property        = "shape.circularity",
    shape_prop_name = "Alpha_Shape_50_HF10",
    rotation_res    = 64,
    use_default     = False,
)

# Added Alpha_Shape_50_HF10_circularity

Finally, visualize the new property.

Plot the Circularity Shape Property#
protein_structure.plot_property_3d(
    "Alpha_Shape_50_HF10_circularity",
    background="#14181e",
)