Source code for ddg.halfedge._surface_generator

"""Module '_surface_generator' defines some primitive halfedge surfaces"""
import numpy as np
from scipy.spatial import ConvexHull

import ddg
import ddg.halfedge._modify as modify
import ddg.indexedfaceset._ifs_generator as ifs_gen
from ddg.math.euclidean import rotation_from_to

#######
# grids
#######


def _convert_to_halfedge(ifs_obj):
    """
    Convert an indexed face set to a surface.

    This is a helper function.

    Parameters
    ----------
    ifs_obj : ddg.datastructures.indexedfaceset.ifs.IndexedFaceSet
        The indexed face set that should be converted.

    Returns
    -------
    ddg.datastructures.halfedge.surface.Surface
        The converted indexed face set as a surface.
    """
    from ddg.indexedfaceset._utils import indexed_face_set_to_surface

    return indexed_face_set_to_surface(ifs_obj, None, None)


def _add_vertex_coordinates(ifs_obj, s, co_attr):
    """
    Add vertex coordinates from `ifs_obj` to `s`.

    This is a helper function that adds the coordinates of `ifs_obj` to `s`.
    Note that the coordinates get added to `s` in-place and nothing is returned.

    Parameters
    ----------
    ifs_obj : ddg.datastructures.indexedfaceset.ifs.IndexedFaceSet
        The equivalent of `s` as an indexed face set.
    s : ddg.datastructures.halfedge.surface.Surface
        The surface to which the coordinates should be added to.
    co_attr : str
        The name of the vertex attribute that stores the coordinates.
    """
    coords = ifs_obj.vertex_attributes[co_attr]

    s.verts.add_attribute(co_attr)
    for v, co in zip(s.verts, coords, strict=True):
        getattr(s.verts, co_attr)[v] = co


[docs]def grid(shape, co_attr="co"): """ Create a triangle grid as a half-edge surface. The `shape` defines the number of vertices in each direction. Both 2D and 3D grids are supported. Parameters ---------- shape : tuple of length 2 or 3 Shape of the grid. The first entry is the number of vertices in the x-direction, the second the number of vertices in the y-direction and the third (if given) the number of vertices in the z-direction. co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A quadrilateral grid. """ ifs_obj = ifs_gen.grid(shape, co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def triangle_grid(shape, co_attr="co"): """ Create a triangle grid as a halfedge. The `shape` defines the number of vertices in each direction. Both 2D and 3D grids are supported. Parameters ---------- shape : tuple of length 2 or 3 Shape of the grid. The first entry is the number of vertices in the x-direction, the second the number of vertices in the y-direction and the third (if given) the number of vertices in the z-direction. co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A triangle grid. """ ifs_obj = ifs_gen.triangle_grid(shape, co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
################# # platonic solids #################
[docs]def tetrahedron(co_attr="co"): """ Create a tetrahedron as a half-edge surface. By default, standard coordinates of a unit tetrahedron, centered at (0, 0, 0), will be assigned to the vertices. Parameters ---------- co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A tetrahedron. """ ifs_obj = ifs_gen.tetrahedron(co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def cube(co_attr="co"): """ Create a cube as a half-edge surface. By default, standard coordinates of a unit cube, centered at (0, 0, 0), will be assigned to the vertices. Parameters ---------- co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A cube. """ ifs_obj = ifs_gen.cube(co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def octahedron(co_attr="co"): """ Create an octahedron as a half-edge surface. By default, standard coordinates of a unit octahedron, centered at [0,0,0], will be assigned to the vertices and can be accessed via the attribute coordinate `co`. Parameters ---------- co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface An octahedron. """ ifs_obj = ifs_gen.octahedron(co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def dodecahedron(co_attr="co"): """ Create a dodecahedron as a half-edge surface. By default, standard coordinates of a unit dodecahedron, centered at [0, 0, 0], will be assigned to the vertices and can be accessed via the attribute coordinate `co`. Parameters ---------- co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A dodecahedron. """ ifs_obj = ifs_gen.dodecahedron(co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def icosahedron(co_attr="co"): """ Create an icosahedron as a half-edge surface. By default, standard coordinates of a unit icosahedron, centered at [0, 0, 0], will be assigned to the vertices and can be accessed via the attribute coordinate `co`. Parameters ---------- co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface An icosahedron. """ ifs_obj = ifs_gen.icosahedron(co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def icosphere(subdivision_steps=1, radius=1, co_attr="co"): """ Create an icosphere as a half-edge surface. By default, standard coordinates of a unit icosphere, centered at [0, 0, 0], will be assigned to the vertices and can be accessed via the attribute coordinate `co`. Parameters ---------- subdivision_steps : int (default=1) Number of subdivisions performed, e.g. 0 for the icosahedron, 1 to subdivide each face of the icosahedron into 4 triangles, 2 to subdivide each face of the icosahedron into 16 triangles and so on. radius : float (default=1) radius of the icosphere co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface An icosphere. Notes ----- * Coordinate attribute The coordinate attribute `co_attr` assigned to the vertices is of type `numpy.ndarray` and contains the standard coordinates of a unit icosphere. """ icosphere = icosahedron() radius = radius if co_attr: modify.subdivide(icosphere, subdivision_steps, co_attr) else: modify.subdivide(icosphere, subdivision_steps, None) for v in icosphere.verts: v.co *= radius / np.linalg.norm(v.co) return icosphere
[docs]def convexhull_3d(A, join_coplanar_triangles=True, co_attr="co"): """ Creates a halfedge surface of the convex hull of the input points. Parameters ---------- A : array Input array of 3 dimensional coordinates of the form [ , , ] Dimension of the input points has to be three, i.e. non-planar. join_coplanar_triangles : bool (default=True) If `True`, planar faces will stay triangulated. Otherwise the triangulation will be removed and result in one bigger face. co_attr : str (default="co") The name of the vertex attribute that stores the coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface The convex hull. Notes ----- * Coordinate attribute The coordinate attribute `co_attr` assigned to the vertices is of type `numpy.ndarray` and contains the coordinates of the input. """ hull = ConvexHull(A) s = ddg.indexedfaceset.indexed_face_set_to_surface( ddg.indexedfaceset.GeneralizedIndexedFaceSet(hull.simplices) ) index_list = list(v.ifs_index for v in s.verts) # generating coordinates s.verts.add_attribute(co_attr) for f in s.faces: for e in ddg.halfedge.edge_loop(f.edge): i = e.head.ifs_index if i in index_list: e.head.co = np.array(A[i]) index_list.remove(i) # Check for lin independency and joins faces if needed if join_coplanar_triangles: modify.join_coplanar_faces(s) return s
[docs]def disc( circle_subdivisions=20, fill_face=True, radius=1, center=(0, 0, 0), normal=(0, 0, 1), co_attr="co", ): """ Create a disc as a half-edge surface. By default, the disc is embedded in R^3 with center (0, 0, 0), radius 1 and normal vector (0, 0, 1). Parameters ---------- circle_subdivisions: int (default=20) Number of vertices on the boundary of the circle or disc. fill_face: bool (default=True) If `True`, fill the edge loop with a face. radius : float (default=1) The radius of the disc. center : array_like of shape (3,) (default=(0, 0, 0)) The center of the disc as an array_like in 3D space. normal : array_like of shape (3,) (default=(0, 0, 1)) The normal vector of the disc. co_attr : str (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A disc. """ ifs_obj = ifs_gen.disc(circle_subdivisions, center, normal, radius, co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) if not fill_face: modify.remove_face(ddg.halfedge.some_face(s)) return s
def cone( resolution=20, fill_face=True, radius=1, length=1, center=(0, 0, 0), normal=(0, 0, 1), co_attr="co", ): """ Create a cone as a half-edge surface. Parameters ---------- resolution: int (default=20) The number of vertices of the base. fill_face : bool (default=True) If `True`, include the face of the base. radius : float (default=1) The radius of the base of the cone. length : float (default=1) The length of the cone. center : array_like of shape (3,) (default=(0, 0, 0)) The center of the base of the cone. normal : array_like of shape (3,) (default=(0, 0, 1)) The normal of the cone's base face. co_attr : str, optional (default="co") Name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A cone. """ ifs_obj = ifs_gen.cone( resolution, fill_face, radius, length, center, normal, co_attr ) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
[docs]def cylinder( resolution=20, fill_caps=True, top_radius=1, bot_radius=1, length=1, center=(0, 0, 0), normal=(0, 0, 1), co_attr="co", ): """ Create a cylinder as a half-edge surface. Parameters ---------- resolution: int (default=20) The number of vertices on each side. fill_caps : bool (default=True) If `True`, include the faces of the bottom and the top. top_radius : float (default=1) The radius of the top of the cylinder. bot_radius : float (default=1) The radius of the bottom of the cylinder length : float (default=1) The length of the cylinder. center : array_like of shape (3,) (default=(0, 0, 0)) The center of the bottom of the cylinder. normal : array_like of shape (3,) (default=(0, 0, 1)) The normal of the cylinder's top and bottom faces. co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A cylinder. """ ifs_obj = ifs_gen.cylinder( resolution, fill_caps, top_radius, bot_radius, length, center, normal, co_attr, ) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s
def _cylinder_along_y_axis( resolution=20, fill_caps=True, top_radius=1, bot_radius=1, length=1, center=(0, 0, 0), normal=(0, 0, 1), co_attr="co", ): """ Create a cylinder as a half-edge surface with axis being the y-axis. This is a legacy function that should not be used. Parameters ---------- resolution: int (default=20) The number of vertices on each side. fill_caps : bool (default=True) If `True`, include the faces of the bottom and the top. top_radius : float (default=1) The radius of the top of the cylinder. bot_radius : float (default=1) The radius of the bottom of the cylinder length : float (default=1) The length of the cylinder. center : array_like of shape (3,) (default=(0, 0, 0)) The center of the bottom of the cylinder. normal : array_like of shape (3,) (default=(0, 0, 1)) The normal of the cylinder's top and bottom faces. co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface A cylinder. """ s = cylinder( resolution, fill_caps, top_radius, bot_radius, length, center, normal, co_attr ) F = rotation_from_to((0, 0, 1), (0, 1, 0), homogeneous=False) for v in s.verts: v.co = F @ v.co return s
[docs]def arrow( resolution=20, heights=(0, 0.7, 0.7, 1), radii=(0.05, 0.05, 0.125), co_attr="co", ): """ Create an arrow as a half-edge surface. This is useful to visualise vectors. By default, the arrow will point up. The bottom will be at (0, 0, 0) and the head at (0, 0, 1). Parameters ---------- resolution : int (default=20) Number of vertices around the center of rotation. Minimum is 3. heights : float sequence of length 4 (default=(0, 0.7, 0.7, 1)) First element defines height of the bottom of the stick, the second the height of the top of the stick, the third the height of the base of the tip and the forth the height of the tip of the head. radii : float sequence of length 3 (default=(0.05, 0.05, 0.125)) First element defines the radius of the bottom of the stick, second the radius of the top of the stick and the third the radius of the base of the tip. co_attr : str, optional (default="co") The name of the vertex attribute that stores the coordinates. If `co_attr=None`, don't assign any coordinates. Returns ------- s : ddg.datastructures.halfedge.surface.Surface An arrow. """ ifs_obj = ifs_gen.arrow(resolution, heights, radii, co_attr) s = _convert_to_halfedge(ifs_obj) if co_attr: _add_vertex_coordinates(ifs_obj, s, co_attr) return s