{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tetrahedral Mesh" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from lapy import TetMesh" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, instead of loading, we define a small tetrahedral mesh representing a cube with a center vertex and twelve tetrahedra." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# cube9 (cube with center node)\n", "points = [\n", " [0, 0, 0],\n", " [1, 0, 0],\n", " [1, 1, 0],\n", " [0, 1, 0],\n", " [0, 0, 1],\n", " [1, 0, 1],\n", " [1, 1, 1],\n", " [0, 1, 1],\n", " [0.5, 0.5, 0.5],\n", "]\n", "tets = [\n", " [0, 5, 8, 1],\n", " [0, 4, 5, 8],\n", " [2, 5, 6, 8],\n", " [1, 5, 2, 8],\n", " [6, 7, 3, 8],\n", " [6, 3, 2, 8],\n", " [0, 3, 4, 8],\n", " [3, 7, 4, 8],\n", " [0, 1, 2, 8],\n", " [0, 2, 3, 8],\n", " [4, 6, 5, 8],\n", " [4, 7, 6, 8],\n", "]\n", "T = TetMesh(points, tets)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note, that we flipped the first tetrahedron vertex order on purpose (it should be 0,5,1,8) to test and correct orientation below." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can check if our tet mesh has free vertices (these are vertices that are not used in any tetrahedron)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "T.has_free_vertices()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No free vertices are found, so we cannot remove any and the attempt will keep all 9." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([0, 1, 2, 3, 4, 5, 6, 7, 8]), [])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "T.rm_free_vertices_()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see next, if we have consistent orientations (this should fail)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Orientations are not uniform\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "T.is_oriented()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some functions don't care about the orientation, for example the average edge length computation." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0543647924813107" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "T.avg_edge_length()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also get the boundary of the tet mesh as a triangle mesh." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 12 triangles on boundary.\n" ] } ], "source": [ "BT = T.boundary_tria()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But also the boundary is not oriented consistently (triangle normals of neighboring triangles point in opposite directions)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "BT.is_oriented()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's repeat those steps after correcting the orientation in the tet mesh." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Flipped 1 tetrahedra\n", "All tet orientations are correct\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "T.orient_()\n", "T.is_oriented()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we extract the boundary surface now, we see it is also consistently oriented." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found 12 triangles on boundary.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "BT = T.boundary_tria()\n", "BT.is_oriented()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Correct orientation is needed, e.g., to compute the volume of a surface mesh." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "BT.volume()" ] } ], "metadata": { "kernelspec": { "display_name": "Python3", "language": "python", "name": "python3" }, "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" } }, "nbformat": 4, "nbformat_minor": 4 }