Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f380b26

Browse filesBrowse files
committed
attach and detach buffers to a graphic, not tested
1 parent c205b10 commit f380b26
Copy full SHA for f380b26

File tree

Expand file treeCollapse file tree

4 files changed

+69
-23
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+69
-23
lines changed

‎fastplotlib/graphics/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_base.py
+61-5Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import numpy as np
88
import pylinalg as la
99

10-
from pygfx import WorldObject
10+
import pygfx
1111

12-
from ._features import GraphicFeature, PresentFeature, BufferManager, GraphicFeatureDescriptor, Deleted
12+
from ._features import GraphicFeature, PresentFeature, BufferManager, GraphicFeatureDescriptor, Deleted, PointsDataFeature, ColorFeature, PointsSizesFeature
1313

1414

1515
HexStr: TypeAlias = str
@@ -112,14 +112,20 @@ def name(self, name: str):
112112
self._name = name
113113

114114
@property
115-
def world_object(self) -> WorldObject:
115+
def world_object(self) -> pygfx.WorldObject:
116116
"""Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly."""
117117
# We use weakref to simplify garbage collection
118118
return weakref.proxy(WORLD_OBJECTS[self._fpl_address])
119119

120-
def _set_world_object(self, wo: WorldObject):
120+
def _set_world_object(self, wo: pygfx.WorldObject):
121121
WORLD_OBJECTS[self._fpl_address] = wo
122122

123+
def detach_feature(self, feature: str):
124+
raise NotImplementedError
125+
126+
def attach_feature(self, feature: BufferManager):
127+
raise NotImplementedError
128+
123129
@property
124130
def position(self) -> np.ndarray:
125131
"""position of the graphic, [x, y, z]"""
@@ -175,7 +181,7 @@ def visible(self, v: bool):
175181
self.world_object.visible = v
176182

177183
@property
178-
def children(self) -> list[WorldObject]:
184+
def children(self) -> list[pygfx.WorldObject]:
179185
"""Return the children of the WorldObject."""
180186
return self.world_object.children
181187

@@ -264,6 +270,56 @@ def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"):
264270
self.rotation = la.quat_mul(rot, self.rotation)
265271

266272

273+
class PositionsGraphic(Graphic):
274+
"""Base class for LineGraphic and ScatterGraphic"""
275+
276+
def detach_feature(self, feature: str):
277+
if not isinstance(feature, str):
278+
raise TypeError
279+
280+
f = getattr(self, feature)
281+
if f.shared == 0:
282+
raise BufferError("Cannot detach an independent buffer")
283+
284+
if feature == "colors":
285+
self._colors._buffer = pygfx.Buffer(self._colors.value.copy())
286+
self.world_object.geometry.colors = self._colors.buffer
287+
self._colors._shared -= 1
288+
289+
elif feature == "data":
290+
self._data._buffer = pygfx.Buffer(self._data.value.copy())
291+
self.world_object.geometry.positions = self._data.buffer
292+
self._data._shared -= 1
293+
294+
elif feature == "sizes":
295+
self._sizes._buffer = pygfx.Buffer(self._sizes.value.copy())
296+
self.world_object.geometry.positions = self._sizes.buffer
297+
self._sizes._shared -= 1
298+
299+
def attach_feature(self, feature: PointsDataFeature | ColorFeature | PointsSizesFeature):
300+
if isinstance(feature, PointsDataFeature):
301+
# TODO: check if this causes a memory leak
302+
self._data._shared -= 1
303+
304+
self._data = feature
305+
self._data._shared += 1
306+
self.world_object.geometry.positions = self._data.buffer
307+
308+
elif isinstance(feature, ColorFeature):
309+
self._colors._shared -= 1
310+
311+
self._colors = feature
312+
self._colors._shared += 1
313+
self.world_object.geometry.colors = self._colors.buffer
314+
315+
elif isinstance(feature, PointsSizesFeature):
316+
self._sizes._shared -= 1
317+
318+
self._sizes = feature
319+
self._sizes._shared += 1
320+
self.world_object.geometry.sizes = self._sizes.buffer
321+
322+
267323
class Interaction(ABC):
268324
"""Mixin class that makes graphics interactive"""
269325

‎fastplotlib/graphics/_features/_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_features/_base.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def __init__(
201201

202202
self._event_handlers: list[callable] = list()
203203

204-
self._shared = False
204+
self._shared: int = 0
205205

206206
@property
207207
def value(self) -> NDArray:
@@ -212,8 +212,8 @@ def buffer(self) -> pygfx.Buffer | pygfx.Texture:
212212
return self._buffer
213213

214214
@property
215-
def shared(self) -> bool:
216-
"""If the buffer is shared between multiple graphics"""
215+
def shared(self) -> int:
216+
"""Number of graphics that share this buffer"""
217217
return self._shared
218218

219219
def __getitem__(self, item):

‎fastplotlib/graphics/line.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/line.py
+3-13Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
import pygfx
77

88
from ..utils import parse_cmap_values
9-
from ._base import Graphic, Interaction, PreviouslyModifiedData
9+
from ._base import PositionsGraphic, Interaction, PreviouslyModifiedData
1010
from ._features import GraphicFeatureDescriptor, PointsDataFeature, ColorFeature#, CmapFeature, ThicknessFeature
1111
from .selectors import LinearRegionSelector, LinearSelector
1212

1313

14-
class LineGraphic(Graphic, Interaction):
14+
class LineGraphic(PositionsGraphic, Interaction):
1515
features = {"data", "colors"}#, "cmap", "thickness", "present"}
1616

1717
def __init__(
@@ -94,7 +94,7 @@ def __init__(
9494

9595
if isinstance(colors, ColorFeature):
9696
self._colors = colors
97-
self._shared = True
97+
self._colors._shared += 1
9898
else:
9999
self._colors = ColorFeature(
100100
colors,
@@ -126,16 +126,6 @@ def __init__(
126126
if z_position is not None:
127127
self.position_z = z_position
128128

129-
def unshare_buffer(self, feature: str):
130-
f = getattr(self, feature)
131-
if not f.shared:
132-
raise BufferError
133-
134-
if isinstance(f, ColorFeature):
135-
self._colors._buffer = pygfx.Buffer(self._colors.value.copy())
136-
self.world_object.geometry.colors = self._colors.buffer
137-
self._colors._shared = False
138-
139129
def add_linear_selector(
140130
self, selection: int = None, padding: float = 50, **kwargs
141131
) -> LinearSelector:

‎fastplotlib/graphics/scatter.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/scatter.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
import pygfx
55

66
from ..utils import parse_cmap_values
7-
from ._base import Graphic
7+
from ._base import PositionsGraphic
88
from ._features import PointsDataFeature, ColorFeature, CmapFeature, PointsSizesFeature
99

1010

11-
class ScatterGraphic(Graphic):
11+
class ScatterGraphic(PositionsGraphic):
1212
feature_events = {"data", "sizes", "colors", "cmap", "present"}
1313

1414
def __init__(

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.