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

.. only:: html

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

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

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

.. _sphx_glr_auto_examples_04_demos_viz_network_animated.py:


=======================================================
Visualize Networks (Animated version)
=======================================================

The goal of this demo is to show how to visualize a
complex network and use an force directed algorithm to
layout the network. A simpler animation of the network
made by adding some random displacements to nodes
positions is also demoed.

.. GENERATED FROM PYTHON SOURCE LINES 14-15

First, let's import some useful functions

.. GENERATED FROM PYTHON SOURCE LINES 15-23

.. code-block:: Python


    import math
    from os.path import join as pjoin

    import numpy as np

    import fury


.. GENERATED FROM PYTHON SOURCE LINES 24-31

This demo has two modes.
Use `mode = 0` to visualize a randomly generated geographic network by
iterating it using a force-directed layout heuristic.

Use `mode = 1` to visualize a large network being animated with random
displacements


.. GENERATED FROM PYTHON SOURCE LINES 31-34

.. code-block:: Python


    mode = 0


.. GENERATED FROM PYTHON SOURCE LINES 35-36

Then let's download some available datasets. (mode 1)

.. GENERATED FROM PYTHON SOURCE LINES 36-43

.. code-block:: Python


    if mode == 1:
        from fury.data.fetcher import fetch_viz_wiki_nw

        files, folder = fetch_viz_wiki_nw()
        categories_file, edges_file, positions_file = sorted(files.keys())


.. GENERATED FROM PYTHON SOURCE LINES 44-45

We read our datasets (mode 1)

.. GENERATED FROM PYTHON SOURCE LINES 45-52

.. code-block:: Python


    if mode == 1:
        positions = np.loadtxt(pjoin(folder, positions_file))
        categories = np.loadtxt(pjoin(folder, categories_file), dtype=str)
        edges = np.loadtxt(pjoin(folder, edges_file), dtype=int)
        vertices_count = len(positions)


.. GENERATED FROM PYTHON SOURCE LINES 53-54

Generate a geographic random network, requires networkx package (mode 0)

.. GENERATED FROM PYTHON SOURCE LINES 54-66

.. code-block:: Python


    if mode == 0:
        import networkx as nx

        vertices_count = 100
        view_size = 100
        network = nx.random_geometric_graph(vertices_count, 0.2)
        positions = view_size * np.random.random((vertices_count, 3)) - view_size / 2.0
        categories = np.arange(0, vertices_count)
        edges = np.array(network.edges())
        positions = view_size * np.random.random((vertices_count, 3)) - view_size / 2.0


.. GENERATED FROM PYTHON SOURCE LINES 67-69

We attribute a color to each category of our dataset which correspond to our
nodes colors.

.. GENERATED FROM PYTHON SOURCE LINES 69-80

.. code-block:: Python


    category2index = {category: i for i, category in enumerate(np.unique(categories))}

    index2category = np.unique(categories)

    category_colors = fury.colormap.distinguishable_colormap(nb_colors=len(index2category))

    colors = np.array(
        [category_colors[category2index[category]] for category in categories]
    )


.. GENERATED FROM PYTHON SOURCE LINES 81-82

We define our node size

.. GENERATED FROM PYTHON SOURCE LINES 82-85

.. code-block:: Python


    radii = 1 + np.random.rand(len(positions))


.. GENERATED FROM PYTHON SOURCE LINES 86-88

Let's create our edges now. They will indicate a citation between two nodes.
The colors of each edge are interpolated between the two endpoints.

.. GENERATED FROM PYTHON SOURCE LINES 88-95

.. code-block:: Python


    edges_colors = []
    for source, target in edges:
        edges_colors.append(np.array([colors[source], colors[target]]))

    edges_colors = np.average(np.array(edges_colors), axis=1)


.. GENERATED FROM PYTHON SOURCE LINES 96-99

Our data preparation is ready, it is time to visualize them all. We start to
build 2 actors that we represent our data : sphere_actor for the nodes and
lines_actor for the edges.

.. GENERATED FROM PYTHON SOURCE LINES 99-113

.. code-block:: Python


    sphere_actor = fury.actor.sphere(
        centers=np.zeros(positions.shape), colors=colors, radii=radii * 0.5, theta=8, phi=8
    )


    lines_actor = fury.actor.line(
        np.zeros((len(edges), 2, 3)),
        colors=edges_colors,
        lod=False,
        fake_tube=True,
        linewidth=3,
    )


.. GENERATED FROM PYTHON SOURCE LINES 114-115

Defining timer callback and layout iterator

.. GENERATED FROM PYTHON SOURCE LINES 115-222

.. code-block:: Python



    def new_layout_timer(
        showm,
        edges_list,
        vertices_count,
        max_iterations=1000,
        vertex_initial_positions=None,
    ):
        view_size = 500
        viscosity = 0.10
        alpha = 0.5
        a = 0.0005
        b = 1.0
        deltaT = 1.0

        sphere_geometry = np.array(fury.utils.vertices_from_actor(sphere_actor))
        geometry_length = sphere_geometry.shape[0] / vertices_count

        if vertex_initial_positions is not None:
            pos = np.array(vertex_initial_positions)
        else:
            pos = view_size * np.random.random((vertices_count, 3)) - view_size / 2.0

        velocities = np.zeros((vertices_count, 3))

        def iterate(iterationCount):
            nonlocal pos, velocities
            for _ in range(iterationCount):
                forces = np.zeros((vertices_count, 3))
                # repulstive forces
                for vertex1 in range(vertices_count):
                    for vertex2 in range(vertex1):
                        x1, y1, z1 = pos[vertex1]
                        x2, y2, z2 = pos[vertex2]
                        distance = (
                            math.sqrt(
                                (x2 - x1) * (x2 - x1)
                                + (y2 - y1) * (y2 - y1)
                                + (z2 - z1) * (z2 - z1)
                            )
                            + alpha
                        )
                        rx = (x2 - x1) / distance
                        ry = (y2 - y1) / distance
                        rz = (z2 - z1) / distance
                        Fx = -b * rx / distance / distance
                        Fy = -b * ry / distance / distance
                        Fz = -b * rz / distance / distance
                        forces[vertex1] += np.array([Fx, Fy, Fz])
                        forces[vertex2] -= np.array([Fx, Fy, Fz])
                # attractive forces
                for vFrom, vTo in edges_list:
                    if vFrom == vTo:
                        continue
                    x1, y1, z1 = pos[vFrom]
                    x2, y2, z2 = pos[vTo]
                    distance = math.sqrt(
                        (x2 - x1) * (x2 - x1)
                        + (y2 - y1) * (y2 - y1)
                        + (z2 - z1) * (z2 - z1)
                    )
                    Rx = x2 - x1
                    Ry = y2 - y1
                    Rz = z2 - z1
                    Fx = a * Rx * distance
                    Fy = a * Ry * distance
                    Fz = a * Rz * distance
                    forces[vFrom] += np.array([Fx, Fy, Fz])
                    forces[vTo] -= np.array([Fx, Fy, Fz])
                velocities += forces * deltaT
                velocities *= 1.0 - viscosity
                pos += velocities * deltaT
            pos[:, 0] -= np.mean(pos[:, 0])
            pos[:, 1] -= np.mean(pos[:, 1])
            pos[:, 2] -= np.mean(pos[:, 2])

        counter = 0

        def _timer(_obj, _event):
            nonlocal counter, pos
            counter += 1
            if mode == 0:
                iterate(1)
            else:
                pos[:] += (np.random.random(pos.shape) - 0.5) * 1.5
            spheres_positions = fury.utils.vertices_from_actor(sphere_actor)
            spheres_positions[:] = sphere_geometry + np.repeat(pos, geometry_length, axis=0)

            edges_positions = fury.utils.vertices_from_actor(lines_actor)
            edges_positions[::2] = pos[edges_list[:, 0]]
            edges_positions[1::2] = pos[edges_list[:, 1]]

            fury.utils.update_actor(lines_actor)
            fury.utils.compute_bounds(lines_actor)

            fury.utils.update_actor(sphere_actor)
            fury.utils.compute_bounds(lines_actor)
            showm.scene.reset_clipping_range()
            showm.render()

            if counter >= max_iterations:
                showm.exit()

        return _timer



.. GENERATED FROM PYTHON SOURCE LINES 223-225

All actors need to be added in a scene, so we build one and add our
lines_actor and sphere_actor.

.. GENERATED FROM PYTHON SOURCE LINES 225-234

.. code-block:: Python



    scene = fury.window.Scene()

    camera = scene.camera()

    scene.add(lines_actor)
    scene.add(sphere_actor)


.. GENERATED FROM PYTHON SOURCE LINES 235-239

The final step! Visualize the result of our creation! Also, we need to move
the camera a little bit farther from the network. you can increase the
parameter max_iteractions of the timer callback to let the animation run for
more time.

.. GENERATED FROM PYTHON SOURCE LINES 239-264

.. code-block:: Python


    showm = fury.window.ShowManager(
        scene=scene,
        reset_camera=False,
        size=(900, 768),
        order_transparent=True,
        multi_samples=8,
    )


    scene.set_camera(position=(0, 0, -300))

    timer_callback = new_layout_timer(
        showm, edges, vertices_count, max_iterations=200, vertex_initial_positions=positions
    )


    # Run every 16 milliseconds
    showm.add_timer_callback(True, 16, timer_callback)

    showm.start()

    fury.window.record(
        scene=showm.scene, size=(900, 768), out_path="viz_animated_networks.png"
    )


.. _sphx_glr_download_auto_examples_04_demos_viz_network_animated.py:

.. only:: html

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

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

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

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

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

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

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


.. only:: html

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

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