Skip to content

CoordinateSystem

FluxRender.core.CoordinateSystem

CoordinateSystem(x_range: tuple, y_range: tuple, width: int, height: int, keep_aspect_ratio: bool = False)

Handles coordinate transformations between the 2D screen space (pixels) and the mathematical simulation space.

This class manages the mapping from a mathematical domain (e.g., -10 to 10) to the render resolution (e.g., 0 to 1200). It supports aspect ratio correction to ensure that geometrical shapes retain their proportions (e.g., circles remain circular).

Attributes:

Name Type Description
width int

Screen width in pixels.

height int

Screen height in pixels.

x_min float

The actual lower bound of the mathematical X axis.

x_max float

The actual upper bound of the mathematical X axis.

y_min float

The actual lower bound of the mathematical Y axis.

y_max float

The actual upper bound of the mathematical Y axis.

math_width float

The total span of the X axis (max - min).

math_height float

The total span of the Y axis (max - min).

Initializes the coordinate system with specific bounds and screen dimensions.

If keep_aspect_ratio is set to True, the provided ranges will be adjusted (expanded) to match the screen's aspect ratio. The strategy used is "Fit/Expand": it ensures the requested range is fully visible, adding extra margins to the shorter axis if necessary.

Parameters:

Name Type Description Default
x_range tuple[float, float]

The desired (min, max) values for the X axis.

required
y_range tuple[float, float]

The desired (min, max) values for the Y axis.

required
width int

Window/buffer width in pixels.

required
height int

Window/buffer height in pixels.

required
keep_aspect_ratio bool

If True, adjusts x_range or y_range to preserve 1:1 scaling (square pixels). Defaults to False.

False
Example

To create a coordinate system that maps the mathematical range of -2 to 2 on both axes to a screen resolution of 1800x950 pixels without keeping the aspect ratio:

coords = CoordinateSystem((-2, 2), (-2, 2), 1800, 950, keep_aspect_ratio=False)

To create the same coordinate system but with aspect ratio correction (ensuring circles look like circles):

coords = CoordinateSystem((-2, 2), (-2, 2), 1800, 950, keep_aspect_ratio=True)
Then, in the above case, the ranges on the X and Y axis will be adjusted accordingly to maintain the aspect ratio.

Source code in FluxRender/core.py
def __init__(self, x_range: tuple, y_range: tuple, width: int, height: int, keep_aspect_ratio: bool = False):
    """
    Initializes the coordinate system with specific bounds and screen dimensions.

    If `keep_aspect_ratio` is set to True, the provided ranges will be adjusted
    (expanded) to match the screen's aspect ratio. The strategy used is "Fit/Expand":
    it ensures the requested range is fully visible, adding extra margins to the
    shorter axis if necessary.

    Args:
        x_range (tuple[float, float]): The desired (min, max) values for the X axis.
        y_range (tuple[float, float]): The desired (min, max) values for the Y axis.
        width (int): Window/buffer width in pixels.
        height (int): Window/buffer height in pixels.
        keep_aspect_ratio (bool, optional): If True, adjusts x_range or y_range
            to preserve 1:1 scaling (square pixels). Defaults to False.


    Example:
        To create a coordinate system that maps the mathematical range of -2 to 2 on both axes to a screen resolution of 1800x950 pixels without keeping the aspect ratio:
        ```python
        coords = CoordinateSystem((-2, 2), (-2, 2), 1800, 950, keep_aspect_ratio=False)
        ```

        To create the same coordinate system but with aspect ratio correction (ensuring circles look like circles):
        ```python
        coords = CoordinateSystem((-2, 2), (-2, 2), 1800, 950, keep_aspect_ratio=True)
        ```
        Then, in the above case, the ranges on the X and Y axis will be adjusted accordingly to maintain the aspect ratio.
    """

    self.width = width
    self.height = height

    self.math_width = x_range[1] - x_range[0]
    self.math_height = y_range[1] - y_range[0]
    x_center = (x_range[0] + x_range[1]) / 2.0
    y_center = (y_range[0] + y_range[1]) / 2.0

    if keep_aspect_ratio:
        screen_ratio = width / height
        data_ratio = self.math_width / self.math_height

        if screen_ratio < data_ratio:
            self.x_min = x_range[0]
            self.x_max = x_range[1]

            new_height = self.math_width / screen_ratio
            self.y_min = y_center - new_height / 2.0
            self.y_max = y_center + new_height / 2.0

        else:
            self.y_min = y_range[0]
            self.y_max = y_range[1]

            new_width = self.math_height * screen_ratio
            self.x_min = x_center - new_width / 2.0
            self.x_max = x_center + new_width / 2.0

        self.math_width = self.x_max - self.x_min
        self.math_height = self.y_max - self.y_min
    else:
        self.x_min, self.x_max = x_range
        self.y_min, self.y_max = y_range


    self.gpu_cam = CameraObj.field(shape=())
    self.push_to_gpu()

move

move(x_offset, y_offset)

Moves the virtual mathematical environment by given values in units of the width and height of the mathematical space.

Parameters:

Name Type Description Default
x_offset float

Camera offset relative to the X axis by x_offset*math_width

required
y_offset float

Camera offset relative to the Y axis by y_offset*math_height

required
Example

To move the camera to the right by 10% of the current mathematical width and up by 10% of the current mathematical height:

coords.move(0.1, -0.1)

Source code in FluxRender/core.py
def move(self, x_offset, y_offset):
    """
    Moves the virtual mathematical environment by given values
    in units of the width and height of the mathematical space.

    Args:
        x_offset (float): Camera offset relative to the X axis by x_offset*math_width
        y_offset (float): Camera offset relative to the Y axis by y_offset*math_height

    Example:
        To move the camera to the right by 10% of the current mathematical width and up by 10% of the current mathematical height:
        ```pyhon
        coords.move(0.1, -0.1)
        ```
    """

    math_dx = x_offset * self.math_width
    math_dy = y_offset * self.math_height

    self.x_min -= math_dx
    self.x_max -= math_dx

    self.y_min -= math_dy
    self.y_max -= math_dy

    self.push_to_gpu()

to_math

to_math(pixel_x, pixel_y)

Converts screen coordinates (pixels) to mathematical coordinates.

Parameters:

Name Type Description Default
pixel_x float / tuple / list

X coordinate on the screen.

required
pixel_y float / tuple / list

Y coordinate on the screen.

required

Returns:

Name Type Description
point tuple[Float, Float]

Corresponding coordinates in the mathematical domain.

Source code in FluxRender/core.py
def to_math(self, pixel_x, pixel_y):
    """
    Converts screen coordinates (pixels) to mathematical coordinates.

    Args:
        pixel_x (float/tuple/list): X coordinate on the screen.
        pixel_y (float/tuple/list): Y coordinate on the screen.

    Returns:
        point (tuple[Float, Float]): Corresponding coordinates in the mathematical domain.
    """

    u = self.x_min + (pixel_x / self.width) * self.math_width
    v = self.y_min + (pixel_y / self.height) * self.math_height

    return u, v

to_screen

to_screen(x_math, y_math)

Converts mathematical coordinates to screen coordinates (pixels).

Parameters:

Name Type Description Default
x_math float / tuple / list

X coordinate(s) in the mathematical domain.

required
y_math float / tuple / list

Y coordinate(s) in the mathematical domain.

required

Returns:

Name Type Description
point tuple[Float, Float]

Corresponding coordinates on the screen in pixels.

Source code in FluxRender/core.py
def to_screen(self, x_math, y_math):
    """
    Converts mathematical coordinates to screen coordinates (pixels).

    Args:
        x_math (float/tuple/list): X coordinate(s) in the mathematical domain.
        y_math (float/tuple/list): Y coordinate(s) in the mathematical domain.

    Returns:
        point (tuple[Float, Float]): Corresponding coordinates on the screen in pixels.
    """

    u = (x_math - self.x_min) / self.math_width
    v = (y_math - self.y_min) / self.math_height

    return u * self.width, v * self.height

zoom

zoom(factor, pivot)

Zooms the camera in/out in a virtual mathematical environment relative to the pivot point (in standard units from 0 to 1) by a given factor.

Parameters:

Name Type Description Default
factor float

Scale multiplier (e.g. 1.1 = zoom out, 0.9 = zoom in)

required
pivot tuple

The pivot point for zooming, given as (x, y) in normalized screen coordinates (0 to 1).

required
Example

To zoom in by 10% towards the center of the screen:

coords.zoom(0.9, (0.5, 0.5))

Source code in FluxRender/core.py
def zoom(self, factor, pivot):
    """
    Zooms the camera in/out in a virtual mathematical environment relative
    to the pivot point (in standard units from 0 to 1) by a given factor.

    Args:
        factor (float): Scale multiplier (e.g. 1.1 = zoom out, 0.9 = zoom in)
        pivot (tuple): The pivot point for zooming, given as (x, y) in normalized screen coordinates (0 to 1).

    Example:
        To zoom in by 10% towards the center of the screen:
        ```pyhon
        coords.zoom(0.9, (0.5, 0.5))
        ```
    """

    mouse_math_x = self.x_min + pivot[0] * self.math_width
    mouse_math_y = self.y_min + pivot[1] * self.math_height

    new_width = self.math_width * factor
    new_height = self.math_height * factor

    self.x_min = mouse_math_x - (pivot[0] * new_width)
    self.x_max = self.x_min + new_width

    self.y_min = mouse_math_y - (pivot[1] * new_height)
    self.y_max = self.y_min + new_height

    self.math_width = new_width
    self.math_height = new_height

    self.push_to_gpu()