.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "auto_examples/17_pybullet/viz_brick_wall.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_17_pybullet_viz_brick_wall.py>`
        to download the full example code

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

.. _sphx_glr_auto_examples_17_pybullet_viz_brick_wall.py:


=====================
Brick Wall Simulation
=====================

This example simulation shows how to use pybullet to render physics simulations
in fury. In this example we specifically render a ball beign thrown at a brick
wall.

First some imports.

.. GENERATED FROM PYTHON SOURCE LINES 12-19

.. code-block:: Python


    import itertools

    import numpy as np
    import pybullet as p
    import fury








.. GENERATED FROM PYTHON SOURCE LINES 20-22

Next, we initialize a pybullet client to render the physics. We use `DIRECT`
mode to initialize pybullet without a GUI.

.. GENERATED FROM PYTHON SOURCE LINES 22-25

.. code-block:: Python


    p.connect(p.DIRECT)





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

 .. code-block:: none


    3



.. GENERATED FROM PYTHON SOURCE LINES 26-27

Apply gravity to the scene. In pybullet all values are in SI units.

.. GENERATED FROM PYTHON SOURCE LINES 27-29

.. code-block:: Python

    p.setGravity(0, 0, -10)








.. GENERATED FROM PYTHON SOURCE LINES 30-32

We define some global parameters so that its easier for us to tweak the
tweak the simulation.

.. GENERATED FROM PYTHON SOURCE LINES 32-52

.. code-block:: Python


    # Ball Parameters
    ball_radius = 0.3
    ball_color = np.array([1, 0, 0])
    ball_mass = 3
    ball_position = np.array([2, 0, 1.5])
    ball_orientation = np.array([0, 0, 0, 1])

    # Base Plane Parameters
    base_size = np.array([5, 5, 0.2])
    base_color = np.array([1, 1, 1])
    base_position = np.array([0, 0, -0.1])
    base_orientation = np.array([0, 0, 0, 1])

    # Wall Parameters
    wall_height = 10
    wall_width = 10
    brick_mass = 0.5
    brick_size = np.array([0.2, 0.4, 0.2])








.. GENERATED FROM PYTHON SOURCE LINES 53-54

Now we define the required parameters to render the Ball.

.. GENERATED FROM PYTHON SOURCE LINES 54-74

.. code-block:: Python


    # Ball actor
    ball_actor = fury.actor.sphere(
        centers=np.array([[0, 0, 0]]), colors=ball_color, radii=ball_radius
    )

    # Collision shape for the ball.
    ball_coll = p.createCollisionShape(p.GEOM_SPHERE, radius=ball_radius)

    # Creating a multi-body which will be tracked by pybullet.
    ball = p.createMultiBody(
        baseMass=3,
        baseCollisionShapeIndex=ball_coll,
        basePosition=ball_position,
        baseOrientation=ball_orientation,
    )

    # Change the dynamics of the ball by adding friction and restitution.
    p.changeDynamics(ball, -1, lateralFriction=0.3, restitution=0.5)








.. GENERATED FROM PYTHON SOURCE LINES 75-76

Render a base plane to support the bricks.

.. GENERATED FROM PYTHON SOURCE LINES 76-95

.. code-block:: Python


    base_actor = fury.actor.box(
        centers=np.array([[0, 0, 0]]),
        directions=[0, 0, 0],
        scales=base_size,
        colors=base_color,
    )

    base_coll = p.createCollisionShape(p.GEOM_BOX, halfExtents=base_size / 2)
    # half of the actual size.

    base = p.createMultiBody(
        baseCollisionShapeIndex=base_coll,
        basePosition=base_position,
        baseOrientation=base_orientation,
    )

    p.changeDynamics(base, -1, lateralFriction=0.3, restitution=0.5)








.. GENERATED FROM PYTHON SOURCE LINES 96-98

Now we render the bricks. All the bricks are rendered by a single actor for
better performance.

.. GENERATED FROM PYTHON SOURCE LINES 98-141

.. code-block:: Python


    nb_bricks = wall_height * wall_width

    brick_centers = np.zeros((nb_bricks, 3))

    brick_directions = np.zeros((nb_bricks, 3))
    brick_directions[:] = np.array([1.57, 0, 0])

    brick_orns = np.zeros((nb_bricks, 4))

    brick_sizes = np.zeros((nb_bricks, 3))
    brick_sizes[:] = brick_size

    brick_colors = np.random.rand(nb_bricks, 3)

    brick_coll = p.createCollisionShape(p.GEOM_BOX, halfExtents=brick_size / 2)

    # We use this array to store the reference of brick objects in pybullet world.
    bricks = np.zeros(nb_bricks, dtype=np.int8)

    # Logic to position the bricks appropriately to form a wall.
    i = 0
    for k in range(wall_height):
        for j in range(wall_width):
            center_pos = np.array([-1, (j * 0.4) - 1.8, (0.2 * k) + 0.1])
            brick_centers[i] = center_pos
            brick_orns[i] = np.array([0, 0, 0, 1])
            bricks[i] = p.createMultiBody(
                baseMass=brick_mass,
                baseCollisionShapeIndex=brick_coll,
                basePosition=center_pos,
                baseOrientation=brick_orns[i],
            )
            p.changeDynamics(bricks[i], -1, lateralFriction=0.1, restitution=0.1)
            i += 1

    brick_actor = fury.actor.box(
        centers=brick_centers,
        directions=brick_directions,
        scales=brick_sizes,
        colors=brick_colors,
    )








.. GENERATED FROM PYTHON SOURCE LINES 142-143

Now, we define a scene and add actors to it.

.. GENERATED FROM PYTHON SOURCE LINES 143-162

.. code-block:: Python


    scene = fury.window.Scene()
    scene.add(fury.actor.axes())
    scene.add(ball_actor)
    scene.add(base_actor)
    scene.add(brick_actor)

    # Create show manager.
    showm = fury.window.ShowManager(
        scene, size=(900, 768), reset_camera=False, order_transparent=True
    )


    # Counter iterator for tracking simulation steps.
    counter = itertools.count()

    # Variable for tracking applied force.
    apply_force = True





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

 .. code-block:: none

    /opt/homebrew/Caskroom/miniforge/base/envs/py311-fury/lib/python3.11/site-packages/sphinx_gallery/gen_rst.py:722: UserWarning: We'll no longer accept the way you call the __init__ function in future versions of FURY.

    Here's how to call the Function __init__: __init__(self_value, scene='value', title='value', size='value', png_magnify='value', reset_camera='value', order_transparent='value', interactor_style='value', stereo='value', multi_samples='value', max_peels='value', occlusion_ratio='value')

      exec(self.code, self.fake_main.__dict__)




.. GENERATED FROM PYTHON SOURCE LINES 163-164

Now, we define methods to sync objects between fury and Pybullet.

.. GENERATED FROM PYTHON SOURCE LINES 164-180

.. code-block:: Python


    # Get the position of base and set it.
    base_pos, _ = p.getBasePositionAndOrientation(base)
    base_actor.SetPosition(*base_pos)

    # Do the same for ball.
    ball_pos, _ = p.getBasePositionAndOrientation(ball)
    ball_actor.SetPosition(*ball_pos)

    # Calculate the vertices of the bricks.
    vertices = fury.utils.vertices_from_actor(brick_actor)
    num_vertices = vertices.shape[0]
    num_objects = brick_centers.shape[0]
    sec = int(num_vertices / num_objects)









.. GENERATED FROM PYTHON SOURCE LINES 181-195

==============
Syncing Bricks
==============

Here, we perform three major steps to sync bricks accurately.
* Get the position and orientation of the bricks from pybullet.
* Calculate the Rotation Matrix.

  - Get the difference in orientations (Quaternion).
  - Generate the corresponding rotation matrix according to that difference.
  - Reshape it in a 3x3 matrix.

* Perform calculations to get the required position and orientation.
* Update the position and orientation.

.. GENERATED FROM PYTHON SOURCE LINES 195-216

.. code-block:: Python



    def sync_brick(object_index, multibody):
        pos, orn = p.getBasePositionAndOrientation(multibody)

        rot_mat = np.reshape(
            p.getMatrixFromQuaternion(
                p.getDifferenceQuaternion(orn, brick_orns[object_index])
            ),
            (3, 3),
        )

        vertices[object_index * sec : object_index * sec + sec] = (
            vertices[object_index * sec : object_index * sec + sec]
            - brick_centers[object_index]
        ) @ rot_mat + pos

        brick_centers[object_index] = pos
        brick_orns[object_index] = orn









.. GENERATED FROM PYTHON SOURCE LINES 217-219

A simpler but inaccurate approach is used here to update the position and
orientation.

.. GENERATED FROM PYTHON SOURCE LINES 219-228

.. code-block:: Python



    def sync_actor(actor, multibody):
        pos, orn = p.getBasePositionAndOrientation(multibody)
        actor.SetPosition(*pos)
        orn_deg = np.degrees(p.getEulerFromQuaternion(orn))
        actor.SetOrientation(*orn_deg)









.. GENERATED FROM PYTHON SOURCE LINES 229-230

Here, we define a textblock to display the Avg. FPS and simulation steps.

.. GENERATED FROM PYTHON SOURCE LINES 230-237

.. code-block:: Python


    fpss = np.array([])
    tb = fury.ui.TextBlock2D(
        text="Avg. FPS: \nSim Steps: ", position=(0, 680), font_size=30, color=(1, 0.5, 0)
    )
    scene.add(tb)








.. GENERATED FROM PYTHON SOURCE LINES 238-239

Set the camera for better visualization.

.. GENERATED FROM PYTHON SOURCE LINES 239-247

.. code-block:: Python


    scene.set_camera(
        position=(10.46, -8.13, 6.18),
        focal_point=(0.0, 0.0, 0.79),
        view_up=(-0.27, 0.26, 0.90),
    )









.. GENERATED FROM PYTHON SOURCE LINES 248-250

Timer callback is created which is responsible for calling the sync and
simulation methods.

.. GENERATED FROM PYTHON SOURCE LINES 250-303

.. code-block:: Python



    # Create timer callback which will execute at each step of simulation.
    def timer_callback(_obj, _event):
        global apply_force, fpss
        cnt = next(counter)
        showm.render()

        if cnt % 1 == 0:
            fps = showm.frame_rate
            fpss = np.append(fpss, fps)
            tb.message = (
                "Avg. FPS: " + str(np.round(np.mean(fpss), 0)) + "\nSim Steps: " + str(cnt)
            )

        # Get the position and orientation of the ball.
        ball_pos, ball_orn = p.getBasePositionAndOrientation(ball)

        # Apply force for 5 times for the first step of simulation.
        if apply_force:
            # Apply the force.
            p.applyExternalForce(
                ball, -1, forceObj=[-10000, 0, 0], posObj=ball_pos, flags=p.WORLD_FRAME
            )
            apply_force = False

        # Set position and orientation of the ball.
        sync_actor(ball_actor, ball)

        # Updating the position and orientation of each individual brick.
        for idx, brick in enumerate(bricks):
            sync_brick(idx, brick)
        fury.utils.update_actor(brick_actor)

        # Simulate a step.
        p.stepSimulation()

        # Exit after 2000 steps of simulation.
        if cnt == 130:
            showm.exit()


    # Add the timer callback to showmanager.
    # Increasing the duration value will slow down the simulation.
    showm.add_timer_callback(True, 1, timer_callback)

    interactive = False

    # start simulation
    if interactive:
        showm.start()

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



.. image-sg:: /auto_examples/17_pybullet/images/sphx_glr_viz_brick_wall_001.png
   :alt: viz brick wall
   :srcset: /auto_examples/17_pybullet/images/sphx_glr_viz_brick_wall_001.png
   :class: sphx-glr-single-img


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

 .. code-block:: none

    /opt/homebrew/Caskroom/miniforge/base/envs/py311-fury/lib/python3.11/site-packages/sphinx_gallery/gen_rst.py:722: UserWarning: We'll no longer accept the way you call the record function in future versions of FURY.

    Here's how to call the Function record: record(scene='value', cam_pos='value', cam_focal='value', cam_view='value', out_path='value', path_numbering='value', n_frames='value', az_ang='value', magnification='value', size='value', reset_camera='value', screen_clip='value', stereo='value', verbose='value')

      exec(self.code, self.fake_main.__dict__)





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

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


.. _sphx_glr_download_auto_examples_17_pybullet_viz_brick_wall.py:

.. only:: html

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

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

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

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

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


.. only:: html

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

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