10BC0 auto-replace buffers by kushalkolar · Pull Request #974 · fastplotlib/fastplotlib · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a rang 10BC0 e
84e6590
remove isolated_buffer
kushalkolar Jan 13, 2026
7163c96
remove isolated_buffer from mixin
kushalkolar Jan 13, 2026
426ee5c
basics works for positions data
kushalkolar Jan 13, 2026
23622b0
replaceable buffers for all positions related features
kushalkolar Jan 13, 2026
e19588a
image data buffer can change
kushalkolar Jan 13, 2026
d9f5ca9
resizeable buffers for volume
kushalkolar Jan 13, 2026
ea0d791
black
kushalkolar Jan 13, 2026
734be0e
buffer resize condition checked only if new value is an array
kushalkolar Jan 13, 2026
7974436
gc for buffer managers
kushalkolar Jan 13, 2026
fe749ce
uniform colors WIP
kushalkolar Jan 14, 2026
e667a9d
Merge branch 'main' into replaceable-buffers
kushalkolar Feb 2, 2026
b8f8613
switching color modes works!
kushalkolar Feb 2, 2026
543d4e9
typo
kushalkolar Feb 2, 2026
8a1c15a
balck
kushalkolar Feb 2, 2026
fdeecb1
update tests for color_mode
kushalkolar Feb 2, 2026
6299cdd
update examples
kushalkolar Feb 2, 2026
8b217cc
backend tests passing
kushalkolar Feb 2, 2026
5fd68e2
default for all uniforms is True
kushalkolar Feb 2, 2026
e1d40ba
update examples
kushalkolar Feb 2, 2026
096a51c
forgot
kushalkolar Feb 2, 2026
274d449
update test
kushalkolar Feb 2, 2026
8d776d4
example tests passing
kushalkolar Feb 2, 2026
fce7087
dereferencing test and fixes
kushalkolar Feb 3, 2026
44fdbb9
simplify texture array tests a bit
kushalkolar Feb 3, 2026
d090dca
image replace buffer tests pass yay
kushalkolar Feb 3, 2026
d6754d0
forgot a file
kushalkolar Feb 4, 2026
83b924e
comments, check image graphic
kushalkolar Feb 4, 2026
2237d51
add image reshaping example
kushalkolar Feb 4, 2026
630435e
add buffer replace imgui thing for manual testing
kushalkolar Feb 4, 2026
a87dfdf
black
kushalkolar Feb 4, 2026
30e87ad
dont call wgpu_obj.destroy(), seems to work and clear VRAM with norma…
kushalkolar Feb 4, 2026
6228d6c
slower changes
kushalkolar Feb 4, 2026
e207d5b
update
kushalkolar Feb 4, 2026
5c894c3
update example
kushalkolar Feb 4, 2026
7452fa8
fixes and tweaks for test
kushalkolar Feb 4, 2026
16c979d
remove unecessary stuff
kushalkolar Feb 4, 2026
68be2e0
update
kushalkolar Feb 4, 2026
cb85ac1
Merge branch 'main' into replaceable-buffers
kushalkolar Feb 4, 2026
500426d
docstrings
kushalkolar Feb 4, 2026
dd7a829
fix example
kushalkolar Feb 4, 2026
da54a9c
update example
kushalkolar Feb 4, 2026
eb7fbae
update example
kushalkolar Feb 4, 2026
2e8726f
update docs
kushalkolar Feb 4, 2026
fd87cae
Merge branch 'main' into replaceable-buffers
kushalkolar Feb 4, 2026
bf3041a
Merge branch 'ndwidget' into replaceable-buffers
kushalkolar Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/api/graphics/LineGraphic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Properties
LineGraphic.axes
LineGraphic.block_events
LineGraphic.cmap
LineGraphic.color_mode
LineGraphic.colors
LineGraphic.data
LineGraphic.deleted
Expand Down
1 change: 1 addition & 0 deletions docs/source/api/graphics/ScatterGraphic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Properties
ScatterGraphic.axes
ScatterGraphic.block_events
ScatterGraphic.cmap
ScatterGraphic.color_mode
ScatterGraphic.colors
ScatterGraphic.data
ScatterGraphic.deleted
Expand Down
2 changes: 1 addition & 1 deletion examples/events/cmap_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
xs = np.linspace(0, 4 * np.pi, 100)
ys = np.sin(xs)

figure["sine"].add_line(np.column_stack([xs, ys]))
figure["sine"].add_line(np.column_stack([xs, ys]), color_mode="vertex")

# make a 2D gaussian cloud
cloud_data = np.random.normal(0, scale=3, size=1000).reshape(500, 2)
Expand Down
2 changes: 1 addition & 1 deletion examples/gridplot/multigraphic_gridplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray:
gaussian_cloud2 = np.random.multivariate_normal(mean, covariance, n_points)

# add the scatter graphics to the figure
figure["scatter"].add_scatter(data=gaussian_cloud, sizes=2, cmap="jet")
figure["scatter"].add_scatter(data=gaussian_cloud, sizes=2, cmap="jet", color_mode="vertex")
figure["scatter"].add_scatter(data=gaussian_cloud2, colors="r", sizes=2)

figure.show()
Expand Down
4 changes: 2 additions & 2 deletions examples/guis/imgui_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
figure = fpl.Figure(size=(700, 560))

# make some scatter points at every 10th point
figure[0, 0].add_scatter(data[::10], colors="cyan", sizes=15, name="sine-scatter", uniform_color=True)
figure[0, 0].add_scatter(data[::10], colors="cyan", sizes=15, name="sine-scatter")

# place a line above the scatter
figure[0, 0].add_line(data, thickness=3, colors="r", name="sine-wave", uniform_color=True)
figure[0, 0].add_line(data, thickness=3, colors="r", name="sine-wave")


class ImguiExample(EdgeWindow):
Expand Down
50 changes: 50 additions & 0 deletions examples/image/image_reshaping.py
10BC0
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
Image reshaping
===============

An example that shows replacement of the image data with new data of a different shape. Under the hood, this creates a
new buffer and a new array of Textures on the GPU that replace the older Textures. Creating a new buffer and textures
has a performance cost, so you should do this only if you need to or if the performance drawback is not a concern for
your use case.

Note that the vmin-vmax is reset when you replace the buffers.
"""

# test_example = false
# sphinx_gallery_pygfx_docs = 'animate'


import numpy as np
import fastplotlib as fpl

# create some data, diagonal sinusoidal bands
xs = np.linspace(0, 2300, 2300, dtype=np.float16)
full_data = np.vstack([np.cos(np.sqrt(xs + (np.pi / 2) * i)) * i for i in range(2_300)])

figure = fpl.Figure()

image = figure[0, 0].add_image(full_data)

figure.show()

i, j = 1, 1


def update():
global i, j
# set the new image data as a subset of the full data
row = np.abs(np.sin(i)) * 2300
col = np.abs(np.cos(i)) * 2300
image.data = full_data[: int(row), : int(col)]

i += 0.01
j += 0.01


figure.add_animations(update)

# NOTE: fpl.loop.run() should not be used for interactive sessions
# See the "JupyterLab and IPython" section in the user guide
if __name__ == "__main__":
print(__doc__)
fpl.loop.run()
4 changes: 2 additions & 2 deletions examples/line/line_cmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
data=sine_data,
thickness=10,
cmap="plasma",
cmap_transform=sine_data[:, 1]
cmap_transform=sine_data[:, 1],
)

# qualitative colormaps, useful for cluster labels or other types of categorical labels
Expand All @@ -36,7 +36,7 @@
data=cosine_data,
thickness=10,
cmap="tab10",
cmap_transform=labels
cmap_transform=labels,
)

figure.show()
Expand Down
25 changes: 22 additions & 3 deletions examples/line/line_cmap_more.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,35 @@
# set colormap by mapping data using a transform
# here we map the color using the y-values of the sine data
# i.e., the color is a function of sine(x)
line2 = figure[0, 0].add_line(sine, thickness=10, cmap="jet", cmap_transform=sine[:, 1], offset=(0, 4, 0))
line2 = figure[0, 0].add_line(
sine,
thickness=10,
cmap="jet",
cmap_transform=sine[:, 1],
offset=(0, 4, 0),
)

# make a line and change the cmap afterward, here we are using the cosine instead fot the transform
line3 = figure[0, 0].add_line(sine, thickness=10, cmap="jet", cmap_transform=cosine[:, 1], offset=(0, 6, 0))
line3 = figure[0, 0].add_line(
sine,
thickness=10,
cmap="jet",
cmap_transform=cosine[:, 1],
offset=(0, 6, 0)
)

# change the cmap
line3.cmap = "bwr"

# use quantitative colormaps with categorical cmap_transforms
labels = [0] * 25 + [1] * 5 + [2] * 50 + [3] * 20
line4 = figure[0, 0].add_line(sine, thickness=10, cmap="tab10", cmap_transform=labels, offset=(0, 8, 0))
line4 = figure[0, 0].add_line(
sine,
thickness=10,
cmap="tab10",
cmap_transform=labels,
offset=(0, 8, 0),
)

# some text labels
for i in range(5):
Expand Down
4 changes: 3 additions & 1 deletion examples/line/line_colorslice.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
sine = figure[0, 0].add_line(
data=sine_data,
thickness=5,
colors="magenta"
colors="magenta",
color_mode="vertex", # initialize with same color across vertices, but we will change the per-vertex colors later
)

# you can also use colormaps for lines!
Expand All @@ -56,6 +57,7 @@
data=zeros_data,
thickness=8,
colors="w",
color_mode="vertex", # initialize with same color across vertices, but we will change the per-vertex colors later
offset=(0, 10, 0)
)

Expand Down
1 change: 1 addition & 0 deletions examples/line_collection/line_collection_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
multi_data,
thickness=[2, 10, 2, 5, 5, 5, 8, 8, 8, 9, 3, 3, 3, 4, 4],
separation=4,
color_mode="vertex", # this will allow us to set per-vertex colors on each line
metadatas=list(range(15)), # some metadata
names=list("abcdefghijklmno"), # unique name for each line
)
Expand Down
1 change: 1 addition & 0 deletions examples/machine_learning/kmeans.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
sizes=5,
cmap="tab10", # use a qualitative cmap
cmap_transform=kmeans.labels_, # color by the predicted cluster
uniform_size=False,
)

# initial index
Expand Down
91 changes: 91 additions & 0 deletions examples/misc/buffer_replace_gc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
Buffer replacement garbage collection test
==========================================

This is an example that used for a manual test to ensure that GPU VRAM is free when buffers are replaced.

Use while monitoring VRAM usage with nvidia-smi
"""

# test_example = false
# sphinx_gallery_pygfx_docs = 'code'


from typing import Literal
import numpy as np
import fastplotlib as fpl
from fastplotlib.ui import EdgeWindow
from imgui_bundle import imgui


def generate_dataset(size: int) -> dict[str, np.ndarray]:
return {
"data": np.random.rand(size, 3),
"colors": np.random.rand(size, 4),
# TODO: there's a wgpu bind group issue with edge_colors, will figure out later
# "edge_colors": np.random.rand(size, 4),
"markers": np.random.choice(list("osD+x^v<>*"), size=size),
"sizes": np.random.rand(size) * 5,
"point_rotations": np.random.rand(size) * 180,
}


datasets = {
"init": generate_dataset(50_000),
"small": generate_dataset(100),
"large": generate_dataset(5_000_000),
}


class UI(EdgeWindow):
def __init__(self, figure):
super().__init__(figure=figure, size=200, location="right", title="UI")
init_data = datasets["init"]
self._figure["line"].add_line(
data=init_data["data"], colors=init_data["colors"], name="line"
)
self._figure["scatter"].add_scatter(
**init_data,
uniform_size=False,
uniform_marker=False,
uniform_edge_color=False,
point_rotation_mode="vertex",
name="scatter",
)

def update(self):
for graphic in ["line", "scatter"]:
if graphic == "line":
features = ["data", "colors"]

elif graphic == "scatter":
features = list(datasets["init"].keys())

for size in ["small", "large"]:
for fea in features:
if imgui.button(f"{size} - {graphic} - {fea}"):
self._replace(graphic, fea, size)

def _replace(
self,
graphic: Literal["line", "scatter", "image"],
feature: Literal["data", "colors", "markers", "sizes", "point_rotations"],
size: Literal["small", "large"],
):
new_value = datasets[size][feature]

setattr(self._figure[graphic][graphic], feature, new_value)


figure = fpl.Figure(shape=(3, 1), size=(700, 1600), names=["line", "scatter", "image"])
ui = UI(figure)
figure.add_gui(ui)

figure.show()


# NOTE: fpl.loop.run() should not be used for interactive sessions
# See the "JupyterLab and IPython" section in the user guide
if __name__ == "__main__":
print(__doc__)
fpl.loop.run()
7 changes: 6 additions & 1 deletion examples/misc/lorenz_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ def lorenz(xyz, *, s=10, r=28, b=2.667):
scatter_markers = list()

for graphic in lorenz_line:
marker = figure[0, 0].add_scatter(graphic.data.value[0], sizes=16, colors=graphic.colors[0])
marker = figure[0, 0].add_scatter(
graphic.data.value[0],
sizes=16,
colors=graphic.colors,
edge_colors="w",
)
scatter_markers.append(marker)

# initialize time
Expand Down
92 changes: 92 additions & 0 deletions examples/misc/reshape_lines_scatters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""
Change number of points in lines and scatters
=============================================

This example sets lines and scatters with new data of a different shape, i.e. new data with more or fewer datapoints.
Internally, this creates new buffers for the feature that is being set (data, colors, markers, etc.). Note that there
are performance drawbacks to doing this, so it is recommended to maintain the same number of datapoints in a graphic
when possible. You only want to change the number of datapoints when it's really necessary, and you don't want to do
it constantly (such as tens or hundreds of times per second).

This example is also useful for manually checking that GPU buffers are freed when they're no longer in use. Run this
example while monitoring VRAM usage with `nvidia-smi`
"""

# test_example = false
# sphinx_gallery_pygfx_docs = 'animate'


import numpy as np
import fastplotlib as fpl

# create some data to start with
xs = np.linspace(0, 10 * np.pi, 100)
ys = np.sin(xs)

data = np.column_stack([xs, ys])

# create a figure, add a line, scatter and line_stack
figure = fpl.Figure(shape=(3, 1), size=(700, 700))

line = figure[0, 0].add_line(data)

scatter = figure[1, 0].add_scatter(
np.random.rand(100, 3),
colors=np.random.rand(100, 4),
markers=np.random.choice(list("osD+x^v<>*"), size=100),
sizes=(np.random.rand(100) + 1) * 3,
edge_colors=np.random.rand(100, 4),
point_rotations=np.random.rand(100) * 180,
uniform_marker=False,
uniform_size=False,
uniform_edge_color=False,
point_rotation_mode="vertex",
)

line_stack = figure[2, 0].add_line_stack(np.stack([data] * 10), cmap="viridis")

text = figure[0, 0].add_text(f"n_points: {100}", offset=(0, 1.5, 0), anchor="middle-left")

figure.show(maintain_aspect=False)

i = 0


def update():
# set a new larger or smaller data array on every render
global i

# create new data
freq = np.abs(np.sin(i)) * 10
n_points = int((freq * 20_000) + 10)

xs = np.linspace(0, 10 * np.pi, n_points)
ys = np.sin(xs * freq)

new_data = np.column_stack([xs, ys])

# update line data
line.data = new_data

# update scatter data, colors, markers, etc.
scatter.data = np.random.rand(n_points, 3)
scatter.colors = np.random.rand(n_points, 4)
scatter.markers = np.random.choice(list("osD+x^v<>*"), size=n_points)
scatter.edge_colors = np.random.rand(n_points, 4)
scatter.point_rotations = np.random.rand(n_points) * 180

# update line stack data
line_stack.data = np.stack([new_data] * 10)

text.text = f"n_points: {n_points}"

i += 0.01


figure.add_animations(update)

# NOTE: fpl.loop.run() should not be used for interactive sessions
# See the "JupyterLab and IPython" section in the user guide
if __name__ == "__main__":
print(__doc__)
fpl.loop.run()
2 changes: 1 addition & 1 deletion examples/misc/scatter_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
figure = fpl.Figure(size=(700, 560))
subplot_scatter = figure[0, 0]
# use an alpha value since this will be a lot of points
scatter = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6)
scatter = subplot_scatter.add_scatter(data=cloud, sizes=3, uniform_size=False, colors=colors, alpha=0.6)


def update_points(subplot):
Expand Down
Loading
0