diff --git a/examples/gridplot.ipynb b/examples/gridplot.ipynb
index 2e558fa16..a5b7f4209 100644
--- a/examples/gridplot.ipynb
+++ b/examples/gridplot.ipynb
@@ -28,7 +28,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "8c47742795824d9e95c8d3b46df08a51",
+ "model_id": "cae156b0748142ff9335f4612d39e9ed",
"version_major": 2,
"version_minor": 0
},
@@ -42,7 +42,7 @@
{
"data": {
"text/html": [
- "

initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -54,7 +54,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "3d95102a394d4fbcb4d4bba9fa527089",
+ "model_id": "03295486e8ba449d80429f53832698e4",
"version_major": 2,
"version_minor": 0
},
@@ -90,7 +90,7 @@
"grid_plot = GridPlot(\n",
" shape=grid_shape,\n",
" controllers=controllers,\n",
- " names=names\n",
+ " names=names,\n",
")\n",
"\n",
"\n",
@@ -130,10 +130,10 @@
{
"data": {
"text/plain": [
- "subplot0: Subplot @ 0x7f418bbcaef0\n",
+ "subplot0: Subplot @ 0x7fbddc5bf9d0\n",
" parent: None\n",
" Graphics:\n",
- "\t'rand-image' fastplotlib.ImageGraphic @ 0x7f418bbcae90"
+ "\t'rand-image' fastplotlib.ImageGraphic @ 0x7fbddc5bf970"
]
},
"execution_count": 3,
@@ -155,10 +155,10 @@
{
"data": {
"text/plain": [
- "subplot0: Subplot @ 0x7f418bbcaef0\n",
+ "subplot0: Subplot @ 0x7fbddc5bf9d0\n",
" parent: None\n",
" Graphics:\n",
- "\t'rand-image' fastplotlib.ImageGraphic @ 0x7f418bbcae90"
+ "\t'rand-image' fastplotlib.ImageGraphic @ 0x7fbddc5bf970"
]
},
"execution_count": 4,
@@ -189,7 +189,7 @@
{
"data": {
"text/plain": [
- "'rand-image' fastplotlib.ImageGraphic @ 0x7f418bbcae90"
+ "'rand-image' fastplotlib.ImageGraphic @ 0x7fbddc5bf970"
]
},
"execution_count": 5,
@@ -209,7 +209,8 @@
"metadata": {},
"outputs": [],
"source": [
- "grid_plot[\"subplot0\"][\"rand-image\"].clim = (0.6, 0.8)"
+ "grid_plot[\"subplot0\"][\"rand-image\"].vmin = 0.6\n",
+ "grid_plot[\"subplot0\"][\"rand-image\"].vmax = 0.8"
]
},
{
@@ -222,17 +223,18 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 9,
"id": "2fafe992-4783-40f2-b044-26a2835dd50a",
"metadata": {},
"outputs": [],
"source": [
- "grid_plot[1, 0][\"rand-image\"].clim = (0.1, 0.3)"
+ "grid_plot[1, 0][\"rand-image\"].vim = 0.1\n",
+ "grid_plot[1, 0][\"rand-image\"].vmax = 0.3"
]
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 10,
"id": "a025b76c-77f8-4aeb-ac33-5bb6d0bb5a9a",
"metadata": {},
"outputs": [
@@ -242,7 +244,7 @@
"'image'"
]
},
- "execution_count": 8,
+ "execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
diff --git a/examples/simple.ipynb b/examples/simple.ipynb
index b1d3b88b5..b64aad56c 100644
--- a/examples/simple.ipynb
+++ b/examples/simple.ipynb
@@ -41,7 +41,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "d8de4d1574414bfcb186eaa62872c781",
+ "model_id": "a4fb8c6563f14824971deecd96965972",
"version_major": 2,
"version_minor": 0
},
@@ -55,7 +55,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -67,7 +67,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "5c46808362e840ad9cc50bfa2e5f7346",
+ "model_id": "95958dc9ae4e4bf18aa1d1f68ac667fb",
"version_major": 2,
"version_minor": 0
},
@@ -94,6 +94,16 @@
"plot.show()"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "de816c88-1c4a-4071-8a5e-c46c93671ef5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "image_graphic.cmap = \"viridis\""
+ ]
+ },
{
"cell_type": "markdown",
"id": "be5b408f-dd91-4e36-807a-8c22c8d7d216",
@@ -112,17 +122,17 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 4,
"id": "e6ba689c-ff4a-44ef-9663-f2c8755072c4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "['random-image' fastplotlib.ImageGraphic @ 0x7fa21d5dd1b0]"
+ "['random-image' fastplotlib.ImageGraphic @ 0x7f748162fd90]"
]
},
- "execution_count": 3,
+ "execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@@ -133,17 +143,17 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 5,
"id": "5b18f4e3-e13b-46d5-af1f-285c5a7fdc12",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "'random-image' fastplotlib.ImageGraphic @ 0x7faf6bd71600"
+ "'random-image' fastplotlib.ImageGraphic @ 0x7f748162fd90"
]
},
- "execution_count": 7,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@@ -162,17 +172,17 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 6,
"id": "2b5c1321-1fd4-44bc-9433-7439ad3e22cf",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "'random-image' fastplotlib.ImageGraphic @ 0x7fa21d5dd1b0"
+ "'random-image' fastplotlib.ImageGraphic @ 0x7f748162fd90"
]
},
- "execution_count": 4,
+ "execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@@ -183,7 +193,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 7,
"id": "b12bf75e-4e93-4930-9146-e96324fdf3f6",
"metadata": {},
"outputs": [
@@ -193,7 +203,7 @@
"True"
]
},
- "execution_count": 5,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -205,7 +215,9 @@
{
"cell_type": "markdown",
"id": "1cb03f42-1029-4b16-a16b-35447d9e2955",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
"### Image updates\n",
"\n",
@@ -214,14 +226,14 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 8,
"id": "aadd757f-6379-4f52-a709-46aa57c56216",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "f2ccae25614d47cda980e011f17771cb",
+ "model_id": "ab597f9780064497b7ab0fc8d52dd538",
"version_major": 2,
"version_minor": 0
},
@@ -235,7 +247,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -247,7 +259,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "33ca1052a871445188bbe7d3c8725cf1",
+ "model_id": "c75d9b5ae24b4c98b865ee7a869d665f",
"version_major": 2,
"version_minor": 0
},
@@ -255,7 +267,7 @@
"JupyterWgpuCanvas()"
]
},
- "execution_count": 6,
+ "execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -264,6 +276,8 @@
"# create another `Plot` instance\n",
"plot_v = Plot()\n",
"\n",
+ "plot.canvas.max_buffered_frames = 1\n",
+ "\n",
"# make some random data again\n",
"data = np.random.rand(512, 512)\n",
"\n",
@@ -295,14 +309,14 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 9,
"id": "86e70b1e-4328-4035-b992-70dff16d2a69",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "f486d69ebedb454ebc8d0fd164b3c04c",
+ "model_id": "36a85c99623947c9a3ed729b09f6b212",
"version_major": 2,
"version_minor": 0
},
@@ -316,7 +330,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -328,7 +342,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "c275d36864264618af7c9adde7ee5173",
+ "model_id": "692cfda570284fd0ae43f45530f87885",
"version_major": 2,
"version_minor": 0
},
@@ -336,7 +350,7 @@
"JupyterWgpuCanvas()"
]
},
- "execution_count": 7,
+ "execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -377,19 +391,19 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 10,
"id": "ef9743b3-5f81-4b79-9502-fa5fca08e56d",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "4dd8df34e05948638f5191c81735ddc8",
+ "model_id": "b9561a7e5aec4ad2b42b263c2fbdb87d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "VBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 819, 'timestamp': 1671971212.7522302, 'localtime': 1…"
+ "VBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 137, 'timestamp': 1671994231.3569584, 'localtime': 1…"
]
},
"metadata": {},
@@ -411,19 +425,19 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 11,
"id": "11839d95-8ff7-444c-ae13-6b072c3112c5",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "a7b4e59995ce420e8b983ae6f9b3d4d8",
+ "model_id": "70b73156a4dd4710858258eee985ecae",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "HBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 1004, 'timestamp': 1671971220.7120316, 'localtime': …"
+ "HBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 244, 'timestamp': 1671994235.1249204, 'localtime': 1…"
]
},
"metadata": {},
@@ -454,7 +468,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 12,
"id": "0bcedf83-cbdd-4ec2-b8d5-172aa72a3e04",
"metadata": {},
"outputs": [],
@@ -529,7 +543,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 13,
"id": "8b560151-c258-415c-a20d-3cccd421f44a",
"metadata": {},
"outputs": [
@@ -539,7 +553,7 @@
"(1000, 512, 512)"
]
},
- "execution_count": 11,
+ "execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@@ -566,14 +580,14 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 14,
"id": "62166a9f-ab43-45cc-a6db-6d441387e9a5",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "f4b20b310229436889a80e9676e36865",
+ "model_id": "bbc9fc354724480898744eefc88ab995",
"version_major": 2,
"version_minor": 0
},
@@ -587,7 +601,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "5b1232536af6482e926793142c22aa3b",
+ "model_id": "0462fb2ba19a42c587111e7652d4343c",
"version_major": 2,
"version_minor": 0
},
@@ -633,7 +647,9 @@
{
"cell_type": "markdown",
"id": "e7859338-8162-408b-ac72-37e606057045",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
"# Line plots\n",
"\n",
@@ -642,6 +658,18 @@
"This example plots a sine wave, cosine wave, and ricker wavelet and demonstrates how **Graphic Features** can be modified by slicing!"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "b69b8edd-f87f-406d-af56-e851d4fc6e77",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fastplotlib import Plot\n",
+ "from ipywidgets import VBox, HBox, IntSlider\n",
+ "import numpy as np"
+ ]
+ },
{
"cell_type": "markdown",
"id": "a6fee1c2-4a24-4325-bca2-26e5a4bf6338",
@@ -652,7 +680,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 16,
"id": "8e8280da-b421-43a5-a1a6-2a196a408e9a",
"metadata": {},
"outputs": [],
@@ -670,7 +698,7 @@
"# sinc function\n",
"a = 0.5\n",
"ys = np.sinc(xs) * 3 + 8\n",
- "sinc = np.dstack([xs, ys])[0]\n"
+ "sinc = np.dstack([xs, ys])[0]"
]
},
{
@@ -683,14 +711,14 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 17,
"id": "93a5d1e6-d019-4dd0-a0d1-25d1704ab7a7",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "d81c444c49124d7d9bbb88a9c64690fa",
+ "model_id": "380434748ed44486979846c314606408",
"version_major": 2,
"version_minor": 0
},
@@ -704,7 +732,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -716,7 +744,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "85e6e88bd68b4255b190bf7563ad77e4",
+ "model_id": "f1cab77b2f60497ab52fbf19764146ad",
"version_major": 2,
"version_minor": 0
},
@@ -724,7 +752,7 @@
"JupyterWgpuCanvas()"
]
},
- "execution_count": 14,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -734,7 +762,7 @@
"plot_l = Plot()\n",
"\n",
"# plot sine wave, use a single color\n",
- "sine_graphic = plot_l.add_line(data=sine, size=1.5, colors=\"magenta\")\n",
+ "sine_graphic = plot_l.add_line(data=sine, size=5, colors=\"magenta\")\n",
"\n",
"# you can also use colormaps for lines!\n",
"cosine_graphic = plot_l.add_line(data=cosine, size=12, cmap=\"autumn\")\n",
@@ -756,17 +784,21 @@
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 18,
"id": "cb0d13ed-ef07-46ff-b19e-eeca4c831037",
"metadata": {},
"outputs": [],
"source": [
- "# fancy indexing of colors\n",
+ "# indexing of colors\n",
"cosine_graphic.colors[:15] = \"magenta\"\n",
"cosine_graphic.colors[90:] = \"red\"\n",
"cosine_graphic.colors[60] = \"w\"\n",
"\n",
- "# more complex indexing, set the blue value from an array\n",
+ "# indexing to assign colormaps to entire lines or segments\n",
+ "sinc_graphic.cmap[10:50] = \"gray\"\n",
+ "sine_graphic.cmap = \"seismic\"\n",
+ "\n",
+ "# more complex indexing, set the blue value directly from an array\n",
"cosine_graphic.colors[65:90, 0] = np.linspace(0, 1, 90-65)"
]
},
@@ -775,12 +807,12 @@
"id": "c9689887-cdf3-4a4d-948f-7efdb09bde4e",
"metadata": {},
"source": [
- "## You can capture changes to a graphic features as events"
+ "## You can capture changes to a graphic feature as events"
]
},
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 19,
"id": "cfa001f6-c640-4f91-beb0-c19b030e503f",
"metadata": {},
"outputs": [],
@@ -794,7 +826,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 20,
"id": "bb8a0f95-0063-4cd4-a117-e3d62c6e120d",
"metadata": {},
"outputs": [
@@ -802,9 +834,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "FeatureEvent @ 0x7fa1f3a9ac50\n",
+ "FeatureEvent @ 0x7f7429bca830\n",
"type: color-changed\n",
- "pick_info: {'index': range(15, 50, 3), 'world_object': , 'new_data': array([[0., 1., 1., 1.],\n",
+ "pick_info: {'index': range(15, 50, 3), 'world_object': , 'new_data': array([[0., 1., 1., 1.],\n",
" [0., 1., 1., 1.],\n",
" [0., 1., 1., 1.],\n",
" [0., 1., 1., 1.],\n",
@@ -836,7 +868,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 21,
"id": "d1a4314b-5723-43c7-94a0-b4cbb0e44d60",
"metadata": {},
"outputs": [],
@@ -847,7 +879,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 22,
"id": "682db47b-8c7a-4934-9be4-2067e9fb12d5",
"metadata": {},
"outputs": [],
@@ -865,7 +897,7 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 23,
"id": "fcba75b7-9a1e-4aae-9dec-715f7f7456c3",
"metadata": {},
"outputs": [],
@@ -875,7 +907,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 24,
"id": "763b9943-53a4-4e2a-b47a-4e9e5c9d7be3",
"metadata": {},
"outputs": [],
@@ -893,7 +925,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 25,
"id": "64a20a16-75a5-4772-a849-630ade9be4ff",
"metadata": {},
"outputs": [],
@@ -903,7 +935,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 26,
"id": "fb093046-c94c-4085-86b4-8cd85cb638ff",
"metadata": {},
"outputs": [],
@@ -913,7 +945,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 27,
"id": "f05981c3-c768-4631-ae62-6a8407b20c4c",
"metadata": {},
"outputs": [],
@@ -931,14 +963,14 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 28,
"id": "9c51229f-13a2-4653-bff3-15d43ddbca7b",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "ee8725a8c6944a799eac247a98737688",
+ "model_id": "8462601c909049dda4e8fdcbb526c6f6",
"version_major": 2,
"version_minor": 0
},
@@ -960,7 +992,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -972,7 +1004,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "d1595cc3616e4018902c09e65cd99f40",
+ "model_id": "8ae62d829add48c1b5f26f9633b5b0ed",
"version_major": 2,
"version_minor": 0
},
@@ -980,7 +1012,7 @@
"JupyterWgpuCanvas()"
]
},
- "execution_count": 25,
+ "execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
@@ -1019,14 +1051,26 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 29,
+ "id": "2ecb2385-8fa4-4239-881c-b754c24aed9f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fastplotlib import Plot\n",
+ "from ipywidgets import VBox, HBox, IntSlider\n",
+ "import numpy as np"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
"id": "39252df5-9ae5-4132-b97b-2785c5fa92ea",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "9a091850b30e49b7b04153d6604176aa",
+ "model_id": "50c0b259d1f04e239dfbf733463fac3e",
"version_major": 2,
"version_minor": 0
},
@@ -1040,7 +1084,7 @@
{
"data": {
"text/html": [
- "
initial snapshot
"
+ "
initial snapshot
"
],
"text/plain": [
""
@@ -1052,7 +1096,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "cc57fd6c0807411bbde07b968fe7deef",
+ "model_id": "c138433d6890443faeab644cc5521b0d",
"version_major": 2,
"version_minor": 0
},
@@ -1060,7 +1104,7 @@
"JupyterWgpuCanvas()"
]
},
- "execution_count": 26,
+ "execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
@@ -1111,7 +1155,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 31,
"id": "8fa46ec0-8680-44f5-894c-559de3145932",
"metadata": {},
"outputs": [],
@@ -1122,7 +1166,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 32,
"id": "e4dc71e4-5144-436f-a464-f2a29eee8f0b",
"metadata": {},
"outputs": [],
@@ -1133,7 +1177,7 @@
},
{
"cell_type": "code",
- "execution_count": 29,
+ "execution_count": 33,
"id": "5b637a29-cd5e-4011-ab81-3f91490d9ecd",
"metadata": {},
"outputs": [],
@@ -1144,7 +1188,7 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 34,
"id": "a4084fce-78a2-48b3-9a0d-7b57c165c3c1",
"metadata": {},
"outputs": [],
@@ -1155,7 +1199,7 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 35,
"id": "f486083e-7c58-4255-ae1a-3fe5d9bfaeed",
"metadata": {},
"outputs": [],
@@ -1176,19 +1220,19 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 36,
"id": "f404a5ea-633b-43f5-87d1-237017bbca2a",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "62aa7a1eb8794301912c4f2da74abb25",
+ "model_id": "38cd7d8eacf3493c9bddd01ee3ec40f4",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "VBox(children=(HBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 2402, 'timestamp': 1671971185.3935852…"
+ "VBox(children=(HBox(children=(JupyterWgpuCanvas(frame_feedback={'index': 123, 'timestamp': 1671994226.4736164,…"
]
},
"metadata": {},
diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py
index a1a2633b9..3cf358451 100644
--- a/fastplotlib/graphics/_base.py
+++ b/fastplotlib/graphics/_base.py
@@ -1,63 +1,33 @@
from typing import *
-import pygfx
+from pygfx import WorldObject
+from pygfx.linalg import Vector3
-from ..utils import get_colors
-from .features import GraphicFeature, DataFeature, ColorFeature, PresentFeature
+from .features import GraphicFeature, PresentFeature
-class Graphic:
+class BaseGraphic:
+ def __init_subclass__(cls, **kwargs):
+ """set the type of the graphic in lower case like "image", "line_collection", etc."""
+ cls.type = cls.__name__.lower().replace("graphic", "").replace("collection", "_collection")
+ super().__init_subclass__(**kwargs)
+
+
+class Graphic(BaseGraphic):
def __init__(
self,
- data,
- colors: Any = False,
- n_colors: int = None,
- cmap: str = None,
- alpha: float = 1.0,
name: str = None
):
"""
Parameters
----------
- data: array-like
- data to show in the graphic, must be float32.
- Automatically converted to float32 for numpy arrays.
- Tensorflow Tensors also work but this is not fully
- tested and might not be supported in the future.
-
- colors: Any
- if ``False``, no color generation is performed, cmap is also ignored.
-
- n_colors
-
- cmap: str
- name of colormap to use
-
- alpha: float, optional
- alpha value for the colors
-
name: str, optional
name this graphic, makes it indexable within plots
"""
- # self.data = data.astype(np.float32)
- self.data = DataFeature(parent=self, data=data, graphic_name=self.__class__.__name__)
- self.colors = None
self.name = name
-
- if n_colors is None:
- n_colors = self.data.feature_data.shape[0]
-
- if cmap is not None and colors is not False:
- colors = get_colors(n_colors=n_colors, cmap=cmap, alpha=alpha)
-
- if colors is not False:
- self.colors = ColorFeature(parent=self, colors=colors, n_colors=n_colors, alpha=alpha)
-
- # different from visible, toggles the Graphic presence in the Scene
- # useful for bbox calculations to ignore these Graphics
self.present = PresentFeature(parent=self)
valid_features = ["visible"]
@@ -69,9 +39,14 @@ def __init__(
self._valid_features = tuple(valid_features)
@property
- def world_object(self) -> pygfx.WorldObject:
+ def world_object(self) -> WorldObject:
return self._world_object
+ @property
+ def position(self) -> Vector3:
+ """The position of the graphic"""
+ return self.world_object.position
+
@property
def interact_features(self) -> Tuple[str]:
"""The features for this ``Graphic`` that support interaction."""
@@ -87,7 +62,7 @@ def visible(self, v):
self.world_object.visible = v
@property
- def children(self) -> pygfx.WorldObject:
+ def children(self) -> WorldObject:
return self.world_object.children
def __setattr__(self, key, value):
diff --git a/fastplotlib/graphics/features/__init__.py b/fastplotlib/graphics/features/__init__.py
index 2c489c94f..1fcb71246 100644
--- a/fastplotlib/graphics/features/__init__.py
+++ b/fastplotlib/graphics/features/__init__.py
@@ -1,4 +1,4 @@
-from ._colors import ColorFeature
-from ._data import DataFeature
+from ._colors import ColorFeature, CmapFeature, ImageCmapFeature
+from ._data import PointsDataFeature, ImageDataFeature
from ._present import PresentFeature
from ._base import GraphicFeature
diff --git a/fastplotlib/graphics/features/_base.py b/fastplotlib/graphics/features/_base.py
index 9292f4944..519bf40d0 100644
--- a/fastplotlib/graphics/features/_base.py
+++ b/fastplotlib/graphics/features/_base.py
@@ -90,6 +90,22 @@ def _call_event_handlers(self, event_data: FeatureEvent):
def cleanup_slice(key: Union[int, slice], upper_bound) -> Union[slice, int]:
+ """
+
+ If the key in an `int`, it just returns it. Otherwise,
+ it parses it and removes the `None` vals and replaces
+ them with corresponding values that can be used to
+ create a `range`, get `len` etc.
+
+ Parameters
+ ----------
+ key
+ upper_bound
+
+ Returns
+ -------
+
+ """
if isinstance(key, int):
return key
@@ -157,7 +173,7 @@ def _upper_bound(self) -> int:
return self.feature_data.shape[0]
def _update_range_indices(self, key):
- """Currently used by colors and data"""
+ """Currently used by colors and positions data"""
key = cleanup_slice(key, self._upper_bound)
if isinstance(key, int):
@@ -178,5 +194,3 @@ def _update_range_indices(self, key):
self._buffer.update_range(ix, size=1)
else:
raise TypeError("must pass int or slice to update range")
-
-
diff --git a/fastplotlib/graphics/features/_colors.py b/fastplotlib/graphics/features/_colors.py
index f45f99040..afb0d85a8 100644
--- a/fastplotlib/graphics/features/_colors.py
+++ b/fastplotlib/graphics/features/_colors.py
@@ -1,6 +1,7 @@
import numpy as np
-from ._base import GraphicFeatureIndexable, cleanup_slice, FeatureEvent
+from ._base import GraphicFeature, GraphicFeatureIndexable, cleanup_slice, FeatureEvent
+from ...utils import get_colors, get_cmap_texture
from pygfx import Color
@@ -15,7 +16,7 @@ def __getitem__(self, item):
def __repr__(self):
return repr(self._buffer.data)
- def __init__(self, parent, colors, n_colors, alpha: float = 1.0):
+ def __init__(self, parent, colors, n_colors: int, alpha: float = 1.0):
"""
ColorFeature
@@ -27,7 +28,12 @@ def __init__(self, parent, colors, n_colors, alpha: float = 1.0):
specify colors as a single human readable string, RGBA array,
or an iterable of strings or RGBA arrays
- n_colors: number of colors to hold, if passing in a single str or single RGBA array
+ n_colors: int
+ number of colors to hold, if passing in a single str or single RGBA array
+
+ alpha: float
+ alpha value for the colors
+
"""
# if provided as a numpy array of str
if isinstance(colors, np.ndarray):
@@ -185,3 +191,55 @@ def _feature_changed(self, key, new_data):
event_data = FeatureEvent(type="color-changed", pick_info=pick_info)
self._call_event_handlers(event_data)
+
+
+class CmapFeature(ColorFeature):
+ """
+ Indexable colormap feature, mostly wraps colors and just provides a way to set colormaps.
+ """
+ def __init__(self, parent, colors):
+ super(ColorFeature, self).__init__(parent, colors)
+
+ def __setitem__(self, key, value):
+ key = cleanup_slice(key, self._upper_bound)
+ if not isinstance(key, slice):
+ raise TypeError("Cannot set cmap on single indices, must pass a slice object or "
+ "set it on the entire data.")
+
+ n_colors = len(range(key.start, key.stop, key.step))
+
+ colors = get_colors(n_colors, cmap=value)
+ super(CmapFeature, self).__setitem__(key, colors)
+
+
+class ImageCmapFeature(GraphicFeature):
+ """
+ Colormap for ImageGraphic
+ """
+ def __init__(self, parent, cmap: str):
+ cmap_texture_view = get_cmap_texture(cmap)
+ super(ImageCmapFeature, self).__init__(parent, cmap_texture_view)
+ self.name = cmap
+
+ def _set(self, cmap_name: str):
+ self._parent.world_object.material.map.texture.data[:] = get_colors(256, cmap_name)
+ self._parent.world_object.material.map.texture.update_range((0, 0, 0), size=(256, 1, 1))
+ self.name = cmap_name
+
+ self._feature_changed(key=None, new_data=self.name)
+
+ def __repr__(self):
+ return repr(self.name)
+
+ def _feature_changed(self, key, new_data):
+ # this is a non-indexable feature so key=None
+
+ pick_info = {
+ "index": None,
+ "world_object": self._parent.world_object,
+ "new_data": new_data
+ }
+
+ event_data = FeatureEvent(type="cmap-changed", pick_info=pick_info)
+
+ self._call_event_handlers(event_data)
diff --git a/fastplotlib/graphics/features/_data.py b/fastplotlib/graphics/features/_data.py
index 6e1feac2a..1839bd9a1 100644
--- a/fastplotlib/graphics/features/_data.py
+++ b/fastplotlib/graphics/features/_data.py
@@ -1,32 +1,30 @@
-from ._base import GraphicFeatureIndexable, cleanup_slice, FeatureEvent
-from pygfx import Buffer
from typing import *
-from ...utils import fix_data, to_float32
+
+import numpy as np
+from pygfx import Buffer, Texture
+
+from ._base import GraphicFeatureIndexable, cleanup_slice, FeatureEvent
-class DataFeature(GraphicFeatureIndexable):
+def to_float32(array):
+ if isinstance(array, np.ndarray):
+ return array.astype(np.float32, copy=False)
+
+ return array
+
+
+class PointsDataFeature(GraphicFeatureIndexable):
"""
- Access to the buffer data being shown in the graphic.
- Supports fancy indexing if the data array also does.
+ Access to the vertex buffer data shown in the graphic.
+ Supports fancy indexing if the data array also supports it.
"""
- # the correct data buffer is search for in this order
- data_buffer_names = ["grid", "positions"]
-
- def __init__(self, parent, data: Any, graphic_name):
- data = fix_data(data, graphic_name=graphic_name)
- self.graphic_name = graphic_name
- super(DataFeature, self).__init__(parent, data)
+ def __init__(self, parent, data: Any):
+ data = self._fix_data(data, parent)
+ super(PointsDataFeature, self).__init__(parent, data)
@property
def _buffer(self) -> Buffer:
- buffer = getattr(self._parent.world_object.geometry, self._buffer_name)
- return buffer
-
- @property
- def _buffer_name(self) -> str:
- for buffer_name in self.data_buffer_names:
- if hasattr(self._parent.world_object.geometry, buffer_name):
- return buffer_name
+ return self._parent.world_object.geometry.positions
def __repr__(self):
return repr(self._buffer.data)
@@ -34,31 +32,103 @@ def __repr__(self):
def __getitem__(self, item):
return self._buffer.data[item]
+ def _fix_data(self, data, parent):
+ graphic_type = parent.__class__.__name__
+
+ if data.ndim == 1:
+ # for scatter if we receive just 3 points in a 1d array, treat it as just a single datapoint
+ # this is different from fix_data for LineGraphic since there we assume that a 1d array
+ # is just y-values
+ if graphic_type == "ScatterGraphic":
+ data = np.array([data])
+ elif graphic_type == "LineGraphic":
+ data = np.dstack([np.arange(data.size), data])[0].astype(np.float32)
+
+ if data.shape[1] != 3:
+ if data.shape[1] != 2:
+ raise ValueError(f"Must pass 1D, 2D or 3D data to {graphic_type}")
+
+ # zeros for z
+ zs = np.zeros(data.shape[0], dtype=np.float32)
+
+ data = np.dstack([data[:, 0], data[:, 1], zs])[0]
+
+ return data
+
def __setitem__(self, key, value):
+ # put data into right shape if they're only indexing datapoints
if isinstance(key, (slice, int)):
- # data must be provided in the right shape
- value = fix_data(value, graphic_name=self.graphic_name)
- else:
- # otherwise just make sure float32
- value = to_float32(value)
+ value = self._fix_data(value, self._parent)
+ # otherwise assume that they have the right shape
+ # numpy will throw errors if it can't broadcast
+
self._buffer.data[key] = value
self._update_range(key)
+ # avoid creating dicts constantly if there are no events to handle
+ if len(self._event_handlers) > 0:
+ self._feature_changed(key, value)
def _update_range(self, key):
- if self._buffer_name == "grid":
- self._update_range_grid(key)
- self._feature_changed(key=None, new_data=None)
- elif self._buffer_name == "positions":
- self._update_range_indices(key)
- self._feature_changed(key=key, new_data=None)
+ self._update_range_indices(key)
+
+ def _feature_changed(self, key, new_data):
+ if key is not None:
+ key = cleanup_slice(key, self._upper_bound)
+ if isinstance(key, int):
+ indices = [key]
+ elif isinstance(key, slice):
+ indices = range(key.start, key.stop, key.step)
+ elif key is None:
+ indices = None
+
+ pick_info = {
+ "index": indices,
+ "world_object": self._parent.world_object,
+ "new_data": new_data
+ }
+
+ event_data = FeatureEvent(type="data-changed", pick_info=pick_info)
+
+ self._call_event_handlers(event_data)
- def _update_range_grid(self, key):
- # image data
- self._buffer.update_range((0, 0, 0), self._buffer.size)
+
+class ImageDataFeature(GraphicFeatureIndexable):
+ """
+ Access to the TextureView buffer shown in an ImageGraphic.
+ """
+
+ def __init__(self, parent, data: Any):
+ if data.ndim != 2:
+ raise ValueError("`data.ndim !=2`, you must pass only a 2D array to an Image graphic")
+
+ data = to_float32(data)
+ super(ImageDataFeature, self).__init__(parent, data)
+
+ @property
+ def _buffer(self) -> Texture:
+ return self._parent.world_object.geometry.grid.texture
+
+ def __repr__(self):
+ return repr(self._buffer.data)
+
+ def __getitem__(self, item):
+ return self._buffer.data[item]
+
+ def __setitem__(self, key, value):
+ # make sure float32
+ value = to_float32(value)
+
+ self._buffer.data[key] = value
+ self._update_range(key)
+
+ # avoid creating dicts constantly if there are no events to handle
+ if len(self._event_handlers) > 0:
+ self._feature_changed(key, value)
+
+ def _update_range(self, key):
+ self._buffer.update_range((0, 0, 0), size=self._buffer.size)
def _feature_changed(self, key, new_data):
- # for now if key=None that means all data changed, i.e. ImageGraphic
- # also for now new data isn't stored for DataFeature
if key is not None:
key = cleanup_slice(key, self._upper_bound)
if isinstance(key, int):
diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py
index 77c531c8a..48459c63e 100644
--- a/fastplotlib/graphics/image.py
+++ b/fastplotlib/graphics/image.py
@@ -1,10 +1,10 @@
from typing import *
-import numpy as np
import pygfx
from ._base import Graphic
-from ..utils import quick_min_max, get_cmap_texture
+from .features import ImageCmapFeature, ImageDataFeature
+from ..utils import quick_min_max
class ImageGraphic(Graphic):
@@ -14,6 +14,7 @@ def __init__(
vmin: int = None,
vmax: int = None,
cmap: str = 'plasma',
+ filter: str = "nearest",
*args,
**kwargs
):
@@ -32,10 +33,15 @@ def __init__(
vmax: int, optional
maximum value for color scaling, calculated from data if not provided
- cmap: str, optional
+ cmap: str, optional, default "nearest"
colormap to use to display the image data, default is ``"plasma"``
+
+ filter: str, optional, default "nearest"
+ interpolation filter, one of "nearest" or "linear"
+
args:
additional arguments passed to Graphic
+
kwargs:
additional keyword arguments passed to Graphic
@@ -59,23 +65,41 @@ def __init__(
plot.show()
"""
- if data.ndim != 2:
- raise ValueError("`data.ndim !=2`, you must pass only a 2D array to `data`")
- super().__init__(data, cmap=cmap, *args, **kwargs)
+ super().__init__(*args, **kwargs)
+
+ self.data = ImageDataFeature(self, data)
if (vmin is None) or (vmax is None):
vmin, vmax = quick_min_max(data)
+ self.cmap = ImageCmapFeature(self, cmap)
+
+ texture_view = pygfx.Texture(self.data.feature_data, dim=2).get_view(filter=filter)
+
self._world_object: pygfx.Image = pygfx.Image(
- pygfx.Geometry(grid=pygfx.Texture(self.data.feature_data, dim=2)),
- pygfx.ImageBasicMaterial(clim=(vmin, vmax), map=get_cmap_texture(cmap))
+ pygfx.Geometry(grid=texture_view),
+ pygfx.ImageBasicMaterial(clim=(vmin, vmax), map=self.cmap.feature_data)
)
@property
- def clim(self) -> Tuple[float, float]:
- return self.world_object.material.clim
+ def vmin(self) -> float:
+ return self.world_object.material.clim[0]
+
+ @vmin.setter
+ def vmin(self, value: float):
+ self.world_object.material.clim = (
+ value,
+ self.world_object.material.clim[1]
+ )
- @clim.setter
- def clim(self, levels: Tuple[float, float]):
- self.world_object.material.clim = levels
+ @property
+ def vmax(self) -> float:
+ return self.world_object.material.clim[1]
+
+ @vmax.setter
+ def vmax(self, value: float):
+ self.world_object.material.clim = (
+ self.world_object.material.clim[0],
+ value
+ )
diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py
index edf99e43c..e53eb9203 100644
--- a/fastplotlib/graphics/line.py
+++ b/fastplotlib/graphics/line.py
@@ -3,16 +3,19 @@
import pygfx
from ._base import Graphic
+from .features import PointsDataFeature, ColorFeature, CmapFeature
+from ..utils import get_colors
class LineGraphic(Graphic):
def __init__(
self,
data: Any,
- z_position: float = 0.0,
size: float = 2.0,
colors: Union[str, np.ndarray, Iterable] = "w",
+ alpha: float = 1.0,
cmap: str = None,
+ z_position: float = 0.0,
*args,
**kwargs
):
@@ -24,34 +27,46 @@ def __init__(
data: array-like
Line data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3]
- z_position: float, optional
- z-axis position for placing the graphic
-
- size: float, optional
+ size: float, optional, default 2.0
thickness of the line
- colors: str, array, or iterable
+ colors: str, array, or iterable, default "w"
specify colors as a single human readable string, a single RGBA array,
or an iterable of strings or RGBA arrays
cmap: str, optional
- apply a colormap to the line instead of assigning colors manually
+ apply a colormap to the line instead of assigning colors manually, this
+ overrides any argument passed to "colors"
+
+ alpha: float, optional, default 1.0
+ alpha value for the colors
+
+ z_position: float, optional
+ z-axis position for placing the graphic
args
passed to Graphic
+
kwargs
passed to Graphic
+
"""
- super(LineGraphic, self).__init__(data, colors=colors, cmap=cmap, *args, **kwargs)
+ self.data = PointsDataFeature(self, data)
+
+ if cmap is not None:
+ colors = get_colors(n_colors=self.data.feature_data.shape[0], cmap=cmap, alpha=alpha)
+
+ self.colors = ColorFeature(self, colors, n_colors=self.data.feature_data.shape[0], alpha=alpha)
+ self.cmap = CmapFeature(self, self.colors.feature_data)
+
+ super(LineGraphic, self).__init__(*args, **kwargs)
if size < 1.1:
material = pygfx.LineThinMaterial
else:
material = pygfx.LineMaterial
- # self.data = np.ascontiguousarray(self.data)
-
self._world_object: pygfx.Line = pygfx.Line(
# self.data.feature_data because data is a Buffer
geometry=pygfx.Geometry(positions=self.data.feature_data, colors=self.colors.feature_data),
diff --git a/fastplotlib/graphics/linecollection.py b/fastplotlib/graphics/linecollection.py
index ec4b1e4dd..cfdf5389f 100644
--- a/fastplotlib/graphics/linecollection.py
+++ b/fastplotlib/graphics/linecollection.py
@@ -1,11 +1,12 @@
import numpy as np
import pygfx
from typing import Union
+from ._base import BaseGraphic
from .line import LineGraphic
from typing import *
-class LineCollection():
+class LineCollection(BaseGraphic):
def __init__(self, data: List[np.ndarray], z_position: Union[List[float], float] = None, size: Union[float, List[float]] = 2.0, colors: Union[List[np.ndarray], np.ndarray] = None,
cmap: Union[List[str], str] = None, *args, **kwargs):
diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py
index 0ea5a8831..a1083d132 100644
--- a/fastplotlib/graphics/scatter.py
+++ b/fastplotlib/graphics/scatter.py
@@ -4,11 +4,61 @@
import pygfx
from ._base import Graphic
+from .features import PointsDataFeature, ColorFeature, CmapFeature
+from ..utils import get_colors
class ScatterGraphic(Graphic):
- def __init__(self, data: np.ndarray, z_position: float = 0.0, sizes: Union[int, np.ndarray, list] = 1, colors: np.ndarray = "w", cmap: str = None, *args, **kwargs):
- super(ScatterGraphic, self).__init__(data, colors=colors, cmap=cmap, *args, **kwargs)
+ def __init__(
+ self,
+ data: np.ndarray,
+ sizes: Union[int, np.ndarray, list] = 1,
+ colors: np.ndarray = "w",
+ alpha: float = 1.0,
+ cmap: str = None,
+ z_position: float = 0.0,
+ *args,
+ **kwargs
+ ):
+ """
+ Create a Scatter Graphic, 2d or 3d
+
+ Parameters
+ ----------
+ data: array-like
+ Scatter data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3]
+
+ sizes: float or iterable of float, optional, default 1.0
+ size of the scatter points
+
+ colors: str, array, or iterable, default "w"
+ specify colors as a single human readable string, a single RGBA array,
+ or an iterable of strings or RGBA arrays
+
+ cmap: str, optional
+ apply a colormap to the scatter instead of assigning colors manually, this
+ overrides any argument passed to "colors"
+
+ alpha: float, optional, default 1.0
+ alpha value for the colors
+
+ z_position: float, optional
+ z-axis position for placing the graphic
+
+ args
+ passed to Graphic
+
+ kwargs
+ passed to Graphic
+
+ """
+ self.data = PointsDataFeature(self, data)
+
+ if cmap is not None:
+ colors = get_colors(n_colors=self.data.feature_data.shape[0], cmap=cmap, alpha=alpha)
+
+ self.colors = ColorFeature(self, colors, n_colors=self.data.feature_data.shape[0], alpha=alpha)
+ self.cmap = CmapFeature(self, self.colors.feature_data)
if isinstance(sizes, int):
sizes = np.full(self.data.feature_data.shape[0], sizes, dtype=np.float32)
@@ -20,6 +70,8 @@ def __init__(self, data: np.ndarray, z_position: float = 0.0, sizes: Union[int,
if len(sizes) != self.data.feature_data.shape[0]:
raise ValueError("list of `sizes` must have the same length as the number of datapoints")
+ super(ScatterGraphic, self).__init__(*args, **kwargs)
+
self._world_object: pygfx.Points = pygfx.Points(
pygfx.Geometry(positions=self.data.feature_data, sizes=sizes, colors=self.colors.feature_data),
material=pygfx.PointsMaterial(vertex_colors=True, vertex_sizes=True)
diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py
index ed47bd270..0096e102c 100644
--- a/fastplotlib/graphics/text.py
+++ b/fastplotlib/graphics/text.py
@@ -2,8 +2,10 @@
import pygfx
import numpy as np
+from ._base import BaseGraphic
-class TextGraphic:
+
+class TextGraphic(BaseGraphic):
def __init__(
self,
text: str,
diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py
index c8e8d2b23..7a0e923de 100644
--- a/fastplotlib/layouts/_subplot.py
+++ b/fastplotlib/layouts/_subplot.py
@@ -6,7 +6,7 @@
from warnings import warn
from pygfx import Scene, OrthographicCamera, PanZoomController, OrbitOrthoController, \
- AxesHelper, GridHelper, WgpuRenderer, Background, BackgroundMaterial
+ AxesHelper, GridHelper, WgpuRenderer
from wgpu.gui.auto import WgpuCanvas
from ._base import PlotArea
@@ -82,8 +82,8 @@ def __init__(
pfunc.__signature__ = signature(cls)
pfunc.__doc__ = cls.__init__.__doc__
- graphic_cls_name = graphic_cls_name.lower().replace("graphic", "").replace("collection", "_collection")
- setattr(self, f"add_{graphic_cls_name}", pfunc)
+ # cls.type is defined in Graphic.__init_subclass__
+ setattr(self, f"add_{cls.type}", pfunc)
self._title_graphic: TextGraphic = None
if self.name is not None:
@@ -130,13 +130,13 @@ def get_rect(self):
x_pos = ((width_canvas / self.ncols) + ((col_ix - 1) * (width_canvas / self.ncols))) + self.spacing
y_pos = ((height_canvas / self.nrows) + ((row_ix - 1) * (height_canvas / self.nrows))) + self.spacing
width_subplot = (width_canvas / self.ncols) - self.spacing
- height_suplot = (height_canvas / self.nrows) - self.spacing
+ height_subplot = (height_canvas / self.nrows) - self.spacing
rect = np.array([
x_pos,
y_pos,
width_subplot,
- height_suplot
+ height_subplot
])
for dv in self.docked_viewports.values():
@@ -221,6 +221,7 @@ def remove_animation(self, func):
self._animate_funcs_post.remove(func)
def add_graphic(self, graphic, center: bool = True):
+ graphic.world_object.position.z = len(self._graphics)
super(Subplot, self).add_graphic(graphic, center)
if isinstance(graphic, graphics.HeatmapGraphic):
diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py
index 4df784b6e..698d20113 100644
--- a/fastplotlib/utils/functions.py
+++ b/fastplotlib/utils/functions.py
@@ -88,36 +88,3 @@ def quick_min_max(data: np.ndarray) -> Tuple[float, float]:
data = data[tuple(sl)]
return float(np.nanmin(data)), float(np.nanmax(data))
-
-
-def to_float32(array):
- if isinstance(array, np.ndarray):
- return array.astype(np.float32, copy=False)
-
- return array
-
-
-def fix_data(array, graphic_name: str) -> np.ndarray:
- """1d or 2d to 3d, cleanup data passed from user before instantiating any Graphic class"""
- if graphic_name == "ImageGraphic":
- return to_float32(array)
-
- if array.ndim == 1:
- # for scatter if we receive just 3 points in a 1d array, treat it as just a single datapoint
- # this is different from fix_data for LineGraphic since there we assume that a 1d array
- # is just y-values
- if graphic_name == "ScatterGraphic":
- array = np.array([array])
- elif graphic_name == "LineGraphic":
- array = np.dstack([np.arange(array.size), array])[0].astype(np.float32)
-
- if array.shape[1] != 3:
- if array.shape[1] != 2:
- raise ValueError(f"Must pass 1D, 2D or 3D data to {graphic_name}")
-
- # zeros for z
- zs = np.zeros(array.shape[0], dtype=np.float32)
-
- array = np.dstack([array[:, 0], array[:, 1], zs])[0]
-
- return array
diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py
index 5fba44d56..f8dc6c73f 100644
--- a/fastplotlib/widgets/image.py
+++ b/fastplotlib/widgets/image.py
@@ -472,8 +472,8 @@ def __init__(
max=minmax[1] + data_range_30p,
step=data_range / 150,
description=f"min-max",
- readout = True,
- readout_format = '.3f',
+ readout=True,
+ readout_format='.3f',
)
minmax_slider.observe(
@@ -779,7 +779,9 @@ def _vmin_vmax_slider_changed(
data_ix: int,
change: dict
):
- self.image_graphics[data_ix].clim = change["new"]
+ vmin, vmax = change["new"]
+ self.image_graphics[data_ix].vmin = vmin
+ self.image_graphics[data_ix].vmax = vmax
def _set_slider_layout(self, *args):
w, h = self.plot.renderer.logical_size
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
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:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy