#! /usr/bin/python3 # Last edited on 2024-09-19 08:53:49 by stolfi from math import sin, cos, tan, sqrt, pi, inf, floor, sqrt import rn import sys import re from slicing_lib import make_edge, make_face, check_repeated_edge, prte, prtf def monotonize(Ni, Vind, Vlst, Elst, Flst, prec, verbose): # See {slicing_naf_example.py} and # {slicing_naf_vertices.make_vertices} for the description of the # object. # # Subdivides the faces of the original mesh of the object into # {Z}-monotone polygons. Assumes that the vertices are given by the # structured table {Vind} and flat list {Vlst}, the edges are given by # the flat list {Elst}, and the faces are given by the flat list # {Flst}. The lists {Vlst,Elst,Flst} are indexed from 1; element 0 is # not used. # # Returns new flat lists {Fmon} and {Emon} of faces and edges of the # monotone refinement of the mesh. No new vertices are created. The # original edges are preserved with their original indices, and the # new diagonal edges are appended after them, with {etype=1}. The # faces are replaced by the monotone polygons. # # Original faces "fu.{ki}" (upper chain) and "fs.{ky}" (spoke faces) # are preserved, with the same labels, since they are monotone. Each # plaza "fp.{kx}" is split into two monotone polygons, with labels # "fp.{kx}.{km}" where {km} is 0 or 1. # # The vertical diagonal added across each plaza will have labels # "dp.{kx}". assert Ni >= 2 and Ni %2 == 0, "{Ni} must be positive and even" Nv_in = len(Vlst)-1 # Total vertex count (input and output). Ne_in = len(Elst)-1 # Total input edge count. Nf_in = len(Flst)-1 # Total input faces. Nv_ot = 2*(Ni+2) # Expected output vertex count. Ne_ot = 3*(Ni+2) + 2 # Expected output edge count. Nf_ot = Ni + 6 # Expected output face (monotone polygon) count. sys.stderr.write("Expecting %d edges\n" % Ne_ot) sys.stderr.write("Expecting %d faces\n" % Nf_ot) assert Nv_ot == Nv_in, "{Ni,Vlst} inconsistent" Fmon = [None]*(Nf_ot+1) Emon = Elst.copy() + [None]*(Ne_ot-Ne_in) Eset = set() # Set of edges as pairs, to check for repetitions. for je in range(1,Ne_in+1): e = Elst[je] Eset.add((e[0],e[1])) ne = Ne_in # Number of edges created so far. nf = 0 # Number of faces created so far. def Svdi(kv_org, kv_dst, elab): # Adds the diagonal edge from {Vlst[kv_org]} to {Vlst[kv_dst]} to {Emon}, # reoriented in increasing index sense. nonlocal ne, nf if kv_org > kv_dst: kv_org, kv_dst = kv_dst, kv_org etype = 1 e = make_edge(Vlst[kv_org], Vlst[kv_dst], ne+1, etype, elab, prec) assert e != None if verbose: prte(e, prec) ne += 1 Emon[ne] = e check_repeated_edge(ne, Emon, Eset, prec) return None def Mon(Miv,flab): # Adds to {Emon} the polygonal face with vertices {Vlst[kv]} for # {kv} in {Miv}, assumed to be monotone and CCW seen from outside. nonlocal ne, nf nf += 1 t = make_face(Miv, nf, flab, Vlst, prec) if verbose: prtf(t, Vlst, prec) Fmon[nf] = t for f in Flst[1:]: Nx,Ny,Nz,Fiv,kf,flab = f; if verbose: sys.stderr.write("\n") sys.stderr.write(" IN: ") prtf(f, Vlst, prec) sys.stderr.write("\n") if flab[0:2] == "fu" or flab[0:2] == "fs": # Upper chain or spoke face: assert len(Fiv) == 4 Mon(Fiv,flab) elif flab[0:2] == "fp": # Plaza face: assert len(Fiv) == Ni+2; kx = int(flab[3]) assert kx == 0 or kx == 1 if kx == 0: # Assume that the vertices are upper chain then bottom center. jmid = Ni//2 # Middle vertex of upper chain: jctr = Ni+1 Miv0 = Fiv[0:jmid+1] + [ Fiv[jctr] ] Miv1 = Fiv[jmid:] else: # Assume that the vertices are bottom center then upper chain, reversed. jmid = Ni//2+1 # Middle vertex of upper chain: jctr = 0 Miv0 = Fiv[0:jmid+1] Miv1 = Fiv[jmid:] + [ Fiv[jctr] ] Mon(Miv0,flab + f".0") Mon(Miv1,flab + f".1") Svdi(Fiv[jctr],Fiv[jmid],"d" + flab[1:]) else: assert False, f"invalid face label '{flab}'" if nf < Nf_ot: sys.stderr.write("!! missing some faces\n") assert nf <= Nf_ot if ne < Ne_ot: sys.stderr.write("!! missing some edges\n") assert ne <= Ne_ot return Emon, Fmon