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 01be111

Browse filesBrowse files
committed
flatten VertexCmap, add new UniformAlpha gfeature
1 parent a691be2 commit 01be111
Copy full SHA for 01be111

File tree

3 files changed

+190
-84
lines changed
Filter options

3 files changed

+190
-84
lines changed

‎fastplotlib/graphics/_features/__init__.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_features/__init__.py
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
from ._positions_graphics import (
22
VertexColors,
33
UniformColor,
4+
UniformAlpha,
45
UniformSize,
56
Thickness,
67
VertexPositions,
78
PointsSizesFeature,
89
VertexCmap,
10+
VertexCmapTransform,
11+
VertexAlpha,
912
)
1013
from ._image import (
1114
TextureArray,

‎fastplotlib/graphics/_features/_positions_graphics.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_features/_positions_graphics.py
+129-80Lines changed: 129 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ def __len__(self):
142142
return len(self.buffer.data)
143143

144144

145-
# Manages uniform color for line or scatter material
146145
class UniformColor(GraphicFeature):
146+
"""Manages uniform color buffer for line or scatter material"""
147147
def __init__(
148148
self, value: str | np.ndarray | tuple | list | pygfx.Color, alpha: float = 1.0
149149
):
@@ -164,6 +164,36 @@ def set_value(self, graphic, value: str | np.ndarray | tuple | list | pygfx.Colo
164164
self._call_event_handlers(event)
165165

166166

167+
class UniformAlpha(GraphicFeature):
168+
"""
169+
Manages alpha when colors are in a uniform buffer
170+
"""
171+
def __init__(
172+
self,
173+
value: float,
174+
uniform_colors: UniformColor,
175+
):
176+
self._value = value
177+
super().__init__()
178+
179+
self._uniform_colors = uniform_colors
180+
181+
@property
182+
def value(self) -> float:
183+
return self._value
184+
185+
def set_value(self, graphic, value: float):
186+
187+
color = (*tuple(self._uniform_colors.value)[:-1], value) # apply alpha
188+
189+
self._uniform_colors._value = pygfx.Color(color)
190+
graphic.world_object.material.color = color
191+
192+
self._value = value
193+
event = FeatureEvent(type="alpha", info={"value": value})
194+
self._call_event_handlers(event)
195+
196+
167197
# manages uniform size for scatter material
168198
class UniformSize(GraphicFeature):
169199
def __init__(self, value: int | float):
@@ -331,10 +361,9 @@ def set_value(self, graphic, value: float):
331361
self._call_event_handlers(event)
332362

333363

334-
class VertexCmap(BufferManager):
364+
class VertexCmap(GraphicFeature):
335365
"""
336-
Sliceable colormap feature, manages a VertexColors instance and
337-
provides a way to set colormaps with arbitrary transforms
366+
Provides a way to set colormaps
338367
"""
339368

340369
def __init__(
@@ -344,115 +373,135 @@ def __init__(
344373
transform: np.ndarray | None,
345374
alpha: float = 1.0,
346375
):
347-
super().__init__(data=vertex_colors.buffer)
348376

349377
self._vertex_colors = vertex_colors
350-
self._cmap_name = cmap_name
351-
self._transform = transform
352-
self._alpha = alpha
378+
self._value = cmap_name
353379

354-
if self._cmap_name is not None:
355-
if not isinstance(self._cmap_name, str):
380+
if self._value is not None:
381+
if not isinstance(self._value, str):
356382
raise TypeError(
357-
f"cmap name must be of type <str>, you have passed: {self._cmap_name} of type: {type(self._cmap_name)}"
383+
f"cmap name must be of type <str>, you have passed: {self._value} of type: {type(self._value)}"
358384
)
359385

360-
if self._transform is not None:
361-
self._transform = np.asarray(self._transform)
386+
if transform is not None:
387+
transform = np.asarray(transform)
362388

363389
n_datapoints = vertex_colors.value.shape[0]
364390

365391
colors = parse_cmap_values(
366392
n_colors=n_datapoints,
367-
cmap_name=self._cmap_name,
368-
transform=self._transform,
393+
cmap_name=self._value,
394+
transform=transform,
369395
)
370396
colors[:, -1] = alpha
371397
# set vertex colors from cmap
372398
self._vertex_colors[:] = colors
373399

374-
def __setitem__(self, key: slice, cmap_name):
375-
if not isinstance(key, slice):
376-
raise TypeError(
377-
"fancy indexing not supported for VertexCmap, only slices "
378-
"of a continuous are supported for apply a cmap"
379-
)
380-
if key.step is not None:
381-
raise TypeError(
382-
"step sized indexing not currently supported for setting VertexCmap, "
383-
"slices must be a continuous region"
384-
)
400+
super().__init__()
385401

386-
# parse slice
387-
start, stop, step = key.indices(self.value.shape[0])
388-
n_elements = len(range(start, stop, step))
402+
@property
403+
def value(self) -> str:
404+
"""The current cmap name"""
405+
return self._value
389406

390-
colors = parse_cmap_values(
391-
n_colors=n_elements, cmap_name=cmap_name, transform=self._transform
392-
)
393-
colors[:, -1] = self.alpha
407+
def set_value(self, graphic, value: str):
408+
if value is None:
409+
# when cmap value is cleared, for example if vertex colors are set directly
410+
self._value = None
411+
return
394412

395-
self._cmap_name = cmap_name
396-
self._vertex_colors[key] = colors
413+
transform = graphic.cmap_transform
414+
alpha = graphic.alpha
397415

398-
# TODO: should we block vertex_colors from emitting an event?
399-
# Because currently this will result in 2 emitted events, one
400-
# for cmap and another from the colors
401-
self._emit_event("cmap", key, cmap_name)
416+
n_datapoints = graphic.colors.value.shape[0]
402417

403-
@property
404-
def name(self) -> str:
405-
return self._cmap_name
418+
colors = parse_cmap_values(n_colors=n_datapoints, cmap_name=value, transform=transform)
419+
colors[:, -1] = alpha
406420

407-
@property
408-
def transform(self) -> np.ndarray | None:
409-
"""Get or set the cmap transform. Maps values from the transform array to the cmap colors"""
410-
return self._transform
421+
self._vertex_colors[:] = colors
411422

412-
@transform.setter
413-
def transform(
414-
self,
415-
values: np.ndarray | list[float | int],
416-
indices: slice | list | np.ndarray = None,
423+
self._value = value
424+
event = FeatureEvent(type="cmap", info={"value": value})
425+
self._call_event_handlers(event)
426+
427+
428+
class VertexCmapTransform(GraphicFeature):
429+
"""
430+
Manages cmap transform
431+
"""
432+
def __init__(
433+
self,
434+
value: np.ndarray | None,
435+
vertex_colors: VertexColors,
417436
):
418-
if self._cmap_name is None:
419-
raise AttributeError(
420-
"cmap name is not set, set the cmap name before setting the transform"
421-
)
437+
# doesn't do any instantiation that touches the buffer, only allows changes after init
438+
# VertexCmap instantiates does set the transform and alpha in its instantiation
439+
self._value = value
440+
self._vertex_colors = vertex_colors
422441

423-
values = np.asarray(values)
442+
super().__init__()
424443

425-
colors = parse_cmap_values(
426-
n_colors=self.value.shape[0], cmap_name=self._cmap_name, transform=values
427-
)
444+
@property
445+
def value(self) -> np.ndarray:
446+
"""The current transform on the cmap"""
447+
return self._value
428448

429-
colors[:, -1] = self.alpha
449+
def set_value(self, graphic, value: np.ndarray | tuple | list):
450+
if value is None:
451+
# when transform value is cleared, for example if vertex colors are set directly
452+
self._value = None
453+
return
454+
value = np.asarray(value)
430455

431-
self._transform = values
456+
cmap_name = graphic.cmap
432457

433-
if indices is None:
434-
indices = slice(None)
458+
if cmap_name is None:
459+
raise AttributeError("No `cmap` has been set. Must set `cmap` before setting `cmap_transform`")
435460

436-
self._vertex_colors[indices] = colors
461+
alpha = graphic.alpha
437462

438-
self._emit_event("cmap.transform", indices, values)
463+
n_datapoints = graphic.colors.value.shape[0]
439464

440-
@property
441-
def alpha(self) -> float:
442-
"""Get or set the alpha level"""
443-
return self._alpha
465+
colors = parse_cmap_values(n_colors=n_datapoints, cmap_name=cmap_name, transform=value)
444466

445-
@alpha.setter
446-
def alpha(self, value: float, indices: slice | list | np.ndarray = None):
447-
self._vertex_colors[indices, -1] = value
448-
self._alpha = value
467+
colors[:, -1] = alpha
449468

450-
self._emit_event("cmap.alpha", indices, value)
469+
self._vertex_colors[:] = colors
451470

452-
def __len__(self):
453-
raise NotImplementedError(
454-
"len not implemented for `cmap`, use len(colors) instead"
455-
)
471+
self._value = value
472+
event = FeatureEvent(type="cmap_transform", info={"value": value})
473+
self._call_event_handlers(event)
474+
475+
476+
class VertexAlpha(GraphicFeature):
477+
"""
478+
Manages alpha when colors are in a per-vertex buffer
479+
"""
480+
def __init__(
481+
self,
482+
value: float,
483+
vertex_colors: VertexColors,
484+
):
485+
# doesn't do any instantiation that touches the buffer, only allows changes after init
486+
# VertexCmap instantiates does set the transform and alpha in its instantiation
487+
self._value = value
488+
self._vertex_colors = vertex_colors
456489

457-
def __repr__(self):
458-
return f"{self.__class__.__name__} | cmap: {self.name}\ntransform: {self.transform}"
490+
super().__init__()
491+
492+
@property
493+
def value(self) -> float:
494+
"""The current alpha value"""
495+
return self._value
496+
497+
def set_value(self, graphic, value: float):
498+
if value is None:
499+
# when alpha value is cleared, for example if vertex colors are set directly
500+
self._value = None
501+
return
502+
503+
self._vertex_colors[:, -1] = value
504+
505+
self._value = value
506+
event = FeatureEvent(type="alpha", info={"value": value})
507+
self._call_event_handlers(event)

‎fastplotlib/graphics/_positions_base.py

Copy file name to clipboardExpand all lines: fastplotlib/graphics/_positions_base.py
+58-4Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
VertexPositions,
99
VertexColors,
1010
UniformColor,
11+
UniformAlpha,
12+
VertexAlpha,
1113
VertexCmap,
14+
VertexCmapTransform,
1215
PointsSizesFeature,
1316
)
1417

@@ -39,20 +42,61 @@ def colors(self, value: str | np.ndarray | tuple[float] | list[float] | list[str
3942
if isinstance(self._colors, VertexColors):
4043
self._colors[:] = value
4144

45+
# invalidate any cmap, cmap_transform, or alpha that have been set
46+
self._cmap._value = None
47+
self._cmap_transform._value = None
48+
49+
alphas = np.unique(self._colors[:, -1])
50+
# invalidate alpha if it is different between vertices
51+
if alphas.size == 1:
52+
self._alpha._value = alphas[0]
53+
else:
54+
self._alpha._value = None
55+
4256
elif isinstance(self._colors, UniformColor):
4357
self._colors.set_value(self, value)
4458

59+
# push the alpha value
60+
self._alpha._value = np.array(self.colors)[-1]
61+
4562
@property
46-
def cmap(self) -> VertexCmap:
47-
"""Control the cmap, cmap transform, or cmap alpha"""
48-
return self._cmap
63+
def cmap(self) -> str:
64+
"""Get or set the colormap"""
65+
if self._cmap is None:
66+
raise BufferError("Cannot use cmap with uniform_colors=True")
67+
68+
return self._cmap.value
4969

5070
@cmap.setter
5171
def cmap(self, name: str):
5272
if self._cmap is None:
5373
raise BufferError("Cannot use cmap with uniform_colors=True")
5474

55-
self._cmap[:] = name
75+
self._cmap.set_value(self, name)
76+
77+
@property
78+
def cmap_transform(self) -> np.ndarray:
79+
"""Get or set the colormap transform"""
80+
if self._cmap is None:
81+
raise BufferError("Cannot use cmap_transform with uniform_colors=True")
82+
83+
return self._cmap_transform.value
84+
85+
@cmap_transform.setter
86+
def cmap_transform(self, transform: np.ndarray):
87+
if self._cmap is None:
88+
raise BufferError("Cannot use cmap_transform with uniform_colors=True")
89+
90+
self._cmap_transform.set_value(self, transform)
91+
92+
@property
93+
def alpha(self) -> float:
94+
"""Get or set the alpha (transparency) value"""
95+
return self._alpha.value
96+
97+
@alpha.setter
98+
def alpha(self, value: float):
99+
self._alpha.set_value(self, value)
56100

57101
def __init__(
58102
self,
@@ -95,6 +139,12 @@ def __init__(
95139
transform=cmap_transform,
96140
alpha=alpha,
97141
)
142+
143+
self._cmap_transform = VertexCmapTransform(
144+
cmap_transform,
145+
vertex_colors=self._colors,
146+
)
147+
self._alpha = VertexAlpha(alpha, vertex_colors=self._colors)
98148
elif isinstance(cmap, VertexCmap):
99149
# use existing cmap instance
100150
self._cmap = cmap
@@ -121,7 +171,9 @@ def __init__(
121171
"must pass a single color if using `uniform_colors=True`"
122172
)
123173
self._colors = UniformColor(colors, alpha=alpha)
174+
self._alpha = UniformAlpha(alpha, uniform_colors=self._colors)
124175
self._cmap = None
176+
self._cmap_transform = None
125177
else:
126178
self._colors = VertexColors(
127179
colors,
@@ -131,6 +183,8 @@ def __init__(
131183
self._cmap = VertexCmap(
132184
self._colors, cmap_name=None, transform=None, alpha=alpha
133185
)
186+
self._cmap_transform = VertexCmapTransform(None, vertex_colors=self._colors)
187+
self._alpha = VertexAlpha(alpha, vertex_colors=self._colors)
134188

135189
super().__init__(*args, **kwargs)
136190

0 commit comments

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