Source code for ddg.halfedge._math

import numpy as np

import ddg
import ddg.math.projective as pmath
from ddg import nonexact
from ddg.halfedge._copy import copy
from ddg.halfedge._get import single_edges
from ddg.halfedge._modify import remove_edge, remove_vertex


[docs]def dehomogenize( hds, co_attr_homogeneous="co", co_attr_affine="co", in_place=True, atol=None, rtol=None, ): """Dehomogenize a hds, removing infinity crossings. Let x_i = 0 be the hyperplane at infinity. The function works by deleting vertices that are in this plane and edges (and therefore faces) that cross this plane. **This depends on the representative vectors!** Parameters ---------- hds : Surface co_attr_homogeneous : str (default='co') Name of attribute that stores homogeneous coordinates. co_attr_affine : str (default='co') Name of attribute to store affine coordinates after dehomogenizing. in_place : bool (default=True) Whether to modify the `hds` or create a copy. The copy will only have the attributes `co_attr_homogeneous` and `co_attr_affine`. Returns ------- hds : Surface The dehomogenized hds. Either the original, modified object or a copy with vertex attributes with names given in `co_attr_homogeneous` and `co_attr_affine`. """ if not in_place: # co_attr_affine is created/set later hds = copy(hds, verts_attr_list=[co_attr_homogeneous]) # Delete vertices at infinity for v in hds.verts: p = getattr(v, co_attr_homogeneous) if nonexact.isclose(p[-1], 0, atol=atol, rtol=rtol): remove_vertex(v) # Delete edges that pass through the plane at infinity # Convert this generator to list first to get all elements. Modifying the # hds while using the generator can lead to "AttributeError: _succ" for e in list(single_edges(hds)): p1 = getattr(e.head, co_attr_homogeneous) p2 = getattr(e.tail, co_attr_homogeneous) if np.sign(p1[-1]) != np.sign(p2[-1]): remove_edge(e) # Set new dehomogenized coordinate attribute # Node attributes are descriptors stored in class attributes if not hasattr(hds.verts, co_attr_affine): hds.verts.add_attribute(co_attr_affine) for v in hds.verts: p = getattr(v, co_attr_homogeneous) p = pmath.dehomogenize(p, atol=atol, rtol=rtol) setattr(v, co_attr_affine, p) return hds
[docs]def laplace_invariant(edge, co_attr="co", line_family_attr=None, atol=1e-4): r"""Function to compute the Laplace invariant of an interior edge. Namely the cross ratio q(e.tail, L^{(i)}_{j}, e.head, L^{(i)}_{-j}), where L^{(i)}_{\pm j} are the intersection points of the previous and next lines of the same line family with the line given by the given edge. A line family is interpreted as either one of the two directions (e.g. horizontal and vertical) introduced by the preimage lattice of \mathbb{Z}^2. If a boundary edge (or an edge where e.opp is a boundary edge) is given the return value is None. Parameters ---------- edge: ddg.halfedge.Surface.edges Edge to compute the laplace invariant from. co_attr: str (default='co') Coordinate attribute at the vertices used for computation. line_family_attr: NodeAttribute Attribute of edges, that distinguishes the horizontal and vertical family of lines. If given, and True on an edge, than the reciprocal of the computed value is set to this edge. atol: float (default=1e-4) Tolerance to verify computations for coefficients of the planes induces by the quads of either two edges. Returns ------- float, None float for interior edges and None for boundary edges """ if ddg.halfedge._get.is_boundary_edge(edge) or ddg.halfedge._get.is_boundary_edge( edge.opp ): return None Q1 = [e.tail for e in ddg.halfedge._get.edge_loop(edge)] Q2 = [ e.tail for e in ddg.halfedge._get.edge_loop( ddg.halfedge._get.opposite_edge_in_face(edge.opp).opp ) ] Q1 = np.array([ddg.math.projective.homogenize(getattr(v, co_attr)) for v in Q1]) Q2 = np.array([ddg.math.projective.homogenize(getattr(v, co_attr)) for v in Q2]) C1 = ddg.math.linalg.linear_dependence(Q1, atol=atol) C2 = ddg.math.linalg.linear_dependence(Q2, atol=atol) assert nonexact.allclose(np.dot(C1, Q1), 0, atol=atol) assert nonexact.allclose(np.dot(C2, Q2), 0, atol=atol) value = (C1[1] * C2[3]) / (C1[0] * C2[2]) if line_family_attr: if line_family_attr[edge]: value = 1 / value return value
[docs]def laplace_invariant_cross(v, laplace_inv_attr): r"""Function to compute the product of the laplace invariants of the four edges incident to a single vertex. Enumerating successive edges in a vertex star (L_0 * L_2) / (L_1 * L_3) is the value that is returned. For discrete Koenigs nets this value is 1. Parameters ---------- v: ddg.halfedge.Surface.verts Vertex to compute the laplace invariant from. laplace_inv_attr: NodeAttribute of edges Attribute to read the laplace invariants of the edges from. Can be set e.g. using the laplace_invariant function. Returns ------- float, None None if the given vertex is a boundary vertex else the computed value""" if ddg.halfedge._get.is_boundary_vertex(v): return None out_edges = [e for e in ddg.halfedge._get.out_edges(v)] assert len(out_edges) == 4 laplace_inv = [laplace_inv_attr[e] for e in out_edges] laplace_inv_cross = (laplace_inv[0] * laplace_inv[2]) / ( laplace_inv[1] * laplace_inv[3] ) return laplace_inv_cross
[docs]def laplace_invariant_quad(face, laplace_inv_attr): r"""Function to return the product of the laplace invariants of the four edges incident to a face. Enumerating successive edges in a face (L_0 * L_2) / (L_1 * L_3) is the value that is returned. For a Q-net that is build from the intersection points of the diagonals of faces of a discrete Koenigs net (the Doliwa dual) this value is 1. Parameters ---------- face: ddg.halfedge.Surface.faces Face to compute the laplace invariant from. laplace_inv_attr: NodeAttribute of edges Attribute to read the laplace invariants of the edges from. Can be set e.g. using the laplace_invariant function. Returns ------- float, None None if the given face is a boundary face else the computed value""" if not np.alltrue( [ not ddg.halfedge._get.is_boundary_vertex(v) for v in ddg.halfedge._get.face_vertices(face) ] ): return None face_edges = [e for e in ddg.halfedge._get.edge_loop(face.edge)] assert len(face_edges) == 4 laplace_inv = [laplace_inv_attr[e] for e in face_edges] laplace_inv_quad = (laplace_inv[0] * laplace_inv[2]) / ( laplace_inv[1] * laplace_inv[3] ) return laplace_inv_quad