Skip to content

Basic Mesh#

Interactive mesh example demonstrating ColorModel subclasses.

This example shows: - Per-vertex coloring with VertexColors (gradient based on position) - Uniform coloring with UniformColor (solid purple) - Interactive face removal on mouse hover - Cycling between color models with right-click - Mesh reset on left-click

The mesh starts with per-vertex colors creating a colorful gradient. Right-clicking toggles between per-vertex and uniform coloring modes.

Screenshot of Basic Mesh

import cmap
import numpy as np

import scenex as snx
from scenex.app.events import (
    Event,
    MouseButton,
    MouseMoveEvent,
    MousePressEvent,
)


# Create a more complex mesh - a grid of vertices
def create_grid_mesh(
    size: int = 10, spacing: float = 0.2
) -> tuple[np.ndarray, np.ndarray]:
    """Create a grid mesh with given size and spacing."""
    vertices = []
    faces = []

    # Create vertices in a grid
    for i in range(size):
        for j in range(size):
            x = i * spacing
            y = j * spacing
            z = 0.0
            vertices.append([x, y, z])

    # Create triangular faces
    for i in range(size - 1):
        for j in range(size - 1):
            # Current vertex indices
            v0 = i * size + j
            v1 = i * size + (j + 1)
            v2 = (i + 1) * size + j
            v3 = (i + 1) * size + (j + 1)

            # Two triangles per grid square
            faces.append([v0, v1, v2])
            faces.append([v1, v3, v2])

    return np.array(vertices), np.array(faces)


# Create the mesh
original_vertices, original_faces = create_grid_mesh(size=15, spacing=0.15)

# Create per-vertex colors (gradient based on position)
per_vertex_model = snx.VertexColors(
    color=[
        cmap.Color(f"hsl({int(v[0] * 50 + v[1] * 50) % 360}, 100%, 50%)")
        for v in original_vertices
    ],
)

# Create uniform colors
uniform_model = snx.UniformColor(color=cmap.Color("purple"))

mesh = snx.Mesh(
    vertices=original_vertices,
    faces=original_faces,
    color=per_vertex_model,
)

view = snx.View(
    scene=snx.Scene(children=[mesh]),
    camera=snx.Camera(controller=snx.PanZoom(), interactive=True),
)


def event_filter(event: Event) -> bool:
    """Interactive mesh manipulation based on mouse events."""
    if isinstance(event, MouseMoveEvent):
        if not (ray := view.to_ray(event.pos)):
            return False
        if ray.intersections(mesh):
            # Remove the intersected face
            indices = [i for i, _d in mesh.intersecting_faces(ray)]
            mesh.faces = np.delete(mesh.faces, indices, axis=0)
            return True
    elif isinstance(event, MousePressEvent):
        if event.buttons & MouseButton.LEFT:
            # Reset the mesh on click
            mesh.vertices = original_vertices.copy()
            mesh.faces = original_faces.copy()
            return True
        elif event.buttons & MouseButton.RIGHT:
            # Right click cycles the colormodel
            if mesh.color == uniform_model:
                mesh.color = per_vertex_model
            else:
                mesh.color = uniform_model

    return False


# Set up the event filter
view.set_event_filter(event_filter)

# Show and position camera
snx.show(view)

print("Interactive Mesh Demo:")
print("- Move mouse over mesh to delete intersected faces")
print("- Left click to reset all faces")
print("- Right click to cycle color models")
print("- Use mouse to pan/zoom the camera")

snx.run()