Skip to content

VectorField

FluxRender.entities.VectorField

VectorField(vec_function, color_mapper: ColorMapper = ColorMapper(), color_property: Property = Property.VELOCITY, color_clipping_percentiles: Sequence[float] = (5.0, 95.0), spacing_x=None, spacing_y=None, normalized: bool = True, length=None, thickness=2, mode: FieldMode = FieldMode.ZOOM_DENSITY_ADAPTIVE, arrow_size=15, arrow_style=ArrowStyle.HARPOON, draw_arrows: bool = True, antyaliasing: bool = True, max_vectors: int = 10000, details_factor: int = 6, chaos_sensitivity: float = 3.0, max_length_reduction: float = 0.6, base_angle_vector=None, custom_color_function=None)

Bases: VectorEntity

A visual entity that renders a mathematical vector field using directional arrows.

The VectorField acts as the static, geometric counterpart to the dynamic ParticleSystem. It queries the underlying mathematical engine across a spatial grid and visualizes the results as a structured collection of arrows, where each arrow represents the local magnitude and direction of the field at a specific point in space.

This class excels at topological analysis. It features highly advanced rendering modes (such as FieldMode.ZOOM_DENSITY_ADAPTIVE) that dynamically recalculate grid spacing based on the camera's zoom level and local mathematical chaos. By coupling it with a ColorMapper, complex physical properties (like divergence, curl, or custom tensor evaluations) can be instantly translated into intuitive, color-coded visual maps.

Parameters:

Name Type Description Default
vec_function Callable / VectorMathEngine

The math driving the flow. A function taking (x, y) and optionally (t) returning vector components (dx, dy), or a pre-built VectorMathEngine.

IMPORTANT REFERENCE NOTE: If you pass an existing VectorMathEngine instance AND simultaneously provide overriding parameters (such as base_angle_vector or custom_color_function), this entity will silently create an independent clone of the engine under the hood. Consequently, any subsequent modifications made to your original engine instance will NOT be reflected in this visual entity.

required
color_mapper ColorMapper

An instance of a ColorMapper that maps vector properties to colors. If min_value and max_value are not provided, they will be automatically calculated. (Default: ColorMapper())

ColorMapper()
color_property Property

The vector property used for coloring (e.g., velocity, divergence, curl, angle, or a custom function). (Default: Property.VELOCITY)

VELOCITY
color_clipping_percentiles tuple

The percentiles used to clip the color values. Defaults to (5.0, 95.0).

(5.0, 95.0)
spacing_x float

Horizontal spacing between vectors. Units depend on the mode:

  • SCREEN_FIXED: spacing is in screen pixels (e.g., 40). Default is 40.
  • WORLD_FIXED: spacing is in mathematical world units (e.g., 0.5). Default is 0.4.
  • ZOOM_ADAPTIVE: spacing is in screen pixels but scales with zooming. Default is 18.
  • WORLD_DENSITY_ADAPTIVE: spacing is the base spacing in world units. Acts as maximum spacing. Default is 0.4.
  • ZOOM_DENSITY_ADAPTIVE: base spacing in world units that scales with zooming. Default is 0.4.
None
spacing_y float

The vertical spacing between vectors. Same units and defaults as spacing_x.

None
normalized bool

Whether to normalize vectors to unit length for visualization purposes. (Default: True)

True
length float

The base length of the vectors. Units depend on the mode:

  • SCREEN_FIXED: length is in screen pixels. Default is 30.
  • WORLD_FIXED: length is in world units. Default is 0.3.
  • ZOOM_ADAPTIVE: length is in screen pixels. Default is 30.
  • WORLD_DENSITY_ADAPTIVE: length is in screen pixels but scales with local density based on max_length_reduction. Default is 30.
  • ZOOM_DENSITY_ADAPTIVE: length is in screen pixels but scales with local density. Default is 30.
None
thickness float

The thickness of the drawn vectors in pixels. (Default: 2)

2
mode FieldMode

The mode of vector field rendering determining spacing and scaling (default: FieldMode.ZOOM_DENSITY_ADAPTIVE):

  • SCREEN_FIXED: Attached to the screen, length/position independent of view.
  • WORLD_FIXED: Attached to the math world, length/position depend on view.
  • ZOOM_ADAPTIVE: Grid spacing dynamically adjusts based on zoom level.
  • WORLD_DENSITY_ADAPTIVE: Placement is selectively filtered based on local chaos.
  • ZOOM_DENSITY_ADAPTIVE: Hybrid approach (zoom scaling + topological filtering) - default.
ZOOM_DENSITY_ADAPTIVE
arrow_size float

The size of the arrowheads in pixels. (Default: 15)

15
arrow_style ArrowStyle

The visual style of the arrowheads (e.g., OPEN, TRIANGLE_TIGHT, HARPOON). (Default: HARPOON)

HARPOON
draw_arrows bool

Whether to draw arrowheads at the end of vectors.

True
antyaliasing bool

Whether to apply anti-aliasing for smoother visuals. (Default: True)

True
max_vectors int

Maximum number of vectors to render. (Default: 10000)

10000
details_factor int

[Density Adaptive] How many times denser is the arrow spacing compared to the standard spacing at points with the greatest chaos. (Default: 6)

6
chaos_sensitivity float

[Density Adaptive] How fast the density increases with chaos (directional derivatives magnitude). (Default: 3.0)

3.0
max_length_reduction float

[Density Adaptive] The maximum length reduction for arrows in the most chaotic areas. (Default: 0.6)

0.6
base_angle_vector tuple, list, or Callable

[Property.ANGLE] The reference vector used to calculate angles. Defined as:

  • Static 2D vector: tuple or list (e.g., [1.0, 0.0]). (Default: [1.0, 0.0])
  • Vector function: Callable taking (x, y) and optionally (t), returning components (comp_x, comp_y).
None
custom_color_function Callable

[Property.CUSTOM] A user-defined function for calculating colors.

To maximize performance, the engine strictly avoids redundant math. Since the VectorField or ParticleSystem already evaluates the primary vector function to render arrows or move particles, it passes these exact, pre-calculated results directly into your color function.

The function must accept 4 or 5 parameters:

  • vec_dx (float/ndarray): The evaluated X vector difference (the pre-calculated result at the anchor point).
  • vec_dy (float/ndarray): The evaluated Y vector difference.
  • world_x (float/ndarray): The X anchor point (sampling coordinate) in world space, determined by the entity's internal grid mode or particle position.
  • world_y (float/ndarray): The Y anchor point in world space.
  • t (float, optional): The current simulation time (if the engine is time-dependent).

Should return a scalar or a NumPy array. Use vectorized NumPy operations for maximum efficiency.

None
Example

Basic setup for a simple vector field defined by a mathematical function, with default coloring based on velocity magnitude and directly defined VectorMathEngine:

import FluxRender as fr
import numpy as np

# [Initializing the scene and coordinate system]

def my_field(x, y):
    return np.sin(x), np.cos(y)

engine = fr.VectorMathEngine(primary_vector_function = my_field)
field = fr.VectorField(
    vec_function = engine,
    mode = fr.FieldMode.SCREEN_FIXED,
)

scene.add(field)

The same, but animated vector field and without directly defined VectorMathEngine (the engine will be created internally):

import FluxRender as fr
import numpy as np

# [Initializing the scene and coordinate system]

def my_field(x, y, t):
    return np.sin(x + t), np.cos(y + t)

field = fr.VectorField(
    vec_function = my_field,
    mode = fr.FieldMode.SCREEN_FIXED,
)

scene.add(field)

Source code in FluxRender/entities.py
def __init__(
            # Universal parameters
            self, vec_function,
            color_mapper: ColorMapper = ColorMapper(),
            color_property: Property = Property.VELOCITY,
            color_clipping_percentiles: Sequence[float] = (5.0, 95.0),
            spacing_x=None, spacing_y=None,
            normalized: bool=True, length=None, thickness=2,
            mode: FieldMode = FieldMode.ZOOM_DENSITY_ADAPTIVE,
            arrow_size=15, arrow_style=ArrowStyle.HARPOON, draw_arrows: bool = True,
            antyaliasing: bool = True,
            max_vectors: int = 10000,

            # Parameters specific to density adaptive modes (WORLD_DENSITY_ADAPTIVE and ZOOM_DENSITY_ADAPTIVE)
            details_factor: int = 6,
            chaos_sensitivity: float = 3.0,
            max_length_reduction: float = 0.6,

            # Parameters specific to Property.ANGLE color_property
            base_angle_vector = None,

            # Parameters specific to custom color function mode (when color_property is Property.CUSTOM_FUNCTION)
            custom_color_function = None
            ):
    """
    Parameters:
        vec_function (Callable/VectorMathEngine): The math driving the flow. A function taking (x, y)
            and optionally (t) returning vector components (dx, dy), or a pre-built VectorMathEngine.

            **IMPORTANT REFERENCE NOTE**: If you pass an existing VectorMathEngine instance
            AND simultaneously provide overriding parameters (such as `base_angle_vector`
            or `custom_color_function`), this entity will silently create an independent
            **clone** of the engine under the hood. Consequently, any subsequent modifications
            made to your original engine instance will NOT be reflected in this visual entity.
        color_mapper (ColorMapper): An instance of a ColorMapper that maps vector properties to colors. If min_value and max_value are not provided, they will be automatically calculated. (Default: ColorMapper())
        color_property (Property): The vector property used for coloring (e.g., velocity, divergence, curl, angle, or a custom function). (Default: Property.VELOCITY)
        color_clipping_percentiles (tuple): The percentiles used to clip the color values. Defaults to (5.0, 95.0).
        spacing_x (float): Horizontal spacing between vectors. Units depend on the mode:

            * **SCREEN_FIXED**: spacing is in screen pixels (e.g., 40). Default is 40.
            * **WORLD_FIXED**: spacing is in mathematical world units (e.g., 0.5). Default is 0.4.
            * **ZOOM_ADAPTIVE**: spacing is in screen pixels but scales with zooming. Default is 18.
            * **WORLD_DENSITY_ADAPTIVE**: spacing is the base spacing in world units. Acts as maximum spacing. Default is 0.4.
            * **ZOOM_DENSITY_ADAPTIVE**: base spacing in world units that scales with zooming. Default is 0.4.
        spacing_y (float): The vertical spacing between vectors. Same units and defaults as spacing_x.
        normalized (bool): Whether to normalize vectors to unit length for visualization purposes. (Default: True)
        length (float): The base length of the vectors. Units depend on the mode:

            * **SCREEN_FIXED**: length is in screen pixels. Default is 30.
            * **WORLD_FIXED**: length is in world units. Default is 0.3.
            * **ZOOM_ADAPTIVE**: length is in screen pixels. Default is 30.
            * **WORLD_DENSITY_ADAPTIVE**: length is in screen pixels but scales with local density based on max_length_reduction. Default is 30.
            * **ZOOM_DENSITY_ADAPTIVE**: length is in screen pixels but scales with local density. Default is 30.
        thickness (float): The thickness of the drawn vectors in pixels. (Default: 2)
        mode (FieldMode): The mode of vector field rendering determining spacing and scaling (default: FieldMode.ZOOM_DENSITY_ADAPTIVE):

            * **SCREEN_FIXED**: Attached to the screen, length/position independent of view.
            * **WORLD_FIXED**: Attached to the math world, length/position depend on view.
            * **ZOOM_ADAPTIVE**: Grid spacing dynamically adjusts based on zoom level.
            * **WORLD_DENSITY_ADAPTIVE**: Placement is selectively filtered based on local chaos.
            * **ZOOM_DENSITY_ADAPTIVE**: Hybrid approach (zoom scaling + topological filtering) - default.
        arrow_size (float): The size of the arrowheads in pixels. (Default: 15)
        arrow_style (ArrowStyle): The visual style of the arrowheads (e.g., OPEN, TRIANGLE_TIGHT, HARPOON). (Default: HARPOON)
        draw_arrows (bool): Whether to draw arrowheads at the end of vectors.
        antyaliasing (bool): Whether to apply anti-aliasing for smoother visuals. (Default: True)
        max_vectors (int): Maximum number of vectors to render. (Default: 10000)
        details_factor (int): **[Density Adaptive]** How many times denser is the arrow spacing compared to the standard spacing at points with the greatest chaos. (Default: 6)
        chaos_sensitivity (float): **[Density Adaptive]** How fast the density increases with chaos (directional derivatives magnitude). (Default: 3.0)
        max_length_reduction (float): **[Density Adaptive]** The maximum length reduction for arrows in the most chaotic areas. (Default: 0.6)
        base_angle_vector (tuple, list, or Callable): **[Property.ANGLE]** The reference vector used to calculate angles. Defined as:

            * **Static 2D vector**: tuple or list (e.g., [1.0, 0.0]). (Default: [1.0, 0.0])
            * **Vector function**: Callable taking (x, y) and optionally (t), returning components (comp_x, comp_y).
        custom_color_function (Callable, optional): **[Property.CUSTOM]** A user-defined function for calculating colors.

            To maximize performance, the engine strictly avoids redundant math. Since the
            VectorField or ParticleSystem already evaluates the primary vector function to
            render arrows or move particles, it passes these exact, pre-calculated results
            directly into your color function.

            The function must accept 4 or 5 parameters:

            * `vec_dx` (float/ndarray): The evaluated X vector difference (the pre-calculated result at the anchor point).
            * `vec_dy` (float/ndarray): The evaluated Y vector difference.
            * `world_x` (float/ndarray): The X anchor point (sampling coordinate) in world space, determined by the entity's internal grid mode or particle position.
            * `world_y` (float/ndarray): The Y anchor point in world space.
            * `t` (float, optional): The current simulation time (if the engine is time-dependent).

            Should return a scalar or a NumPy array. Use vectorized NumPy operations for maximum efficiency.


    Example:
        Basic setup for a simple vector field defined by a mathematical function, with default
        coloring based on velocity magnitude and directly defined VectorMathEngine:

        ```python
        import FluxRender as fr
        import numpy as np

        # [Initializing the scene and coordinate system]

        def my_field(x, y):
            return np.sin(x), np.cos(y)

        engine = fr.VectorMathEngine(primary_vector_function = my_field)
        field = fr.VectorField(
            vec_function = engine,
            mode = fr.FieldMode.SCREEN_FIXED,
        )

        scene.add(field)
        ```

        The same, but animated vector field and without directly defined VectorMathEngine (the engine will be created internally):
        ```python
        import FluxRender as fr
        import numpy as np

        # [Initializing the scene and coordinate system]

        def my_field(x, y, t):
            return np.sin(x + t), np.cos(y + t)

        field = fr.VectorField(
            vec_function = my_field,
            mode = fr.FieldMode.SCREEN_FIXED,
        )

        scene.add(field)
        ```
    """

    super().__init__(vec_function, color_mapper, color_property, color_clipping_percentiles)

    # Dynamic configuration GPU
    self._thickness = thickness
    self._arrow_size = arrow_size

    # Dynamic Python configuration
    self.draw_arrows = draw_arrows
    self.normalized = normalized
    self.arrow_style = arrow_style
    self.mode = mode
    self.detail_factor = details_factor # in the case of WORLD_DENSITY_ADAPTIVE/ZOOM_DENSITY_ADAPTIVE mode, how many times denser is the arrow spacing compared to the standard spacing (spacing_x and spacing_y) at the points with the greatest chaos
    self.chaos_sensitivity = chaos_sensitivity # in the case of WORLD_DENSITY_ADAPTIVE/ZOOM_DENSITY_ADAPTIVE mode, how fast the density increases with chaos (directional derivatives magnitude)
    self.max_length_reduction = max_length_reduction # in the case of WORLD_DENSITY_ADAPTIVE/ZOOM_DENSITY_ADAPTIVE mode, the maximum length reduction for arrows in the most chaotic areas (0.0 means no length reduction, 1.0 means arrows are reduced to points)

    self.custom_color_function = custom_color_function

    self.base_angle_vector = base_angle_vector

    # region Default spacing based on mode [blue]
    if spacing_x is None:
        if self.mode == FieldMode.SCREEN_FIXED:
            self.spacing_x = 40
        elif self.mode == FieldMode.ZOOM_ADAPTIVE:
            self.spacing_x = 18
        else:
            self.spacing_x = 0.4
    else:
        self.spacing_x = spacing_x

    if spacing_y is None:
        if self.mode == FieldMode.SCREEN_FIXED:
            self.spacing_y = 40
        elif self.mode == FieldMode.ZOOM_ADAPTIVE:
            self.spacing_y = 18
        else:
            self.spacing_y = 0.4
    else:
        self.spacing_y = spacing_y

    if length is None:
        if self.mode == FieldMode.SCREEN_FIXED or self.mode == FieldMode.ZOOM_ADAPTIVE or self.mode == FieldMode.WORLD_DENSITY_ADAPTIVE or self.mode == FieldMode.ZOOM_DENSITY_ADAPTIVE:
            self.length = 30.0
        else:
            self.length = 0.3
    else:
        self.length = length
    # endregion

    # Static configuration
    self.antyaliasing = antyaliasing
    self.max_vectors = max_vectors

    self.coords = None
    self.scene = None
    self.math_engine = None
    self._last_cam_state = None
    self.num_vectors = 0

    self._vec_positions = ti.Vector.field(4, dtype=float, shape=max_vectors) # x_start, y_start, x_end, y_end
    self._vec_colors = ti.Vector.field(4, dtype=float, shape=max_vectors) # RGBA

    self._vec_positions_np = np.zeros((self.max_vectors, 4), dtype=np.float32)
    self._vec_colors_np = np.zeros((self.max_vectors, 4), dtype=np.float32)

    self.arrow_atlas = ArrowAtlas(style=arrow_style)

    self.settings = VectorFieldConfig.field(shape=())

    self._is_initialized = True
    self._update_settings()

change_mode

change_mode(new_mode) -> None

Changes the rendering mode of a vector field and updates the related parameters accordingly, using default values.

Parameters:

Name Type Description Default
new_mode FieldMode

The new rendering mode to switch to.

required
Example
import FluxRender as fr

# [Initializing the scene and coordinate system and vector field]

# a function called when you click a button that changes the vector field mode
def on_click():
    my_vector_field.change_mode(fr.FieldMode.WORLD_FIXED)
Source code in FluxRender/entities.py
def change_mode(self, new_mode) -> None:
    """
    Changes the rendering mode of a vector field and updates the related parameters accordingly, using default values.

    Args:
        new_mode (FieldMode): The new rendering mode to switch to.

    Example:
        ```python
        import FluxRender as fr

        # [Initializing the scene and coordinate system and vector field]

        # a function called when you click a button that changes the vector field mode
        def on_click():
            my_vector_field.change_mode(fr.FieldMode.WORLD_FIXED)
        ```
    """
    self.mode = new_mode
    self._last_cam_state = (0, 0, 0, 0, 0, 0)

    if new_mode == FieldMode.SCREEN_FIXED:
        spacing = 40
        self.length = 30
    elif new_mode == FieldMode.ZOOM_ADAPTIVE:
        spacing = 18
        self.length = 30
    elif new_mode == FieldMode.WORLD_DENSITY_ADAPTIVE or new_mode == FieldMode.ZOOM_DENSITY_ADAPTIVE:
        spacing = 0.4
        self.length = 30
    else:
        # WORLD_FIXED
        spacing = 0.4
        self.length = 0.3

    self.spacing_x = spacing
    self.spacing_y = spacing

evaluate_angle_vector

evaluate_angle_vector(x: float, y: float) -> tuple

Evaluates the base reference angle vector at the specified spatial coordinates.

This method determines whether the reference angle vector is a static coordinate pair or a dynamically evaluated mathematical function. If it is a callable function, it safely executes it, automatically injecting the current time if the function signature requires it.

Parameters:

Name Type Description Default
x float or ndarray

The x-coordinate(s) in the mathematical world space.

required
y float or ndarray

The y-coordinate(s) in the mathematical world space.

required

Returns:

Name Type Description
tuple tuple

A tuple (component_x, component_y) representing the evaluated reference vector components.

Notes
  • Broadcasting: This method fully supports NumPy broadcasting. You can pass single float values for pinpoint evaluation, or large multidimensional arrays (like those generated by numpy.meshgrid) to evaluate the entire mathematical space simultaneously.
Example

Evaluating a dynamically rotating reference angle at the origin:

import FluxRender as fr
import numpy as np

# [Initializing the scene and coordinate system]

def rotating_reference(x, y, t):
    direction_x = np.cos(t)
    direction_y = np.sin(t)
    return direction_x, direction_y

field = fr.VectorField(
    vec_function = lambda x, y: (y, -x),
    color_property = fr.Property.ANGLE,
    base_angle_vector = rotating_reference
)
scene.add(field)

# The engine automatically handles the underlying time injection
angle_vector_x, angle_vector_y = field.evaluate_angle_vector(0.0, 0.0)

print(f"Reference angle vector at the origin: ({angle_vector_x}, {angle_vector_y})")

Source code in FluxRender/entities.py
def evaluate_angle_vector(self, x: float, y: float) -> tuple:
    """Evaluates the base reference angle vector at the specified spatial coordinates.

    This method determines whether the reference angle vector is a static
    coordinate pair or a dynamically evaluated mathematical function. If it is
    a callable function, it safely executes it, automatically injecting the
    current time if the function signature requires it.

    Args:
        x (float or numpy.ndarray): The x-coordinate(s) in the mathematical world space.
        y (float or numpy.ndarray): The y-coordinate(s) in the mathematical world space.

    Returns:
        tuple: A tuple (component_x, component_y) representing the evaluated reference vector components.

    Notes:
        * **Broadcasting:** This method fully supports NumPy broadcasting. You can pass single
          float values for pinpoint evaluation, or large multidimensional arrays (like those
          generated by `numpy.meshgrid`) to evaluate the entire mathematical space simultaneously.

    Example:
        Evaluating a dynamically rotating reference angle at the origin:
        ```python
        import FluxRender as fr
        import numpy as np

        # [Initializing the scene and coordinate system]

        def rotating_reference(x, y, t):
            direction_x = np.cos(t)
            direction_y = np.sin(t)
            return direction_x, direction_y

        field = fr.VectorField(
            vec_function = lambda x, y: (y, -x),
            color_property = fr.Property.ANGLE,
            base_angle_vector = rotating_reference
        )
        scene.add(field)

        # The engine automatically handles the underlying time injection
        angle_vector_x, angle_vector_y = field.evaluate_angle_vector(0.0, 0.0)

        print(f"Reference angle vector at the origin: ({angle_vector_x}, {angle_vector_y})")
        ```
    """

    return self.math_engine.evaluate_angle_vector(x, y)

evaluate_scalar_function

evaluate_scalar_function(user_defined_function, *spatial_arguments) -> np.ndarray

Evaluates a user-provided mathematical scalar function, automatically handling time injection and vectorization.

This method serves as a robust adapter for custom user logic that maps spatial coordinates (and potentially vector components) to a single scalar value. It analyzes the signature of the provided function to dynamically inject the simulation time if required. It attempts to execute the function using native NumPy vectorization for maximum performance, automatically falling back to numpy.vectorize if strictly scalar Python operations are detected.

Parameters:

Name Type Description Default
user_defined_function Callable

The custom scalar function or lambda to evaluate.

required
*spatial_arguments

The base spatial and/or vector arrays to pass into the function (e.g., evaluated_vector_x, evaluated_vector_y, world_x, world_y).

()

Returns:

Type Description
ndarray

numpy.ndarray: A single NumPy array containing the computed scalar values, properly sanitized and ready for rendering or further mathematical processing.

Notes
  • Time Injection: If user_defined_function accepts exactly one parameter more than the number of provided *spatial_arguments, the current scene time is automatically injected during execution.
  • Vectorization Fallback: You do not need to write strictly vectorized NumPy code. Regular Python scalar operations will be caught and vectorized automatically, though writing native NumPy code is highly recommended for optimal rendering performance.
  • Scalar Output: Unlike its vector counterpart, this method strictly expects the user's function to return a single value (or a single array) per spatial coordinate, not a tuple.
Example

Calculating and printing a custom physical metric (like kinetic energy) at specific points, while the field continues to render its default colors visually:

import FluxRender as fr
import numpy as np

# [Initializing the scene and coordinate system]

field = fr.VectorField(
    vec_function = lambda x, y: (y, -x),
)
scene.add(field)

# Custom function that internally queries the field for vector data and calculates a scalar property (e.g., kinetic energy = 0.5 * (vx^2 + vy^2))
def calculate_kinetic_energy(x, y):
    vector_dx, vector_dy = field.evaluate_vector_field(x, y)
    kinetic_energy = 0.5 * (vector_dx**2 + vector_dy**2)
    return kinetic_energy

# Define the exact spatial points we want to analyze
target_coordinates_x = np.array([0.0, 1.0, 2.0])
target_coordinates_y = np.array([0.0, 1.0, 2.0])

# Evaluate the custom metric across all points simultaneously
energy_results = field.evaluate_scalar_function(
    calculate_kinetic_energy,
    target_coordinates_x,
    target_coordinates_y
)

print(f"Kinetic energy at points (0,0), (1,1) and (2,2): {energy_results}")

Source code in FluxRender/entities.py
def evaluate_scalar_function(self, user_defined_function, *spatial_arguments) -> np.ndarray:
    """Evaluates a user-provided mathematical scalar function, automatically handling time injection and vectorization.

    This method serves as a robust adapter for custom user logic that maps spatial coordinates (and potentially
    vector components) to a single scalar value. It analyzes the signature of the provided function to dynamically
    inject the simulation time if required. It attempts to execute the function using native NumPy vectorization
    for maximum performance, automatically falling back to `numpy.vectorize` if strictly scalar Python operations
    are detected.

    Args:
        user_defined_function (Callable): The custom scalar function or lambda to evaluate.
        *spatial_arguments: The base spatial and/or vector arrays to pass into the function
            (e.g., evaluated_vector_x, evaluated_vector_y, world_x, world_y).

    Returns:
        numpy.ndarray: A single NumPy array containing the computed scalar values, properly
            sanitized and ready for rendering or further mathematical processing.

    Notes:
        * **Time Injection:** If `user_defined_function` accepts exactly one parameter more than the
          number of provided `*spatial_arguments`, the current scene time is automatically injected
          during execution.
        * **Vectorization Fallback:** You do not need to write strictly vectorized NumPy code. Regular
          Python scalar operations will be caught and vectorized automatically, though writing native
          NumPy code is highly recommended for optimal rendering performance.
        * **Scalar Output:** Unlike its vector counterpart, this method strictly expects the user's
          function to return a single value (or a single array) per spatial coordinate, not a tuple.

    Example:
        Calculating and printing a custom physical metric (like kinetic energy) at specific points,
        while the field continues to render its default colors visually:
        ```python
        import FluxRender as fr
        import numpy as np

        # [Initializing the scene and coordinate system]

        field = fr.VectorField(
            vec_function = lambda x, y: (y, -x),
        )
        scene.add(field)

        # Custom function that internally queries the field for vector data and calculates a scalar property (e.g., kinetic energy = 0.5 * (vx^2 + vy^2))
        def calculate_kinetic_energy(x, y):
            vector_dx, vector_dy = field.evaluate_vector_field(x, y)
            kinetic_energy = 0.5 * (vector_dx**2 + vector_dy**2)
            return kinetic_energy

        # Define the exact spatial points we want to analyze
        target_coordinates_x = np.array([0.0, 1.0, 2.0])
        target_coordinates_y = np.array([0.0, 1.0, 2.0])

        # Evaluate the custom metric across all points simultaneously
        energy_results = field.evaluate_scalar_function(
            calculate_kinetic_energy,
            target_coordinates_x,
            target_coordinates_y
        )

        print(f"Kinetic energy at points (0,0), (1,1) and (2,2): {energy_results}")
        ```
    """

    return self.math_engine._safe_evaluate_scalar_function(user_defined_function, *spatial_arguments)

evaluate_vector_field

evaluate_vector_field(x: float, y: float) -> tuple

Evaluates the primary vector field function at the specified spatial coordinates.

This method acts as a safe execution wrapper for the user-defined vector function. It delegates the execution to the internal evaluation handler, which manages potential numpy broadcasting issues, scalar fallbacks, and automatic time-parameter injection.

Parameters:

Name Type Description Default
x float or ndarray

The x-coordinate(s) in the mathematical world space.

required
y float or ndarray

The y-coordinate(s) in the mathematical world space.

required

Returns:

Name Type Description
tuple tuple

A tuple (vector_x, vector_y) representing the evaluated vector field components.

Notes
  • Broadcasting: This method fully supports NumPy broadcasting. You can pass single float values for pinpoint evaluation, or large multidimensional arrays (like those generated by numpy.meshgrid) to evaluate the entire mathematical space simultaneously.
Example

Evaluating the field at a single focal point:

import FluxRender as fr

# [Initializing the scene and coordinate system]

field = fr.VectorField(
    vec_function = lambda x, y: (y, -x),
    color_property = fr.Property.VELOCITY
)
scene.add(field)

vector_component_x, vector_component_y = field.evaluate_vector_field(1.0, 0.0)

print(f"Vector field at (1.0, 0.0): ({vector_component_x}, {vector_component_y})")
# Result: (0.0, -1.0)

Evaluating the field at multiple points simultaneously using numpy arrays:

import numpy as np

# Define the exact spatial points we want to analyze
target_coordinates_x = np.array([0.0, 1.0, 2.0])
target_coordinates_y = np.array([0.0, 1.0, 2.0])

x_vectors, y_vectors = vec_field.evaluate_vector_field(
    target_coordinates_x,
    target_coordinates_y
)
print(f"Vector field at points (0,0), (1,1) and (2,2): [{x_vectors[0]}, {y_vectors[0]}] | [{x_vectors[1]}, {y_vectors[1]}] | [{x_vectors[2]}, {y_vectors[2]}]")

Source code in FluxRender/entities.py
def evaluate_vector_field(self, x: float, y: float) -> tuple:
    """Evaluates the primary vector field function at the specified spatial coordinates.

    This method acts as a safe execution wrapper for the user-defined vector
    function. It delegates the execution to the internal evaluation handler,
    which manages potential numpy broadcasting issues, scalar fallbacks, and
    automatic time-parameter injection.

    Args:
        x (float or numpy.ndarray): The x-coordinate(s) in the mathematical world space.
        y (float or numpy.ndarray): The y-coordinate(s) in the mathematical world space.

    Returns:
        tuple: A tuple (vector_x, vector_y) representing the evaluated vector field components.

    Notes:
        * **Broadcasting:** This method fully supports NumPy broadcasting. You can pass single
          float values for pinpoint evaluation, or large multidimensional arrays (like those
          generated by `numpy.meshgrid`) to evaluate the entire mathematical space simultaneously.

    Example:
        Evaluating the field at a single focal point:
        ```python
        import FluxRender as fr

        # [Initializing the scene and coordinate system]

        field = fr.VectorField(
            vec_function = lambda x, y: (y, -x),
            color_property = fr.Property.VELOCITY
        )
        scene.add(field)

        vector_component_x, vector_component_y = field.evaluate_vector_field(1.0, 0.0)

        print(f"Vector field at (1.0, 0.0): ({vector_component_x}, {vector_component_y})")
        # Result: (0.0, -1.0)
        ```

        Evaluating the field at multiple points simultaneously using numpy arrays:
        ```python
        import numpy as np

        # Define the exact spatial points we want to analyze
        target_coordinates_x = np.array([0.0, 1.0, 2.0])
        target_coordinates_y = np.array([0.0, 1.0, 2.0])

        x_vectors, y_vectors = vec_field.evaluate_vector_field(
            target_coordinates_x,
            target_coordinates_y
        )
        print(f"Vector field at points (0,0), (1,1) and (2,2): [{x_vectors[0]}, {y_vectors[0]}] | [{x_vectors[1]}, {y_vectors[1]}] | [{x_vectors[2]}, {y_vectors[2]}]")
        ```
    """

    return self.math_engine.evaluate_primary_vector_function(x, y)

evaluate_vector_function

evaluate_vector_function(user_defined_function, *spatial_arguments) -> tuple

Evaluates a user-provided mathematical function, automatically handling time injection and vectorization.

This method serves as a robust adapter for custom user logic. It analyzes the signature of the provided function to dynamically inject the simulation time if required. Furthermore, it attempts to execute the function using native NumPy vectorization for maximum performance. If the function is strictly scalar (e.g., uses standard Python math modules instead of numpy), it automatically falls back to numpy.vectorize to ensure compatibility across large coordinate grids.

Parameters:

Name Type Description Default
user_defined_function Callable

The custom function or lambda to evaluate.

required
*spatial_arguments

The base spatial arrays to pass into the function (typically world_x, world_y).

()

Returns:

Name Type Description
tuple tuple

A tuple (evaluated_vector_x, evaluated_vector_y) containing the computed field components as sanitized NumPy arrays.

Notes
  • Time Injection: If user_defined_function accepts exactly one parameter more than the number of provided *spatial_arguments (e.g., taking x, y, and t), the current scene time is automatically injected during execution.
  • Vectorization Fallback: You do not need to write strictly vectorized NumPy code. Regular Python scalar operations will be caught and vectorized automatically, though writing native NumPy code is highly recommended for optimal rendering performance.

Examples:

Evaluating a custom mathematical perturbation with automatic time injection:

import FluxRender as fr
import numpy as np

# [Initializing the scene and coordinate system]

field = fr.VectorField(
    vec_function = lambda x, y: (y, -x),
)
scene.add(field)

# Notice the third parameter 't'. The engine detects this and injects it.
def custom_wind_perturbation(x, y, t):
    perturbation_x = np.sin(x + t)
    perturbation_y = np.cos(y + t)
    return perturbation_x, perturbation_y

spatial_coordinates_x = np.array([0.0, 1.0, 2.0])
spatial_coordinates_y = np.array([0.0, 1.0, 2.0])

result_vectors_x, result_vectors_y = field.evaluate_vector_function(
    custom_wind_perturbation,
    spatial_coordinates_x,
    spatial_coordinates_y
)

Source code in FluxRender/entities.py
def evaluate_vector_function(self, user_defined_function, *spatial_arguments) -> tuple:
    """Evaluates a user-provided mathematical function, automatically handling time injection and vectorization.

    This method serves as a robust adapter for custom user logic. It analyzes the signature of the
    provided function to dynamically inject the simulation time if required. Furthermore, it attempts
    to execute the function using native NumPy vectorization for maximum performance. If the function
    is strictly scalar (e.g., uses standard Python `math` modules instead of `numpy`), it automatically
    falls back to `numpy.vectorize` to ensure compatibility across large coordinate grids.

    Args:
        user_defined_function (Callable): The custom function or lambda to evaluate.
        *spatial_arguments: The base spatial arrays to pass into the function (typically world_x, world_y).

    Returns:
        tuple: A tuple (evaluated_vector_x, evaluated_vector_y) containing the computed field components
            as sanitized NumPy arrays.

    Notes:
        * **Time Injection:** If `user_defined_function` accepts exactly one parameter more than the
          number of provided `*spatial_arguments` (e.g., taking x, y, and t), the current scene time
          is automatically injected during execution.
        * **Vectorization Fallback:** You do not need to write strictly vectorized NumPy code. Regular
          Python scalar operations will be caught and vectorized automatically, though writing native
          NumPy code is highly recommended for optimal rendering performance.


    Examples:
        Evaluating a custom mathematical perturbation with automatic time injection:
        ```python
        import FluxRender as fr
        import numpy as np

        # [Initializing the scene and coordinate system]

        field = fr.VectorField(
            vec_function = lambda x, y: (y, -x),
        )
        scene.add(field)

        # Notice the third parameter 't'. The engine detects this and injects it.
        def custom_wind_perturbation(x, y, t):
            perturbation_x = np.sin(x + t)
            perturbation_y = np.cos(y + t)
            return perturbation_x, perturbation_y

        spatial_coordinates_x = np.array([0.0, 1.0, 2.0])
        spatial_coordinates_y = np.array([0.0, 1.0, 2.0])

        result_vectors_x, result_vectors_y = field.evaluate_vector_function(
            custom_wind_perturbation,
            spatial_coordinates_x,
            spatial_coordinates_y
        )
        ```
    """

    return self.math_engine._safe_evaluate_vector_function(user_defined_function, *spatial_arguments)

update_configuration

update_configuration() -> None

This function refreshes a vector field.

Most often used when editing parameters not strictly related to the VectorField object (e.g., changing parameters in ColorMapper).

Example

If you change colors in ColorMapper:

my_color_mapper = ColorMapper()

# [Set up your vector field with my_color_mapper]

# a function called when you click a button that changes the color range in ColorMapper and updates the configuration in VectorField
def on_click():
    my_color_mapper.max_hue = 180
    my_color_mapper.min_hue = 240

    my_vector_field.update_configuration()

Source code in FluxRender/entities.py
def update_configuration(self) -> None:
    """
        This function refreshes a vector field.

        Most often used when editing parameters not strictly related to the VectorField object (e.g., changing parameters in ColorMapper).

        Example:
            If you change colors in ColorMapper:
            ```python
            my_color_mapper = ColorMapper()

            # [Set up your vector field with my_color_mapper]

            # a function called when you click a button that changes the color range in ColorMapper and updates the configuration in VectorField
            def on_click():
                my_color_mapper.max_hue = 180
                my_color_mapper.min_hue = 240

                my_vector_field.update_configuration()
            ```
    """

    self._update_settings()
    self._last_cam_state = (0, 0, 0, 0, 0, 0)