
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "gallery/scene/instanced_markers.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_gallery_scene_instanced_markers.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_gallery_scene_instanced_markers.py:


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)

.. GENERATED FROM PYTHON SOURCE LINES 34-219



.. image-sg:: /gallery/scene/images/sphx_glr_instanced_markers_001.png
   :alt: instanced markers
   :srcset: /gallery/scene/images/sphx_glr_instanced_markers_001.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none



    method=instanced | symbol=disc | size=16px | clamp=off | scaling=fixed | lighting=off                                   





|

.. code-block:: Python


    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()


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 0.793 seconds)


.. _sphx_glr_download_gallery_scene_instanced_markers.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: instanced_markers.ipynb <instanced_markers.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: instanced_markers.py <instanced_markers.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: instanced_markers.zip <instanced_markers.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
