pyblenderSDIC.meshes.create_axisymmetric_mesh#
- create_axisymmetric_mesh(profile_curve: ~typing.Callable[[float], float] = <function <lambda>>, frame: ~py3dframe.frame.Frame = Frame(origin=[[0.] [0.] [0.]], x_axis=[[1.] [0.] [0.]], y_axis=[[0.] [1.] [0.]], z_axis=[[0.] [0.] [1.]], height_bounds: tuple[float, float] = (0.0, 1.0), theta_bounds: tuple[float, float] = (0.0, 6.283185307179586), Nheight: int = 10, Ntheta: int = 10, closed: bool = False, first_diagonal: bool = True, direct: bool = True, uv_layout: int = 0) TriangleMesh3D [source]#
Create a 3D axisymmetric mesh using a given profile curve.
The profile curve is a function that takes a single argument (height) and returns the radius at that height. The returned radius must be strictly positive for all z in the range defined by
height_bounds
.The
frame
parameter defines the orientation and the position of the mesh in 3D space. The axis of symmetry is aligned with the z-axis of the frame, and z=0 corresponds to the origin of the frame. The x-axis of the frame defines the direction of \(\theta=0\), and the y-axis defines the direction of \(\theta=\pi/2\).The
height_bounds
parameter defines the vertical extent of the mesh, andtheta_bounds
defines the angular sweep around the axis.Nheight
andNtheta
determine the number of vertices in the height and angular directions, respectively. Nodes are uniformly distributed along both directions.Note
Nheight
andNtheta
refer to the number of vertices, not segments.
For example, the following code generates a mesh of a half-cylinder whose flat face is centered on the world x-axis:
from pyblenderSDIC.meshes import create_axisymmetric_mesh import numpy as np cylinder_mesh = create_axisymmetric_mesh( profile_curve=lambda z: 1.0, height_bounds=(-1.0, 1.0), theta_bounds=(-np.pi/4, np.pi/4), Nheight=10, Ntheta=20, ) cylinder_mesh.visualize()
Demi-cylinder mesh with the face centered on the world x-axis.#
Nodes are ordered first in height (indexed by
i_H
) and then in theta (indexed byi_T
). So the vertex at height indexi_H
and angular indexi_T
(both starting from 0) is located at:mesh.vertices[i_T * Nheight + i_H, :]
Each quadrilateral element is defined by the vertices:
\((i_H, i_T)\)
\((i_H + 1, i_T)\)
\((i_H + 1, i_T + 1)\)
\((i_H, i_T + 1)\)
This quadrilateral is then split into two triangles depending on the value of
first_diagonal
:If
first_diagonal
isTrue
:Triangle 1: \((i_H, i_T)\), \((i_H, i_T + 1)\), \((i_H + 1, i_T + 1)\)
Triangle 2: \((i_H, i_T)\), \((i_H + 1, i_T + 1)\), \((i_H + 1, i_T)\)
If
first_diagonal
isFalse
:Triangle 1: \((i_H, i_T)\), \((i_H, i_T + 1)\), \((i_H + 1, i_T)\)
Triangle 2: \((i_H, i_T + 1)\), \((i_H + 1, i_T + 1)\), \((i_H + 1, i_T)\)
These triangles are oriented in a direct (counterclockwise) order by default (for an observer outside the cylinder). If
direct
is False, the orientation is reversed by swapping the second and third vertices in each triangle.If
closed
is True, the mesh is closed in the angular direction. In that case,theta_bounds
should be set to:\[(\theta_0, \theta_0 \pm 2\pi (1 - \frac{1}{Ntheta}))\]to avoid duplicating vertices at the seam.
To generate a closed full cylinder:
cylinder_mesh = create_axisymmetric_mesh( profile_curve=lambda z: 1.0, height_bounds=(-1.0, 1.0), theta_bounds=(0.0, 2.0 * np.pi * (1 - 1.0 / 50)), Nheight=10, Ntheta=50, closed=True, )
The UV coordinates are generated based on the vertex positions in the mesh and uniformly distributed in the range [0, 1] for the OpenGL texture mapping convention. Several UV mapping strategies are available and synthesized in the
uv_layout
parameter. The following options are available foruv_layout
:uv_layout
Vertex lower-left corner
Vertex upper-left corner
Vertex lower-right corner
Vertex upper-right corner
0
(0, 0)
(Nheight-1, 0)
(0, Ntheta-1)
(Nheight-1, Ntheta-1)
1
(0, 0)
(0, Ntheta-1)
(Nheight-1, 0)
(Nheight-1, Ntheta-1)
2
(Nheight-1, 0)
(0, 0)
(Nheight-1, Ntheta-1)
(0, Ntheta-1)
3
(0, Ntheta-1)
(0, 0)
(Nheight-1, Ntheta-1)
(Nheight-1, 0)
4
(0, Ntheta-1)
(Nheight-1, Ntheta-1)
(0, 0)
(Nheight-1, 0)
5
(Nheight-1, 0)
(Nheight-1, Ntheta-1)
(0, 0)
(0, Ntheta-1)
6
(Nheight-1, Ntheta-1)
(0, Ntheta-1)
(Nheight-1, 0)
(0, 0)
7
(Nheight-1, Ntheta-1)
(Nheight-1, 0)
(0, Ntheta-1)
(0, 0)
The table above gives for the 4 corners of a image the corresponding vertex in the mesh.
See also
pyblenderSDIC.meshes.TriangleMesh3D
for more information on how to visualize and manipulate the mesh.Artezaru/py3dframe for details on the
Frame
class.
- Parameters:
profile_curve (Callable[[float], float], optional) – A function that takes a single height coordinate z and returns a strictly positive radius. The default is a function that returns 1.0 for all z.
frame (Frame, optional) – The reference frame for the mesh. Defaults to the identity frame.
height_bounds (tuple[float, float], optional) – The lower and upper bounds for the height coordinate. Defaults to (0.0, 1.0). The order determines the direction of vertex placement.
theta_bounds (tuple[float, float], optional) – The angular sweep in radians. Defaults to (-numpy.pi, numpy.pi). The order determines the angular direction of vertex placement.
Nheight (int, optional) – Number of vertices along the height direction. Must be more than 1. Default is 10.
Ntheta (int, optional) – Number of vertices along the angular direction. Must be more than 1. Default is 10.
closed (bool, optional) – If True, the mesh is closed in the angular direction. Default is False.
first_diagonal (bool, optional) – If True, the quad is split along the first diagonal (bottom-left to top-right). Default is True.
direct (bool, optional) – If True, triangle vertices are ordered counterclockwise. Default is True.
uv_layout (int, optional) – The UV mapping strategy. Default is 0.
- Returns:
The generated axisymmetric mesh as a TriangleMesh3D object.
- Return type: