diff --git a/docs/source/api/graphic_features/SizeSpace.rst b/docs/source/api/graphic_features/SizeSpace.rst new file mode 100644 index 000000000..0bca1ecc8 --- /dev/null +++ b/docs/source/api/graphic_features/SizeSpace.rst @@ -0,0 +1,35 @@ +.. _api.SizeSpace: + +SizeSpace +********* + +========= +SizeSpace +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace.add_event_handler + SizeSpace.block_events + SizeSpace.clear_event_handlers + SizeSpace.remove_event_handler + SizeSpace.set_value + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index ea3ce8903..dc88e97d6 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -7,6 +7,7 @@ Graphic Features VertexColors UniformColor UniformSize + SizeSpace Thickness VertexPositions PointsSizesFeature diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index cb924b4dc..4302ab56c 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -31,6 +31,7 @@ Properties LineGraphic.offset LineGraphic.right_click_menu LineGraphic.rotation + LineGraphic.size_space LineGraphic.supported_events LineGraphic.thickness LineGraphic.visible diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 8f15e827a..83e734c61 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -31,6 +31,7 @@ Properties ScatterGraphic.offset ScatterGraphic.right_click_menu ScatterGraphic.rotation + ScatterGraphic.size_space ScatterGraphic.sizes ScatterGraphic.supported_events ScatterGraphic.visible diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index 4f9013425..a1915bbe9 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -2,6 +2,7 @@ VertexColors, UniformColor, UniformSize, + SizeSpace, Thickness, VertexPositions, PointsSizesFeature, @@ -42,6 +43,7 @@ "VertexColors", "UniformColor", "UniformSize", + "SizeSpace", "Thickness", "VertexPositions", "PointsSizesFeature", diff --git a/fastplotlib/graphics/_features/_positions_graphics.py b/fastplotlib/graphics/_features/_positions_graphics.py index ee7927a36..c4e153a31 100644 --- a/fastplotlib/graphics/_features/_positions_graphics.py +++ b/fastplotlib/graphics/_features/_positions_graphics.py @@ -182,6 +182,27 @@ def set_value(self, graphic, value: float | int): self._call_event_handlers(event) +# manages the coordinate space for scatter/line +class SizeSpace(GraphicFeature): + def __init__(self, value: str): + self._value = value + super().__init__() + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + if "Line" in graphic.world_object.material.__class__.__name__: + graphic.world_object.material.thickness_space = value + else: + graphic.world_object.material.size_space = value + self._value = value + + event = FeatureEvent(type="size_space", info={"value": value}) + self._call_event_handlers(event) + + class VertexPositions(BufferManager): """ +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ diff --git a/fastplotlib/graphics/_positions_base.py b/fastplotlib/graphics/_positions_base.py index 3727087cc..565a4cd98 100644 --- a/fastplotlib/graphics/_positions_base.py +++ b/fastplotlib/graphics/_positions_base.py @@ -10,6 +10,7 @@ UniformColor, VertexCmap, PointsSizesFeature, + SizeSpace, ) @@ -54,6 +55,19 @@ def cmap(self, name: str): self._cmap[:] = name + @property + def size_space(self): + """ + The coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + + See https://docs.pygfx.org/stable/_autosummary/utils/utils/enums/pygfx.utils.enums.CoordSpace.html#pygfx.utils.enums.CoordSpace for available options. + """ + return self._size_space.value + + @size_space.setter + def size_space(self, value: str): + self._size_space.set_value(self, value) + def __init__( self, data: Any, @@ -63,6 +77,7 @@ def __init__( cmap: str | VertexCmap = None, cmap_transform: np.ndarray = None, isolated_buffer: bool = True, + size_space: str = "screen", *args, **kwargs, ): @@ -132,6 +147,7 @@ def __init__( self._colors, cmap_name=None, transform=None, alpha=alpha ) + self._size_space = SizeSpace(size_space) super().__init__(*args, **kwargs) def unshare_property(self, property: str): diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 1574587fe..8fe505ba9 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -6,11 +6,11 @@ from ._positions_base import PositionsGraphic from .selectors import LinearRegionSelector, LinearSelector, RectangleSelector -from ._features import Thickness +from ._features import Thickness, SizeSpace class LineGraphic(PositionsGraphic): - _features = {"data", "colors", "cmap", "thickness"} + _features = {"data", "colors", "cmap", "thickness", "size_space"} def __init__( self, @@ -22,6 +22,7 @@ def __init__( cmap: str = None, cmap_transform: np.ndarray | Iterable = None, isolated_buffer: bool = True, + size_space: str = "screen", **kwargs, ): """ @@ -53,6 +54,9 @@ def __init__( cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap + size_space: str, default "screen" + coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + **kwargs passed to Graphic @@ -66,6 +70,7 @@ def __init__( cmap=cmap, cmap_transform=cmap_transform, isolated_buffer=isolated_buffer, + size_space=size_space, **kwargs, ) @@ -83,10 +88,14 @@ def __init__( color_mode="uniform", color=self.colors, pick_write=True, + thickness_space=self.size_space, ) else: material = MaterialCls( - thickness=self.thickness, color_mode="vertex", pick_write=True + thickness=self.thickness, + color_mode="vertex", + pick_write=True, + thickness_space=self.size_space, ) geometry = pygfx.Geometry( positions=self._data.buffer, colors=self._colors.buffer diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 39d815c95..8dad7cd43 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -4,11 +4,11 @@ import pygfx from ._positions_base import PositionsGraphic -from ._features import PointsSizesFeature, UniformSize +from ._features import PointsSizesFeature, UniformSize, SizeSpace class ScatterGraphic(PositionsGraphic): - _features = {"data", "sizes", "colors", "cmap"} + _features = {"data", "sizes", "colors", "cmap", "size_space"} def __init__( self, @@ -21,6 +21,7 @@ def __init__( isolated_buffer: bool = True, sizes: float | np.ndarray | Iterable[float] = 1, uniform_size: bool = False, + size_space: str = "screen", **kwargs, ): """ @@ -60,6 +61,9 @@ def __init__( if True, uses a uniform buffer for the scatter point sizes, basically saves GPU VRAM when all scatter points are the same size + size_space: str, default "screen" + coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + kwargs passed to Graphic @@ -73,6 +77,7 @@ def __init__( cmap=cmap, cmap_transform=cmap_transform, isolated_buffer=isolated_buffer, + size_space=size_space, **kwargs, ) @@ -80,6 +85,7 @@ def __init__( geo_kwargs = {"positions": self._data.buffer} material_kwargs = {"pick_write": True} + self._size_space = SizeSpace(size_space) if uniform_color: material_kwargs["color_mode"] = "uniform" @@ -97,6 +103,7 @@ def __init__( self._sizes = PointsSizesFeature(sizes, n_datapoints=n_datapoints) geo_kwargs["sizes"] = self.sizes.buffer + material_kwargs["size_space"] = self.size_space world_object = pygfx.Points( pygfx.Geometry(**geo_kwargs), material=pygfx.PointsMaterial(**material_kwargs), diff --git a/tests/test_positions_graphics.py b/tests/test_positions_graphics.py index 81403c06b..b76ece2ca 100644 --- a/tests/test_positions_graphics.py +++ b/tests/test_positions_graphics.py @@ -443,3 +443,45 @@ def test_thickness(thickness): else: assert isinstance(graphic.world_object.material, pygfx.LineMaterial) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("size_space", ["screen", "world", "model"]) +def test_size_space(graphic_type, size_space): + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["size_space"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if size_space is None: + size_space = "screen" # default space + + # size_space is really an alias for pygfx.utils.enums.CoordSpace + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data, **kwargs) + + # test getter + assert graphic.world_object.material.thickness_space == size_space + assert graphic.size_space == size_space + + # test setter + graphic.size_space = "world" + assert graphic.size_space == "world" + assert graphic.world_object.material.thickness_space == "world" + + elif graphic_type == "scatter": + + # test getter + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + assert graphic.world_object.material.size_space == size_space + assert graphic.size_space == size_space + + # test setter + graphic.size_space = "world" + assert graphic.size_space == "world" + assert graphic.world_object.material.size_space == "world"
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: