{ "cells": [ { "cell_type": "markdown", "id": "1171f244", "metadata": {}, "source": [ "# Introduction" ] }, { "cell_type": "code", "execution_count": 1, "id": "3bf7058d", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.159368Z", "iopub.status.busy": "2025-05-13T11:30:37.159210Z", "iopub.status.idle": "2025-05-13T11:30:37.733839Z", "shell.execute_reply": "2025-05-13T11:30:37.733418Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [], "source": [ "import libcarna\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "61f2f668", "metadata": {}, "source": [ "## Meshes" ] }, { "cell_type": "markdown", "id": "ab31c1d4", "metadata": {}, "source": [ "Using polygonal geometries is useful, for example, to create *markers* or to generally enrich visualizations. In 3D\n", "graphics, *meshes* are used to define polygonal geometries. We start with the definition of a mesh for a *cube*:" ] }, { "cell_type": "code", "execution_count": 2, "id": "cd38b675", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.735613Z", "iopub.status.busy": "2025-05-13T11:30:37.735453Z", "iopub.status.idle": "2025-05-13T11:30:37.737396Z", "shell.execute_reply": "2025-05-13T11:30:37.737148Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [], "source": [ "cube = libcarna.meshes.create_box(40, 40, 40)" ] }, { "cell_type": "markdown", "id": "d618b43b", "metadata": {}, "source": [ "The size of the cube is given in *scene units* (SU), and here it is 40 SU in width, height, and depth. Scene units can\n", "be anything that we agree them to be, like micrometers (e.g., for visualization of cellular image data) or millimeters\n", "(e.g., for image data from computer tomography). It is only important that they are used consistently.\n", "\n", "Next, we define some *materials*:" ] }, { "cell_type": "code", "execution_count": 3, "id": "8518d1b2", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.738879Z", "iopub.status.busy": "2025-05-13T11:30:37.738781Z", "iopub.status.idle": "2025-05-13T11:30:37.740641Z", "shell.execute_reply": "2025-05-13T11:30:37.740403Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [], "source": [ "green = libcarna.material('solid', color=libcarna.color.GREEN)\n", "red = libcarna.material('solid', color=libcarna.color.RED )" ] }, { "cell_type": "markdown", "id": "d2571cec", "metadata": {}, "source": [ "Materials determine how meshes (i.e. polygonal gemetries) are rendered. In LibCarna, a material consists of a *shader*\n", "and a set of *parameters* like colors. Supported shaders comprise `solid` for materials whose colors are affected by\n", "light (the default), and `unshaded` for materials that are colored uniformly.\n", "\n", "## Scenes\n", "\n", "Now that we have a mesh and some materials in place, we can create a *scene* that defines some spatial relations. We\n", "create two spatial `geometry` objects, both using the `cube` mesh, but with different colors:" ] }, { "cell_type": "code", "execution_count": 4, "id": "df9403bf", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.742124Z", "iopub.status.busy": "2025-05-13T11:30:37.742021Z", "iopub.status.idle": "2025-05-13T11:30:37.746048Z", "shell.execute_reply": "2025-05-13T11:30:37.745798Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [ { "data": { "text/plain": [ ".Geometry at 0x7c02b5141eb0>" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "GEOMETRY_TYPE_OPAQUE = 1\n", "\n", "root = libcarna.node()\n", "\n", "libcarna.geometry(\n", " GEOMETRY_TYPE_OPAQUE,\n", " parent=root,\n", " mesh=cube,\n", " material=green,\n", ").translate(-10, -10, -40)\n", "\n", "libcarna.geometry(\n", " GEOMETRY_TYPE_OPAQUE,\n", " parent=root,\n", " mesh=cube,\n", " material=red,\n", ").translate(+10, +10, +40)" ] }, { "cell_type": "markdown", "id": "d34de318", "metadata": {}, "source": [ "*Scenes* are defined hierarchically, so they form a tree-like structure. The local coordinate system of a node always\n", "is defined with respect to the coordinate system of its parent node. In this example, the green cube is moved by -10 SU\n", "along the x- and y-axes, and by -40 SU along the z-axis. The red cube is moved in the opposite direction.\n", "\n", "Note on `GEOMETRY_TYPE_OPAQUE`: A *geometry type* is an arbitrary integer constant, that establishes a relation between\n", "the `geometry` nodes of a scene, and the corresponding rendering stages (see [below](#Rendering)).\n", "\n", "Finally, we define a `camera` that will serve as the point of view for scene rendering:" ] }, { "cell_type": "code", "execution_count": 5, "id": "4a1a1c31", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.747577Z", "iopub.status.busy": "2025-05-13T11:30:37.747480Z", "iopub.status.idle": "2025-05-13T11:30:37.749306Z", "shell.execute_reply": "2025-05-13T11:30:37.749077Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [], "source": [ "camera = (\n", " libcarna.camera(parent=root)\n", " .frustum(fov=90, z_near=1, z_far=1000)\n", " .translate(z=250)\n", ")" ] }, { "cell_type": "markdown", "id": "0d14105e", "metadata": {}, "source": [ "The `frustum` method defines the projection from 3D to planar coordinates. The `fov` argument defines the *field of\n", "view* of the camera in degrees. The `z_near` and `z_far` arguments define the distance of the *near and far clipping\n", "planes* to the camera; geometries, that are closer than 1 SU or farther than 1000 SU, will not be rendered.\n", "\n", "## Rendering\n", "\n", "Now we are all set to perform the rendering — almost! One ingredient is missing: The *renderer*. We only have *opaque*\n", "geometries involved, so we set up the rendering pipeline accordingly:" ] }, { "cell_type": "code", "execution_count": 6, "id": "0ca491a8", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.750878Z", "iopub.status.busy": "2025-05-13T11:30:37.750675Z", "iopub.status.idle": "2025-05-13T11:30:37.833847Z", "shell.execute_reply": "2025-05-13T11:30:37.833433Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [], "source": [ "opaque = libcarna.opaque_renderer(GEOMETRY_TYPE_OPAQUE)\n", "r = libcarna.renderer(500, 370, [opaque])" ] }, { "cell_type": "markdown", "id": "b1e98946", "metadata": {}, "source": [ "Each renderer can have an arbitrary number of *rendering stages*. Here, we only use the `opaque` rendering stage to\n", "render all geometries in the scene that we have annotated with the `GEOMETRY_TYPE_OPAQUE` as the geometry type.\n", "\n", "And then it's time to render. We can inspect the result with matplotlib, for example:" ] }, { "cell_type": "code", "execution_count": 7, "id": "37ff9c22", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.835247Z", "iopub.status.busy": "2025-05-13T11:30:37.835139Z", "iopub.status.idle": "2025-05-13T11:30:37.931324Z", "shell.execute_reply": "2025-05-13T11:30:37.930910Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "img = r.render(camera)\n", "plt.imshow(img)" ] }, { "cell_type": "markdown", "id": "83a936e6", "metadata": {}, "source": [ "## Animations\n", "\n", "It is much easier to visually grasp the information in a 3D scene by looking at it from different angles. For this\n", "reason, there is a set of convenience functions that fascilitates creating animations, by rendering multiple frames at\n", "once:" ] }, { "cell_type": "code", "execution_count": 8, "id": "10ab7a0a", "metadata": { "execution": { "iopub.execute_input": "2025-05-13T11:30:37.932674Z", "iopub.status.busy": "2025-05-13T11:30:37.932571Z", "iopub.status.idle": "2025-05-13T11:30:38.079719Z", "shell.execute_reply": "2025-05-13T11:30:38.079276Z" }, "vscode": { "languageId": "plaintext" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", " \n", "
\n", " \n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Define animation\n", "animation = libcarna.animate(\n", " libcarna.animate.rotate_local(camera),\n", " n_frames=50,\n", ")\n", "\n", "# Render and show animation\n", "libcarna.imshow(animation.render(r, camera))" ] }, { "cell_type": "markdown", "id": "7d4913a6", "metadata": {}, "source": [ "In this example, the camera is rotated around the *center of the scene* (more precisely: around it's parent node, that\n", "happens to be the ``root`` node of the scene). The scene is rendered from 50 different angles. For each angle, the\n", "result is a NumPy array.\n", "\n", "Use `libcarna.imshow` to view animations, matplotlib does not work nicely." ] } ], "metadata": { "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.12.10" } }, "nbformat": 4, "nbformat_minor": 5 }