CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyglm

OpenGL Mathematics (GLM) library for Python providing comprehensive vector and matrix manipulation capabilities for graphics programming.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utility and Conversion Functions

Utility functions for type conversion, memory access, testing, packing/unpacking, and interfacing with external libraries. These functions provide essential tools for working with graphics APIs, file formats, and performance-critical applications.

Capabilities

Memory Access and Pointers

Functions for accessing the underlying memory of PyGLM types for interfacing with C libraries and graphics APIs.

def value_ptr(x):
    """
    Get a ctypes pointer to the underlying data of a PyGLM object.

    Args:
        x: Any PyGLM vector, matrix, or quaternion

    Returns:
        ctypes pointer to the data (c_float, c_double, c_int, etc.)

    Example:
        vec = glm.vec3(1, 2, 3)
        ptr = glm.value_ptr(vec)  # c_float pointer
        # Pass ptr to OpenGL functions like glUniform3fv()
    """

def sizeof(glm_type):
    """
    Get the size in bytes of a PyGLM type.

    Args:
        glm_type: PyGLM type (vec3, mat4, quat, etc.)

    Returns:
        Size in bytes

    Example:
        size = glm.sizeof(glm.vec3)  # 12 bytes (3 * 4-byte floats)
        size = glm.sizeof(glm.mat4)  # 64 bytes (16 * 4-byte floats)
        size = glm.sizeof(glm.dvec3) # 24 bytes (3 * 8-byte doubles)
    """

Type Construction from Pointers

Functions for creating PyGLM objects from memory pointers, useful when interfacing with external libraries.

def make_vec2(ptr):
    """
    Create a vec2 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data

    Returns:
        vec2 constructed from the pointer data

    Example:
        # Create vec2 from existing float array
        import ctypes
        data = (ctypes.c_float * 2)(1.0, 2.0)
        vec = glm.make_vec2(data)
    """

def make_vec3(ptr):
    """
    Create a vec3 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data

    Returns:
        vec3 constructed from the pointer data
    """

def make_vec4(ptr):
    """
    Create a vec4 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data

    Returns:
        vec4 constructed from the pointer data
    """

def make_mat2(ptr):
    """
    Create a mat2 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data (4 elements)

    Returns:
        mat2x2 constructed from the pointer data
    """

def make_mat3(ptr):
    """
    Create a mat3 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data (9 elements)

    Returns:
        mat3x3 constructed from the pointer data
    """

def make_mat4(ptr):
    """
    Create a mat4 from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data (16 elements)

    Returns:
        mat4x4 constructed from the pointer data
    """

def make_quat(ptr):
    """
    Create a quat from a ctypes pointer.

    Args:
        ptr: ctypes pointer to float data (4 elements: w, x, y, z)

    Returns:
        quat constructed from the pointer data
    """

Numerical Testing Functions

Functions for testing special floating-point values and performing epsilon-based comparisons.

def isinf(x):
    """
    Test if value(s) are infinite.

    Args:
        x: Scalar or vector input

    Returns:
        Boolean or boolean vector indicating infinity

    Example:
        isinf(float('inf'))  # True
        isinf(glm.vec3(1, float('inf'), 3))  # bvec3(False, True, False)
    """

def isnan(x):
    """
    Test if value(s) are NaN (Not a Number).

    Args:
        x: Scalar or vector input

    Returns:
        Boolean or boolean vector indicating NaN

    Example:
        isnan(float('nan'))  # True
        isnan(glm.vec2(1.0, float('nan')))  # bvec2(False, True)
    """

def epsilonEqual(x, y, epsilon):
    """
    Compare values using epsilon tolerance.

    Args:
        x: First value (scalar or vector)
        y: Second value (same type as x)
        epsilon: Tolerance value (scalar or vector)

    Returns:
        Boolean or boolean vector indicating equality within epsilon

    Example:
        epsilonEqual(1.0001, 1.0002, 0.001)  # True
        epsilonEqual(glm.vec3(1, 2, 3), glm.vec3(1.01, 2.01, 3.01), 0.02)  # bvec3(True, True, True)
    """

def epsilonNotEqual(x, y, epsilon):
    """
    Compare values for inequality using epsilon tolerance.

    Args:
        x: First value (scalar or vector)
        y: Second value (same type as x)
        epsilon: Tolerance value (scalar or vector)

    Returns:
        Boolean or boolean vector indicating inequality beyond epsilon

    Example:
        epsilonNotEqual(1.0, 1.1, 0.05)  # True (difference > epsilon)
    """

Bit Manipulation and Packing

Functions for packing and unpacking floating-point values and performing bit-level operations.

def floatBitsToInt(value):
    """
    Reinterpret float bits as signed integer.

    Args:
        value: Float value (scalar or vector)

    Returns:
        Integer with same bit pattern as input float

    Example:
        int_bits = glm.floatBitsToInt(1.0)
        # Useful for bit manipulation or hashing
    """

def intBitsToFloat(value):
    """
    Reinterpret integer bits as float.

    Args:
        value: Integer value (scalar or vector)

    Returns:
        Float with same bit pattern as input integer

    Example:
        float_val = glm.intBitsToFloat(1065353216)  # Should be 1.0
    """

def floatBitsToUint(value):
    """
    Reinterpret float bits as unsigned integer.

    Args:
        value: Float value (scalar or vector)

    Returns:
        Unsigned integer with same bit pattern as input float
    """

def uintBitsToFloat(value):
    """
    Reinterpret unsigned integer bits as float.

    Args:
        value: Unsigned integer value (scalar or vector)

    Returns:
        Float with same bit pattern as input unsigned integer
    """

Half-Precision Packing

Functions for packing and unpacking 16-bit half-precision floating-point values.

def packHalf2x16(v):
    """
    Pack two 32-bit floats into a single 32-bit unsigned integer as half-precision values.

    Args:
        v: vec2 containing two float values

    Returns:
        32-bit unsigned integer containing packed half-precision values

    Example:
        packed = glm.packHalf2x16(glm.vec2(1.0, -2.5))
        # Useful for reducing memory usage in shaders
    """

def unpackHalf2x16(p):
    """
    Unpack 32-bit unsigned integer into two half-precision floats.

    Args:
        p: 32-bit unsigned integer containing packed half-precision values

    Returns:
        vec2 containing the unpacked float values

    Example:
        unpacked = glm.unpackHalf2x16(packed_value)  # Returns vec2
    """

def packHalf4x16(v):
    """
    Pack four 32-bit floats into two 32-bit unsigned integers as half-precision values.

    Args:
        v: vec4 containing four float values

    Returns:
        uvec2 containing packed half-precision values

    Example:
        packed = glm.packHalf4x16(glm.vec4(1, 2, 3, 4))
    """

def unpackHalf4x16(p):
    """
    Unpack two 32-bit unsigned integers into four half-precision floats.

    Args:
        p: uvec2 containing packed half-precision values

    Returns:
        vec4 containing the unpacked float values
    """

Normalized Value Packing

Functions for packing normalized floating-point values into integer formats.

def packUnorm2x16(v):
    """
    Pack two normalized floats [0,1] into 16-bit unsigned integers.

    Args:
        v: vec2 with values in range [0, 1]

    Returns:
        32-bit unsigned integer containing packed values

    Example:
        packed = glm.packUnorm2x16(glm.vec2(0.5, 1.0))
    """

def unpackUnorm2x16(p):
    """
    Unpack 32-bit unsigned integer into two normalized floats.

    Args:
        p: 32-bit unsigned integer containing packed values

    Returns:
        vec2 with values in range [0, 1]
    """

def packSnorm2x16(v):
    """
    Pack two signed normalized floats [-1,1] into 16-bit signed integers.

    Args:
        v: vec2 with values in range [-1, 1]

    Returns:
        32-bit unsigned integer containing packed values

    Example:
        packed = glm.packSnorm2x16(glm.vec2(-0.5, 1.0))
    """

def unpackSnorm2x16(p):
    """
    Unpack 32-bit unsigned integer into two signed normalized floats.

    Args:
        p: 32-bit unsigned integer containing packed values

    Returns:
        vec2 with values in range [-1, 1]
    """

def packUnorm4x8(v):
    """
    Pack four normalized floats [0,1] into 8-bit unsigned integers.

    Args:
        v: vec4 with values in range [0, 1]

    Returns:
        32-bit unsigned integer containing packed values

    Example:
        # Pack RGBA color
        color = glm.vec4(1.0, 0.5, 0.25, 1.0)  # Red, half green, quarter blue, full alpha
        packed_color = glm.packUnorm4x8(color)
    """

def unpackUnorm4x8(p):
    """
    Unpack 32-bit unsigned integer into four normalized floats.

    Args:
        p: 32-bit unsigned integer containing packed values

    Returns:
        vec4 with values in range [0, 1]
    """

def packSnorm4x8(v):
    """
    Pack four signed normalized floats [-1,1] into 8-bit signed integers.

    Args:
        v: vec4 with values in range [-1, 1]

    Returns:
        32-bit unsigned integer containing packed values
    """

def unpackSnorm4x8(p):
    """
    Unpack 32-bit unsigned integer into four signed normalized floats.

    Args:
        p: 32-bit unsigned integer containing packed values

    Returns:
        vec4 with values in range [-1, 1]
    """

Double-Precision Packing

Functions for packing and unpacking double-precision floating-point values.

def packDouble2x32(v):
    """
    Pack a double-precision float into two 32-bit unsigned integers.

    Args:
        v: Double-precision float value

    Returns:
        uvec2 containing the packed double

    Example:
        packed_double = glm.packDouble2x32(3.141592653589793)
    """

def unpackDouble2x32(p):
    """
    Unpack two 32-bit unsigned integers into a double-precision float.

    Args:
        p: uvec2 containing packed double-precision value

    Returns:
        Double-precision float value
    """

Bit Manipulation Functions

Advanced bit manipulation functions for integer operations.

def bitfieldExtract(value, offset, bits):
    """
    Extract a range of bits from an integer.

    Args:
        value: Integer value (scalar or vector)
        offset: Bit offset (least significant bit = 0)
        bits: Number of bits to extract

    Returns:
        Extracted bits as integer
    """

def bitfieldInsert(base, insert, offset, bits):
    """
    Insert bits into a range of an integer.

    Args:
        base: Base integer value
        insert: Integer containing bits to insert
        offset: Bit offset for insertion
        bits: Number of bits to insert

    Returns:
        Integer with bits inserted
    """

def bitfieldReverse(value):
    """
    Reverse the bits in an integer.

    Args:
        value: Integer value (scalar or vector)

    Returns:
        Integer with bits reversed
    """

def bitCount(value):
    """
    Count the number of 1 bits in an integer.

    Args:
        value: Integer value (scalar or vector)

    Returns:
        Number of set bits
    """

def findLSB(value):
    """
    Find the index of the least significant set bit.

    Args:
        value: Integer value (scalar or vector)

    Returns:
        Index of LSB, or -1 if no bits set
    """

def findMSB(value):
    """
    Find the index of the most significant set bit.

    Args:
        value: Integer value (scalar or vector)

    Returns:
        Index of MSB, or -1 if no bits set
    """

Extended Integer Functions

Functions for extended precision integer arithmetic.

def uaddCarry(x, y, carry):
    """
    Add two unsigned integers with carry detection.

    Args:
        x: First unsigned integer
        y: Second unsigned integer  
        carry: Output parameter for carry bit

    Returns:
        Sum of x and y
    """

def usubBorrow(x, y, borrow):
    """
    Subtract two unsigned integers with borrow detection.

    Args:
        x: First unsigned integer (minuend)
        y: Second unsigned integer (subtrahend)
        borrow: Output parameter for borrow bit

    Returns:
        Difference of x and y
    """

def umulExtended(x, y, msb, lsb):
    """
    Multiply two unsigned integers producing extended precision result.

    Args:
        x: First unsigned integer
        y: Second unsigned integer
        msb: Output parameter for most significant bits
        lsb: Output parameter for least significant bits

    Returns:
        Extended precision product
    """

def imulExtended(x, y, msb, lsb):
    """
    Multiply two signed integers producing extended precision result.

    Args:
        x: First signed integer
        y: Second signed integer
        msb: Output parameter for most significant bits
        lsb: Output parameter for least significant bits

    Returns:
        Extended precision product
    """

def iround(x):
    """
    Round float to nearest integer (signed).

    Args:
        x: Float value (scalar or vector)

    Returns:
        Rounded integer value
    """

def uround(x):
    """
    Round float to nearest unsigned integer.

    Args:
        x: Float value (scalar or vector)

    Returns:
        Rounded unsigned integer value
    """

Control Functions

Functions for controlling PyGLM behavior and warnings.

def silence(id):
    """
    Silence specific PyGLM warnings.

    Args:
        id: Warning ID to silence (0 = silence all warnings)

    Example:
        glm.silence(1)  # Silence warning ID 1
        glm.silence(0)  # Silence all warnings
    """

Usage Examples

from pyglm import glm
import ctypes

# === Memory Access for Graphics APIs ===

# Pass matrix to OpenGL uniform
model_matrix = glm.mat4()
model_matrix = glm.rotate(model_matrix, glm.radians(45), glm.vec3(0, 1, 0))

# Get pointer for OpenGL
matrix_ptr = glm.value_ptr(model_matrix)
# In OpenGL: glUniformMatrix4fv(location, 1, GL_FALSE, matrix_ptr)

# Get size information
matrix_size = glm.sizeof(glm.mat4)  # 64 bytes
vector_size = glm.sizeof(glm.vec3)  # 12 bytes

# === Creating Types from External Data ===

# Create PyGLM vector from C array
float_array = (ctypes.c_float * 3)(1.0, 2.0, 3.0)
vec3_from_array = glm.make_vec3(float_array)

# Create matrix from external data
matrix_data = (ctypes.c_float * 16)(*[1 if i == j else 0 for i in range(4) for j in range(4)])
identity_matrix = glm.make_mat4(matrix_data)

# === Numerical Testing ===

# Test for special values
values = glm.vec4(1.0, float('inf'), float('nan'), -5.0)
inf_test = glm.isinf(values)  # bvec4(False, True, False, False)
nan_test = glm.isnan(values)  # bvec4(False, False, True, False)

# Epsilon comparisons for floating-point equality
a = glm.vec3(1.0, 2.0, 3.0)
b = glm.vec3(1.0001, 1.9999, 3.0001)
epsilon = 0.001

equal_test = glm.epsilonEqual(a, b, epsilon)  # bvec3(True, True, True)
not_equal_test = glm.epsilonNotEqual(a, b, epsilon)  # bvec3(False, False, False)

# === Bit Manipulation ===

# Convert between float and integer bit representations
float_val = 1.0
int_bits = glm.floatBitsToInt(float_val)  # 1065353216
back_to_float = glm.intBitsToFloat(int_bits)  # 1.0

# Useful for bit manipulation or hashing
def simple_hash(vec):
    # Convert vector components to integer bits for hashing
    x_bits = glm.floatBitsToInt(vec.x)
    y_bits = glm.floatBitsToInt(vec.y)
    z_bits = glm.floatBitsToInt(vec.z)
    return hash((x_bits, y_bits, z_bits))

position = glm.vec3(1.5, 2.7, 3.1)
position_hash = simple_hash(position)

# === Half-Precision Packing for Memory Efficiency ===

# Pack two floats into half-precision for storage
original_vec2 = glm.vec2(1.5, -2.25)
packed_half = glm.packHalf2x16(original_vec2)  # Stores in 32 bits instead of 64
unpacked_vec2 = glm.unpackHalf2x16(packed_half)  # Slight precision loss

# Pack four floats into half-precision
original_vec4 = glm.vec4(1.0, 2.0, 3.0, 4.0)
packed_half4 = glm.packHalf4x16(original_vec4)  # Returns uvec2
unpacked_vec4 = glm.unpackHalf4x16(packed_half4)

# === Normalized Value Packing ===

# Pack RGBA color values efficiently
color = glm.vec4(1.0, 0.5, 0.25, 1.0)  # Red, half green, quarter blue, full alpha
packed_color = glm.packUnorm4x8(color)  # Pack into 32-bit integer
unpacked_color = glm.unpackUnorm4x8(packed_color)  # Unpack back to vec4

# Pack normal vectors (commonly in range [-1, 1])
normal = glm.vec3(-0.5, 0.707, 0.5)  # Normalized normal vector
# Extend to vec4 for packing
normal_vec4 = glm.vec4(normal.x, normal.y, normal.z, 0.0)
packed_normal = glm.packSnorm4x8(normal_vec4)
unpacked_normal = glm.unpackSnorm4x8(packed_normal)

# === Double-Precision Packing ===

# Pack double-precision value for storage or transmission
double_val = 3.141592653589793
packed_double = glm.packDouble2x32(double_val)  # Returns uvec2
unpacked_double = glm.unpackDouble2x32(packed_double)

# === Practical Example: Vertex Data Compression ===

class CompressedVertex:
    def __init__(self, position, normal, uv, color):
        # Store position as-is (high precision needed)
        self.position = position
        
        # Pack normal into 32-bit integer (normals are in [-1,1] range)
        normal_vec4 = glm.vec4(normal.x, normal.y, normal.z, 0.0)
        self.packed_normal = glm.packSnorm4x8(normal_vec4)
        
        # Pack UV coordinates into 32-bit integer (UVs are in [0,1] range)
        uv_vec4 = glm.vec4(uv.x, uv.y, 0.0, 0.0)  # Pad to vec4
        self.packed_uv = glm.packUnorm4x8(uv_vec4)
        
        # Pack color into 32-bit integer
        self.packed_color = glm.packUnorm4x8(color)
    
    def get_normal(self):
        unpacked = glm.unpackSnorm4x8(self.packed_normal)
        return glm.vec3(unpacked.x, unpacked.y, unpacked.z)
    
    def get_uv(self):
        unpacked = glm.unpackUnorm4x8(self.packed_uv)
        return glm.vec2(unpacked.x, unpacked.y)
    
    def get_color(self):
        return glm.unpackUnorm4x8(self.packed_color)

# Create compressed vertex
original_position = glm.vec3(1.0, 2.0, 3.0)
original_normal = glm.vec3(0.0, 1.0, 0.0)
original_uv = glm.vec2(0.5, 0.75)
original_color = glm.vec4(1.0, 0.5, 0.25, 1.0)

compressed = CompressedVertex(original_position, original_normal, original_uv, original_color)

# Retrieve data (with some precision loss from compression)
retrieved_normal = compressed.get_normal()
retrieved_uv = compressed.get_uv()
retrieved_color = compressed.get_color()

# === OpenGL Integration Example ===

def upload_matrix_to_gpu(matrix, uniform_location):
    """Upload matrix to OpenGL uniform."""
    # Get pointer to matrix data
    matrix_ptr = glm.value_ptr(matrix)
    matrix_size = glm.sizeof(type(matrix))
    
    # Determine matrix dimensions and upload
    if matrix_size == 64:  # mat4
        # glUniformMatrix4fv(uniform_location, 1, GL_FALSE, matrix_ptr)
        pass
    elif matrix_size == 36:  # mat3
        # glUniformMatrix3fv(uniform_location, 1, GL_FALSE, matrix_ptr)
        pass
    # etc.

# === Performance Monitoring ===

def compare_storage_sizes():
    """Compare storage sizes of different representations."""
    
    # Original vec4
    original = glm.vec4(1.0, 0.5, 0.25, 1.0)
    original_size = glm.sizeof(glm.vec4)  # 16 bytes
    
    # Half-precision packed
    packed_half = glm.packHalf4x16(original)
    packed_half_size = glm.sizeof(glm.uvec2)  # 8 bytes
    
    # Normalized packed
    packed_norm = glm.packUnorm4x8(original)
    packed_norm_size = 4  # 4 bytes (single uint32)
    
    print(f"Original: {original_size} bytes")
    print(f"Half-precision: {packed_half_size} bytes ({packed_half_size/original_size:.1%} of original)")
    print(f"Normalized: {packed_norm_size} bytes ({packed_norm_size/original_size:.1%} of original)")

compare_storage_sizes()

# === Error Handling and Warnings ===

# Silence specific warnings
glm.silence(1)  # Silence warning with ID 1

# Silence all warnings
glm.silence(0)

# Example function that might generate warnings
def risky_operation():
    # Some operation that might trigger frexp warning
    result = glm.frexp(glm.vec3(1.5, 2.0, 3.0))
    return result

# Call without warnings (if silenced)
result = risky_operation()

Integration Best Practices

  1. Memory Management: Always ensure that ctypes arrays remain in scope when using make_* functions
  2. Precision Trade-offs: Understand precision loss when using packed formats - test with your specific data ranges
  3. Performance: Use packed formats for large datasets (vertex buffers, texture data) but full precision for calculations
  4. Graphics API Integration: Use value_ptr() to pass data directly to OpenGL, Vulkan, and other graphics APIs
  5. Error Handling: Use numerical testing functions (isinf, isnan) when working with computed values
  6. Bit Manipulation: Be careful with bit manipulation functions - they're primarily for specialized use cases

Install with Tessl CLI

npx tessl i tessl/pypi-pyglm

docs

extensions.md

index.md

math-functions.md

matrices.md

quaternions.md

random-noise.md

transformations.md

utilities.md

vectors.md

tile.json