Skip to content

Contraction Expansion

Contraction and expansion analysis for body movement patterns.

This module provides optimized functions for analyzing contraction and expansion of body configurations in 2D and 3D space. It computes area (2D) or volume (3D) metrics for sets of body points and tracks changes relative to a baseline.

The module uses Numba JIT compilation for performance optimization, making it suitable for real-time motion capture analysis.

Key Features
  • Fast area calculation using Shoelace formula (2D)
  • Tetrahedron volume calculation using determinants (3D)
  • Baseline-relative expansion/contraction indices
  • Support for both single-frame and time-series analysis
  • Automatic warmup for JIT compilation
Typical Applications
  • Dance movement analysis (body expansion/contraction)
  • Gesture recognition (hand/arm configurations)
  • Sports biomechanics (body positioning)
  • Clinical movement assessment

ContractionExpansion

Analyze body movement contraction/expansion patterns.

This class provides a standardized API for computing area (2D) or volume (3D) metrics for body point configurations and tracking expansion/contraction relative to a baseline.

Parameters:

Name Type Description Default
mode ('2D', '3D', None)

Analysis mode. If None, auto-detects from data dimensions.

"2D"
baseline_frame int

Frame index to use as baseline for time series (default: 0).

0

Examples:

>>> # Create analyzer
>>> ce = ContractionExpansion(mode="2D")
>>>
>>> # Single frame analysis
>>> points_2d = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
>>> result = ce(points_2d)
>>> print(result['metric'])  # Area of square
1.0
>>> # Time series analysis
>>> ce_3d = ContractionExpansion(mode="3D", baseline_frame=0)
>>> frames = np.random.randn(100, 4, 3)
>>> result = ce_3d(frames)
>>> print(result['states'][:10])  # First 10 frame states
Source code in pyeyesweb/low_level/contraction_expansion.py
class ContractionExpansion:
    """Analyze body movement contraction/expansion patterns.

    This class provides a standardized API for computing area (2D) or volume (3D)
    metrics for body point configurations and tracking expansion/contraction
    relative to a baseline.

    Parameters
    ----------
    mode : {"2D", "3D", None}, optional
        Analysis mode. If None, auto-detects from data dimensions.
    baseline_frame : int, optional
        Frame index to use as baseline for time series (default: 0).

    Examples
    --------
    >>> # Create analyzer
    >>> ce = ContractionExpansion(mode="2D")
    >>>
    >>> # Single frame analysis
    >>> points_2d = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
    >>> result = ce(points_2d)
    >>> print(result['metric'])  # Area of square
    1.0

    >>> # Time series analysis
    >>> ce_3d = ContractionExpansion(mode="3D", baseline_frame=0)
    >>> frames = np.random.randn(100, 4, 3)
    >>> result = ce_3d(frames)
    >>> print(result['states'][:10])  # First 10 frame states
    """

    def __init__(self, mode=None, baseline_frame=0):
        self.mode = mode
        self.baseline_frame = baseline_frame

    def __call__(self, data):
        """Analyze movement data using the configured settings.

        Parameters
        ----------
        data : ndarray
            Either single frame (4, 2) or (4, 3) for 2D/3D points,
            or time series (n_frames, 4, 2) or (n_frames, 4, 3).

        Returns
        -------
        dict
            For single frame:
                - 'metric': area or volume value
                - 'dimension': "2D" or "3D"
            For time series:
                - 'metrics': array of area/volume values
                - 'indices': array of expansion indices relative to baseline
                - 'states': array of states (-1=contraction, 0=neutral, 1=expansion)
                - 'dimension': "2D" or "3D"
        """
        return analyze_movement(data, mode=self.mode, baseline_frame=self.baseline_frame)

__call__(data)

Analyze movement data using the configured settings.

Parameters:

Name Type Description Default
data ndarray

Either single frame (4, 2) or (4, 3) for 2D/3D points, or time series (n_frames, 4, 2) or (n_frames, 4, 3).

required

Returns:

Type Description
dict

For single frame: - 'metric': area or volume value - 'dimension': "2D" or "3D" For time series: - 'metrics': array of area/volume values - 'indices': array of expansion indices relative to baseline - 'states': array of states (-1=contraction, 0=neutral, 1=expansion) - 'dimension': "2D" or "3D"

Source code in pyeyesweb/low_level/contraction_expansion.py
def __call__(self, data):
    """Analyze movement data using the configured settings.

    Parameters
    ----------
    data : ndarray
        Either single frame (4, 2) or (4, 3) for 2D/3D points,
        or time series (n_frames, 4, 2) or (n_frames, 4, 3).

    Returns
    -------
    dict
        For single frame:
            - 'metric': area or volume value
            - 'dimension': "2D" or "3D"
        For time series:
            - 'metrics': array of area/volume values
            - 'indices': array of expansion indices relative to baseline
            - 'states': array of states (-1=contraction, 0=neutral, 1=expansion)
            - 'dimension': "2D" or "3D"
    """
    return analyze_movement(data, mode=self.mode, baseline_frame=self.baseline_frame)

analyze_movement(data, mode=None, baseline_frame=0)

Analyze body movement contraction/expansion patterns.

This function computes area (2D) or volume (3D) metrics for body point configurations and tracks expansion/contraction relative to a baseline.

Parameters:

Name Type Description Default
data ndarray

Either single frame (4, 2) or (4, 3) for 2D/3D points, or time series (n_frames, 4, 2) or (n_frames, 4, 3).

required
mode ('2D', '3D', None)

Analysis mode. If None, auto-detects from data dimensions.

"2D"
baseline_frame int

Frame index to use as baseline for time series (default: 0).

0

Returns:

Type Description
dict

For single frame: - 'metric': area or volume value - 'dimension': "2D" or "3D" For time series: - 'metrics': array of area/volume values - 'indices': array of expansion indices relative to baseline - 'states': array of states (-1=contraction, 0=neutral, 1=expansion) - 'dimension': "2D" or "3D"

Raises:

Type Description
ValueError

If data shape is invalid or mode doesn't match data dimensions.

Examples:

>>> # Single frame 2D analysis
>>> points_2d = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
>>> result = analyze_movement(points_2d, mode="2D")
>>> print(result['metric'])  # Area of square
1.0
>>> # Time series 3D analysis
>>> frames = np.random.randn(100, 4, 3)
>>> result = analyze_movement(frames, mode="3D", baseline_frame=0)
>>> print(result['states'][:10])  # First 10 frame states
Source code in pyeyesweb/low_level/contraction_expansion.py
def analyze_movement(data, mode=None, baseline_frame=0):
    """Analyze body movement contraction/expansion patterns.

    This function computes area (2D) or volume (3D) metrics for body point
    configurations and tracks expansion/contraction relative to a baseline.

    Parameters
    ----------
    data : ndarray
        Either single frame (4, 2) or (4, 3) for 2D/3D points,
        or time series (n_frames, 4, 2) or (n_frames, 4, 3).
    mode : {"2D", "3D", None}, optional
        Analysis mode. If None, auto-detects from data dimensions.
    baseline_frame : int, optional
        Frame index to use as baseline for time series (default: 0).

    Returns
    -------
    dict
        For single frame:
            - 'metric': area or volume value
            - 'dimension': "2D" or "3D"
        For time series:
            - 'metrics': array of area/volume values
            - 'indices': array of expansion indices relative to baseline
            - 'states': array of states (-1=contraction, 0=neutral, 1=expansion)
            - 'dimension': "2D" or "3D"

    Raises
    ------
    ValueError
        If data shape is invalid or mode doesn't match data dimensions.

    Examples
    --------
    >>> # Single frame 2D analysis
    >>> points_2d = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
    >>> result = analyze_movement(points_2d, mode="2D")
    >>> print(result['metric'])  # Area of square
    1.0

    >>> # Time series 3D analysis
    >>> frames = np.random.randn(100, 4, 3)
    >>> result = analyze_movement(frames, mode="3D", baseline_frame=0)
    >>> print(result['states'][:10])  # First 10 frame states
    """
    if data.ndim == 2:
        dims = data.shape[1]
        is_timeseries = False
    elif data.ndim == 3:
        dims = data.shape[2]
        is_timeseries = True
        if data.shape[1] != 4:
            raise ValueError("Invalid shape: second dimension must be 4")
    else:
        raise ValueError("Invalid data dimensions")

    if mode is None:
        mode = "2D" if dims == 2 else "3D" if dims == 3 else None
        if mode is None:
            raise ValueError("Invalid coordinate dimensions")

    expected_dims = 2 if mode == "2D" else 3
    if dims != expected_dims:
        raise ValueError(f"Mode {mode} requires {expected_dims}D data")

    if not is_timeseries:
        if mode == "2D":
            metric = _area_2d_fast(data)
        else:
            metric = _volume_3d_fast(data)

        return {"metric": metric, "dimension": mode}

    if mode == "2D":
        metrics, indices, states = _process_timeseries_2d(data, baseline_frame)
    else:
        metrics, indices, states = _process_timeseries_3d(data, baseline_frame)

    return {
        "metrics": metrics,
        "indices": indices, 
        "states": states,
        "dimension": mode
    }