{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%gui qt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Markers with Instanced Rendering\n\nCompare instanced and GL_POINTS rendering methods for Markers visual.\n\nThis example shows how instanced rendering works around platform point size limits\n(many platforms limit GL_POINTS to 64px or smaller) and demonstrates the\ncanvas_size_limits feature for constraining marker sizes during zoom/pan.\n\nControls:\n* m: Toggle between 'points' and 'instanced' rendering methods\n* s: Cycle through marker sizes\n* c: Toggle canvas size clamping (min 10px, max 100px)\n* z: Toggle scaling mode: 'fixed' vs 'scene' (grows/shrinks with zoom)\n* l: Toggle spherical lighting (3D sphere effect)\n* k: Cycle through marker shapes (disc, arrow, ring, etc.)\n\nNotes:\n\n* you may not see a difference between methods on your platform - that's fine!\n* 'instanced' method should be the same across platforms (arbitrarily large markers)\n* Canvas size clamping keeps markers readable during zoom\n* Spherical lighting adds depth to markers with simulated 3D lighting\n* There may be lighting direction differences between methods (known issue)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from itertools import cycle\n\nfrom vispy import scene, use\nfrom vispy.visuals.markers import symbol_shaders\n\nuse(gl=\"gl+\")\n\n\nmarker_sizes = cycle([16, 32, 64, 96, 128, 256, 512])\nscaling_modes = cycle([\"fixed\", \"scene\"])\nmarker_symbols = cycle(symbol_shaders.keys())\n\n\nclass Canvas(scene.SceneCanvas):\n def __init__(self):\n scene.SceneCanvas.__init__(\n self, keys=\"interactive\", size=(512, 512), title=\"Instanced Markers Demo\"\n )\n self.unfreeze()\n self.view = self.central_widget.add_view()\n self.view.camera = scene.PanZoomCamera(rect=(0, 0, 512, 512), aspect=1.0)\n\n self.marker_positions, self.face_colors = _create_markers_pattern()\n self.method = \"instanced\"\n self.current_size = next(marker_sizes)\n self.current_symbol = next(marker_symbols)\n self.clamping_enabled = False\n self.scaling_mode = next(scaling_modes)\n self.spherical_enabled = False\n self.markers = scene.visuals.Markers(\n method=self.method,\n parent=self.view.scene,\n scaling=self.scaling_mode,\n spherical=self.spherical_enabled,\n )\n\n self.freeze()\n\n self.markers.set_data(\n self.marker_positions,\n face_color=self.face_colors,\n edge_color=\"black\",\n size=self.current_size,\n edge_width=2,\n symbol=self.current_symbol,\n )\n\n self.view.bgcolor = \"#2e3440\"\n\n self.print_state()\n self.show()\n\n def print_state(self, changed=None):\n \"\"\"Print current state with optional highlighting of what changed.\"\"\"\n clamp_str = (\n f\"{self.markers.canvas_size_limits}\"\n if self.clamping_enabled\n else \"off\"\n )\n\n parts = {\n 'method': f\"method={self.method}\",\n 'symbol': f\"symbol={self.current_symbol}\",\n 'size': f\"size={self.current_size}px\",\n 'clamp': f\"clamp={clamp_str}\",\n 'scaling': f\"scaling={self.scaling_mode}\",\n 'lighting': f\"lighting={'on' if self.spherical_enabled else 'off'}\",\n }\n\n # highlight the changed part in bold\n if changed and changed in parts:\n parts[changed] = f\"\\033[1m{parts[changed]}\\033[0m\"\n\n state_line = \" | \".join(parts.values())\n state_line = state_line.ljust(120)\n print(f\"\\r{state_line}\", end=\"\", flush=True)\n\n def on_key_press(self, event):\n if event.text == \"m\":\n self.method = \"instanced\" if self.method == \"points\" else \"points\"\n\n # recreate markers with new method, cannot change method on the fly\n self.markers.parent = None\n self.markers = scene.visuals.Markers(\n method=self.method, parent=self.view.scene\n )\n self.markers.set_data(\n self.marker_positions,\n face_color=self.face_colors,\n edge_color=\"black\",\n size=self.current_size,\n edge_width=2,\n symbol=self.current_symbol,\n )\n self.markers.scaling = self.scaling_mode\n self.markers.spherical = self.spherical_enabled\n if self.clamping_enabled:\n self.markers.canvas_size_limits = (10, 100)\n\n self.print_state(changed='method')\n self.update()\n\n elif event.text == \"s\":\n self.current_size = next(marker_sizes)\n self.markers.set_data(\n self.marker_positions,\n face_color=self.face_colors,\n edge_color=\"black\",\n size=self.current_size,\n edge_width=2,\n symbol=self.current_symbol,\n )\n self.print_state(changed='size')\n self.update()\n\n elif event.text == \"c\":\n self.clamping_enabled = not self.clamping_enabled\n if self.clamping_enabled:\n self.markers.canvas_size_limits = (10, 100)\n else:\n self.markers.canvas_size_limits = None\n self.print_state(changed='clamp')\n self.update()\n\n elif event.text == \"z\":\n self.scaling_mode = next(scaling_modes)\n self.markers.scaling = self.scaling_mode\n self.print_state(changed='scaling')\n self.update()\n\n elif event.text == \"l\":\n self.spherical_enabled = not self.spherical_enabled\n self.markers.spherical = self.spherical_enabled\n self.print_state(changed='lighting')\n self.update()\n\n elif event.text == \"k\":\n self.current_symbol = next(marker_symbols)\n self.markers.symbol = self.current_symbol\n self.print_state(changed='symbol')\n self.update()\n\n\ndef _create_markers_pattern():\n import numpy as np\n\n # Create positions in a circle plus one in the center\n n = 12\n angles = np.linspace(0, 2 * np.pi, n, endpoint=False)\n radius = 150\n center = np.array([256, 256])\n pos = np.column_stack(\n [center[0] + radius * np.cos(angles), center[1] + radius * np.sin(angles)]\n ).astype(np.float32)\n\n pos = np.vstack([pos, center])\n\n colors = np.zeros((n + 1, 4), dtype=np.float32)\n for i in range(n):\n hue = i / n\n h = hue * 6\n x = 1 - abs(h % 2 - 1)\n if h < 1:\n colors[i] = [1, x, 0, 1]\n elif h < 2:\n colors[i] = [x, 1, 0, 1]\n elif h < 3:\n colors[i] = [0, 1, x, 1]\n elif h < 4:\n colors[i] = [0, x, 1, 1]\n elif h < 5:\n colors[i] = [x, 0, 1, 1]\n else:\n colors[i] = [1, 0, x, 1]\n colors[-1] = [1, 1, 1, 1]\n\n return pos, colors\n\n\nif __name__ == \"__main__\":\n from vispy import app\n print(__doc__)\n canvas = Canvas()\n app.run()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.23" } }, "nbformat": 4, "nbformat_minor": 0 }