1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
# vispy: gallery 2
"""
Markers with Instanced Rendering
================================
Compare instanced and GL_POINTS rendering methods for Markers visual.
This example shows how instanced rendering works around platform point size limits
(many platforms limit GL_POINTS to 64px or smaller) and demonstrates the
canvas_size_limits feature for constraining marker sizes during zoom/pan.
Controls:
* m: Toggle between 'points' and 'instanced' rendering methods
* s: Cycle through marker sizes
* c: Toggle canvas size clamping (min 10px, max 100px)
* z: Toggle scaling mode: 'fixed' vs 'scene' (grows/shrinks with zoom)
* l: Toggle spherical lighting (3D sphere effect)
* k: Cycle through marker shapes (disc, arrow, ring, etc.)
Notes:
* you may not see a difference between methods on your platform - that's fine!
* 'instanced' method should be the same across platforms (arbitrarily large markers)
* Canvas size clamping keeps markers readable during zoom
* Spherical lighting adds depth to markers with simulated 3D lighting
* There may be lighting direction differences between methods (known issue)
"""
from itertools import cycle
from vispy import scene, use
from vispy.visuals.markers import symbol_shaders
use(gl="gl+")
marker_sizes = cycle([16, 32, 64, 96, 128, 256, 512])
scaling_modes = cycle(["fixed", "scene"])
marker_symbols = cycle(symbol_shaders.keys())
class Canvas(scene.SceneCanvas):
def __init__(self):
scene.SceneCanvas.__init__(
self, keys="interactive", size=(512, 512), title="Instanced Markers Demo"
)
self.unfreeze()
self.view = self.central_widget.add_view()
self.view.camera = scene.PanZoomCamera(rect=(0, 0, 512, 512), aspect=1.0)
self.marker_positions, self.face_colors = _create_markers_pattern()
self.method = "instanced"
self.current_size = next(marker_sizes)
self.current_symbol = next(marker_symbols)
self.clamping_enabled = False
self.scaling_mode = next(scaling_modes)
self.spherical_enabled = False
self.markers = scene.visuals.Markers(
method=self.method,
parent=self.view.scene,
scaling=self.scaling_mode,
spherical=self.spherical_enabled,
)
self.freeze()
self.markers.set_data(
self.marker_positions,
face_color=self.face_colors,
edge_color="black",
size=self.current_size,
edge_width=2,
symbol=self.current_symbol,
)
self.view.bgcolor = "#2e3440"
self.print_state()
self.show()
def print_state(self, changed=None):
"""Print current state with optional highlighting of what changed."""
clamp_str = (
f"{self.markers.canvas_size_limits}"
if self.clamping_enabled
else "off"
)
parts = {
'method': f"method={self.method}",
'symbol': f"symbol={self.current_symbol}",
'size': f"size={self.current_size}px",
'clamp': f"clamp={clamp_str}",
'scaling': f"scaling={self.scaling_mode}",
'lighting': f"lighting={'on' if self.spherical_enabled else 'off'}",
}
# highlight the changed part in bold
if changed and changed in parts:
parts[changed] = f"\033[1m{parts[changed]}\033[0m"
state_line = " | ".join(parts.values())
state_line = state_line.ljust(120)
print(f"\r{state_line}", end="", flush=True)
def on_key_press(self, event):
if event.text == "m":
self.method = "instanced" if self.method == "points" else "points"
# recreate markers with new method, cannot change method on the fly
self.markers.parent = None
self.markers = scene.visuals.Markers(
method=self.method, parent=self.view.scene
)
self.markers.set_data(
self.marker_positions,
face_color=self.face_colors,
edge_color="black",
size=self.current_size,
edge_width=2,
symbol=self.current_symbol,
)
self.markers.scaling = self.scaling_mode
self.markers.spherical = self.spherical_enabled
if self.clamping_enabled:
self.markers.canvas_size_limits = (10, 100)
self.print_state(changed='method')
self.update()
elif event.text == "s":
self.current_size = next(marker_sizes)
self.markers.set_data(
self.marker_positions,
face_color=self.face_colors,
edge_color="black",
size=self.current_size,
edge_width=2,
symbol=self.current_symbol,
)
self.print_state(changed='size')
self.update()
elif event.text == "c":
self.clamping_enabled = not self.clamping_enabled
if self.clamping_enabled:
self.markers.canvas_size_limits = (10, 100)
else:
self.markers.canvas_size_limits = None
self.print_state(changed='clamp')
self.update()
elif event.text == "z":
self.scaling_mode = next(scaling_modes)
self.markers.scaling = self.scaling_mode
self.print_state(changed='scaling')
self.update()
elif event.text == "l":
self.spherical_enabled = not self.spherical_enabled
self.markers.spherical = self.spherical_enabled
self.print_state(changed='lighting')
self.update()
elif event.text == "k":
self.current_symbol = next(marker_symbols)
self.markers.symbol = self.current_symbol
self.print_state(changed='symbol')
self.update()
def _create_markers_pattern():
import numpy as np
# Create positions in a circle plus one in the center
n = 12
angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
radius = 150
center = np.array([256, 256])
pos = np.column_stack(
[center[0] + radius * np.cos(angles), center[1] + radius * np.sin(angles)]
).astype(np.float32)
pos = np.vstack([pos, center])
colors = np.zeros((n + 1, 4), dtype=np.float32)
for i in range(n):
hue = i / n
h = hue * 6
x = 1 - abs(h % 2 - 1)
if h < 1:
colors[i] = [1, x, 0, 1]
elif h < 2:
colors[i] = [x, 1, 0, 1]
elif h < 3:
colors[i] = [0, 1, x, 1]
elif h < 4:
colors[i] = [0, x, 1, 1]
elif h < 5:
colors[i] = [x, 0, 1, 1]
else:
colors[i] = [1, 0, x, 1]
colors[-1] = [1, 1, 1, 1]
return pos, colors
if __name__ == "__main__":
from vispy import app
print(__doc__)
canvas = Canvas()
app.run()
|