diff --git a/.gitignore b/.gitignore index f61e9cd..e8ce1fb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ logs* *.DS_Store *.so .DS_Store - +*vscode* +*pycache* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f9fe157 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,73 @@ +{ + "files.associations": { + "array": "cpp", + "vector": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ratio": "cpp", + "regex": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "algorithm": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 553b8d3..1c0f10a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,12 +41,14 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_subdirectory(src/core) add_subdirectory(src/quadratic_spline_surface) add_subdirectory(src/contour_network) +add_subdirectory(src/clough_tocher_patches) target_include_directories(QuadraticContoursCoreLib PUBLIC ${PROJECT_SOURCE_DIR}/ext ) add_library(QuadraticContoursLib INTERFACE) target_link_libraries(QuadraticContoursLib INTERFACE QuadraticContoursCoreLib QuadraticSplineSurfaceLib ContourNetworkLib + CloughTocherPatchLib ) # Build utility library diff --git a/src/clough_tocher_patches/CMakeLists.txt b/src/clough_tocher_patches/CMakeLists.txt new file mode 100644 index 0000000..9232c02 --- /dev/null +++ b/src/clough_tocher_patches/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(CloughTocherPatchLib + clough_tocher_patch.hpp + clough_tocher_patch.cpp + clough_tocher_autogen_matrix_coeffs.hpp + clough_tocher_matrices.cpp + clough_tocher_surface.cpp + clough_tocher_autogen_constraint_matrices.hpp + clough_tocher_constraint_matrices.hpp + clough_tocher_constraint_matrices.cpp +) +target_include_directories(CloughTocherPatchLib PUBLIC .) +target_link_libraries(CloughTocherPatchLib PUBLIC + QuadraticContoursCoreLib + QuadraticSplineSurfaceLib +) +target_compile_definitions(CloughTocherPatchLib PUBLIC + SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG + # SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF +) + +target_compile_options(CloughTocherPatchLib PRIVATE + -Wall -Wpedantic -Wextra -Werror +) \ No newline at end of file diff --git a/src/clough_tocher_patches/Clough_Toucher_derivation.py b/src/clough_tocher_patches/Clough_Toucher_derivation.py new file mode 100644 index 0000000..1ba4ea4 --- /dev/null +++ b/src/clough_tocher_patches/Clough_Toucher_derivation.py @@ -0,0 +1,536 @@ +from sympy import Rational, Matrix, symbols, det, collect, simplify, solve, diff, Eq, expand, Poly +import sympy as sp +import numpy as np + +# ********* helper functions ********* +def nxt(i): + return (i+1) % 3 +def prev(i): + return (i+2) % 3 + +# Equation of a line in homogeneous coordinates +def ep2line(a, b): + """Returns the implicit form of a line passing through points a and b.""" + return collect(det(Matrix([a.T, b.T, Matrix([[u, v, w]])])), [u, v, w]) + +def ep2param_line(a, b): + """Returns the param equation of a line passing through points a and b.""" + return a + (b - a) * t + +# Derivatives in homogeneous coordinates +# these are linearly dependent, using 3 for symmetry for variables u,v,w +def gradb(f): + return [ + (1 / Rational(2)) * (2 * diff(f, u) - diff(f, v) - diff(f, w)), + (1 / Rational(2)) * (2 * diff(f, v) - diff(f, u) - diff(f, w)), + (1 / Rational(2)) * (2 * diff(f, w) - diff(f, v) - diff(f, u)) + ] + + + +# ********* global definitions, barycentric coord symbols ********* + +u, v, w = symbols('u v w') +uvwvars = (u,v,w) + +# parametric line parameter +t = symbols('t') + +# ********* global definitions, triangle geometry ********* + +# reference triangle geometry +# triangle corners, homogeneous coords +pt = [ + Matrix([Rational(1), Rational(0), Rational(0)]), + Matrix([Rational(0), Rational(1), Rational(0)]), + Matrix([Rational(0), Rational(0), Rational(1)]) + ] + # edge midpoints +midpt = [ + (pt[0] + pt[1]) / 2, + (pt[1] + pt[2]) / 2, + (pt[2] + pt[0]) / 2 + ] + +center = (pt[0] + pt[1] + pt[2]) / 3 + +# three subtriangles +CTtri = [ + [pt[0], pt[1], center], # adjacent to the edge w = 0, 01 + [pt[1], pt[2], center], # adjacent to the edge u = 0, 12 + [pt[2], pt[0], center] # adjacent to the edge v = 0, 20 + ] + +# Lines passing through the center and triangle vertices +lineseq_center = [simplify(ep2line(pt[i], center)) for i in range(3)] + +# equations of lines bounding subtriangles +CTtri_bounds = [ Matrix([ [q.coeff(bc) for bc in uvwvars] for q in p]) for p in list( map( lambda x: [ep2line(x[0],x[1]), + ep2line(x[1],x[2]), ep2line(x[2],x[0])], CTtri))] + + +# ********* global definitions, Lagrange nodes ********* + +#subtriangle corner barycentric coordinates +bp0 = np.array([Rational(1),Rational(0),Rational(0)]) +bp1 = np.array([Rational(0),Rational(1),Rational(0)]) +bp2 = np.array([Rational(0),Rational(0),Rational(1)]) +bc = np.array([Rational(1,3),Rational(1,3),Rational(1,3)]) + +# barycentric coords of all Lagrange nodes, multiplied by 9, so that these are all integers +# computed as linear combinations of corners of subtriangles +# coefficients should sum up to 9 +node_bary = [ + 9*bp0, 9*bp1, 9*bp2, # corners + 6*bp0+3*bp1, 3*bp0+6*bp1, 6*bp1+3*bp2, 3*bp1+6*bp2, 6*bp2+3*bp0, 3*bp2+6*bp0, # 2 nodes per exterior edge,split the edges 2:1 and 1:2 + 3*bp0+3*bp1+3*bc, 3*bp1+3*bp2+3*bc, 3*bp2+3*bp0+3*bc, # subtriangle center nodes + 6*bp0+3*bc, 3*bp0+6*bc, 6*bp1+3*bc, 3*bp1+6*bc, 6*bp2+3*bc, 3*bp2+6*bc, # 2 nodes per interior edge split the edges 2:1 and 1:2 + 9*bc # center node +] +subtri_nodes = [ + [0,1, 3+0, 3+1, 9 , 12+0, 12+1, 12+2, 12+3, 18], + [1,2, 3+2, 3+3, 10, 12+2, 12+3, 12+4, 12+5, 18], + [2,0, 3+4, 3+5, 11, 12+4, 12+5, 12+0, 12+1, 18], +] + +# a helpde function to create symbolic names for nodes based on their barycentric coords +def bary2name(bc): + return 'c'+''.join([ str(c) for c in bc]) + +# dictionary barycentric coordinate tuples -> symbolic name for an node +node_vars_dict = {tuple(map(lambda x: int(x), b)): symbols(bary2name(b)) for b in node_bary} + +# array of barycentric coord tuples in the same order as in node_bary +ind2bary = [tuple(map(lambda x: int(x), b)) for b in node_bary] + +# array of variables in the order matching node_bary +node_vars = [ node_vars_dict[ind2bary[i]] for i in range(19)] + +# polynomials that can be used to evaluate each of the Lagrange nodes +node_subtri = \ +[0, 1, 2, # corners, one index of two possible + 0, 0, 1, 1, 2,2, # exterior edges + 0, 1, 2, # subtriangle centers + 0, 0, 1, 1, 2, 2, # interior edges, one index of two possible + 0 # any polynomial can be used +] + + +# ********* global definitions, boundary dofs of the element ********* + +# boundary data: corners, derivatives along edges, midedge derivatives in the opposite vertex direction +p0,p1,p2 = symbols(['p0','p1','p2']) +vtx_unknowns = (p0,p1,p2) +G01,G10,G12,G21,G20,G02 = symbols('G01 G10 G12 G21 G20 G02') +side_deriv_unknowns =(G01,G10,G12,G21,G20,G02) +N01, N12, N20 = symbols('N01 N12 N20') +mid_deriv_unknowns = (N01, N12, N20) +all_unknowns = vtx_unknowns + side_deriv_unknowns + mid_deriv_unknowns + +# ********* main function: derives subtriangle polynomials ********* + +def derive_C1_polynomials(vtx_unknowns,side_deriv_unknowns,mid_deriv_unknowns): + """ This function derives expressions for Clough-Tocher polynomials on subtriangles + with coefficients determined by the boundary data and joined with C1 continuity + in the interior. The boundary data includes: + -- vtx-unknowns: function vertex values + -- side_deriv_unknowns: values of scaled derivatives Gij along triangle sides at each vertex + i.e. grad F(p_i) dot e_ij where p_i is the vertex and e_ij is the edge vector + -- mid_deriv_unknowns at the midpoints of scaled derivatives along vectors to vertices + i.e., grad F(m_ij) dot (p_k - m_ij), where k is the vertex opposite ij, m_ij = (p_i + p_j)/2 + The ordering of the unknowns is p0,p1,p2, G01,G10, G12,G21, G20, G21, N01, N12, N20 + + The function returns a list of 3 polynomials, one per triangle, in variables u,v,w + and with coefficienets dependent only on the unknowns passed in + """ + global pt, midpt, center, CTtri, lineseq_center + + # coefficients of an inknown polynomial for the first triangle, to be sovled for + q300, q030, q210, q120, q201, q021, q003, q111, q102, q012 = \ + symbols('q300 q030 q210 q120 q201 q021 q003 q111 q102 q012') + first_poly_coeffs = (q300, q030, q210, q120, q201, q021, q003, q111, q102, q012) + + # additional coefficients to define other polynomials relative to the first one + lambda_ = symbols('lambda_0:4') + mu = symbols('mu_0:4') + nu = symbols('nu_0:4') + + + # Polynomial for subtriangle 0 + startpoly = q300 * u**3 + q030 * v**3 + q210 * u**2 * v + q120 * u * v**2 \ + + q201 * u**2 * w + q021 * v**2 * w + q003 * w**3 + q111 * u * v * w \ + + q102 * u * w**2 + q012 * v * w**2 + + # Define 3 polynomials joined with C1 continuity walking around the center vertex and adding a term of the form + # d = (lambda[i]*u + mu[i]*v + nu[i]*w)*(L)^2 each time we cross a line with equation L(u,v,w)=0 + # this ensures C1: d(u,v) has zero values and derivatives on L, so the two polys on two sides + # differ by d(u,v) only. Crossing the last line takes us back to the original triangle, + # so the polynomial should be the same, which allows to eliminate all unknowns. + + polys = [ + startpoly + sum( (lambda_[i]*u + mu[i] * v + nu[i] * w) * lineseq_center[nxt(i)]**2 for i in range(j)) + for j in range(4) ] + + # solve for all mu's and nu's and one of lambdas + # based on equating the last polynimal and first + depvars = solve( Poly(polys[3]-polys[0], [u,v,w]).coeffs(), + [lambda_[0],mu[0],mu[1],mu[2], nu[0], nu[1], nu[2]]) + + indepvars = first_poly_coeffs + (lambda_[1], lambda_[2]) + + # Substitute into the C1 polynomials + polys_C1 = [poly.subs(depvars) for poly in polys] + + # Values at triangle corners + vp = [polys_C1[0].subs({u: 1, v: 0, w: 0}), + polys_C1[1].subs({u: 0, v: 1, w: 0}), + polys_C1[2].subs({u: 0, v: 0, w: 1}) + ] + + # Variables for side derivatives at vertices + + # variable permutations for 6 scaled derivatives along triangle sides, + # eg at corner point #0, with barycentric coords [1,0,0], i.e. u=1, and with exterior edge corresponding to w = 0, i.e., + # the increasing variable along the edge is v, so we set side_vars[0][0] (in this case w) to 0, differentiate with respect to side_vars[0][1] (in this case v) + # and then set side_vars[[0][1] = 0 and side_vars[0][2] (in this case u) to 1. + # side_vars[i][0]: side line equation is side_vars[i][0] == 0 + # side_vars[i][1]: variable changing along the edge line, with value 0 at the vertex i + # side_vars[i][2]: variable changing along the edge line, with value 1 at the vertex i + + #side_vars := [ [w,v,u],[w,u,v],[u,w,v],[u,v,w],[v,u,w],[v,w,u]]: + #pside_deriv := [seq( subs( + # {side_vars[i][2]=0,side_vars[i][3]=1}, + # diff( subs(side_vars[i][1] = 0, polys_C1[i+6]),side_vars[i][2]) - + # diff( subs(side_vars[i][1] = 0, polys_C1[i+6]),side_vars[i][3])), i=1..6)]; + + + side_vars = [ + [w, v, u], # edge w = 0, corner u = 1 G01 + [w, u, v], # edge w = 0, corner v = 1 G10 + [u, w, v], # edge u = 0, corner v = 1 G12 + [u, v, w], # edge u = 0, corner w = 1 G21 + [v, u, w], # edge v = 0, corner w = 1 G20 + [v, w, u]] # edge v = 0, corner u = 1 G02 + side_poly_ind = [0, 0, 1, 1, 2, 2] + + pside_deriv = [ + (diff( + polys_C1[side_poly_ind[i]].subs(side_vars[i][0], 0), side_vars[i][1]) + - diff( + polys_C1[side_poly_ind[i]].subs(side_vars[i][0], 0), side_vars[i][2])).subs( + {side_vars[i][1]: 0, side_vars[i][2]: 1}) + for i in range(6)] + + + # Midpoint derivatives in the direction of the opposite vertex + # e.g., diff( poly[0], w)- 1/2( diff(poly[1],u) - diff(poly[2],v)) evaluated at (1/2, 1/2, 0) + # reusing side_vars here, for edges 0,1,2 - does not matter which of two vertices to pick + + mid_deriv = [ + (diff(polys_C1[i], side_vars[2 * i][0]) + - (1 / Rational(2)) * diff(polys_C1[i], side_vars[2 * i][1]) + - (1 / Rational(2)) * diff(polys_C1[i], side_vars[2 * i][2])).subs( + {side_vars[2 * i][0]: 0, side_vars[2 * i][1]: Rational(1, 2), side_vars[2 * i][2]: Rational(1, 2)}, + ) + for i in range(3) + ] + polys_C1_f = [p.subs(solve( + [side_deriv_unknowns[i]- pside_deriv[i] for i in range(6)]+ # equations for derivatives along sides + [mid_deriv_unknowns[i] - mid_deriv[i] for i in range(3)] + # equations for midpoint derivatives + [vp[i].subs(depvars)-vtx_unknowns[i] for i in range(3)] # corner positions + , indepvars)) for p in polys_C1] + return polys_C1_f + + +# ********* For testing: compute all element boundary data from a function f ********* + +def build_data(f): + """ Compute vertex values, edge derivatives and midpoint derivatives from a function f(u,v) + """ + u, v = sp.symbols('u v') + + ff = sp.Lambda((u, v), f) + df = sp.Lambda((u, v), Matrix([[sp.diff(f, u), sp.diff(f, v)]])) + + vtx = sp.sympify('[[1, 0], [0, 1], [0, 0]]') + midpt = sp.sympify('[[1/2, 1/2], [0, 1/2], [1/2, 0]]') + edges = sp.sympify('[[-1, 1], [1, -1], [0, -1], [0, 1], [1, 0], [-1, 0]]') + middir = sp.sympify('[[-1/2, -1/2], [1, -1/2], [-1/2, 1]]') + p_values = [ff(*vtx[i]) for i in range(3)] + grad = [df(*v) for v in vtx] + # gradients per endpoint of edges + grad_e = [grad[0],grad[1],grad[1],grad[2],grad[2],grad[0]] + G_values = [grad_e[i].dot(Matrix(edges[i])) for i in range(6)] + N_values = [Matrix(df(*midpt[i])).dot(Matrix( middir[i])) for i in range(3)] + return p_values + G_values + N_values + +def subs_data(p, bdata): + """ Substitute boundary data values into a polynomial expression""" + global vtx_unknowns, side_deriv_unknowns, mid_deriv_unknowns + subs_eqs = \ + [[vtx_unknowns[i],bdata[i]] for i in range(3)] +\ + [[side_deriv_unknowns[i], bdata[i+3]] for i in range(6)] +\ + [[mid_deriv_unknowns[i], bdata[i+9]] for i in range(3)] + return p.subs(subs_eqs) + +# ********* Bezier form ********* + +# currently not used, all polynomials stay in complete triangle barycentric coords, not in subtriangle +# order 003, 012,021,030, 102,111,120, 201, 210, 300 +bezier_deg = 3 +unity_poly = ((u+v+w)**bezier_deg).expand() +bernstein_basis = [unity_poly.coeff(u**i *v**j * w**(bezier_deg-i-j))*u**i *v**j * w**(bezier_deg-i-j) + for i in range(bezier_deg+1) for j in range(bezier_deg+1-i)] +monomial_basis = [ u**i *v**j * w**(bezier_deg-i-j) for i in range(bezier_deg+1) for j in range(bezier_deg+1-i)] +bernstein_scales = [ [1/unity_poly.coeff(u**i *v**j * w**(bezier_deg-i-j)) for j in range(bezier_deg+1-i)] for i in range(bezier_deg+1)] +def coeff2bezier_cubic(p): + return [p.coeff(u**i *v**j * w**(bezier_deg-i-j))*bernstein_scales[i][j] + for i in range(bezier_deg+1) for j in range(bezier_deg+1-i)] + + +# ******** Evaluation ********* + +def poly2matrix(p): + return Matrix( [[p.expand().coeff(u**i *v**j * w**(bezier_deg-i-j)*all_unknowns[k]) + for i in range(bezier_deg+1) for j in range(bezier_deg+1-i)] for k in range(12)]).T + +def compute_CT_matrices(polys): + return [poly2matrix(p) for p in polys] + +def triangle_ind(CTtri_bounds,u,v,w): + """Check which triangle defined in CTtri_bounds if any contains the point (u,v,w). + CTtri_bounds is a list of 3 x 3 matrices of the coefficients of lines bounding subtriangles""" + for i in range(3): + if all( [CTtri_bounds[i][j,0]*u +CTtri_bounds[i][j,1]*v +CTtri_bounds[i][j,2]*w >= -1e-7 for j in range(len(CTtri_bounds))]): + return i + return -1 + +monomial_basis_eval = sp.lambdify( (u,v,w), Matrix(monomial_basis).evalf(),'numpy') + +# This function can be easily converted to C: just need to write out the numerical matrices +# CT_matrices, CTtri_bounds, and write a function evaluating the monomial basis vector from u,v,w +# (monomial_basis_eval) +def CT_eval(CT_matrices, CTtri_bounds, boundary_data, u,v,w): + """ + This function evaluates the Clough-Tocher interpolant at barycentric point (u,v,w) + It takes as input + -- CT_matrices: array of three 10 x 12 matrices computed from the coefficients + of the C-T polynomials on subtriangles using compute_CT_matrices. These are constant matrices, do not depend on data. + -- CTtri_bounds: 3 3 x 3 matrices, one per subtriangle, each row is the coefficients of the line along an edge of the subtriangle + -- boundary_data: vector of 12 values for the boundary data in the order p0, p1, p2, G01, ... G02, N01, N12, N20 + """ + ind = triangle_ind(CTtri_bounds, u,v,w) + if ind >= 0: + bb_vector = monomial_basis_eval(u,v,w) + return (CT_matrices[ind]*boundary_data).dot(bb_vector) + else: + return np.nan + +# ******* Converting to Lagrange basis ****** +def generate_L2L(polys_C1, node_bary, node_subtri, all_inknowns): + """ + Generates a matrix L_d2L that maps a vector of 12 dofs (pi, Gij, Nij) to the vector of Lagrange node values + enumerated as described by node_bary and node_vars. node_subtri is the 3 polynomials on subtriangles with + coefficients given in terms of (pi, Gij, Nij) + """ + subtri_polys = [Poly(p,[u,v,w]) for p in polys_C1] + Lagr_node_vals = [ subtri_polys[node_subtri[i]](*(node_bary[i]/9)) for i in range(19) ] + local_dof_vars = all_unknowns + L_d2L = Matrix( [ [ Lnode.coeff(v) for v in local_dof_vars] for Lnode in Lagr_node_vals]) + return L_d2L + +# a vector of coefficients c_e + +def generate_ce(subtri_polys): + "Creates a vector of coefficients which, when applied to the vector [p0,p1,G01,G10,N01] yields the derivative along the edge at the midpoint." + midpt_dfde = subtri_polys[0].as_expr().subs({w:0,v:1-u}).expand().collect(u).diff(u).subs({u:Rational(1,2)}) + edge_endpt_vars = [p0,p1,G01,G10, N01] + c_e = [midpt_dfde.coeff(v) for v in edge_endpt_vars] + return c_e + +# ******** Testing ********* + +def runTests(polys_C1_f): + global pt, center; + q300, q030, q210, q120, q201, q021, q003, q111, q102, q012 = \ + symbols('q300 q030 q210 q120 q201 q021 q003 q111 q102 q012') + testpoly = q300 * u**3 + q030 * v**3 + q210 * u**2 * v + q120 * u * v**2 \ + + q201 * u**2 * w + q021 * v**2 * w + q003 * w**3 + q111 * u * v * w \ + + q102 * u * w**2 + q012 * v * w**2 + + # Test: infer the input dofs from a polynomial (using generic deg 3) and verify that the result is the same, should be [0,0,0,0] + bd = build_data(testpoly.subs(w,1-u-v)) + passed = not any([subs_data(p-testpoly,bd) for p in polys_C1_f ]) + print('Cubic polynomial reproduction '+ ('passed' if passed else 'not passed')) + + # Test that polynomials on boundaries between subtriangles match + passed = not any ([ (polys_C1_f[j].subs( dict( [[uvwvars[i], ep2param_line(pt[j],center)[i]] for i in range(3)]))-\ + polys_C1_f[prev(j)].subs( dict( [[uvwvars[i], ep2param_line(pt[j],center)[i]] for i in range(3)]))).expand() for j in range(3)]) + print('Polynomial on internal subtriangle boundaries match '+ ('passed' if passed else 'not passed')) + + # Test that derivatives of polynomials on boundaries between subtriangles match + hom_grads = [gradb(polys_C1_f[i]) for i in range(3)] + passed = not any([ (hom_grads[j][k].subs( dict( [[uvwvars[i], ep2param_line(pt[j],center)[i]] for i in range(3)]))-\ + hom_grads[prev(j)][k].subs( dict( [[uvwvars[i], ep2param_line(pt[j],center)[i]] for i in range(3)]))).expand() + for j in range(3) for k in range(3)]) + print('Gradient on internal subtriangle boundaries match '+ ('passed' if passed else 'not passed')) + + + +def test_Lagrange_consistency(polys_C1_f, L_L2d_ind, L_ind2dep): + """ + polys_C1_f: 3 subtriangle polynomials with coefficients in terms of (pi, Gij, Nij) variables, + L_L2d_ind: 12 x12 matrix expressing (pi, Gij, Nij) in terms of 12 indep. Lagrange node values + L_ind2dep: matrix expressing 7 dependence Lagrange nodes in terms of independent; this ensures internal edge C1 constraints + The test verifies that + (1) when we use L_L2d_ind to express polynomial coefficient in terms of indep Lagrange nodes + we get these nodes back when evaluating at their barycentric coordinates. + (2) when we evaluate at the dependent node coordinates we get the same as we get by applying L_ind2dep to indep node values. + As for any choice of (pi, Gij, Nij) dofs the polynomials are already verified to be C1 at the interior edges, + this also verifies that if the dependent node values are computed from independent using L_ind2dep, the resulting polynomials + are C1 at the interior edges. + """ + global all_unknowns, node_vars, node_bary, subtri_nodes + dof_vars = all_unknowns + + # express the triangle dof values (pi, Gij, Nij) in terms of independ lagrange node values using provided matrix + dof_lagr_subs = dict( [[ dof_vars[i], (L_L2d_ind * Matrix( [node_vars[0:12]]).T)[i] ] for i in range(12)]) + # express dependent node values in terms of indepedent + dep_nodes_expr = L_ind2dep*Matrix([node_vars[0:12]]).T + + # evaluate the polynomials on subtriangles at node barycentric coordinates, verify we get the Lagrange nodes back + passed = True + for i in range(3): + passed = passed and not any([ Poly( polys_C1_f[i].subs( dof_lagr_subs), [u,v,w])(*node_bary[j]/9)- node_vars[j] for j in subtri_nodes[i] if j < 12]+\ + [ Poly( polys_C1_f[i].subs( dof_lagr_subs), [u,v,w])(*node_bary[j]/9)- dep_nodes_expr[j-12] for j in subtri_nodes[i] if j >= 12]) + print('Lagrange consistency test '+ ('passed' if passed else 'not passed')) + +# ******** Plotting ********* +# !!!! ChatGPT generated - probably there is a better way to plot a function on a triangle but not sure +import plotly.graph_objects as go + +def plot_function_on_triangle(func, fun_name, vertices, resolution=20): + """ + Plots a function on a single triangular domain using a regular grid. + + Args: + func (callable): A function of two arguments (u, v) to be plotted. + vertices (list): A list of 3 corner points defining the triangle in the (u, v)-plane, e.g., [[0, 0], [1, 0], [0, 1]]. + resolution (int): Number of divisions along each edge of the triangle. + + Returns: + None. Displays an interactive plot. + """ + # Convert vertices to a numpy array + vertices = np.array(vertices) + + # Create a regular grid of barycentric coordinates + bary_coords, triangles = create_regular_grid_and_triangles(resolution) + + # Convert barycentric coordinates to Cartesian coordinates + grid_points = barycentric_to_cartesian(bary_coords, vertices) + + # Evaluate the function at grid points + z_vals = np.array([float(func(u, v)) for u, v in grid_points]) + + # Add the function surface as a triangulated mesh + fig = go.Figure(go.Mesh3d( + x=grid_points[:, 0], + y=grid_points[:, 1], + z=z_vals, + i=triangles[:, 0], + j=triangles[:, 1], + k=triangles[:, 2], + intensity=z_vals, + colorscale="Viridis", + colorbar=dict(title="Function Value"), + opacity=0.9 + )) + + # Add the triangle base in the (u, v)-plane for context + fig.add_trace(go.Mesh3d( + x=vertices[:, 0], + y=vertices[:, 1], + z=[0, 0, 0], + i=[0], + j=[1], + k=[2], + color="lightblue", + opacity=0.5, + name="Triangle Base" + )) + + # Update the layout for better visualization + fig.update_layout( + title="Clough-Tocher interpolant of "+fun_name, + scene=dict( + xaxis_title="u", + yaxis_title="v", + zaxis_title="Function Value", + aspectratio=dict(x=1, y=1, z=0.5), + ), + width=1000, + height=1000 + ) + # Show the interactive plot + fig.show() + return fig + + +def create_regular_grid_and_triangles(resolution): + """ + Creates a regular grid of points and corresponding triangles inside a triangular domain. + + Args: + resolution: Number of divisions along each edge. + + Returns: + tuple: + - np.ndarray: Array of barycentric coordinates for grid points. + - np.ndarray: Array of triangles, where each row contains 3 vertex indices. + """ + bary_coords = [] + triangles = [] + + # Generate barycentric coordinates for grid points + for i in range(resolution + 1): + for j in range(resolution + 1 - i): + k = resolution - i - j + bary_coords.append([i / resolution, j / resolution, k / resolution]) + s = bary_coords[-1][0]+ bary_coords[-1][1]+ bary_coords[-1][2] + if s > 1: + print(s) + + # Generate triangle indices + for i in range(resolution): + for j in range(resolution - i): + # Index of the top-left vertex + idx = i * (resolution + 1) - (i * (i - 1)) // 2 + j + # Triangle 1 + triangles.append([idx, idx + 1, idx + resolution + 1 - i]) + # Triangle 2 + if j < resolution - i - 1: + triangles.append([idx + 1, idx + resolution + 2 - i, idx + resolution + 1 - i]) + + return np.array(bary_coords), np.array(triangles) + + +def barycentric_to_cartesian(bary_coords, vertices): + """ + Converts barycentric coordinates to Cartesian coordinates. + + Args: + bary_coords: Barycentric coordinates as a numpy array. + vertices: Triangle vertices as a numpy array. + + Returns: + np.ndarray: Cartesian coordinates of the points. + """ + return np.dot(bary_coords, vertices) + +# end ChatGpt code + +if __name__ == '__main__': + # run validation + polys_C1_f = derive_C1_polynomials(vtx_unknowns,side_deriv_unknowns,mid_deriv_unknowns) + runTests(polys_C1_f) diff --git a/src/clough_tocher_patches/TestCloughTocher.ipynb b/src/clough_tocher_patches/TestCloughTocher.ipynb new file mode 100644 index 0000000..d35a81b --- /dev/null +++ b/src/clough_tocher_patches/TestCloughTocher.ipynb @@ -0,0 +1,6340 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "e8e9d29b-e0c3-4ae1-9365-006a052cb2a5", + "metadata": {}, + "outputs": [], + "source": [ + "from Clough_Toucher_derivation import *" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2a7ff18f-135e-4688-8fd9-0d06d6dd33cf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cubic polynomial reproduction passed\n", + "Polynomial on internal subtriangle boundaries match passed\n", + "Gradient on internal subtriangle boundaries match passed\n" + ] + } + ], + "source": [ + "polys_C1_f = derive_C1_polynomials(vtx_unknowns,side_deriv_unknowns,mid_deriv_unknowns)\n", + "runTests(polys_C1_f)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "15a935dd-9f07-4357-a658-5bd5b24e9172", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "colorbar": { + "title": { + "text": "Function Value" + } + }, + "colorscale": [ + [ + 0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1, + "#fde725" + ] + ], + "i": [ + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 21, + 22, + 22, + 23, + 23, + 24, + 24, + 25, + 25, + 26, + 26, + 27, + 27, + 28, + 28, + 29, + 29, + 30, + 30, + 31, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 37, + 37, + 38, + 38, + 39, + 39, + 41, + 42, + 42, + 43, + 43, + 44, + 44, + 45, + 45, + 46, + 46, + 47, + 47, + 48, + 48, + 49, + 49, + 50, + 50, + 51, + 51, + 52, + 52, + 53, + 53, + 54, + 54, + 55, + 55, + 56, + 56, + 57, + 57, + 58, + 58, + 60, + 61, + 61, + 62, + 62, + 63, + 63, + 64, + 64, + 65, + 65, + 66, + 66, + 67, + 67, + 68, + 68, + 69, + 69, + 70, + 70, + 71, + 71, + 72, + 72, + 73, + 73, + 74, + 74, + 75, + 75, + 76, + 76, + 78, + 79, + 79, + 80, + 80, + 81, + 81, + 82, + 82, + 83, + 83, + 84, + 84, + 85, + 85, + 86, + 86, + 87, + 87, + 88, + 88, + 89, + 89, + 90, + 90, + 91, + 91, + 92, + 92, + 93, + 93, + 95, + 96, + 96, + 97, + 97, + 98, + 98, + 99, + 99, + 100, + 100, + 101, + 101, + 102, + 102, + 103, + 103, + 104, + 104, + 105, + 105, + 106, + 106, + 107, + 107, + 108, + 108, + 109, + 109, + 111, + 112, + 112, + 113, + 113, + 114, + 114, + 115, + 115, + 116, + 116, + 117, + 117, + 118, + 118, + 119, + 119, + 120, + 120, + 121, + 121, + 122, + 122, + 123, + 123, + 124, + 124, + 126, + 127, + 127, + 128, + 128, + 129, + 129, + 130, + 130, + 131, + 131, + 132, + 132, + 133, + 133, + 134, + 134, + 135, + 135, + 136, + 136, + 137, + 137, + 138, + 138, + 140, + 141, + 141, + 142, + 142, + 143, + 143, + 144, + 144, + 145, + 145, + 146, + 146, + 147, + 147, + 148, + 148, + 149, + 149, + 150, + 150, + 151, + 151, + 153, + 154, + 154, + 155, + 155, + 156, + 156, + 157, + 157, + 158, + 158, + 159, + 159, + 160, + 160, + 161, + 161, + 162, + 162, + 163, + 163, + 165, + 166, + 166, + 167, + 167, + 168, + 168, + 169, + 169, + 170, + 170, + 171, + 171, + 172, + 172, + 173, + 173, + 174, + 174, + 176, + 177, + 177, + 178, + 178, + 179, + 179, + 180, + 180, + 181, + 181, + 182, + 182, + 183, + 183, + 184, + 184, + 186, + 187, + 187, + 188, + 188, + 189, + 189, + 190, + 190, + 191, + 191, + 192, + 192, + 193, + 193, + 195, + 196, + 196, + 197, + 197, + 198, + 198, + 199, + 199, + 200, + 200, + 201, + 201, + 203, + 204, + 204, + 205, + 205, + 206, + 206, + 207, + 207, + 208, + 208, + 210, + 211, + 211, + 212, + 212, + 213, + 213, + 214, + 214, + 216, + 217, + 217, + 218, + 218, + 219, + 219, + 221, + 222, + 222, + 223, + 223, + 225, + 226, + 226, + 228 + ], + "intensity": [ + 0, + -0.03591174457718357, + -0.13580028412885914, + -0.2878955773852138, + -0.4804275830764352, + -0.7016262599327101, + -0.9397215666842261, + -1.1829434620611703, + -1.4195219047937306, + -1.6376868536120934, + -1.8256682672464462, + -1.9716961044269765, + -2.0640003238838713, + -2.0908108843473183, + -2.040357744547504, + -1.9008708632146167, + -1.6605801990788427, + -1.30771571087037, + -0.8305073573193853, + -0.2171850971560771, + 0.5440211108893698, + 0.03591174457718357, + -3.0357660829594124e-18, + -0.1468754333565604, + -0.34026223935054123, + -0.56839037671213, + -0.8194898041715135, + -1.081790480458879, + -1.3435223643044139, + -1.592915414438307, + -1.8181995895907428, + -2.0076048484919102, + -2.149361149871997, + -2.231698452461189, + -2.2428467149896743, + -2.1710358961876395, + -2.0044959547852725, + -1.7314568495127607, + -1.3401485391002914, + -0.818800982278051, + -0.18127335257889296, + 0.13580028412885914, + 0.1468754333565604, + 3.122502256758253e-17, + -0.23183062819797748, + -0.4927072066963041, + -0.7708596942251666, + -1.054518049514753, + -1.33191223129525, + -1.5912721982968456, + -1.8208279092497257, + -2.008809322884078, + -2.1434463979300915, + -2.212969093117951, + -2.2056073671778456, + -2.109591178839961, + -1.9131504868344862, + -1.6045152498916069, + -1.1932731057437318, + -0.6947070731905266, + 0.2878955773852138, + 0.3402622393505412, + 0.23183062819797745, + 6.938893903907228e-17, + -0.2907773291014351, + -0.5931351861661475, + -0.8953035299243248, + -1.1855123191061538, + -1.4519915124418223, + -1.6829710686615176, + -1.8666809464954262, + -1.9913511046737362, + -2.0452115019266346, + -2.016492096984308, + -1.8934228485769442, + -1.6813198586365077, + -1.3911946101622197, + -1.019820133485156, + 0.4804275830764352, + 0.5683903767121299, + 0.4927072066963041, + 0.2907773291014351, + 1.3877787807814457e-16, + -0.323715536066933, + -0.6415461777600717, + -0.9417218838096038, + -1.2124726129457166, + -1.442028323898597, + -1.6186189753984324, + -1.73047452617541, + -1.765824934959717, + -1.7257147678828733, + -1.6168839721436572, + -1.4361055780731435, + -1.1801526160024078, + 0.7016262599327101, + 0.8194898041715133, + 0.7708596942251668, + 0.5931351861661475, + 0.32371553606693265, + 5.551115123125783e-17, + -0.3306452490944711, + -0.6379401814780762, + -0.9101147558810042, + -1.1353989310334405, + -1.3020226656655725, + -1.4067589901084772, + -1.4520763157604872, + -1.4347476729526785, + -1.3515460920161266, + -1.1992446032819066, + 0.9397215666842261, + 1.081790480458879, + 1.0545180495147528, + 0.8953035299243246, + 0.6415461777600712, + 0.330645249094471, + 1.1102230246251565e-16, + -0.3115664681840489, + -0.5823171973201625, + -0.8047536819389689, + -0.9770727976383602, + -1.0960475747494112, + -1.1584510436031974, + -1.1610562345307947, + -1.1006361778632772, + 1.1829434620611703, + 1.343522364304414, + 1.3319122312952496, + 1.1855123191061536, + 0.9417218838096034, + 0.6379401814780763, + 0.31156646818404926, + -4.5102810375396984e-17, + -0.27217457440292775, + -0.500306440088993, + -0.6811686273892726, + -0.8115341666348411, + -0.8881760881567747, + -0.9078674222861479, + 1.4195219047937306, + 1.592915414438307, + 1.5912721982968454, + 1.4519915124418228, + 1.2124726129457164, + 0.9101147558810041, + 0.5823171973201624, + 0.27217457440292764, + -1.5265566588595902e-16, + -0.23097955621969507, + -0.41753712458723263, + -0.5564457354336898, + -0.6444784190901407, + 1.6376868536120934, + 1.8181995895907432, + 1.820827909249726, + 1.6829710686615178, + 1.4420283238985967, + 1.1353989310334403, + 0.8047536819389692, + 0.5003064400889928, + 0.23097955621969504, + 2.7755575615628914e-17, + -0.18940525890116763, + -0.33400925081488353, + 1.8256682672464462, + 2.0076048484919107, + 2.008809322884078, + 1.8666809464954264, + 1.618618975398432, + 1.3020226656655727, + 0.9770727976383602, + 0.6811686273892725, + 0.417537124587233, + 0.1894052589011674, + -2.7755575615628914e-17, + 1.9716961044269765, + 2.149361149871997, + 2.1434463979300915, + 1.9913511046737362, + 1.7304745261754095, + 1.406758990108477, + 1.0960475747494116, + 0.8115341666348413, + 0.5564457354336897, + 0.3340092508148834, + 2.0640003238838713, + 2.2316984524611887, + 2.2129690931179513, + 2.0452115019266346, + 1.7658249349597166, + 1.452076315760487, + 1.1584510436031978, + 0.888176088156775, + 0.6444784190901406, + 2.0908108843473183, + 2.2428467149896743, + 2.205607367177845, + 2.0164920969843085, + 1.7257147678828733, + 1.4347476729526782, + 1.161056234530795, + 0.9078674222861479, + 2.040357744547504, + 2.1710358961876395, + 2.1095911788399606, + 1.8934228485769444, + 1.6168839721436565, + 1.3515460920161264, + 1.1006361778632776, + 1.9008708632146167, + 2.004495954785273, + 1.9131504868344857, + 1.6813198586365081, + 1.436105578073143, + 1.1992446032819066, + 1.6605801990788427, + 1.7314568495127611, + 1.6045152498916062, + 1.39119461016222, + 1.1801526160024074, + 1.30771571087037, + 1.3401485391002919, + 1.1932731057437316, + 1.0198201334851562, + 0.8305073573193853, + 0.8188009822780515, + 0.6947070731905263, + 0.2171850971560771, + 0.1812733525788935, + -0.5440211108893698 + ], + "j": [ + 1, + 22, + 2, + 23, + 3, + 24, + 4, + 25, + 5, + 26, + 6, + 27, + 7, + 28, + 8, + 29, + 9, + 30, + 10, + 31, + 11, + 32, + 12, + 33, + 13, + 34, + 14, + 35, + 15, + 36, + 16, + 37, + 17, + 38, + 18, + 39, + 19, + 40, + 20, + 22, + 42, + 23, + 43, + 24, + 44, + 25, + 45, + 26, + 46, + 27, + 47, + 28, + 48, + 29, + 49, + 30, + 50, + 31, + 51, + 32, + 52, + 33, + 53, + 34, + 54, + 35, + 55, + 36, + 56, + 37, + 57, + 38, + 58, + 39, + 59, + 40, + 42, + 61, + 43, + 62, + 44, + 63, + 45, + 64, + 46, + 65, + 47, + 66, + 48, + 67, + 49, + 68, + 50, + 69, + 51, + 70, + 52, + 71, + 53, + 72, + 54, + 73, + 55, + 74, + 56, + 75, + 57, + 76, + 58, + 77, + 59, + 61, + 79, + 62, + 80, + 63, + 81, + 64, + 82, + 65, + 83, + 66, + 84, + 67, + 85, + 68, + 86, + 69, + 87, + 70, + 88, + 71, + 89, + 72, + 90, + 73, + 91, + 74, + 92, + 75, + 93, + 76, + 94, + 77, + 79, + 96, + 80, + 97, + 81, + 98, + 82, + 99, + 83, + 100, + 84, + 101, + 85, + 102, + 86, + 103, + 87, + 104, + 88, + 105, + 89, + 106, + 90, + 107, + 91, + 108, + 92, + 109, + 93, + 110, + 94, + 96, + 112, + 97, + 113, + 98, + 114, + 99, + 115, + 100, + 116, + 101, + 117, + 102, + 118, + 103, + 119, + 104, + 120, + 105, + 121, + 106, + 122, + 107, + 123, + 108, + 124, + 109, + 125, + 110, + 112, + 127, + 113, + 128, + 114, + 129, + 115, + 130, + 116, + 131, + 117, + 132, + 118, + 133, + 119, + 134, + 120, + 135, + 121, + 136, + 122, + 137, + 123, + 138, + 124, + 139, + 125, + 127, + 141, + 128, + 142, + 129, + 143, + 130, + 144, + 131, + 145, + 132, + 146, + 133, + 147, + 134, + 148, + 135, + 149, + 136, + 150, + 137, + 151, + 138, + 152, + 139, + 141, + 154, + 142, + 155, + 143, + 156, + 144, + 157, + 145, + 158, + 146, + 159, + 147, + 160, + 148, + 161, + 149, + 162, + 150, + 163, + 151, + 164, + 152, + 154, + 166, + 155, + 167, + 156, + 168, + 157, + 169, + 158, + 170, + 159, + 171, + 160, + 172, + 161, + 173, + 162, + 174, + 163, + 175, + 164, + 166, + 177, + 167, + 178, + 168, + 179, + 169, + 180, + 170, + 181, + 171, + 182, + 172, + 183, + 173, + 184, + 174, + 185, + 175, + 177, + 187, + 178, + 188, + 179, + 189, + 180, + 190, + 181, + 191, + 182, + 192, + 183, + 193, + 184, + 194, + 185, + 187, + 196, + 188, + 197, + 189, + 198, + 190, + 199, + 191, + 200, + 192, + 201, + 193, + 202, + 194, + 196, + 204, + 197, + 205, + 198, + 206, + 199, + 207, + 200, + 208, + 201, + 209, + 202, + 204, + 211, + 205, + 212, + 206, + 213, + 207, + 214, + 208, + 215, + 209, + 211, + 217, + 212, + 218, + 213, + 219, + 214, + 220, + 215, + 217, + 222, + 218, + 223, + 219, + 224, + 220, + 222, + 226, + 223, + 227, + 224, + 226, + 229, + 227, + 229 + ], + "k": [ + 21, + 21, + 22, + 22, + 23, + 23, + 24, + 24, + 25, + 25, + 26, + 26, + 27, + 27, + 28, + 28, + 29, + 29, + 30, + 30, + 31, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 37, + 37, + 38, + 38, + 39, + 39, + 40, + 41, + 41, + 42, + 42, + 43, + 43, + 44, + 44, + 45, + 45, + 46, + 46, + 47, + 47, + 48, + 48, + 49, + 49, + 50, + 50, + 51, + 51, + 52, + 52, + 53, + 53, + 54, + 54, + 55, + 55, + 56, + 56, + 57, + 57, + 58, + 58, + 59, + 60, + 60, + 61, + 61, + 62, + 62, + 63, + 63, + 64, + 64, + 65, + 65, + 66, + 66, + 67, + 67, + 68, + 68, + 69, + 69, + 70, + 70, + 71, + 71, + 72, + 72, + 73, + 73, + 74, + 74, + 75, + 75, + 76, + 76, + 77, + 78, + 78, + 79, + 79, + 80, + 80, + 81, + 81, + 82, + 82, + 83, + 83, + 84, + 84, + 85, + 85, + 86, + 86, + 87, + 87, + 88, + 88, + 89, + 89, + 90, + 90, + 91, + 91, + 92, + 92, + 93, + 93, + 94, + 95, + 95, + 96, + 96, + 97, + 97, + 98, + 98, + 99, + 99, + 100, + 100, + 101, + 101, + 102, + 102, + 103, + 103, + 104, + 104, + 105, + 105, + 106, + 106, + 107, + 107, + 108, + 108, + 109, + 109, + 110, + 111, + 111, + 112, + 112, + 113, + 113, + 114, + 114, + 115, + 115, + 116, + 116, + 117, + 117, + 118, + 118, + 119, + 119, + 120, + 120, + 121, + 121, + 122, + 122, + 123, + 123, + 124, + 124, + 125, + 126, + 126, + 127, + 127, + 128, + 128, + 129, + 129, + 130, + 130, + 131, + 131, + 132, + 132, + 133, + 133, + 134, + 134, + 135, + 135, + 136, + 136, + 137, + 137, + 138, + 138, + 139, + 140, + 140, + 141, + 141, + 142, + 142, + 143, + 143, + 144, + 144, + 145, + 145, + 146, + 146, + 147, + 147, + 148, + 148, + 149, + 149, + 150, + 150, + 151, + 151, + 152, + 153, + 153, + 154, + 154, + 155, + 155, + 156, + 156, + 157, + 157, + 158, + 158, + 159, + 159, + 160, + 160, + 161, + 161, + 162, + 162, + 163, + 163, + 164, + 165, + 165, + 166, + 166, + 167, + 167, + 168, + 168, + 169, + 169, + 170, + 170, + 171, + 171, + 172, + 172, + 173, + 173, + 174, + 174, + 175, + 176, + 176, + 177, + 177, + 178, + 178, + 179, + 179, + 180, + 180, + 181, + 181, + 182, + 182, + 183, + 183, + 184, + 184, + 185, + 186, + 186, + 187, + 187, + 188, + 188, + 189, + 189, + 190, + 190, + 191, + 191, + 192, + 192, + 193, + 193, + 194, + 195, + 195, + 196, + 196, + 197, + 197, + 198, + 198, + 199, + 199, + 200, + 200, + 201, + 201, + 202, + 203, + 203, + 204, + 204, + 205, + 205, + 206, + 206, + 207, + 207, + 208, + 208, + 209, + 210, + 210, + 211, + 211, + 212, + 212, + 213, + 213, + 214, + 214, + 215, + 216, + 216, + 217, + 217, + 218, + 218, + 219, + 219, + 220, + 221, + 221, + 222, + 222, + 223, + 223, + 224, + 225, + 225, + 226, + 226, + 227, + 228, + 228, + 229, + 230 + ], + "opacity": 0.9, + "type": "mesh3d", + "x": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.75, + 0.75, + 0.75, + 0.75, + 0.75, + 0.75, + 0.8, + 0.8, + 0.8, + 0.8, + 0.8, + 0.85, + 0.85, + 0.85, + 0.85, + 0.9, + 0.9, + 0.9, + 0.95, + 0.95, + 1 + ], + "y": [ + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0.95, + 1, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0.95, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0, + 0.05, + 0.1, + 0.15, + 0, + 0.05, + 0.1, + 0, + 0.05, + 0 + ], + "z": [ + 0, + -0.03591174457718357, + -0.13580028412885914, + -0.2878955773852138, + -0.4804275830764352, + -0.7016262599327101, + -0.9397215666842261, + -1.1829434620611703, + -1.4195219047937306, + -1.6376868536120934, + -1.8256682672464462, + -1.9716961044269765, + -2.0640003238838713, + -2.0908108843473183, + -2.040357744547504, + -1.9008708632146167, + -1.6605801990788427, + -1.30771571087037, + -0.8305073573193853, + -0.2171850971560771, + 0.5440211108893698, + 0.03591174457718357, + -3.0357660829594124e-18, + -0.1468754333565604, + -0.34026223935054123, + -0.56839037671213, + -0.8194898041715135, + -1.081790480458879, + -1.3435223643044139, + -1.592915414438307, + -1.8181995895907428, + -2.0076048484919102, + -2.149361149871997, + -2.231698452461189, + -2.2428467149896743, + -2.1710358961876395, + -2.0044959547852725, + -1.7314568495127607, + -1.3401485391002914, + -0.818800982278051, + -0.18127335257889296, + 0.13580028412885914, + 0.1468754333565604, + 3.122502256758253e-17, + -0.23183062819797748, + -0.4927072066963041, + -0.7708596942251666, + -1.054518049514753, + -1.33191223129525, + -1.5912721982968456, + -1.8208279092497257, + -2.008809322884078, + -2.1434463979300915, + -2.212969093117951, + -2.2056073671778456, + -2.109591178839961, + -1.9131504868344862, + -1.6045152498916069, + -1.1932731057437318, + -0.6947070731905266, + 0.2878955773852138, + 0.3402622393505412, + 0.23183062819797745, + 6.938893903907228e-17, + -0.2907773291014351, + -0.5931351861661475, + -0.8953035299243248, + -1.1855123191061538, + -1.4519915124418223, + -1.6829710686615176, + -1.8666809464954262, + -1.9913511046737362, + -2.0452115019266346, + -2.016492096984308, + -1.8934228485769442, + -1.6813198586365077, + -1.3911946101622197, + -1.019820133485156, + 0.4804275830764352, + 0.5683903767121299, + 0.4927072066963041, + 0.2907773291014351, + 1.3877787807814457e-16, + -0.323715536066933, + -0.6415461777600717, + -0.9417218838096038, + -1.2124726129457166, + -1.442028323898597, + -1.6186189753984324, + -1.73047452617541, + -1.765824934959717, + -1.7257147678828733, + -1.6168839721436572, + -1.4361055780731435, + -1.1801526160024078, + 0.7016262599327101, + 0.8194898041715133, + 0.7708596942251668, + 0.5931351861661475, + 0.32371553606693265, + 5.551115123125783e-17, + -0.3306452490944711, + -0.6379401814780762, + -0.9101147558810042, + -1.1353989310334405, + -1.3020226656655725, + -1.4067589901084772, + -1.4520763157604872, + -1.4347476729526785, + -1.3515460920161266, + -1.1992446032819066, + 0.9397215666842261, + 1.081790480458879, + 1.0545180495147528, + 0.8953035299243246, + 0.6415461777600712, + 0.330645249094471, + 1.1102230246251565e-16, + -0.3115664681840489, + -0.5823171973201625, + -0.8047536819389689, + -0.9770727976383602, + -1.0960475747494112, + -1.1584510436031974, + -1.1610562345307947, + -1.1006361778632772, + 1.1829434620611703, + 1.343522364304414, + 1.3319122312952496, + 1.1855123191061536, + 0.9417218838096034, + 0.6379401814780763, + 0.31156646818404926, + -4.5102810375396984e-17, + -0.27217457440292775, + -0.500306440088993, + -0.6811686273892726, + -0.8115341666348411, + -0.8881760881567747, + -0.9078674222861479, + 1.4195219047937306, + 1.592915414438307, + 1.5912721982968454, + 1.4519915124418228, + 1.2124726129457164, + 0.9101147558810041, + 0.5823171973201624, + 0.27217457440292764, + -1.5265566588595902e-16, + -0.23097955621969507, + -0.41753712458723263, + -0.5564457354336898, + -0.6444784190901407, + 1.6376868536120934, + 1.8181995895907432, + 1.820827909249726, + 1.6829710686615178, + 1.4420283238985967, + 1.1353989310334403, + 0.8047536819389692, + 0.5003064400889928, + 0.23097955621969504, + 2.7755575615628914e-17, + -0.18940525890116763, + -0.33400925081488353, + 1.8256682672464462, + 2.0076048484919107, + 2.008809322884078, + 1.8666809464954264, + 1.618618975398432, + 1.3020226656655727, + 0.9770727976383602, + 0.6811686273892725, + 0.417537124587233, + 0.1894052589011674, + -2.7755575615628914e-17, + 1.9716961044269765, + 2.149361149871997, + 2.1434463979300915, + 1.9913511046737362, + 1.7304745261754095, + 1.406758990108477, + 1.0960475747494116, + 0.8115341666348413, + 0.5564457354336897, + 0.3340092508148834, + 2.0640003238838713, + 2.2316984524611887, + 2.2129690931179513, + 2.0452115019266346, + 1.7658249349597166, + 1.452076315760487, + 1.1584510436031978, + 0.888176088156775, + 0.6444784190901406, + 2.0908108843473183, + 2.2428467149896743, + 2.205607367177845, + 2.0164920969843085, + 1.7257147678828733, + 1.4347476729526782, + 1.161056234530795, + 0.9078674222861479, + 2.040357744547504, + 2.1710358961876395, + 2.1095911788399606, + 1.8934228485769444, + 1.6168839721436565, + 1.3515460920161264, + 1.1006361778632776, + 1.9008708632146167, + 2.004495954785273, + 1.9131504868344857, + 1.6813198586365081, + 1.436105578073143, + 1.1992446032819066, + 1.6605801990788427, + 1.7314568495127611, + 1.6045152498916062, + 1.39119461016222, + 1.1801526160024074, + 1.30771571087037, + 1.3401485391002919, + 1.1932731057437316, + 1.0198201334851562, + 0.8305073573193853, + 0.8188009822780515, + 0.6947070731905263, + 0.2171850971560771, + 0.1812733525788935, + -0.5440211108893698 + ] + }, + { + "color": "lightblue", + "i": [ + 0 + ], + "j": [ + 1 + ], + "k": [ + 2 + ], + "name": "Triangle Base", + "opacity": 0.5, + "type": "mesh3d", + "x": [ + 1, + 0, + 0 + ], + "y": [ + 0, + 1, + 0 + ], + "z": [ + 0, + 0, + 0 + ] + } + ], + "layout": { + "height": 1000, + "scene": { + "aspectratio": { + "x": 1, + "y": 1, + "z": 0.5 + }, + "xaxis": { + "title": { + "text": "u" + } + }, + "yaxis": { + "title": { + "text": "v" + } + }, + "zaxis": { + "title": { + "text": "Function Value" + } + } + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Clough-Tocher interpolant of sin(10*u**2 - 10*v**2)" + }, + "width": 1000 + } + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "colorbar": { + "title": { + "text": "Function Value" + } + }, + "colorscale": [ + [ + 0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1, + "#fde725" + ] + ], + "i": [ + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 21, + 22, + 22, + 23, + 23, + 24, + 24, + 25, + 25, + 26, + 26, + 27, + 27, + 28, + 28, + 29, + 29, + 30, + 30, + 31, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 37, + 37, + 38, + 38, + 39, + 39, + 41, + 42, + 42, + 43, + 43, + 44, + 44, + 45, + 45, + 46, + 46, + 47, + 47, + 48, + 48, + 49, + 49, + 50, + 50, + 51, + 51, + 52, + 52, + 53, + 53, + 54, + 54, + 55, + 55, + 56, + 56, + 57, + 57, + 58, + 58, + 60, + 61, + 61, + 62, + 62, + 63, + 63, + 64, + 64, + 65, + 65, + 66, + 66, + 67, + 67, + 68, + 68, + 69, + 69, + 70, + 70, + 71, + 71, + 72, + 72, + 73, + 73, + 74, + 74, + 75, + 75, + 76, + 76, + 78, + 79, + 79, + 80, + 80, + 81, + 81, + 82, + 82, + 83, + 83, + 84, + 84, + 85, + 85, + 86, + 86, + 87, + 87, + 88, + 88, + 89, + 89, + 90, + 90, + 91, + 91, + 92, + 92, + 93, + 93, + 95, + 96, + 96, + 97, + 97, + 98, + 98, + 99, + 99, + 100, + 100, + 101, + 101, + 102, + 102, + 103, + 103, + 104, + 104, + 105, + 105, + 106, + 106, + 107, + 107, + 108, + 108, + 109, + 109, + 111, + 112, + 112, + 113, + 113, + 114, + 114, + 115, + 115, + 116, + 116, + 117, + 117, + 118, + 118, + 119, + 119, + 120, + 120, + 121, + 121, + 122, + 122, + 123, + 123, + 124, + 124, + 126, + 127, + 127, + 128, + 128, + 129, + 129, + 130, + 130, + 131, + 131, + 132, + 132, + 133, + 133, + 134, + 134, + 135, + 135, + 136, + 136, + 137, + 137, + 138, + 138, + 140, + 141, + 141, + 142, + 142, + 143, + 143, + 144, + 144, + 145, + 145, + 146, + 146, + 147, + 147, + 148, + 148, + 149, + 149, + 150, + 150, + 151, + 151, + 153, + 154, + 154, + 155, + 155, + 156, + 156, + 157, + 157, + 158, + 158, + 159, + 159, + 160, + 160, + 161, + 161, + 162, + 162, + 163, + 163, + 165, + 166, + 166, + 167, + 167, + 168, + 168, + 169, + 169, + 170, + 170, + 171, + 171, + 172, + 172, + 173, + 173, + 174, + 174, + 176, + 177, + 177, + 178, + 178, + 179, + 179, + 180, + 180, + 181, + 181, + 182, + 182, + 183, + 183, + 184, + 184, + 186, + 187, + 187, + 188, + 188, + 189, + 189, + 190, + 190, + 191, + 191, + 192, + 192, + 193, + 193, + 195, + 196, + 196, + 197, + 197, + 198, + 198, + 199, + 199, + 200, + 200, + 201, + 201, + 203, + 204, + 204, + 205, + 205, + 206, + 206, + 207, + 207, + 208, + 208, + 210, + 211, + 211, + 212, + 212, + 213, + 213, + 214, + 214, + 216, + 217, + 217, + 218, + 218, + 219, + 219, + 221, + 222, + 222, + 223, + 223, + 225, + 226, + 226, + 228 + ], + "intensity": [ + 0, + -0.03591174457718357, + -0.13580028412885914, + -0.2878955773852138, + -0.4804275830764352, + -0.7016262599327101, + -0.9397215666842261, + -1.1829434620611703, + -1.4195219047937306, + -1.6376868536120934, + -1.8256682672464462, + -1.9716961044269765, + -2.0640003238838713, + -2.0908108843473183, + -2.040357744547504, + -1.9008708632146167, + -1.6605801990788427, + -1.30771571087037, + -0.8305073573193853, + -0.2171850971560771, + 0.5440211108893698, + 0.03591174457718357, + -3.0357660829594124e-18, + -0.1468754333565604, + -0.34026223935054123, + -0.56839037671213, + -0.8194898041715135, + -1.081790480458879, + -1.3435223643044139, + -1.592915414438307, + -1.8181995895907428, + -2.0076048484919102, + -2.149361149871997, + -2.231698452461189, + -2.2428467149896743, + -2.1710358961876395, + -2.0044959547852725, + -1.7314568495127607, + -1.3401485391002914, + -0.818800982278051, + -0.18127335257889296, + 0.13580028412885914, + 0.1468754333565604, + 3.122502256758253e-17, + -0.23183062819797748, + -0.4927072066963041, + -0.7708596942251666, + -1.054518049514753, + -1.33191223129525, + -1.5912721982968456, + -1.8208279092497257, + -2.008809322884078, + -2.1434463979300915, + -2.212969093117951, + -2.2056073671778456, + -2.109591178839961, + -1.9131504868344862, + -1.6045152498916069, + -1.1932731057437318, + -0.6947070731905266, + 0.2878955773852138, + 0.3402622393505412, + 0.23183062819797745, + 6.938893903907228e-17, + -0.2907773291014351, + -0.5931351861661475, + -0.8953035299243248, + -1.1855123191061538, + -1.4519915124418223, + -1.6829710686615176, + -1.8666809464954262, + -1.9913511046737362, + -2.0452115019266346, + -2.016492096984308, + -1.8934228485769442, + -1.6813198586365077, + -1.3911946101622197, + -1.019820133485156, + 0.4804275830764352, + 0.5683903767121299, + 0.4927072066963041, + 0.2907773291014351, + 1.3877787807814457e-16, + -0.323715536066933, + -0.6415461777600717, + -0.9417218838096038, + -1.2124726129457166, + -1.442028323898597, + -1.6186189753984324, + -1.73047452617541, + -1.765824934959717, + -1.7257147678828733, + -1.6168839721436572, + -1.4361055780731435, + -1.1801526160024078, + 0.7016262599327101, + 0.8194898041715133, + 0.7708596942251668, + 0.5931351861661475, + 0.32371553606693265, + 5.551115123125783e-17, + -0.3306452490944711, + -0.6379401814780762, + -0.9101147558810042, + -1.1353989310334405, + -1.3020226656655725, + -1.4067589901084772, + -1.4520763157604872, + -1.4347476729526785, + -1.3515460920161266, + -1.1992446032819066, + 0.9397215666842261, + 1.081790480458879, + 1.0545180495147528, + 0.8953035299243246, + 0.6415461777600712, + 0.330645249094471, + 1.1102230246251565e-16, + -0.3115664681840489, + -0.5823171973201625, + -0.8047536819389689, + -0.9770727976383602, + -1.0960475747494112, + -1.1584510436031974, + -1.1610562345307947, + -1.1006361778632772, + 1.1829434620611703, + 1.343522364304414, + 1.3319122312952496, + 1.1855123191061536, + 0.9417218838096034, + 0.6379401814780763, + 0.31156646818404926, + -4.5102810375396984e-17, + -0.27217457440292775, + -0.500306440088993, + -0.6811686273892726, + -0.8115341666348411, + -0.8881760881567747, + -0.9078674222861479, + 1.4195219047937306, + 1.592915414438307, + 1.5912721982968454, + 1.4519915124418228, + 1.2124726129457164, + 0.9101147558810041, + 0.5823171973201624, + 0.27217457440292764, + -1.5265566588595902e-16, + -0.23097955621969507, + -0.41753712458723263, + -0.5564457354336898, + -0.6444784190901407, + 1.6376868536120934, + 1.8181995895907432, + 1.820827909249726, + 1.6829710686615178, + 1.4420283238985967, + 1.1353989310334403, + 0.8047536819389692, + 0.5003064400889928, + 0.23097955621969504, + 2.7755575615628914e-17, + -0.18940525890116763, + -0.33400925081488353, + 1.8256682672464462, + 2.0076048484919107, + 2.008809322884078, + 1.8666809464954264, + 1.618618975398432, + 1.3020226656655727, + 0.9770727976383602, + 0.6811686273892725, + 0.417537124587233, + 0.1894052589011674, + -2.7755575615628914e-17, + 1.9716961044269765, + 2.149361149871997, + 2.1434463979300915, + 1.9913511046737362, + 1.7304745261754095, + 1.406758990108477, + 1.0960475747494116, + 0.8115341666348413, + 0.5564457354336897, + 0.3340092508148834, + 2.0640003238838713, + 2.2316984524611887, + 2.2129690931179513, + 2.0452115019266346, + 1.7658249349597166, + 1.452076315760487, + 1.1584510436031978, + 0.888176088156775, + 0.6444784190901406, + 2.0908108843473183, + 2.2428467149896743, + 2.205607367177845, + 2.0164920969843085, + 1.7257147678828733, + 1.4347476729526782, + 1.161056234530795, + 0.9078674222861479, + 2.040357744547504, + 2.1710358961876395, + 2.1095911788399606, + 1.8934228485769444, + 1.6168839721436565, + 1.3515460920161264, + 1.1006361778632776, + 1.9008708632146167, + 2.004495954785273, + 1.9131504868344857, + 1.6813198586365081, + 1.436105578073143, + 1.1992446032819066, + 1.6605801990788427, + 1.7314568495127611, + 1.6045152498916062, + 1.39119461016222, + 1.1801526160024074, + 1.30771571087037, + 1.3401485391002919, + 1.1932731057437316, + 1.0198201334851562, + 0.8305073573193853, + 0.8188009822780515, + 0.6947070731905263, + 0.2171850971560771, + 0.1812733525788935, + -0.5440211108893698 + ], + "j": [ + 1, + 22, + 2, + 23, + 3, + 24, + 4, + 25, + 5, + 26, + 6, + 27, + 7, + 28, + 8, + 29, + 9, + 30, + 10, + 31, + 11, + 32, + 12, + 33, + 13, + 34, + 14, + 35, + 15, + 36, + 16, + 37, + 17, + 38, + 18, + 39, + 19, + 40, + 20, + 22, + 42, + 23, + 43, + 24, + 44, + 25, + 45, + 26, + 46, + 27, + 47, + 28, + 48, + 29, + 49, + 30, + 50, + 31, + 51, + 32, + 52, + 33, + 53, + 34, + 54, + 35, + 55, + 36, + 56, + 37, + 57, + 38, + 58, + 39, + 59, + 40, + 42, + 61, + 43, + 62, + 44, + 63, + 45, + 64, + 46, + 65, + 47, + 66, + 48, + 67, + 49, + 68, + 50, + 69, + 51, + 70, + 52, + 71, + 53, + 72, + 54, + 73, + 55, + 74, + 56, + 75, + 57, + 76, + 58, + 77, + 59, + 61, + 79, + 62, + 80, + 63, + 81, + 64, + 82, + 65, + 83, + 66, + 84, + 67, + 85, + 68, + 86, + 69, + 87, + 70, + 88, + 71, + 89, + 72, + 90, + 73, + 91, + 74, + 92, + 75, + 93, + 76, + 94, + 77, + 79, + 96, + 80, + 97, + 81, + 98, + 82, + 99, + 83, + 100, + 84, + 101, + 85, + 102, + 86, + 103, + 87, + 104, + 88, + 105, + 89, + 106, + 90, + 107, + 91, + 108, + 92, + 109, + 93, + 110, + 94, + 96, + 112, + 97, + 113, + 98, + 114, + 99, + 115, + 100, + 116, + 101, + 117, + 102, + 118, + 103, + 119, + 104, + 120, + 105, + 121, + 106, + 122, + 107, + 123, + 108, + 124, + 109, + 125, + 110, + 112, + 127, + 113, + 128, + 114, + 129, + 115, + 130, + 116, + 131, + 117, + 132, + 118, + 133, + 119, + 134, + 120, + 135, + 121, + 136, + 122, + 137, + 123, + 138, + 124, + 139, + 125, + 127, + 141, + 128, + 142, + 129, + 143, + 130, + 144, + 131, + 145, + 132, + 146, + 133, + 147, + 134, + 148, + 135, + 149, + 136, + 150, + 137, + 151, + 138, + 152, + 139, + 141, + 154, + 142, + 155, + 143, + 156, + 144, + 157, + 145, + 158, + 146, + 159, + 147, + 160, + 148, + 161, + 149, + 162, + 150, + 163, + 151, + 164, + 152, + 154, + 166, + 155, + 167, + 156, + 168, + 157, + 169, + 158, + 170, + 159, + 171, + 160, + 172, + 161, + 173, + 162, + 174, + 163, + 175, + 164, + 166, + 177, + 167, + 178, + 168, + 179, + 169, + 180, + 170, + 181, + 171, + 182, + 172, + 183, + 173, + 184, + 174, + 185, + 175, + 177, + 187, + 178, + 188, + 179, + 189, + 180, + 190, + 181, + 191, + 182, + 192, + 183, + 193, + 184, + 194, + 185, + 187, + 196, + 188, + 197, + 189, + 198, + 190, + 199, + 191, + 200, + 192, + 201, + 193, + 202, + 194, + 196, + 204, + 197, + 205, + 198, + 206, + 199, + 207, + 200, + 208, + 201, + 209, + 202, + 204, + 211, + 205, + 212, + 206, + 213, + 207, + 214, + 208, + 215, + 209, + 211, + 217, + 212, + 218, + 213, + 219, + 214, + 220, + 215, + 217, + 222, + 218, + 223, + 219, + 224, + 220, + 222, + 226, + 223, + 227, + 224, + 226, + 229, + 227, + 229 + ], + "k": [ + 21, + 21, + 22, + 22, + 23, + 23, + 24, + 24, + 25, + 25, + 26, + 26, + 27, + 27, + 28, + 28, + 29, + 29, + 30, + 30, + 31, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 37, + 37, + 38, + 38, + 39, + 39, + 40, + 41, + 41, + 42, + 42, + 43, + 43, + 44, + 44, + 45, + 45, + 46, + 46, + 47, + 47, + 48, + 48, + 49, + 49, + 50, + 50, + 51, + 51, + 52, + 52, + 53, + 53, + 54, + 54, + 55, + 55, + 56, + 56, + 57, + 57, + 58, + 58, + 59, + 60, + 60, + 61, + 61, + 62, + 62, + 63, + 63, + 64, + 64, + 65, + 65, + 66, + 66, + 67, + 67, + 68, + 68, + 69, + 69, + 70, + 70, + 71, + 71, + 72, + 72, + 73, + 73, + 74, + 74, + 75, + 75, + 76, + 76, + 77, + 78, + 78, + 79, + 79, + 80, + 80, + 81, + 81, + 82, + 82, + 83, + 83, + 84, + 84, + 85, + 85, + 86, + 86, + 87, + 87, + 88, + 88, + 89, + 89, + 90, + 90, + 91, + 91, + 92, + 92, + 93, + 93, + 94, + 95, + 95, + 96, + 96, + 97, + 97, + 98, + 98, + 99, + 99, + 100, + 100, + 101, + 101, + 102, + 102, + 103, + 103, + 104, + 104, + 105, + 105, + 106, + 106, + 107, + 107, + 108, + 108, + 109, + 109, + 110, + 111, + 111, + 112, + 112, + 113, + 113, + 114, + 114, + 115, + 115, + 116, + 116, + 117, + 117, + 118, + 118, + 119, + 119, + 120, + 120, + 121, + 121, + 122, + 122, + 123, + 123, + 124, + 124, + 125, + 126, + 126, + 127, + 127, + 128, + 128, + 129, + 129, + 130, + 130, + 131, + 131, + 132, + 132, + 133, + 133, + 134, + 134, + 135, + 135, + 136, + 136, + 137, + 137, + 138, + 138, + 139, + 140, + 140, + 141, + 141, + 142, + 142, + 143, + 143, + 144, + 144, + 145, + 145, + 146, + 146, + 147, + 147, + 148, + 148, + 149, + 149, + 150, + 150, + 151, + 151, + 152, + 153, + 153, + 154, + 154, + 155, + 155, + 156, + 156, + 157, + 157, + 158, + 158, + 159, + 159, + 160, + 160, + 161, + 161, + 162, + 162, + 163, + 163, + 164, + 165, + 165, + 166, + 166, + 167, + 167, + 168, + 168, + 169, + 169, + 170, + 170, + 171, + 171, + 172, + 172, + 173, + 173, + 174, + 174, + 175, + 176, + 176, + 177, + 177, + 178, + 178, + 179, + 179, + 180, + 180, + 181, + 181, + 182, + 182, + 183, + 183, + 184, + 184, + 185, + 186, + 186, + 187, + 187, + 188, + 188, + 189, + 189, + 190, + 190, + 191, + 191, + 192, + 192, + 193, + 193, + 194, + 195, + 195, + 196, + 196, + 197, + 197, + 198, + 198, + 199, + 199, + 200, + 200, + 201, + 201, + 202, + 203, + 203, + 204, + 204, + 205, + 205, + 206, + 206, + 207, + 207, + 208, + 208, + 209, + 210, + 210, + 211, + 211, + 212, + 212, + 213, + 213, + 214, + 214, + 215, + 216, + 216, + 217, + 217, + 218, + 218, + 219, + 219, + 220, + 221, + 221, + 222, + 222, + 223, + 223, + 224, + 225, + 225, + 226, + 226, + 227, + 228, + 228, + 229, + 230 + ], + "opacity": 0.9, + "type": "mesh3d", + "x": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.25, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.35, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.45, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.55, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.6, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.65, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.7, + 0.75, + 0.75, + 0.75, + 0.75, + 0.75, + 0.75, + 0.8, + 0.8, + 0.8, + 0.8, + 0.8, + 0.85, + 0.85, + 0.85, + 0.85, + 0.9, + 0.9, + 0.9, + 0.95, + 0.95, + 1 + ], + "y": [ + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0.95, + 1, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0.95, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0.9, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0.85, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0.8, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0.75, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0.7, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0.65, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0.6, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0.55, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0.5, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0.45, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0.4, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0.35, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0.3, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0.25, + 0, + 0.05, + 0.1, + 0.15, + 0.2, + 0, + 0.05, + 0.1, + 0.15, + 0, + 0.05, + 0.1, + 0, + 0.05, + 0 + ], + "z": [ + 0, + -0.03591174457718357, + -0.13580028412885914, + -0.2878955773852138, + -0.4804275830764352, + -0.7016262599327101, + -0.9397215666842261, + -1.1829434620611703, + -1.4195219047937306, + -1.6376868536120934, + -1.8256682672464462, + -1.9716961044269765, + -2.0640003238838713, + -2.0908108843473183, + -2.040357744547504, + -1.9008708632146167, + -1.6605801990788427, + -1.30771571087037, + -0.8305073573193853, + -0.2171850971560771, + 0.5440211108893698, + 0.03591174457718357, + -3.0357660829594124e-18, + -0.1468754333565604, + -0.34026223935054123, + -0.56839037671213, + -0.8194898041715135, + -1.081790480458879, + -1.3435223643044139, + -1.592915414438307, + -1.8181995895907428, + -2.0076048484919102, + -2.149361149871997, + -2.231698452461189, + -2.2428467149896743, + -2.1710358961876395, + -2.0044959547852725, + -1.7314568495127607, + -1.3401485391002914, + -0.818800982278051, + -0.18127335257889296, + 0.13580028412885914, + 0.1468754333565604, + 3.122502256758253e-17, + -0.23183062819797748, + -0.4927072066963041, + -0.7708596942251666, + -1.054518049514753, + -1.33191223129525, + -1.5912721982968456, + -1.8208279092497257, + -2.008809322884078, + -2.1434463979300915, + -2.212969093117951, + -2.2056073671778456, + -2.109591178839961, + -1.9131504868344862, + -1.6045152498916069, + -1.1932731057437318, + -0.6947070731905266, + 0.2878955773852138, + 0.3402622393505412, + 0.23183062819797745, + 6.938893903907228e-17, + -0.2907773291014351, + -0.5931351861661475, + -0.8953035299243248, + -1.1855123191061538, + -1.4519915124418223, + -1.6829710686615176, + -1.8666809464954262, + -1.9913511046737362, + -2.0452115019266346, + -2.016492096984308, + -1.8934228485769442, + -1.6813198586365077, + -1.3911946101622197, + -1.019820133485156, + 0.4804275830764352, + 0.5683903767121299, + 0.4927072066963041, + 0.2907773291014351, + 1.3877787807814457e-16, + -0.323715536066933, + -0.6415461777600717, + -0.9417218838096038, + -1.2124726129457166, + -1.442028323898597, + -1.6186189753984324, + -1.73047452617541, + -1.765824934959717, + -1.7257147678828733, + -1.6168839721436572, + -1.4361055780731435, + -1.1801526160024078, + 0.7016262599327101, + 0.8194898041715133, + 0.7708596942251668, + 0.5931351861661475, + 0.32371553606693265, + 5.551115123125783e-17, + -0.3306452490944711, + -0.6379401814780762, + -0.9101147558810042, + -1.1353989310334405, + -1.3020226656655725, + -1.4067589901084772, + -1.4520763157604872, + -1.4347476729526785, + -1.3515460920161266, + -1.1992446032819066, + 0.9397215666842261, + 1.081790480458879, + 1.0545180495147528, + 0.8953035299243246, + 0.6415461777600712, + 0.330645249094471, + 1.1102230246251565e-16, + -0.3115664681840489, + -0.5823171973201625, + -0.8047536819389689, + -0.9770727976383602, + -1.0960475747494112, + -1.1584510436031974, + -1.1610562345307947, + -1.1006361778632772, + 1.1829434620611703, + 1.343522364304414, + 1.3319122312952496, + 1.1855123191061536, + 0.9417218838096034, + 0.6379401814780763, + 0.31156646818404926, + -4.5102810375396984e-17, + -0.27217457440292775, + -0.500306440088993, + -0.6811686273892726, + -0.8115341666348411, + -0.8881760881567747, + -0.9078674222861479, + 1.4195219047937306, + 1.592915414438307, + 1.5912721982968454, + 1.4519915124418228, + 1.2124726129457164, + 0.9101147558810041, + 0.5823171973201624, + 0.27217457440292764, + -1.5265566588595902e-16, + -0.23097955621969507, + -0.41753712458723263, + -0.5564457354336898, + -0.6444784190901407, + 1.6376868536120934, + 1.8181995895907432, + 1.820827909249726, + 1.6829710686615178, + 1.4420283238985967, + 1.1353989310334403, + 0.8047536819389692, + 0.5003064400889928, + 0.23097955621969504, + 2.7755575615628914e-17, + -0.18940525890116763, + -0.33400925081488353, + 1.8256682672464462, + 2.0076048484919107, + 2.008809322884078, + 1.8666809464954264, + 1.618618975398432, + 1.3020226656655727, + 0.9770727976383602, + 0.6811686273892725, + 0.417537124587233, + 0.1894052589011674, + -2.7755575615628914e-17, + 1.9716961044269765, + 2.149361149871997, + 2.1434463979300915, + 1.9913511046737362, + 1.7304745261754095, + 1.406758990108477, + 1.0960475747494116, + 0.8115341666348413, + 0.5564457354336897, + 0.3340092508148834, + 2.0640003238838713, + 2.2316984524611887, + 2.2129690931179513, + 2.0452115019266346, + 1.7658249349597166, + 1.452076315760487, + 1.1584510436031978, + 0.888176088156775, + 0.6444784190901406, + 2.0908108843473183, + 2.2428467149896743, + 2.205607367177845, + 2.0164920969843085, + 1.7257147678828733, + 1.4347476729526782, + 1.161056234530795, + 0.9078674222861479, + 2.040357744547504, + 2.1710358961876395, + 2.1095911788399606, + 1.8934228485769444, + 1.6168839721436565, + 1.3515460920161264, + 1.1006361778632776, + 1.9008708632146167, + 2.004495954785273, + 1.9131504868344857, + 1.6813198586365081, + 1.436105578073143, + 1.1992446032819066, + 1.6605801990788427, + 1.7314568495127611, + 1.6045152498916062, + 1.39119461016222, + 1.1801526160024074, + 1.30771571087037, + 1.3401485391002919, + 1.1932731057437316, + 1.0198201334851562, + 0.8305073573193853, + 0.8188009822780515, + 0.6947070731905263, + 0.2171850971560771, + 0.1812733525788935, + -0.5440211108893698 + ] + }, + { + "color": "lightblue", + "i": [ + 0 + ], + "j": [ + 1 + ], + "k": [ + 2 + ], + "name": "Triangle Base", + "opacity": 0.5, + "type": "mesh3d", + "x": [ + 1, + 0, + 0 + ], + "y": [ + 0, + 1, + 0 + ], + "z": [ + 0, + 0, + 0 + ] + } + ], + "layout": { + "height": 1000, + "scene": { + "aspectratio": { + "x": 1, + "y": 1, + "z": 0.5 + }, + "xaxis": { + "title": { + "text": "u" + } + }, + "yaxis": { + "title": { + "text": "v" + } + }, + "zaxis": { + "title": { + "text": "Function Value" + } + } + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Clough-Tocher interpolant of sin(10*u**2 - 10*v**2)" + }, + "width": 1000 + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "CT_matrices = compute_CT_matrices(polys_C1_f)\n", + "f = sp.sin(10*(u**2-v**2))\n", + "bd = Matrix(build_data(f)).evalf()\n", + "plot_function_on_triangle(\n", + " lambda uc,vc: CT_eval([m.evalf() for m in CT_matrices],\n", + " [t.evalf() for t in CTtri_bounds],\n", + " bd,uc,vc,1-uc-vc), str(f), [[1,0],[0,1],[0,0]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "763af898-9719-4edc-bae9-98b2ce57ca83", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3be3e9fc-006a-4d77-9179-149fe73b3ef9", + "metadata": {}, + "outputs": [], + "source": [ + "L_d2L = generate_L2L(polys_C1_f, node_bary, node_subtri, all_unknowns)\n", + "L_d2L_ind = L_d2L[0:12,0:12]\n", + "L_d2L_dep = L_d2L[12:19,0:12]\n", + "L_L2d_ind = L_d2L_ind.inverse_GE()\n", + "L_ind2dep = L_d2L_dep*L_L2d_ind\n", + "c_e = generate_ce(polys_C1_f)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "995c3f30-b4dc-4fe3-92be-b4f6c2458ab0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lagrange consistency test passed\n" + ] + } + ], + "source": [ + "test_Lagrange_consistency(polys_C1_f, L_L2d_ind, L_ind2dep)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "50d3fb36-4475-4036-8fcd-4853d2cdf2f4", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "765d3a6b", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "67745ecc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'L[0][0] = 1;\\nL[0][1] = 0;\\nL[0][2] = 0;\\nL[0][3] = 0;\\nL[0][4] = 0;\\nL[0][5] = 0;\\nL[0][6] = 0;\\nL[0][7] = 0;\\nL[0][8] = 0;\\nL[0][9] = 0;\\nL[0][10] = 0;\\nL[0][11] = 0;\\nL[1][0] = 0;\\nL[1][1] = 1;\\nL[1][2] = 0;\\nL[1][3] = 0;\\nL[1][4] = 0;\\nL[1][5] = 0;\\nL[1][6] = 0;\\nL[1][7] = 0;\\nL[1][8] = 0;\\nL[1][9] = 0;\\nL[1][10] = 0;\\nL[1][11] = 0;\\nL[2][0] = 0;\\nL[2][1] = 0;\\nL[2][2] = 1;\\nL[2][3] = 0;\\nL[2][4] = 0;\\nL[2][5] = 0;\\nL[2][6] = 0;\\nL[2][7] = 0;\\nL[2][8] = 0;\\nL[2][9] = 0;\\nL[2][10] = 0;\\nL[2][11] = 0;\\nL[3][0] = -11.0/2.0;\\nL[3][1] = 1;\\nL[3][2] = 0;\\nL[3][3] = 9;\\nL[3][4] = -9.0/2.0;\\nL[3][5] = 0;\\nL[3][6] = 0;\\nL[3][7] = 0;\\nL[3][8] = 0;\\nL[3][9] = 0;\\nL[3][10] = 0;\\nL[3][11] = 0;\\nL[4][0] = 1;\\nL[4][1] = -11.0/2.0;\\nL[4][2] = 0;\\nL[4][3] = -9.0/2.0;\\nL[4][4] = 9;\\nL[4][5] = 0;\\nL[4][6] = 0;\\nL[4][7] = 0;\\nL[4][8] = 0;\\nL[4][9] = 0;\\nL[4][10] = 0;\\nL[4][11] = 0;\\nL[5][0] = 0;\\nL[5][1] = -11.0/2.0;\\nL[5][2] = 1;\\nL[5][3] = 0;\\nL[5][4] = 0;\\nL[5][5] = 9;\\nL[5][6] = -9.0/2.0;\\nL[5][7] = 0;\\nL[5][8] = 0;\\nL[5][9] = 0;\\nL[5][10] = 0;\\nL[5][11] = 0;\\nL[6][0] = 0;\\nL[6][1] = 1;\\nL[6][2] = -11.0/2.0;\\nL[6][3] = 0;\\nL[6][4] = 0;\\nL[6][5] = -9.0/2.0;\\nL[6][6] = 9;\\nL[6][7] = 0;\\nL[6][8] = 0;\\nL[6][9] = 0;\\nL[6][10] = 0;\\nL[6][11] = 0;\\nL[7][0] = 1;\\nL[7][1] = 0;\\nL[7][2] = -11.0/2.0;\\nL[7][3] = 0;\\nL[7][4] = 0;\\nL[7][5] = 0;\\nL[7][6] = 0;\\nL[7][7] = 9;\\nL[7][8] = -9.0/2.0;\\nL[7][9] = 0;\\nL[7][10] = 0;\\nL[7][11] = 0;\\nL[8][0] = -11.0/2.0;\\nL[8][1] = 0;\\nL[8][2] = 1;\\nL[8][3] = 0;\\nL[8][4] = 0;\\nL[8][5] = 0;\\nL[8][6] = 0;\\nL[8][7] = -9.0/2.0;\\nL[8][8] = 9;\\nL[8][9] = 0;\\nL[8][10] = 0;\\nL[8][11] = 0;\\nL[9][0] = 379.0/448.0;\\nL[9][1] = 379.0/448.0;\\nL[9][2] = -65.0/448.0;\\nL[9][3] = -1683.0/224.0;\\nL[9][4] = -1683.0/224.0;\\nL[9][5] = 9.0/32.0;\\nL[9][6] = 171.0/224.0;\\nL[9][7] = 171.0/224.0;\\nL[9][8] = 9.0/32.0;\\nL[9][9] = 6885.0/448.0;\\nL[9][10] = -891.0/448.0;\\nL[9][11] = -891.0/448.0;\\nL[10][0] = -65.0/448.0;\\nL[10][1] = 379.0/448.0;\\nL[10][2] = 379.0/448.0;\\nL[10][3] = 171.0/224.0;\\nL[10][4] = 9.0/32.0;\\nL[10][5] = -1683.0/224.0;\\nL[10][6] = -1683.0/224.0;\\nL[10][7] = 9.0/32.0;\\nL[10][8] = 171.0/224.0;\\nL[10][9] = -891.0/448.0;\\nL[10][10] = 6885.0/448.0;\\nL[10][11] = -891.0/448.0;\\nL[11][0] = 379.0/448.0;\\nL[11][1] = -65.0/448.0;\\nL[11][2] = 379.0/448.0;\\nL[11][3] = 9.0/32.0;\\nL[11][4] = 171.0/224.0;\\nL[11][5] = 171.0/224.0;\\nL[11][6] = 9.0/32.0;\\nL[11][7] = -1683.0/224.0;\\nL[11][8] = -1683.0/224.0;\\nL[11][9] = -891.0/448.0;\\nL[11][10] = -891.0/448.0;\\nL[11][11] = 6885.0/448.0;'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "73c54793", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "2d719cbc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'L[0][0] = 1;\\nL[0][1] = 0;\\nL[0][2] = 0;\\nL[0][3] = 0;\\nL[0][4] = 0;\\nL[0][5] = 0;\\nL[0][6] = 0;\\nL[0][7] = 0;\\nL[0][8] = 0;\\nL[0][9] = 0;\\nL[0][10] = 0;\\nL[0][11] = 0;\\nL[1][0] = 0;\\nL[1][1] = 1;\\nL[1][2] = 0;\\nL[1][3] = 0;\\nL[1][4] = 0;\\nL[1][5] = 0;\\nL[1][6] = 0;\\nL[1][7] = 0;\\nL[1][8] = 0;\\nL[1][9] = 0;\\nL[1][10] = 0;\\nL[1][11] = 0;\\nL[2][0] = 0;\\nL[2][1] = 0;\\nL[2][2] = 1;\\nL[2][3] = 0;\\nL[2][4] = 0;\\nL[2][5] = 0;\\nL[2][6] = 0;\\nL[2][7] = 0;\\nL[2][8] = 0;\\nL[2][9] = 0;\\nL[2][10] = 0;\\nL[2][11] = 0;\\nL[3][0] = -11.0/2.0;\\nL[3][1] = 1;\\nL[3][2] = 0;\\nL[3][3] = 9;\\nL[3][4] = -9.0/2.0;\\nL[3][5] = 0;\\nL[3][6] = 0;\\nL[3][7] = 0;\\nL[3][8] = 0;\\nL[3][9] = 0;\\nL[3][10] = 0;\\nL[3][11] = 0;\\nL[4][0] = 1;\\nL[4][1] = -11.0/2.0;\\nL[4][2] = 0;\\nL[4][3] = -9.0/2.0;\\nL[4][4] = 9;\\nL[4][5] = 0;\\nL[4][6] = 0;\\nL[4][7] = 0;\\nL[4][8] = 0;\\nL[4][9] = 0;\\nL[4][10] = 0;\\nL[4][11] = 0;\\nL[5][0] = 0;\\nL[5][1] = -11.0/2.0;\\nL[5][2] = 1;\\nL[5][3] = 0;\\nL[5][4] = 0;\\nL[5][5] = 9;\\nL[5][6] = -9.0/2.0;\\nL[5][7] = 0;\\nL[5][8] = 0;\\nL[5][9] = 0;\\nL[5][10] = 0;\\nL[5][11] = 0;\\nL[6][0] = 0;\\nL[6][1] = 1;\\nL[6][2] = -11.0/2.0;\\nL[6][3] = 0;\\nL[6][4] = 0;\\nL[6][5] = -9.0/2.0;\\nL[6][6] = 9;\\nL[6][7] = 0;\\nL[6][8] = 0;\\nL[6][9] = 0;\\nL[6][10] = 0;\\nL[6][11] = 0;\\nL[7][0] = 1;\\nL[7][1] = 0;\\nL[7][2] = -11.0/2.0;\\nL[7][3] = 0;\\nL[7][4] = 0;\\nL[7][5] = 0;\\nL[7][6] = 0;\\nL[7][7] = 9;\\nL[7][8] = -9.0/2.0;\\nL[7][9] = 0;\\nL[7][10] = 0;\\nL[7][11] = 0;\\nL[8][0] = -11.0/2.0;\\nL[8][1] = 0;\\nL[8][2] = 1;\\nL[8][3] = 0;\\nL[8][4] = 0;\\nL[8][5] = 0;\\nL[8][6] = 0;\\nL[8][7] = -9.0/2.0;\\nL[8][8] = 9;\\nL[8][9] = 0;\\nL[8][10] = 0;\\nL[8][11] = 0;\\nL[9][0] = 379.0/448.0;\\nL[9][1] = 379.0/448.0;\\nL[9][2] = -65.0/448.0;\\nL[9][3] = -1683.0/224.0;\\nL[9][4] = -1683.0/224.0;\\nL[9][5] = 9.0/32.0;\\nL[9][6] = 171.0/224.0;\\nL[9][7] = 171.0/224.0;\\nL[9][8] = 9.0/32.0;\\nL[9][9] = 6885.0/448.0;\\nL[9][10] = -891.0/448.0;\\nL[9][11] = -891.0/448.0;\\nL[10][0] = -65.0/448.0;\\nL[10][1] = 379.0/448.0;\\nL[10][2] = 379.0/448.0;\\nL[10][3] = 171.0/224.0;\\nL[10][4] = 9.0/32.0;\\nL[10][5] = -1683.0/224.0;\\nL[10][6] = -1683.0/224.0;\\nL[10][7] = 9.0/32.0;\\nL[10][8] = 171.0/224.0;\\nL[10][9] = -891.0/448.0;\\nL[10][10] = 6885.0/448.0;\\nL[10][11] = -891.0/448.0;\\nL[11][0] = 379.0/448.0;\\nL[11][1] = -65.0/448.0;\\nL[11][2] = 379.0/448.0;\\nL[11][3] = 9.0/32.0;\\nL[11][4] = 171.0/224.0;\\nL[11][5] = 171.0/224.0;\\nL[11][6] = 9.0/32.0;\\nL[11][7] = -1683.0/224.0;\\nL[11][8] = -1683.0/224.0;\\nL[11][9] = -891.0/448.0;\\nL[11][10] = -891.0/448.0;\\nL[11][11] = 6885.0/448.0;'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddb29d77", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/clough_tocher_patches/clough_tocher_autogen_constraint_matrices.hpp b/src/clough_tocher_patches/clough_tocher_autogen_constraint_matrices.hpp new file mode 100644 index 0000000..3caaefb --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_autogen_constraint_matrices.hpp @@ -0,0 +1,243 @@ +#pragma once + +inline void L_L2d_ind_matrix(double L[12][12]) { + L[0][0] = 1; + L[0][1] = 0; + L[0][2] = 0; + L[0][3] = 0; + L[0][4] = 0; + L[0][5] = 0; + L[0][6] = 0; + L[0][7] = 0; + L[0][8] = 0; + L[0][9] = 0; + L[0][10] = 0; + L[0][11] = 0; + L[1][0] = 0; + L[1][1] = 1; + L[1][2] = 0; + L[1][3] = 0; + L[1][4] = 0; + L[1][5] = 0; + L[1][6] = 0; + L[1][7] = 0; + L[1][8] = 0; + L[1][9] = 0; + L[1][10] = 0; + L[1][11] = 0; + L[2][0] = 0; + L[2][1] = 0; + L[2][2] = 1; + L[2][3] = 0; + L[2][4] = 0; + L[2][5] = 0; + L[2][6] = 0; + L[2][7] = 0; + L[2][8] = 0; + L[2][9] = 0; + L[2][10] = 0; + L[2][11] = 0; + L[3][0] = -11.0 / 2.0; + L[3][1] = 1; + L[3][2] = 0; + L[3][3] = 9; + L[3][4] = -9.0 / 2.0; + L[3][5] = 0; + L[3][6] = 0; + L[3][7] = 0; + L[3][8] = 0; + L[3][9] = 0; + L[3][10] = 0; + L[3][11] = 0; + L[4][0] = 1; + L[4][1] = -11.0 / 2.0; + L[4][2] = 0; + L[4][3] = -9.0 / 2.0; + L[4][4] = 9; + L[4][5] = 0; + L[4][6] = 0; + L[4][7] = 0; + L[4][8] = 0; + L[4][9] = 0; + L[4][10] = 0; + L[4][11] = 0; + L[5][0] = 0; + L[5][1] = -11.0 / 2.0; + L[5][2] = 1; + L[5][3] = 0; + L[5][4] = 0; + L[5][5] = 9; + L[5][6] = -9.0 / 2.0; + L[5][7] = 0; + L[5][8] = 0; + L[5][9] = 0; + L[5][10] = 0; + L[5][11] = 0; + L[6][0] = 0; + L[6][1] = 1; + L[6][2] = -11.0 / 2.0; + L[6][3] = 0; + L[6][4] = 0; + L[6][5] = -9.0 / 2.0; + L[6][6] = 9; + L[6][7] = 0; + L[6][8] = 0; + L[6][9] = 0; + L[6][10] = 0; + L[6][11] = 0; + L[7][0] = 1; + L[7][1] = 0; + L[7][2] = -11.0 / 2.0; + L[7][3] = 0; + L[7][4] = 0; + L[7][5] = 0; + L[7][6] = 0; + L[7][7] = 9; + L[7][8] = -9.0 / 2.0; + L[7][9] = 0; + L[7][10] = 0; + L[7][11] = 0; + L[8][0] = -11.0 / 2.0; + L[8][1] = 0; + L[8][2] = 1; + L[8][3] = 0; + L[8][4] = 0; + L[8][5] = 0; + L[8][6] = 0; + L[8][7] = -9.0 / 2.0; + L[8][8] = 9; + L[8][9] = 0; + L[8][10] = 0; + L[8][11] = 0; + L[9][0] = 379.0 / 448.0; + L[9][1] = 379.0 / 448.0; + L[9][2] = -65.0 / 448.0; + L[9][3] = -1683.0 / 224.0; + L[9][4] = -1683.0 / 224.0; + L[9][5] = 9.0 / 32.0; + L[9][6] = 171.0 / 224.0; + L[9][7] = 171.0 / 224.0; + L[9][8] = 9.0 / 32.0; + L[9][9] = 6885.0 / 448.0; + L[9][10] = -891.0 / 448.0; + L[9][11] = -891.0 / 448.0; + L[10][0] = -65.0 / 448.0; + L[10][1] = 379.0 / 448.0; + L[10][2] = 379.0 / 448.0; + L[10][3] = 171.0 / 224.0; + L[10][4] = 9.0 / 32.0; + L[10][5] = -1683.0 / 224.0; + L[10][6] = -1683.0 / 224.0; + L[10][7] = 9.0 / 32.0; + L[10][8] = 171.0 / 224.0; + L[10][9] = -891.0 / 448.0; + L[10][10] = 6885.0 / 448.0; + L[10][11] = -891.0 / 448.0; + L[11][0] = 379.0 / 448.0; + L[11][1] = -65.0 / 448.0; + L[11][2] = 379.0 / 448.0; + L[11][3] = 9.0 / 32.0; + L[11][4] = 171.0 / 224.0; + L[11][5] = 171.0 / 224.0; + L[11][6] = 9.0 / 32.0; + L[11][7] = -1683.0 / 224.0; + L[11][8] = -1683.0 / 224.0; + L[11][9] = -891.0 / 448.0; + L[11][10] = -891.0 / 448.0; + L[11][11] = 6885.0 / 448.0; +} + +inline void L_d2L_dep_matrix(double L[7][12]) { + L[0][0] = 73.0 / 81.0; + L[0][1] = 4.0 / 81.0; + L[0][2] = 4.0 / 81.0; + L[0][3] = 152.0 / 2187.0; + L[0][4] = 35.0 / 2187.0; + L[0][5] = -11.0 / 4374.0; + L[0][6] = -11.0 / 4374.0; + L[0][7] = 35.0 / 2187.0; + L[0][8] = 152.0 / 2187.0; + L[0][9] = 40.0 / 2187.0; + L[0][10] = 4.0 / 2187.0; + L[0][11] = 40.0 / 2187.0; + L[1][0] = 53.0 / 81.0; + L[1][1] = 14.0 / 81.0; + L[1][2] = 14.0 / 81.0; + L[1][3] = 163.0 / 2187.0; + L[1][4] = 91.0 / 2187.0; + L[1][5] = 10.0 / 2187.0; + L[1][6] = 10.0 / 2187.0; + L[1][7] = 91.0 / 2187.0; + L[1][8] = 163.0 / 2187.0; + L[1][9] = 104.0 / 2187.0; + L[1][10] = 32.0 / 2187.0; + L[1][11] = 104.0 / 2187.0; + L[2][0] = 4.0 / 81.0; + L[2][1] = 73.0 / 81.0; + L[2][2] = 4.0 / 81.0; + L[2][3] = 35.0 / 2187.0; + L[2][4] = 152.0 / 2187.0; + L[2][5] = 152.0 / 2187.0; + L[2][6] = 35.0 / 2187.0; + L[2][7] = -11.0 / 4374.0; + L[2][8] = -11.0 / 4374.0; + L[2][9] = 40.0 / 2187.0; + L[2][10] = 40.0 / 2187.0; + L[2][11] = 4.0 / 2187.0; + L[3][0] = 14.0 / 81.0; + L[3][1] = 53.0 / 81.0; + L[3][2] = 14.0 / 81.0; + L[3][3] = 91.0 / 2187.0; + L[3][4] = 163.0 / 2187.0; + L[3][5] = 163.0 / 2187.0; + L[3][6] = 91.0 / 2187.0; + L[3][7] = 10.0 / 2187.0; + L[3][8] = 10.0 / 2187.0; + L[3][9] = 104.0 / 2187.0; + L[3][10] = 104.0 / 2187.0; + L[3][11] = 32.0 / 2187.0; + L[4][0] = 4.0 / 81.0; + L[4][1] = 4.0 / 81.0; + L[4][2] = 73.0 / 81.0; + L[4][3] = -11.0 / 4374.0; + L[4][4] = -11.0 / 4374.0; + L[4][5] = 35.0 / 2187.0; + L[4][6] = 152.0 / 2187.0; + L[4][7] = 152.0 / 2187.0; + L[4][8] = 35.0 / 2187.0; + L[4][9] = 4.0 / 2187.0; + L[4][10] = 40.0 / 2187.0; + L[4][11] = 40.0 / 2187.0; + L[5][0] = 14.0 / 81.0; + L[5][1] = 14.0 / 81.0; + L[5][2] = 53.0 / 81.0; + L[5][3] = 10.0 / 2187.0; + L[5][4] = 10.0 / 2187.0; + L[5][5] = 91.0 / 2187.0; + L[5][6] = 163.0 / 2187.0; + L[5][7] = 163.0 / 2187.0; + L[5][8] = 91.0 / 2187.0; + L[5][9] = 32.0 / 2187.0; + L[5][10] = 104.0 / 2187.0; + L[5][11] = 104.0 / 2187.0; + L[6][0] = 1.0 / 3.0; + L[6][1] = 1.0 / 3.0; + L[6][2] = 1.0 / 3.0; + L[6][3] = 7.0 / 162.0; + L[6][4] = 7.0 / 162.0; + L[6][5] = 7.0 / 162.0; + L[6][6] = 7.0 / 162.0; + L[6][7] = 7.0 / 162.0; + L[6][8] = 7.0 / 162.0; + L[6][9] = 4.0 / 81.0; + L[6][10] = 4.0 / 81.0; + L[6][11] = 4.0 / 81.0; +} + +inline void c_e_matrix(double c[5]) { + c[0] = 3 / 2; + c[1] = -3 / 2; + c[2] = 1 / 4; + c[3] = -1 / 4; + c[4] = 0; +} diff --git a/src/clough_tocher_patches/clough_tocher_autogen_matrix_coeffs.hpp b/src/clough_tocher_patches/clough_tocher_autogen_matrix_coeffs.hpp new file mode 100644 index 0000000..4c287e6 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_autogen_matrix_coeffs.hpp @@ -0,0 +1,396 @@ +#pragma once + +namespace CT_interpolant { +inline void CT_tri_bounds_coeffs(double bound_coeffs[3][3][3]) { + bound_coeffs[0][0][0] = 0; + bound_coeffs[0][0][1] = 0; + bound_coeffs[0][0][2] = 1; + bound_coeffs[0][1][0] = 1.0 / 3.0; + bound_coeffs[0][1][1] = 0; + bound_coeffs[0][1][2] = -1.0 / 3.0; + bound_coeffs[0][2][0] = 0; + bound_coeffs[0][2][1] = 1.0 / 3.0; + bound_coeffs[0][2][2] = -1.0 / 3.0; + bound_coeffs[1][0][0] = 1; + bound_coeffs[1][0][1] = 0; + bound_coeffs[1][0][2] = 0; + bound_coeffs[1][1][0] = -1.0 / 3.0; + bound_coeffs[1][1][1] = 1.0 / 3.0; + bound_coeffs[1][1][2] = 0; + bound_coeffs[1][2][0] = -1.0 / 3.0; + bound_coeffs[1][2][1] = 0; + bound_coeffs[1][2][2] = 1.0 / 3.0; + bound_coeffs[2][0][0] = 0; + bound_coeffs[2][0][1] = 1; + bound_coeffs[2][0][2] = 0; + bound_coeffs[2][1][0] = 0; + bound_coeffs[2][1][1] = -1.0 / 3.0; + bound_coeffs[2][1][2] = 1.0 / 3.0; + bound_coeffs[2][2][0] = 1.0 / 3.0; + bound_coeffs[2][2][1] = -1.0 / 3.0; + bound_coeffs[2][2][2] = 0; +} + +inline void CT_sub_tri_matrices(double subtri_matrix[3][10][12]) { + subtri_matrix[0][0][0] = 1.0 / 2.0; + subtri_matrix[0][0][1] = 1.0 / 2.0; + subtri_matrix[0][0][2] = 0; + subtri_matrix[0][0][3] = 2.0 / 3.0; + subtri_matrix[0][0][4] = 2.0 / 3.0; + subtri_matrix[0][0][5] = -7.0 / 12.0; + subtri_matrix[0][0][6] = -1.0 / 12.0; + subtri_matrix[0][0][7] = -1.0 / 12.0; + subtri_matrix[0][0][8] = -7.0 / 12.0; + subtri_matrix[0][0][9] = 4.0 / 3.0; + subtri_matrix[0][0][10] = -2.0 / 3.0; + subtri_matrix[0][0][11] = -2.0 / 3.0; + subtri_matrix[0][1][0] = -3.0 / 2.0; + subtri_matrix[0][1][1] = 0; + subtri_matrix[0][1][2] = 9.0 / 2.0; + subtri_matrix[0][1][3] = -3.0 / 4.0; + subtri_matrix[0][1][4] = -5.0 / 4.0; + subtri_matrix[0][1][5] = 5.0 / 4.0; + subtri_matrix[0][1][6] = 7.0 / 4.0; + subtri_matrix[0][1][7] = -1.0 / 2.0; + subtri_matrix[0][1][8] = 1.0 / 2.0; + subtri_matrix[0][1][9] = -2; + subtri_matrix[0][1][10] = 2; + subtri_matrix[0][1][11] = 0; + subtri_matrix[0][2][0] = 0; + subtri_matrix[0][2][1] = 3; + subtri_matrix[0][2][2] = 0; + subtri_matrix[0][2][3] = 0; + subtri_matrix[0][2][4] = 0; + subtri_matrix[0][2][5] = 1; + subtri_matrix[0][2][6] = 0; + subtri_matrix[0][2][7] = 0; + subtri_matrix[0][2][8] = 0; + subtri_matrix[0][2][9] = 0; + subtri_matrix[0][2][10] = 0; + subtri_matrix[0][2][11] = 0; + subtri_matrix[0][3][0] = 0; + subtri_matrix[0][3][1] = 1; + subtri_matrix[0][3][2] = 0; + subtri_matrix[0][3][3] = 0; + subtri_matrix[0][3][4] = 0; + subtri_matrix[0][3][5] = 0; + subtri_matrix[0][3][6] = 0; + subtri_matrix[0][3][7] = 0; + subtri_matrix[0][3][8] = 0; + subtri_matrix[0][3][9] = 0; + subtri_matrix[0][3][10] = 0; + subtri_matrix[0][3][11] = 0; + subtri_matrix[0][4][0] = 0; + subtri_matrix[0][4][1] = -3.0 / 2.0; + subtri_matrix[0][4][2] = 9.0 / 2.0; + subtri_matrix[0][4][3] = -5.0 / 4.0; + subtri_matrix[0][4][4] = -3.0 / 4.0; + subtri_matrix[0][4][5] = 1.0 / 2.0; + subtri_matrix[0][4][6] = -1.0 / 2.0; + subtri_matrix[0][4][7] = 7.0 / 4.0; + subtri_matrix[0][4][8] = 5.0 / 4.0; + subtri_matrix[0][4][9] = -2; + subtri_matrix[0][4][10] = 0; + subtri_matrix[0][4][11] = 2; + subtri_matrix[0][5][0] = 3; + subtri_matrix[0][5][1] = 3; + subtri_matrix[0][5][2] = 0; + subtri_matrix[0][5][3] = 3.0 / 2.0; + subtri_matrix[0][5][4] = 3.0 / 2.0; + subtri_matrix[0][5][5] = -1; + subtri_matrix[0][5][6] = 0; + subtri_matrix[0][5][7] = 0; + subtri_matrix[0][5][8] = -1; + subtri_matrix[0][5][9] = 4; + subtri_matrix[0][5][10] = 0; + subtri_matrix[0][5][11] = 0; + subtri_matrix[0][6][0] = 0; + subtri_matrix[0][6][1] = 3; + subtri_matrix[0][6][2] = 0; + subtri_matrix[0][6][3] = 0; + subtri_matrix[0][6][4] = 1; + subtri_matrix[0][6][5] = 0; + subtri_matrix[0][6][6] = 0; + subtri_matrix[0][6][7] = 0; + subtri_matrix[0][6][8] = 0; + subtri_matrix[0][6][9] = 0; + subtri_matrix[0][6][10] = 0; + subtri_matrix[0][6][11] = 0; + subtri_matrix[0][7][0] = 3; + subtri_matrix[0][7][1] = 0; + subtri_matrix[0][7][2] = 0; + subtri_matrix[0][7][3] = 0; + subtri_matrix[0][7][4] = 0; + subtri_matrix[0][7][5] = 0; + subtri_matrix[0][7][6] = 0; + subtri_matrix[0][7][7] = 0; + subtri_matrix[0][7][8] = 1; + subtri_matrix[0][7][9] = 0; + subtri_matrix[0][7][10] = 0; + subtri_matrix[0][7][11] = 0; + subtri_matrix[0][8][0] = 3; + subtri_matrix[0][8][1] = 0; + subtri_matrix[0][8][2] = 0; + subtri_matrix[0][8][3] = 1; + subtri_matrix[0][8][4] = 0; + subtri_matrix[0][8][5] = 0; + subtri_matrix[0][8][6] = 0; + subtri_matrix[0][8][7] = 0; + subtri_matrix[0][8][8] = 0; + subtri_matrix[0][8][9] = 0; + subtri_matrix[0][8][10] = 0; + subtri_matrix[0][8][11] = 0; + subtri_matrix[0][9][0] = 1; + subtri_matrix[0][9][1] = 0; + subtri_matrix[0][9][2] = 0; + subtri_matrix[0][9][3] = 0; + subtri_matrix[0][9][4] = 0; + subtri_matrix[0][9][5] = 0; + subtri_matrix[0][9][6] = 0; + subtri_matrix[0][9][7] = 0; + subtri_matrix[0][9][8] = 0; + subtri_matrix[0][9][9] = 0; + subtri_matrix[0][9][10] = 0; + subtri_matrix[0][9][11] = 0; + subtri_matrix[1][0][0] = 0; + subtri_matrix[1][0][1] = 0; + subtri_matrix[1][0][2] = 1; + subtri_matrix[1][0][3] = 0; + subtri_matrix[1][0][4] = 0; + subtri_matrix[1][0][5] = 0; + subtri_matrix[1][0][6] = 0; + subtri_matrix[1][0][7] = 0; + subtri_matrix[1][0][8] = 0; + subtri_matrix[1][0][9] = 0; + subtri_matrix[1][0][10] = 0; + subtri_matrix[1][0][11] = 0; + subtri_matrix[1][1][0] = 0; + subtri_matrix[1][1][1] = 0; + subtri_matrix[1][1][2] = 3; + subtri_matrix[1][1][3] = 0; + subtri_matrix[1][1][4] = 0; + subtri_matrix[1][1][5] = 0; + subtri_matrix[1][1][6] = 1; + subtri_matrix[1][1][7] = 0; + subtri_matrix[1][1][8] = 0; + subtri_matrix[1][1][9] = 0; + subtri_matrix[1][1][10] = 0; + subtri_matrix[1][1][11] = 0; + subtri_matrix[1][2][0] = 0; + subtri_matrix[1][2][1] = 3; + subtri_matrix[1][2][2] = 0; + subtri_matrix[1][2][3] = 0; + subtri_matrix[1][2][4] = 0; + subtri_matrix[1][2][5] = 1; + subtri_matrix[1][2][6] = 0; + subtri_matrix[1][2][7] = 0; + subtri_matrix[1][2][8] = 0; + subtri_matrix[1][2][9] = 0; + subtri_matrix[1][2][10] = 0; + subtri_matrix[1][2][11] = 0; + subtri_matrix[1][3][0] = 0; + subtri_matrix[1][3][1] = 1; + subtri_matrix[1][3][2] = 0; + subtri_matrix[1][3][3] = 0; + subtri_matrix[1][3][4] = 0; + subtri_matrix[1][3][5] = 0; + subtri_matrix[1][3][6] = 0; + subtri_matrix[1][3][7] = 0; + subtri_matrix[1][3][8] = 0; + subtri_matrix[1][3][9] = 0; + subtri_matrix[1][3][10] = 0; + subtri_matrix[1][3][11] = 0; + subtri_matrix[1][4][0] = 0; + subtri_matrix[1][4][1] = 0; + subtri_matrix[1][4][2] = 3; + subtri_matrix[1][4][3] = 0; + subtri_matrix[1][4][4] = 0; + subtri_matrix[1][4][5] = 0; + subtri_matrix[1][4][6] = 0; + subtri_matrix[1][4][7] = 1; + subtri_matrix[1][4][8] = 0; + subtri_matrix[1][4][9] = 0; + subtri_matrix[1][4][10] = 0; + subtri_matrix[1][4][11] = 0; + subtri_matrix[1][5][0] = 0; + subtri_matrix[1][5][1] = 3; + subtri_matrix[1][5][2] = 3; + subtri_matrix[1][5][3] = 0; + subtri_matrix[1][5][4] = -1; + subtri_matrix[1][5][5] = 3.0 / 2.0; + subtri_matrix[1][5][6] = 3.0 / 2.0; + subtri_matrix[1][5][7] = -1; + subtri_matrix[1][5][8] = 0; + subtri_matrix[1][5][9] = 0; + subtri_matrix[1][5][10] = 4; + subtri_matrix[1][5][11] = 0; + subtri_matrix[1][6][0] = 0; + subtri_matrix[1][6][1] = 3; + subtri_matrix[1][6][2] = 0; + subtri_matrix[1][6][3] = 0; + subtri_matrix[1][6][4] = 1; + subtri_matrix[1][6][5] = 0; + subtri_matrix[1][6][6] = 0; + subtri_matrix[1][6][7] = 0; + subtri_matrix[1][6][8] = 0; + subtri_matrix[1][6][9] = 0; + subtri_matrix[1][6][10] = 0; + subtri_matrix[1][6][11] = 0; + subtri_matrix[1][7][0] = 9.0 / 2.0; + subtri_matrix[1][7][1] = -3.0 / 2.0; + subtri_matrix[1][7][2] = 0; + subtri_matrix[1][7][3] = -1.0 / 2.0; + subtri_matrix[1][7][4] = 1.0 / 2.0; + subtri_matrix[1][7][5] = -3.0 / 4.0; + subtri_matrix[1][7][6] = -5.0 / 4.0; + subtri_matrix[1][7][7] = 5.0 / 4.0; + subtri_matrix[1][7][8] = 7.0 / 4.0; + subtri_matrix[1][7][9] = 0; + subtri_matrix[1][7][10] = -2; + subtri_matrix[1][7][11] = 2; + subtri_matrix[1][8][0] = 9.0 / 2.0; + subtri_matrix[1][8][1] = 0; + subtri_matrix[1][8][2] = -3.0 / 2.0; + subtri_matrix[1][8][3] = 7.0 / 4.0; + subtri_matrix[1][8][4] = 5.0 / 4.0; + subtri_matrix[1][8][5] = -5.0 / 4.0; + subtri_matrix[1][8][6] = -3.0 / 4.0; + subtri_matrix[1][8][7] = 1.0 / 2.0; + subtri_matrix[1][8][8] = -1.0 / 2.0; + subtri_matrix[1][8][9] = 2; + subtri_matrix[1][8][10] = -2; + subtri_matrix[1][8][11] = 0; + subtri_matrix[1][9][0] = 0; + subtri_matrix[1][9][1] = 1.0 / 2.0; + subtri_matrix[1][9][2] = 1.0 / 2.0; + subtri_matrix[1][9][3] = -1.0 / 12.0; + subtri_matrix[1][9][4] = -7.0 / 12.0; + subtri_matrix[1][9][5] = 2.0 / 3.0; + subtri_matrix[1][9][6] = 2.0 / 3.0; + subtri_matrix[1][9][7] = -7.0 / 12.0; + subtri_matrix[1][9][8] = -1.0 / 12.0; + subtri_matrix[1][9][9] = -2.0 / 3.0; + subtri_matrix[1][9][10] = 4.0 / 3.0; + subtri_matrix[1][9][11] = -2.0 / 3.0; + subtri_matrix[2][0][0] = 0; + subtri_matrix[2][0][1] = 0; + subtri_matrix[2][0][2] = 1; + subtri_matrix[2][0][3] = 0; + subtri_matrix[2][0][4] = 0; + subtri_matrix[2][0][5] = 0; + subtri_matrix[2][0][6] = 0; + subtri_matrix[2][0][7] = 0; + subtri_matrix[2][0][8] = 0; + subtri_matrix[2][0][9] = 0; + subtri_matrix[2][0][10] = 0; + subtri_matrix[2][0][11] = 0; + subtri_matrix[2][1][0] = 0; + subtri_matrix[2][1][1] = 0; + subtri_matrix[2][1][2] = 3; + subtri_matrix[2][1][3] = 0; + subtri_matrix[2][1][4] = 0; + subtri_matrix[2][1][5] = 0; + subtri_matrix[2][1][6] = 1; + subtri_matrix[2][1][7] = 0; + subtri_matrix[2][1][8] = 0; + subtri_matrix[2][1][9] = 0; + subtri_matrix[2][1][10] = 0; + subtri_matrix[2][1][11] = 0; + subtri_matrix[2][2][0] = -3.0 / 2.0; + subtri_matrix[2][2][1] = 9.0 / 2.0; + subtri_matrix[2][2][2] = 0; + subtri_matrix[2][2][3] = 1.0 / 2.0; + subtri_matrix[2][2][4] = -1.0 / 2.0; + subtri_matrix[2][2][5] = 7.0 / 4.0; + subtri_matrix[2][2][6] = 5.0 / 4.0; + subtri_matrix[2][2][7] = -5.0 / 4.0; + subtri_matrix[2][2][8] = -3.0 / 4.0; + subtri_matrix[2][2][9] = 0; + subtri_matrix[2][2][10] = 2; + subtri_matrix[2][2][11] = -2; + subtri_matrix[2][3][0] = 1.0 / 2.0; + subtri_matrix[2][3][1] = 0; + subtri_matrix[2][3][2] = 1.0 / 2.0; + subtri_matrix[2][3][3] = -7.0 / 12.0; + subtri_matrix[2][3][4] = -1.0 / 12.0; + subtri_matrix[2][3][5] = -1.0 / 12.0; + subtri_matrix[2][3][6] = -7.0 / 12.0; + subtri_matrix[2][3][7] = 2.0 / 3.0; + subtri_matrix[2][3][8] = 2.0 / 3.0; + subtri_matrix[2][3][9] = -2.0 / 3.0; + subtri_matrix[2][3][10] = -2.0 / 3.0; + subtri_matrix[2][3][11] = 4.0 / 3.0; + subtri_matrix[2][4][0] = 0; + subtri_matrix[2][4][1] = 0; + subtri_matrix[2][4][2] = 3; + subtri_matrix[2][4][3] = 0; + subtri_matrix[2][4][4] = 0; + subtri_matrix[2][4][5] = 0; + subtri_matrix[2][4][6] = 0; + subtri_matrix[2][4][7] = 1; + subtri_matrix[2][4][8] = 0; + subtri_matrix[2][4][9] = 0; + subtri_matrix[2][4][10] = 0; + subtri_matrix[2][4][11] = 0; + subtri_matrix[2][5][0] = 3; + subtri_matrix[2][5][1] = 0; + subtri_matrix[2][5][2] = 3; + subtri_matrix[2][5][3] = -1; + subtri_matrix[2][5][4] = 0; + subtri_matrix[2][5][5] = 0; + subtri_matrix[2][5][6] = -1; + subtri_matrix[2][5][7] = 3.0 / 2.0; + subtri_matrix[2][5][8] = 3.0 / 2.0; + subtri_matrix[2][5][9] = 0; + subtri_matrix[2][5][10] = 0; + subtri_matrix[2][5][11] = 4; + subtri_matrix[2][6][0] = 0; + subtri_matrix[2][6][1] = 9.0 / 2.0; + subtri_matrix[2][6][2] = -3.0 / 2.0; + subtri_matrix[2][6][3] = 5.0 / 4.0; + subtri_matrix[2][6][4] = 7.0 / 4.0; + subtri_matrix[2][6][5] = -1.0 / 2.0; + subtri_matrix[2][6][6] = 1.0 / 2.0; + subtri_matrix[2][6][7] = -3.0 / 4.0; + subtri_matrix[2][6][8] = -5.0 / 4.0; + subtri_matrix[2][6][9] = 2; + subtri_matrix[2][6][10] = 0; + subtri_matrix[2][6][11] = -2; + subtri_matrix[2][7][0] = 3; + subtri_matrix[2][7][1] = 0; + subtri_matrix[2][7][2] = 0; + subtri_matrix[2][7][3] = 0; + subtri_matrix[2][7][4] = 0; + subtri_matrix[2][7][5] = 0; + subtri_matrix[2][7][6] = 0; + subtri_matrix[2][7][7] = 0; + subtri_matrix[2][7][8] = 1; + subtri_matrix[2][7][9] = 0; + subtri_matrix[2][7][10] = 0; + subtri_matrix[2][7][11] = 0; + subtri_matrix[2][8][0] = 3; + subtri_matrix[2][8][1] = 0; + subtri_matrix[2][8][2] = 0; + subtri_matrix[2][8][3] = 1; + subtri_matrix[2][8][4] = 0; + subtri_matrix[2][8][5] = 0; + subtri_matrix[2][8][6] = 0; + subtri_matrix[2][8][7] = 0; + subtri_matrix[2][8][8] = 0; + subtri_matrix[2][8][9] = 0; + subtri_matrix[2][8][10] = 0; + subtri_matrix[2][8][11] = 0; + subtri_matrix[2][9][0] = 1; + subtri_matrix[2][9][1] = 0; + subtri_matrix[2][9][2] = 0; + subtri_matrix[2][9][3] = 0; + subtri_matrix[2][9][4] = 0; + subtri_matrix[2][9][5] = 0; + subtri_matrix[2][9][6] = 0; + subtri_matrix[2][9][7] = 0; + subtri_matrix[2][9][8] = 0; + subtri_matrix[2][9][9] = 0; + subtri_matrix[2][9][10] = 0; + subtri_matrix[2][9][11] = 0; +} +} // namespace CT_interpolant diff --git a/src/clough_tocher_patches/clough_tocher_constraint_matrices.cpp b/src/clough_tocher_patches/clough_tocher_constraint_matrices.cpp new file mode 100644 index 0000000..6fae5b9 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_constraint_matrices.cpp @@ -0,0 +1,42 @@ +#include "clough_tocher_constraint_matrices.hpp" +#include "clough_tocher_autogen_constraint_matrices.hpp" + +Eigen::Matrix L_L2d_ind_m() { + double L[12][12]; + L_L2d_ind_matrix(L); + Eigen::Matrix L_L2d_ind_mat; + + for (int i = 0; i < 12; ++i) { + for (int j = 0; j < 12; ++j) { + L_L2d_ind_mat(i, j) = L[i][j]; + } + } + + return L_L2d_ind_mat; +} + +Eigen::Matrix L_d2L_dep_m() { + double L[7][12]; + L_d2L_dep_matrix(L); + Eigen::Matrix L_d2L_dep_mat; + + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 12; ++j) { + L_d2L_dep_mat(i, j) = L[i][j]; + } + } + + return L_d2L_dep_mat; +} + +Eigen::Matrix c_e_m() { + double c[5]; + c_e_matrix(c); + Eigen::Matrix c_e_mat; + + for (int i = 0; i < 5; ++i) { + c_e_mat[i] = c[i]; + } + + return c_e_mat; +} \ No newline at end of file diff --git a/src/clough_tocher_patches/clough_tocher_constraint_matrices.hpp b/src/clough_tocher_patches/clough_tocher_constraint_matrices.hpp new file mode 100644 index 0000000..2c8a2d5 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_constraint_matrices.hpp @@ -0,0 +1,6 @@ +#pragma once +#include "common.h" + +Eigen::Matrix L_L2d_ind_m(); +Eigen::Matrix L_d2L_dep_m(); +Eigen::Matrix c_e_m(); \ No newline at end of file diff --git a/src/clough_tocher_patches/clough_tocher_matrices.cpp b/src/clough_tocher_patches/clough_tocher_matrices.cpp new file mode 100644 index 0000000..7b96a83 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_matrices.cpp @@ -0,0 +1,36 @@ +#include "clough_tocher_matrices.hpp" +#include "clough_tocher_autogen_matrix_coeffs.hpp" + +std::array, 3> CT_subtri_bound_matrices() { + double bound_coeffs[3][3][3]; + CT_interpolant::CT_tri_bounds_coeffs(bound_coeffs); + + std::array, 3> CT_bound_coeffs; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 3; ++k) { + CT_bound_coeffs[i](j, k) = bound_coeffs[i][j][k]; + } + } + } + + return CT_bound_coeffs; +} + +std::array, 3> CT_subtri_matrices() { + double CT_coeffs[3][10][12]; + CT_interpolant::CT_sub_tri_matrices(CT_coeffs); + + std::array, 3> CT_matrices; + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 10; ++j) { + for (int k = 0; k < 12; ++k) { + CT_matrices[i](j, k) = CT_coeffs[i][j][k]; + } + } + } + + return CT_matrices; +} diff --git a/src/clough_tocher_patches/clough_tocher_matrices.hpp b/src/clough_tocher_patches/clough_tocher_matrices.hpp new file mode 100644 index 0000000..d5e20be --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_matrices.hpp @@ -0,0 +1,5 @@ +#pragma once +#include "common.h" + +std::array, 3> CT_subtri_bound_matrices(); +std::array, 3> CT_subtri_matrices(); diff --git a/src/clough_tocher_patches/clough_tocher_patch.cpp b/src/clough_tocher_patches/clough_tocher_patch.cpp new file mode 100644 index 0000000..f8650cc --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_patch.cpp @@ -0,0 +1,110 @@ +#include "clough_tocher_patch.hpp" +#include "clough_tocher_matrices.hpp" + +#include +#include +#include + +const std::array, 3> + CloughTocherPatch::m_CTtri_bounds = CT_subtri_bound_matrices(); + +const std::array, 3> + CloughTocherPatch::m_CT_matrices = CT_subtri_matrices(); + +CloughTocherPatch::CloughTocherPatch( + const std::array &corner_data, + const std::array &midpoint_data) + : m_corner_data(corner_data), m_midpoint_data(midpoint_data) { + + // compute boundary data + for (int i = 0; i < 3; ++i) { + // p0,p1,p2 + m_boundary_data.row(i) = m_corner_data[i].function_value; + // G01, G02, G12, G10, G20, G21 + m_boundary_data.row(3 + i * 2 + 0) = m_corner_data[i].first_edge_derivative; + m_boundary_data.row(3 + i * 2 + 1) = + m_corner_data[i].second_edge_derivative; + } + + // p0, p1, p2, G10, G01, G02, G20, G21, G12, N01, N20, N12 + m_boundary_data.row(0) = m_corner_data[0].function_value; // p0 + m_boundary_data.row(1) = m_corner_data[1].function_value; // p1 + m_boundary_data.row(2) = m_corner_data[2].function_value; // p2 + + m_boundary_data.row(3) = m_corner_data[0].first_edge_derivative; // G01 + m_boundary_data.row(4) = m_corner_data[1].second_edge_derivative; // G10 + m_boundary_data.row(5) = m_corner_data[1].first_edge_derivative; // G12 + m_boundary_data.row(6) = m_corner_data[2].second_edge_derivative; // G21 + m_boundary_data.row(7) = m_corner_data[2].first_edge_derivative; // G20 + m_boundary_data.row(8) = m_corner_data[0].second_edge_derivative; // G02 + + m_boundary_data.row(9) = m_midpoint_data[2].normal_derivative; // N01 + m_boundary_data.row(10) = m_midpoint_data[0].normal_derivative; // N12 + m_boundary_data.row(11) = m_midpoint_data[1].normal_derivative; // N20 + + // compute coeff matrices + for (int i = 0; i < 3; ++i) { + m_CT_coeffs[i] = m_CT_matrices[i] * m_boundary_data; + } +} + +int CloughTocherPatch::triangle_ind(const double &u, const double &v, + const double &w) const { + int idx = -1; + for (int i = 0; i < 3; ++i) { + if (m_CTtri_bounds[i](0, 0) * u + m_CTtri_bounds[i](0, 1) * v + + m_CTtri_bounds[i](0, 2) * w >= + -1e-7 && + m_CTtri_bounds[i](1, 0) * u + m_CTtri_bounds[i](1, 1) * v + + m_CTtri_bounds[i](1, 2) * w >= + -1e-7 && + m_CTtri_bounds[i](2, 0) * u + m_CTtri_bounds[i](2, 1) * v + + m_CTtri_bounds[i](2, 2) * w >= + -1e-7) { + idx = i; + break; + } + } + + assert(idx > -1); + return idx; +} + +Eigen::Matrix +CloughTocherPatch::monomial_basis_eval(const double &u, const double &v, + const double &w) const { + Eigen::Matrix monomial_basis_values; + monomial_basis_values(0, 0) = w * w * w; // w3 + monomial_basis_values(0, 1) = v * w * w; // vw2 + monomial_basis_values(0, 2) = v * v * w; // v2w + monomial_basis_values(0, 3) = v * v * v; // v3 + monomial_basis_values(0, 4) = u * w * w; // uw2 + monomial_basis_values(0, 5) = u * v * w; // uvw + monomial_basis_values(0, 6) = u * v * v; // uv2 + monomial_basis_values(0, 7) = u * u * w; // u2w + monomial_basis_values(0, 8) = u * u * v; // u2v + monomial_basis_values(0, 9) = u * u * u; // u3 + + return monomial_basis_values; +} + +Eigen::Matrix CloughTocherPatch::CT_eval(const double &u, + const double &v, + const double &w) const { + int idx = CloughTocherPatch::triangle_ind(u, v, w); + + // std::cout << "subtri_idx: " << idx << std::endl; + Eigen::Matrix bb_vector = + CloughTocherPatch::monomial_basis_eval(u, v, w); + + // std::cout << "monomial: " << bb_vector << std::endl; + + Eigen::Matrix val; + val = bb_vector * m_CT_coeffs[idx]; + return val; +} + +std::array, 3> +CloughTocherPatch::get_coeffs() const { + return m_CT_coeffs; +} \ No newline at end of file diff --git a/src/clough_tocher_patches/clough_tocher_patch.hpp b/src/clough_tocher_patches/clough_tocher_patch.hpp new file mode 100644 index 0000000..81d6e50 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_patch.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "common.h" +#include "convex_polygon.h" + +#include "evaluate_surface_normal.h" +#include "optimize_spline_surface.h" + +class CloughTocherPatch { + +public: + static const std::array, 3> + m_CTtri_bounds; // constant ct sub tri boundaries + static const std::array, 3> + m_CT_matrices; // constant ct matrices for 3 sub tris + +public: + CloughTocherPatch(); + CloughTocherPatch( + const std::array &corner_data, + const std::array &midpoint_data); + + int triangle_ind(const double &u, const double &v, const double &w) const; + + Eigen::Matrix + monomial_basis_eval(const double &u, const double &v, const double &w) const; + + Eigen::Matrix CT_eval(const double &u, const double &v, + const double &w) const; + + std::array, 3> get_coeffs() const; + +public: + // std::array, 3> m_boundary_data; + + std::array m_corner_data; + std::array m_midpoint_data; + Eigen::Matrix + m_boundary_data; // p0, p1, p2, G01, G10, G12, G21, G20, G02, N01, + // N12,N20, for x, y, z + std::array, 3> + m_CT_coeffs; // constant ct matrices for 3 sub tris +}; \ No newline at end of file diff --git a/src/clough_tocher_patches/clough_tocher_surface.cpp b/src/clough_tocher_patches/clough_tocher_surface.cpp new file mode 100644 index 0000000..900691f --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_surface.cpp @@ -0,0 +1,964 @@ +#include "clough_tocher_surface.hpp" + +#include +#include +#include + +#include "clough_tocher_constraint_matrices.hpp" + +CloughTocherSurface::CloughTocherSurface() {} + +CloughTocherSurface::CloughTocherSurface( + const Eigen::MatrixXd &V, const AffineManifold &affine_manifold, + const OptimizationParameters &optimization_params, + Eigen::SparseMatrix &fit_matrix, + Eigen::SparseMatrix &energy_hessian, + Eigen::CholmodSupernodalLLT> + &energy_hessian_inverse) + : m_affine_manifold(affine_manifold) { + + // Generate normals + MatrixXr N; + generate_face_normals(V, affine_manifold, N); + + // Generate fit matrix by setting the parametrized quadratic surface mapping + // factor to zero + double fit_energy; + VectorXr fit_derivatives; + OptimizationParameters optimization_params_fit = optimization_params; + optimization_params_fit.parametrized_quadratic_surface_mapping_factor = 0.0; + Eigen::CholmodSupernodalLLT> fit_matrix_inverse; + build_twelve_split_spline_energy_system( + V, N, affine_manifold, optimization_params_fit, fit_energy, + fit_derivatives, fit_matrix, fit_matrix_inverse); + + // Build full energy hessian system + double energy; + VectorXr derivatives; + build_twelve_split_spline_energy_system( + V, N, affine_manifold, optimization_params, energy, derivatives, + energy_hessian, energy_hessian_inverse); + + // Build optimized corner and midpoint data + generate_optimized_twelve_split_position_data(V, affine_manifold, fit_matrix, + energy_hessian_inverse, + m_corner_data, m_midpoint_data); + + // compute patches + assert(m_corner_data.size() == m_midpoint_data.size()); + for (size_t i = 0; i < m_corner_data.size(); ++i) { + m_patches.push_back( + CloughTocherPatch(m_corner_data[i], m_midpoint_data[i])); + } +} + +Eigen::Matrix +CloughTocherSurface::evaluate_patch(const PatchIndex &patch_index, + const double &u, const double &v, + const double &w) { + return m_patches[patch_index].CT_eval(u, v, w); +} + +void CloughTocherSurface::generate_face_normals( + const Eigen::MatrixXd &V, const AffineManifold &affine_manifold, + Eigen::MatrixXd &N) { + Eigen::MatrixXi const &F = affine_manifold.get_faces(); + + // Compute the cones of the affine manifold + std::vector cones; + affine_manifold.compute_cones(cones); + + std::cout << "#Cone: " << cones.size() << std::endl; + + // Get vertex normals + Eigen::MatrixXd N_vertices; + igl::per_vertex_normals(V, F, N_vertices); + + // Set the face one ring normals of the cone vertices to the cone vertex + // normal + N.setZero(F.rows(), 3); + for (size_t i = 0; i < cones.size(); ++i) { + int ci = cones[i]; + VertexManifoldChart const &chart = affine_manifold.get_vertex_chart(ci); + for (size_t j = 0; j < chart.face_one_ring.size(); ++j) { + int fj = chart.face_one_ring[j]; + N.row(fj) = N_vertices.row(ci); + } + } +} + +void CloughTocherSurface::write_cubic_surface_to_msh_no_conn( + std::string filename) { + std::ofstream file(filename); + + /* + $MeshFormat + 4.1 0 8 MSH4.1, ASCII + $EndMeshFormat + */ + + file << "$MeshFormat\n" + << "4.1 0 8\n" + << "$EndMeshFormat\n"; + + // msh 10 node 3rd-order triangle nodes + const std::array tri_0_bcs = { + {PlanarPoint(0, 0), PlanarPoint(1., 0), PlanarPoint(1. / 3., 1. / 3.), + PlanarPoint(1. / 3., 0), PlanarPoint(2. / 3., 0), + PlanarPoint(7. / 9., 1. / 9.), PlanarPoint(5. / 9., 2. / 9.), + PlanarPoint(2. / 9., 2. / 9.), PlanarPoint(1. / 9., 1. / 9.), + PlanarPoint(4. / 9., 1. / 9.)}}; + + const std::array tri_1_bcs = { + {PlanarPoint(1, 0), PlanarPoint(0, 1), PlanarPoint(1. / 3., 1. / 3.), + PlanarPoint(2. / 3., 1. / 3.), PlanarPoint(1. / 3., 2. / 3.), + PlanarPoint(1. / 9., 7. / 9.), PlanarPoint(2. / 9., 5. / 9.), + PlanarPoint(5. / 9., 2. / 9.), PlanarPoint(7. / 9., 1. / 9.), + PlanarPoint(4. / 9., 4. / 9.)}}; + + const std::array tri_2_bcs = { + {PlanarPoint(0, 1), PlanarPoint(0, 0), PlanarPoint(1. / 3., 1. / 3.), + PlanarPoint(0, 2. / 3.), PlanarPoint(0, 1. / 3.), + PlanarPoint(1. / 9., 1. / 9.), PlanarPoint(2. / 9., 2. / 9.), + PlanarPoint(2. / 9., 5. / 9.), PlanarPoint(1. / 9., 7. / 9.), + PlanarPoint(1. / 9., 4. / 9.)}}; + + file << "$Nodes\n"; + + const size_t node_size = m_patches.size() * 30; + file << "1 " << node_size << " 1 " << node_size << "\n"; + file << "2 1 0 " << node_size << "\n"; + + for (size_t i = 1; i <= node_size; ++i) { + file << i << "\n"; + } + + for (const auto &patch : m_patches) { + for (const auto &bc : tri_0_bcs) { + auto z = patch.CT_eval(bc[0], bc[1], 1 - bc[0] - bc[1]); + file << z(0, 0) << " " << z(0, 1) << " " << z(0, 2) << "\n"; + } + for (const auto &bc : tri_1_bcs) { + auto z = patch.CT_eval(bc[0], bc[1], 1 - bc[0] - bc[1]); + file << z(0, 0) << " " << z(0, 1) << " " << z(0, 2) << "\n"; + } + for (const auto &bc : tri_2_bcs) { + auto z = patch.CT_eval(bc[0], bc[1], 1 - bc[0] - bc[1]); + file << z(0, 0) << " " << z(0, 1) << " " << z(0, 2) << "\n"; + } + } + + file << "$EndNodes\n"; + + // write elements + const size_t element_size = m_patches.size() * 3; + + file << "$Elements\n"; + file << "1 " << element_size << " 1 " << element_size << "\n"; + file << "2 1 21 " << element_size << "\n"; + for (size_t i = 0; i < element_size; ++i) { + file << i + 1 << " " << i * 10 + 1 << " " << i * 10 + 2 << " " << i * 10 + 3 + << " " << i * 10 + 4 << " " << i * 10 + 5 << " " << i * 10 + 6 << " " + << i * 10 + 7 << " " << i * 10 + 8 << " " << i * 10 + 9 << " " + << i * 10 + 10 << "\n"; + } + + file << "$EndElements\n"; +} + +void CloughTocherSurface::write_coeffs_to_obj(std::string filename) { + std::ofstream file(filename); + + for (const auto &patch : m_patches) { + std::array, 3> coeffs = patch.get_coeffs(); + for (int i = 0; i < 1; ++i) { + for (int j = 0; j < 10; ++j) { + file << "v " << coeffs[i](j, 0) << " " << coeffs[i](j, 1) << " " + << coeffs[i](j, 2) << "\n"; + } + } + } + + file << "f 1 1 1\n"; +} + +void CloughTocherSurface::sample_to_obj(std::string filename, int sample_size) { + std::ofstream file(filename); + + for (const auto &patch : m_patches) { + for (int i = 0; i <= sample_size; ++i) { + for (int j = 0; j <= sample_size - i; ++j) { + double u = 1. / sample_size * i; + double v = 1. / sample_size * j; + double w = 1 - u - v; + + // std::cout << "u: " << u << " v: " << v << " w: " << w << std::endl; + // std::cout << "w^3: " << w * w * w << std::endl; + auto z = patch.CT_eval(u, v, w); + + // std::cout << z << std::endl; + + file << "v " << z[0] << " " << z[1] << " " << z[2] << '\n'; + } + } + } + + file << "f 1 1 1\n"; +} + +void CloughTocherSurface::write_cubic_surface_to_msh_with_conn( + std::string filename) { + std::ofstream file(filename + ".msh"); + + /* + $MeshFormat + 4.1 0 8 MSH4.1, ASCII + $EndMeshFormat + */ + + file << "$MeshFormat\n" + << "4.1 0 8\n" + << "$EndMeshFormat\n"; + + /* + subtri0: 0 4 5 1 12 13 3 11 10 16 + subtri1: 1 6 7 2 14 15 3 13 12 17 + subtri2: 2 8 9 0 10 11 3 15 14 18 + */ + + const std::array CT_nodes = {{ + PlanarPoint(0., 0.), PlanarPoint(1., 0.), + PlanarPoint(0., 1.), PlanarPoint(1. / 3., 1. / 3.), + PlanarPoint(1. / 3., 0.), PlanarPoint(2. / 3., 0.), + PlanarPoint(2. / 3., 1. / 3.), PlanarPoint(1. / 3., 2. / 3.), + PlanarPoint(0., 2. / 3.), PlanarPoint(0., 1. / 3.), + PlanarPoint(1. / 9., 1. / 9.), PlanarPoint(2. / 9., 2. / 9.), + PlanarPoint(7. / 9., 1. / 9.), PlanarPoint(5. / 9., 2. / 9.), + PlanarPoint(1. / 9., 7. / 9.), PlanarPoint(2. / 9., 5. / 9.), + PlanarPoint(4. / 9., 1. / 9.), PlanarPoint(4. / 9., 4. / 9.), + PlanarPoint(1. / 9., 4. / 9.), + }}; + + // std::vector vertices; + std::map, std::array> + boundary_edge_to_v_map; + std::vector> faces; + std::map v_to_v_map; + std::vector vertices; + const auto &F = m_affine_manifold.get_faces(); + + Eigen::MatrixXd edges; + igl::edges(F, edges); + + Eigen::MatrixXd edges_uv; + igl::edges(m_affine_manifold.get_F_uv(), edges_uv); + + // checks + std::cout << "#3d face edges: " << edges.rows() << std::endl; + std::cout << "#uv face edges: " << edges_uv.rows() << std::endl; + std::cout << "#edge charts: " << m_affine_manifold.m_edge_charts.size() + << std::endl; + for (const auto &e : m_affine_manifold.m_edge_charts) { + if (e.is_boundary) { + std::cout << "boundary" << std::endl; + } + } + + for (const auto &v : m_affine_manifold.m_vertex_charts) { + if (v.is_cone) { + std::cout << "cone" << std::endl; + } + } + + // compute corner vertices first + for (size_t p_idx = 0; p_idx < m_patches.size(); p_idx++) { + const auto &patch = m_patches[p_idx]; + // idk why but this is 2 0 1 not 0 1 2, maybe because of the half edge data + // structure + std::array Fv = {{F(p_idx, 2), F(p_idx, 0), F(p_idx, 1)}}; + for (int i = 0; i < 3; ++i) { + if (v_to_v_map.find(Fv[i]) != v_to_v_map.end()) { + // vertex already computed + continue; + } else { + auto z = patch.CT_eval(CT_nodes[i][0], CT_nodes[i][1], + 1 - CT_nodes[i][0] - CT_nodes[i][1]); + v_to_v_map[Fv[i]] = vertices.size(); + vertices.push_back(z); + } + } + } + + assert(size_t(F.rows()) == m_patches.size()); + + for (size_t p_idx = 0; p_idx < m_patches.size(); p_idx++) { + std::array l_vids = {-1}; + // idk why but this is 2 0 1 not 0 1 2, maybe because of the half edge data + // structure + std::array Fv = {{F(p_idx, 2), F(p_idx, 0), F(p_idx, 1)}}; + const auto &patch = m_patches[p_idx]; + + // node 0 - 2 + for (int i = 0; i < 3; ++i) { + l_vids[i] = v_to_v_map[Fv[i]]; + } + + // node 3 + auto zz = patch.CT_eval(CT_nodes[3][0], CT_nodes[3][1], + 1 - CT_nodes[3][0] - CT_nodes[3][1]); + l_vids[3] = vertices.size(); + vertices.push_back(zz); + + // node 4 5 6 7 8 9 + for (int i = 0; i < 3; ++i) { + if (boundary_edge_to_v_map.find(std::make_pair(Fv[(i + 1) % 3], Fv[i])) != + boundary_edge_to_v_map.end()) { + // this edge is processed in some other patch + const auto &vs = + boundary_edge_to_v_map[std::make_pair(Fv[(i + 1) % 3], Fv[i])]; + l_vids[4 + i * 2 + 0] = vs[1]; + l_vids[4 + i * 2 + 1] = vs[0]; + } else { + // eval new vertices + auto z0 = patch.CT_eval( + CT_nodes[4 + i * 2 + 0][0], CT_nodes[4 + i * 2 + 0][1], + 1 - CT_nodes[4 + i * 2 + 0][0] - CT_nodes[4 + i * 2 + 0][1]); + auto z1 = patch.CT_eval( + CT_nodes[4 + i * 2 + 1][0], CT_nodes[4 + i * 2 + 1][1], + 1 - CT_nodes[4 + i * 2 + 1][0] - CT_nodes[4 + i * 2 + 1][1]); + l_vids[4 + i * 2 + 0] = vertices.size(); + vertices.push_back(z0); + l_vids[4 + i * 2 + 1] = vertices.size(); + vertices.push_back(z1); + + boundary_edge_to_v_map[std::make_pair(Fv[i], Fv[(i + 1) % 3])] = { + {l_vids[4 + i * 2 + 0], l_vids[4 + i * 2 + 0 + 1]}}; + } + } + + // node 10 - 18 + for (int i = 10; i < 19; ++i) { + auto z = patch.CT_eval(CT_nodes[i][0], CT_nodes[i][1], + 1 - CT_nodes[i][0] - CT_nodes[i][1]); + l_vids[i] = vertices.size(); + vertices.push_back(z); + } + + /* + subtri0: 0 1 3 4 5 12 13 11 10 16 + subtri1: 1 2 3 6 7 14 15 13 12 17 + subtri2: 2 0 3 8 9 10 11 15 14 18 + */ + faces.push_back( + {{l_vids[0] + 1, l_vids[1] + 1, l_vids[3] + 1, l_vids[4] + 1, + l_vids[5] + 1, l_vids[12] + 1, l_vids[13] + 1, l_vids[11] + 1, + l_vids[10] + 1, l_vids[16] + 1}}); + faces.push_back( + {{l_vids[1] + 1, l_vids[2] + 1, l_vids[3] + 1, l_vids[6] + 1, + l_vids[7] + 1, l_vids[14] + 1, l_vids[15] + 1, l_vids[13] + 1, + l_vids[12] + 1, l_vids[17] + 1}}); + faces.push_back( + {{l_vids[2] + 1, l_vids[0] + 1, l_vids[3] + 1, l_vids[8] + 1, + l_vids[9] + 1, l_vids[10] + 1, l_vids[11] + 1, l_vids[15] + 1, + l_vids[14] + 1, l_vids[18] + 1}}); + + // // debug code + // for (size_t i = 0; i < l_vids.size(); ++i) { + // std::cout << l_vids[i] << ": " << CT_nodes[i] << std::endl; + // } + // std::cout << std::endl; + // if (p_idx == 2) + // break; + } + + file << "$Nodes\n"; + + const size_t node_size = vertices.size(); + file << "1 " << node_size << " 1 " << node_size << "\n"; + file << "2 1 0 " << node_size << "\n"; + + for (size_t i = 1; i <= node_size; ++i) { + file << i << "\n"; + } + + for (size_t i = 0; i < node_size; ++i) { + file << vertices[i][0] << " " << vertices[i][1] << " " << vertices[i][2] + << "\n"; + } + + file << "$EndNodes\n"; + + // write elements + // assert(m_patches.size() * 3 == faces.size()); + const size_t element_size = faces.size(); + + file << "$Elements\n"; + file << "1 " << element_size << " 1 " << element_size << "\n"; + file << "2 1 21 " << element_size << "\n"; + for (size_t i = 0; i < element_size; ++i) { + file << i + 1 << " "; + for (int j = 0; j < 10; ++j) { + file << faces[i][j] << " "; + } + file << "\n"; + } + + file << "$EndElements\n"; + + // mark cones + // const auto &cone_indices = m_affine_manifold.generate_cones(); + + // file << "$NodeData\n"; + // file << "1\n"; // num string tags + // file << "\"Cone\"\n"; // string tag + // file << "1\n"; // num real tags + // file << "0.0\n"; // time step starts + // file << "3\n"; // three integer tags + // file << "0\n"; // time step + // file << "1\n"; // num field + // file << cone_indices.size() << "\n"; // num associated nodal values + // for (const auto &idx : cone_indices) { + // file << v_to_v_map[idx] + 1 << " 1.0\n"; + // } + // file << "$EndNodeData\n"; + + std::ofstream v_map_file(filename + "_input_v_to_output_v_map.txt"); + for (const auto &pair : v_to_v_map) { + v_map_file << pair.first << " " << pair.second << std::endl; + } +} + +void CloughTocherSurface:: + write_cubic_surface_to_msh_with_conn_from_lagrange_nodes( + std::string filename) { + std::ofstream file(filename + ".msh"); + + /* + $MeshFormat + 4.1 0 8 MSH4.1, ASCII + $EndMeshFormat + */ + + file << "$MeshFormat\n" + << "4.1 0 8\n" + << "$EndMeshFormat\n"; + + /* + subtri0: 0 1 18 3 4 14 15 13 12 9 + b0 b1 bc b01 b10 b1c bc1 bc0 b0c b01^c + subtri1: 1 2 18 5 6 16 17 15 14 10 + b1 b2 bc b12 b21 b2c bc2 bc1 b1c b12^c + subtri2: 2 0 18 7 8 12 13 17 16 11 + b2 b0 bc b20 b02 b0c bc0 bc2 b2c b20^c + */ + + // m_affine_manifold.generate_lagrange_nodes(); + + const auto &lagrange_nodes = m_affine_manifold.m_lagrange_nodes; + // evaluate vertices + std::vector vertices; + for (size_t i = 0; i < lagrange_nodes.size(); ++i) { + const auto patch_idx = lagrange_nodes[i].first; + const auto bc = lagrange_nodes[i].second; + auto z = m_patches[patch_idx].CT_eval(bc[0], bc[1], 1. - bc[0] - bc[1]); + vertices.push_back(z); + } + + // build faces + std::vector> faces; + for (const auto &f_chart : m_affine_manifold.m_face_charts) { + const auto &l_nodes = f_chart.lagrange_nodes; + faces.push_back( + {{l_nodes[0], l_nodes[1], l_nodes[18], l_nodes[3], l_nodes[4], + l_nodes[14], l_nodes[15], l_nodes[13], l_nodes[12], l_nodes[9]}}); + faces.push_back( + {{l_nodes[1], l_nodes[2], l_nodes[18], l_nodes[5], l_nodes[6], + l_nodes[16], l_nodes[17], l_nodes[15], l_nodes[14], l_nodes[10]}}); + faces.push_back( + {{l_nodes[2], l_nodes[0], l_nodes[18], l_nodes[7], l_nodes[8], + l_nodes[12], l_nodes[13], l_nodes[17], l_nodes[16], l_nodes[11]}}); + } + + file << "$Nodes\n"; + + const size_t node_size = vertices.size(); + file << "1 " << node_size << " 1 " << node_size << "\n"; + file << "2 1 0 " << node_size << "\n"; + + for (size_t i = 1; i <= node_size; ++i) { + file << i << "\n"; + } + + for (size_t i = 0; i < node_size; ++i) { + file << std::setprecision(16) << vertices[i][0] << " " << vertices[i][1] + << " " << vertices[i][2] << "\n"; + } + + file << "$EndNodes\n"; + + // write elements + // assert(m_patches.size() * 3 == faces.size()); + const size_t element_size = faces.size(); + + file << "$Elements\n"; + file << "1 " << element_size << " 1 " << element_size << "\n"; + file << "2 1 21 " << element_size << "\n"; + for (size_t i = 0; i < element_size; ++i) { + file << i + 1 << " "; + for (int j = 0; j < 10; ++j) { + file << faces[i][j] + 1 << " "; + } + file << "\n"; + } + + file << "$EndElements\n"; + + // mark cones + // const auto &cone_indices = m_affine_manifold.generate_cones(); + + // file << "$NodeData\n"; + // file << "1\n"; // num string tags + // file << "\"Cone\"\n"; // string tag + // file << "1\n"; // num real tags + // file << "0.0\n"; // time step starts + // file << "3\n"; // three integer tags + // file << "0\n"; // time step + // file << "1\n"; // num field + // file << cone_indices.size() << "\n"; // num associated nodal values + // for (const auto &idx : cone_indices) { + // file << v_to_v_map[idx] + 1 << " 1.0\n"; + // } + // file << "$EndNodeData\n"; + + std::ofstream v_map_file(filename + "_input_v_to_output_v_map.txt"); + for (const auto &pair : m_affine_manifold.v_to_lagrange_node_map) { + v_map_file << pair.first << " " << pair.second << std::endl; + } +} + +void CloughTocherSurface::P_G2F(Eigen::SparseMatrix &m) { + const auto N_L = m_affine_manifold.m_lagrange_nodes.size(); + const auto F_cnt = m_affine_manifold.m_face_charts.size(); + + m.resize(19 * F_cnt, N_L); + + std::vector> triplets; + + const auto &face_charts = m_affine_manifold.m_face_charts; + for (size_t i = 0; i < face_charts.size(); ++i) { + for (int j = 0; j < 19; ++j) { + triplets.emplace_back(i * 19 + j, face_charts[i].lagrange_nodes[j], 1); + } + } + + m.setFromTriplets(triplets.begin(), triplets.end()); +} + +void CloughTocherSurface::C_L_int(Eigen::Matrix &m) { + Eigen::Matrix L_L2d_ind = L_L2d_ind_m(); + Eigen::Matrix L_d2L_dep = L_d2L_dep_m(); + + Eigen::Matrix neg_L_dot_L = -L_d2L_dep * L_L2d_ind; + m.block<7, 12>(0, 0) = neg_L_dot_L; + m.block<7, 7>(0, 12) = Eigen::MatrixXd::Identity(7, 7); +} + +void CloughTocherSurface::C_F_int(Eigen::SparseMatrix &m) { + const auto N_L = m_affine_manifold.m_lagrange_nodes.size(); + const auto F_cnt = m_affine_manifold.m_face_charts.size(); + + Eigen::SparseMatrix p_g2f; + P_G2F(p_g2f); + Eigen::Matrix c_l_int; + C_L_int(c_l_int); + + Eigen::SparseMatrix C_diag; + C_diag.resize(7 * F_cnt, 19 * F_cnt); + + for (size_t i = 0; i < F_cnt; ++i) { + for (int j = 0; j < 7; ++j) { + for (int k = 0; k < 19; ++k) { + C_diag.insert(i * 7 + j, i * 19 + k) = c_l_int(j, k); + } + } + } + + m.resize(7 * F_cnt, N_L); + std::cout << 7 * F_cnt << " " << N_L << std::endl; + + m = C_diag * p_g2f; + std::cout << m.rows() << " " << m.cols() << std::endl; +} + +void CloughTocherSurface::P_G2E(Eigen::SparseMatrix &m) { + const auto N_L = m_affine_manifold.m_lagrange_nodes.size(); + const auto E_cnt = m_affine_manifold.m_edge_charts.size(); + + m.resize(24 * E_cnt, N_L); + + const auto &e_charts = m_affine_manifold.m_edge_charts; + const auto &f_charts = m_affine_manifold.m_face_charts; + + std::vector> triplets; + for (size_t i = 0; i < e_charts.size(); ++i) { + const auto &top_face_idx = e_charts[i].top_face_index; + const auto &bottom_face_idx = e_charts[i].bottom_face_index; + for (int j = 0; j < 12; ++j) { + triplets.emplace_back(i * 24 + j, + f_charts[top_face_idx].lagrange_nodes[j], 1); + triplets.emplace_back(i * 24 + 12 + j, + f_charts[bottom_face_idx].lagrange_nodes[j], 1); + } + } + + m.setFromTriplets(triplets.begin(), triplets.end()); +} + +std::array P_dE_helper(int a, int b) { + int hash = a * 10 + b; + + // p0 p1 p2 d01 d10 d12 d21 d20 d02 h01 h12 h20 + + switch (hash) { + case 1: + // 01 + return {{3, 8, 4, 5}}; + case 12: + // 12 + return {{5, 4, 6, 7}}; + case 20: + // 20 + return {{7, 6, 8, 3}}; + case 10: + // 10 + return {{4, 5, 3, 8}}; + case 21: + // 21 + return {{6, 7, 5, 4}}; + case 2: + // 02 + return {{8, 3, 7, 6}}; + default: + break; + } + + return {{-1, -1, -1, -1}}; +} + +void CloughTocherSurface::C_E_end(Eigen::SparseMatrix &m) { + const auto N_L = m_affine_manifold.m_lagrange_nodes.size(); + const auto E_cnt = m_affine_manifold.m_edge_charts.size(); + + m.resize(2 * E_cnt, N_L); + + const auto &e_charts = m_affine_manifold.m_edge_charts; + // const auto &f_charts = m_affine_manifold.m_face_charts; + const auto &v_charts = m_affine_manifold.m_vertex_charts; + const auto &Fv = m_affine_manifold.get_faces(); + // const auto &Fv_uv = m_affine_manifold.get_F_uv(); + const auto &uvs = m_affine_manifold.get_global_uv(); + + const Eigen::Matrix L_L2d_ind = L_L2d_ind_m(); + Eigen::Matrix diag_L_L2d_ind; + diag_L_L2d_ind.setZero(); + diag_L_L2d_ind.block<12, 12>(0, 0) = L_L2d_ind; + diag_L_L2d_ind.block<12, 12>(12, 12) = L_L2d_ind; + + Eigen::SparseMatrix C_E_L; + C_E_L.resize(2 * E_cnt, 24 * E_cnt); + + for (size_t eid = 0; eid < e_charts.size(); ++eid) { + const auto &e = e_charts[eid]; + // T vertices + const int64_t v0 = e.left_global_uv_idx; + const int64_t v1 = e.right_global_uv_idx; + const int64_t v2 = e.top_global_uv_idx; + // T' vertices + const int64_t v0_prime = e.reverse_right_global_uv_idx; + const int64_t v1_prime = e.reverse_left_global_uv_idx; + const int64_t v2_prime = e.bottom_global_uv_idx; + + // T and T' + const auto &T = Fv.row(e.top_face_index); + const auto &T_prime = Fv.row(e.bottom_face_index); + + // u_ij + const auto u_01 = uvs.row(v1) - uvs.row(v0); + const auto u_02 = uvs.row(v2) - uvs.row(v0); + const auto u_12 = uvs.row(v2) - uvs.row(v1); + const auto u_10 = uvs.row(v0) - uvs.row(v1); + + // u_ij_prime + const auto u_01_prime = uvs.row(v1_prime) - uvs.row(v0_prime); + const auto u_02_prime = uvs.row(v2_prime) - uvs.row(v0_prime); + const auto u_12_prime = uvs.row(v2_prime) - uvs.row(v1_prime); + const auto u_10_prime = uvs.row(v0_prime) - uvs.row(v1_prime); + + // D0 D1 + Eigen::Matrix D0; + D0 << u_01[0], u_01[1], u_02[0], u_02[1]; + Eigen::Matrix D1; + D1 << u_12[0], u_12[1], u_10[0], u_10[1]; + + // D0_prime D1_prime + Eigen::Matrix D0_prime; + D0_prime << u_01_prime[0], u_01_prime[1], u_02_prime[0], u_02_prime[1]; + Eigen::Matrix D1_prime; + D1_prime << u_12_prime[0], u_12_prime[1], u_10_prime[0], u_10_prime[1]; + + // u_01_prep u_01_prep_prime + Eigen::Vector2d u_01_prep(-u_01[1], u_01[0]); + Eigen::Vector2d u_01_prep_prime(-u_01_prime[1], u_01_prime[0]); + + // g0 g1 + Eigen::Matrix g0 = u_01_prep.transpose() * D0.inverse(); + Eigen::Matrix g1 = u_01_prep.transpose() * D1.inverse(); + + // g0_prime g1_prime + Eigen::Matrix g0_prime = + u_01_prep_prime.transpose() * D0_prime.inverse(); + Eigen::Matrix g1_prime = + u_01_prep_prime.transpose() * D1_prime.inverse(); + + // C_dE(e) + Eigen::Matrix C_dE; + C_dE << g0(0, 0), g0(0, 1), -g0_prime(0, 0), -g0_prime(0, 1), 0, 0, 0, + 0, // v0 + 0, 0, 0, 0, g1(0, 0), g1(0, 1), -g1_prime(0, 0), -g1_prime(0, 1); // v1 + + // check cones and modify C_dE(e) + if (v_charts[e.left_vertex_index].is_cone) { + // v0 is cone + C_dE.row(0) << 1, 0, 0, 0, 0, 0, 0, 0; + } + if (v_charts[e.right_vertex_index].is_cone) { + // v1 is cone + C_dE.row(1) << 0, 0, 0, 0, 0, 0, 1, 0; + } + + // P_dE reindexing global dof to local index + Eigen::Matrix P_dE; + P_dE.setZero(); + + // v0 v1 local id in T + int64_t lid_0, lid_1 = -1; + for (int i = 0; i < 3; ++i) { + if (T[i] == e.left_vertex_index) { + lid_0 = i; + } + if (T[i] == e.right_vertex_index) { + lid_1 = i; + } + } + assert(lid_0 > -1 && lid_1 > -1); + + // v0_prime v1_prime local id in T' + int64_t lid_0_prime, lid_1_prime = -1; + for (int i = 0; i < 3; ++i) { + if (T_prime[i] == e.right_vertex_index) { + lid_0_prime = i; + } + if (T_prime[i] == e.left_vertex_index) { + lid_1_prime = i; + } + } + assert(lid_0_prime > -1 && lid_1_prime > -1); + + // T and T' reindex + auto T_P_dE_indices = P_dE_helper(lid_0, lid_1); + auto T_prime_P_dE_indices = P_dE_helper(lid_0_prime, lid_1_prime); + + // assemble P_dE respect to [qL_int^T qL_int'^T] + P_dE(0, T_P_dE_indices[0]) = 1; // d01 + P_dE(1, T_P_dE_indices[1]) = 1; // d02 + P_dE(2, T_prime_P_dE_indices[2] + 12) = 1; // d10' + P_dE(3, T_prime_P_dE_indices[3] + 12) = 1; // d12' + P_dE(4, T_P_dE_indices[2]) = 1; // d10 + P_dE(5, T_P_dE_indices[3]) = 1; // d12 + P_dE(6, T_prime_P_dE_indices[0] + 12) = 1; // d01' + P_dE(7, T_prime_P_dE_indices[1] + 12) = 1; // d02' + + // C_E_L(e) + Eigen::Matrix C_E_L_e = C_dE * P_dE * diag_L_L2d_ind; + + // set C_E_L + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 24; ++j) { + C_E_L.insert(eid * 2 + i, eid * 24 + j) = C_E_L_e(i, j); + } + } + } + + Eigen::SparseMatrix p_g2e; + P_G2E(p_g2e); + + m = C_E_L * p_g2e; +} + +std::array P_dM_helper(int a, int b) { + int hash = a * 10 + b; + + // p0 p1 p2 d01 d10 d12 d21 d20 d02 h01 h12 h20 + + switch (hash) { + case 1: + // 01 + return {{0, 1, 3, 4, 9}}; + case 12: + // 12 + return {{1, 2, 5, 6, 10}}; + case 20: + // 20 + return {{2, 0, 7, 8, 11}}; + case 10: + // 10 + return {{1, 0, 4, 3, 9}}; + case 21: + // 21 + return {{2, 1, 6, 5, 10}}; + case 2: + // 02 + return {{0, 2, 8, 7, 11}}; + default: + break; + } + + assert(false); + + return {{-1, -1, -1, -1, -1}}; +} + +void CloughTocherSurface::C_E_mid(Eigen::SparseMatrix &m) { + const auto N_L = m_affine_manifold.m_lagrange_nodes.size(); + const auto E_cnt = m_affine_manifold.m_edge_charts.size(); + + m.resize(E_cnt, N_L); + + const auto &e_charts = m_affine_manifold.m_edge_charts; + // const auto &f_charts = m_affine_manifold.m_face_charts; + // const auto &v_charts = m_affine_manifold.m_vertex_charts; + const auto &Fv = m_affine_manifold.get_faces(); + // const auto &Fv_uv = m_affine_manifold.get_F_uv(); + const auto &uvs = m_affine_manifold.get_global_uv(); + + const Eigen::Matrix L_L2d_ind = L_L2d_ind_m(); + Eigen::Matrix diag_L_L2d_ind; + diag_L_L2d_ind.setZero(); + diag_L_L2d_ind.block<12, 12>(0, 0) = L_L2d_ind; + diag_L_L2d_ind.block<12, 12>(12, 12) = L_L2d_ind; + + Eigen::Matrix c_h; + c_h << 0, 0, 0, 0, 1; + + Eigen::Matrix c_e = c_e_m(); + + Eigen::SparseMatrix C_M_L; + C_M_L.resize(E_cnt, 24 * E_cnt); + + for (size_t eid = 0; eid < e_charts.size(); ++eid) { + const auto &e = e_charts[eid]; + // T vertices + const int64_t v0 = e.left_global_uv_idx; + const int64_t v1 = e.right_global_uv_idx; + const int64_t v2 = e.top_global_uv_idx; + // T' vertices + const int64_t v0_prime = e.reverse_right_global_uv_idx; + const int64_t v1_prime = e.reverse_left_global_uv_idx; + const int64_t v2_prime = e.bottom_global_uv_idx; + + // T and T' + const auto &T = Fv.row(e.top_face_index); + const auto &T_prime = Fv.row(e.bottom_face_index); + + // u_ij + const auto u_01 = uvs.row(v1) - uvs.row(v0); + const auto u_02 = uvs.row(v2) - uvs.row(v0); + const auto u_12 = uvs.row(v2) - uvs.row(v1); + // const auto u_10 = uvs.row(v0) - uvs.row(v1); + + // u_ij_prime + const auto u_01_prime = uvs.row(v1_prime) - uvs.row(v0_prime); + const auto u_02_prime = uvs.row(v2_prime) - uvs.row(v0_prime); + const auto u_12_prime = uvs.row(v2_prime) - uvs.row(v1_prime); + // const auto u_10_prime = uvs.row(v0_prime) - uvs.row(v1_prime); + + // m01 and m01_prime + const auto m_01 = (u_02 + u_12) / 2.0; + const auto m_01_prime = (u_02_prime + u_12_prime) / 2.0; + + // u_01_prep u_01_prep_prime + Eigen::Vector2d u_01_prep(-u_01[1], u_01[0]); + Eigen::Vector2d u_01_prep_prime(-u_01_prime[1], u_01_prime[0]); + + // g_M and g_M_prime + Eigen::Matrix g_M; + g_M = (c_h - (m_01.dot(u_01.normalized())) * c_e).transpose() / + (m_01.dot(u_01_prep.normalized())); + Eigen::Matrix g_M_prime; + g_M_prime = + (c_h - (m_01_prime.dot(u_01_prime.normalized())) * c_e).transpose() / + (m_01_prime.dot(u_01_prep_prime.normalized())); + + // C_dM + Eigen::Matrix C_dM; + C_dM.block<1, 5>(0, 0) = g_M; + C_dM.block<1, 5>(0, 5) = g_M_prime; + + // P_dM + Eigen::Matrix P_dM; + P_dM.setZero(); + + // v0 v1 local id in T + int64_t lid_0, lid_1 = -1; + for (int i = 0; i < 3; ++i) { + if (T[i] == e.left_vertex_index) { + lid_0 = i; + } + if (T[i] == e.right_vertex_index) { + lid_1 = i; + } + } + assert(lid_0 > -1 && lid_1 > -1); + + // v0_prime v1_prime local id in T' + int64_t lid_0_prime, lid_1_prime = -1; + for (int i = 0; i < 3; ++i) { + if (T_prime[i] == e.right_vertex_index) { + lid_0_prime = i; + } + if (T_prime[i] == e.left_vertex_index) { + lid_1_prime = i; + } + } + assert(lid_0_prime > -1 && lid_1_prime > -1); + + // T and T' reindex + auto T_P_dM_indices = P_dM_helper(lid_0, lid_1); + auto T_prime_P_dM_indices = P_dM_helper(lid_0_prime, lid_1_prime); + + // assemble P_dM respect to [qL_int qL_int'] + P_dM(0, T_P_dM_indices[0]) = 1; // p0 + P_dM(1, T_P_dM_indices[1]) = 1; // p1 + P_dM(2, T_P_dM_indices[2]) = 1; // d01 + P_dM(3, T_P_dM_indices[3]) = 1; // d10 + P_dM(4, T_P_dM_indices[4]) = 1; // h01 + P_dM(5, T_prime_P_dM_indices[0]) = 1; // p0' + P_dM(6, T_prime_P_dM_indices[1]) = 1; // p1' + P_dM(7, T_prime_P_dM_indices[2]) = 1; // d01' + P_dM(8, T_prime_P_dM_indices[3]) = 1; // d10' + P_dM(9, T_prime_P_dM_indices[4]) = 1; // h01' + + // C_M_L_e + Eigen::Matrix C_M_L_e = C_dM * P_dM * diag_L_L2d_ind; + + // set C_M_L + for (int i = 0; i < 24; ++i) { + C_M_L.insert(eid, eid * 24 + i) = C_M_L_e(0, i); + } + } + + Eigen::SparseMatrix p_g2e; + P_G2E(p_g2e); + + m = C_M_L * p_g2e; +} diff --git a/src/clough_tocher_patches/clough_tocher_surface.hpp b/src/clough_tocher_patches/clough_tocher_surface.hpp new file mode 100644 index 0000000..0829443 --- /dev/null +++ b/src/clough_tocher_patches/clough_tocher_surface.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "clough_tocher_patch.hpp" +#include "common.h" +#include "optimize_spline_surface.h" +#include "position_data.h" + +class CloughTocherSurface { +public: + typedef size_t PatchIndex; + + CloughTocherSurface(); + CloughTocherSurface(const Eigen::MatrixXd &V, + const AffineManifold &affine_manifold, + const OptimizationParameters &optimization_params, + Eigen::SparseMatrix &fit_matrix, + Eigen::SparseMatrix &energy_hessian, + Eigen::CholmodSupernodalLLT> + &energy_hessian_inverse); + + Eigen::Matrix evaluate_patch(const PatchIndex &patch_index, + const double &u, const double &v, + const double &w); + + void write_cubic_surface_to_msh_no_conn(std::string filename); + void write_coeffs_to_obj(std::string filename); + void sample_to_obj(std::string filename, int sample_size = 10); + + void write_cubic_surface_to_msh_with_conn(std::string filename); + void write_cubic_surface_to_msh_with_conn_from_lagrange_nodes( + std::string filename); + +public: + void generate_face_normals(const Eigen::MatrixXd &V, + const AffineManifold &affine_manifold, + Eigen::MatrixXd &N); + +public: + std::vector m_patches; + + AffineManifold m_affine_manifold; + std::vector> m_corner_data; + std::vector> m_midpoint_data; + +public: + // constraint matrices + + // interior constraints + void P_G2F(Eigen::SparseMatrix &m); + void C_L_int(Eigen::Matrix &m); + void C_F_int(Eigen::SparseMatrix &m); + + // edge endpoint constraints + void P_G2E(Eigen::SparseMatrix &m); + void C_E_end(Eigen::SparseMatrix &m); + + // edge midpoint constraints + void C_E_mid(Eigen::SparseMatrix &m); +}; \ No newline at end of file diff --git a/src/clough_tocher_patches/generate_ct_constraint_matrices.py b/src/clough_tocher_patches/generate_ct_constraint_matrices.py new file mode 100644 index 0000000..b04f716 --- /dev/null +++ b/src/clough_tocher_patches/generate_ct_constraint_matrices.py @@ -0,0 +1,41 @@ +from Clough_Toucher_derivation import * +import pretty_print as pp + +file = open("clough_tocher_autogen_constraint_matrices.hpp", "w") + +file.write("#pragma once\n\n") + +polys_C1_f = derive_C1_polynomials(vtx_unknowns,side_deriv_unknowns,mid_deriv_unknowns) +runTests(polys_C1_f) +CT_matrices = compute_CT_matrices(polys_C1_f) + +L_d2L = generate_L2L(polys_C1_f, node_bary, node_subtri, all_unknowns) +L_d2L_ind = L_d2L[0:12,0:12] +L_d2L_dep = L_d2L[12:19,0:12] +L_L2d_ind = L_d2L_ind.inverse_GE() +L_ind2dep = L_d2L_dep*L_L2d_ind +c_e = generate_ce(polys_C1_f) + +test_Lagrange_consistency(polys_C1_f, L_L2d_ind, L_ind2dep) + +# L_L2d_ind +file.write("inline void L_L2d_ind_matrix(double L[12][12]){\n") +s = pp.C99_print_tensor(L_L2d_ind, "L") +ss = s.replace(", ", "][") +file.write(ss) +file.write("}\n\n") + +# L_d2L_dep +file.write("inline void L_d2L_dep_matrix(double L[7][12]){\n") +s = pp.C99_print_tensor(L_d2L_dep, "L") +ss = s.replace(", ", "][") +file.write(ss) +file.write("}\n\n") + +# c_e +file.write("inline void c_e_matrix(double c[5]){\n") +for i in range(len(c_e)): + file.write("c[{}] = {};\n".format(i, c_e[i])) +file.write("}\n\n") + +file.close() \ No newline at end of file diff --git a/src/clough_tocher_patches/pretty_print.py b/src/clough_tocher_patches/pretty_print.py new file mode 100644 index 0000000..f7b3398 --- /dev/null +++ b/src/clough_tocher_patches/pretty_print.py @@ -0,0 +1,50 @@ +from sympy import * +from sympy.matrices import * +from sympy.printing import ccode +import numpy as np + + +# pretty print +def C99_print(expr): + CSE_results = cse(expr, numbered_symbols("helper_"), optimizations='basic') + lines = [] + for helper in CSE_results[0]: + if isinstance(helper[1], MatrixSymbol): + lines.append( + 'const auto ' + str(helper[0]) + '[' + str(helper[1].rows * helper[1].cols) + '];') + lines.append(ccode(helper[1], helper[0])) + else: + lines.append('const auto ' + ccode(helper[1], helper[0])) + + for i, result in enumerate(CSE_results[1]): + lines.append(ccode(result, "result_%d" % i)) + return '\n'.join(lines) + +# Pretty print a matrix or tensor expression. +def C99_print_tensor(expr, result_name="result"): + # If a tensor expression, the result is reshaped into a 2d matrix for printing. + lines = [] + subs, result = cse(expr, numbered_symbols( + "helper_"), optimizations='basic') + if len(result) == 1: + result = result[0] + + for k, v in subs: + lines.append(f"const double {ccode(v, k)}") + + result_shape = np.array(result).shape + if len(result_shape) == 2: + for i in range(result_shape[0]): + for j in range(result_shape[1]): + s = ccode(result[i, j], f"{result_name}[{i}, {j}]") + lines.append(f"{s}") + elif len(result_shape) == 4: + for i in range(result_shape[0]): + for j in range(result_shape[1]): + for k in range(result_shape[2]): + for l in range(result_shape[3]): + s = ccode(result[i, j, k, l], + f"{result_name}[{i * result_shape[1] + j}, {k * result_shape[3] + l}]") + lines.append(f"{s}") + + return "\n".join(lines) \ No newline at end of file diff --git a/src/core/affine_manifold.cpp b/src/core/affine_manifold.cpp index e1bf968..479250c 100644 --- a/src/core/affine_manifold.cpp +++ b/src/core/affine_manifold.cpp @@ -9,6 +9,8 @@ #include "vertex_circulator.h" +#include + // ************* // Cone Manifold // ************* @@ -17,18 +19,12 @@ // Public Methods // ************** -AffineManifold::AffineManifold() -{ - clear(); -} +AffineManifold::AffineManifold() { clear(); } -AffineManifold::AffineManifold(const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - const Eigen::MatrixXi& F_uv) - : m_F(F) - , m_global_uv(global_uv) - , m_F_uv(F_uv) -{ +AffineManifold::AffineManifold(const Eigen::MatrixXi &F, + const MatrixXr &global_uv, + const Eigen::MatrixXi &F_uv) + : m_F(F), m_global_uv(global_uv), m_F_uv(F_uv) { // Check the input #if CHECK_VALIDITY if (!is_manifold(F)) { @@ -51,7 +47,7 @@ AffineManifold::AffineManifold(const Eigen::MatrixXi& F, // Build halfedge m_halfedge = Halfedge(F, m_corner_to_he, m_he_to_corner); std::vector he_to_edge = - m_halfedge.get_halfedge_to_edge_map(); + m_halfedge.get_halfedge_to_edge_map(); build_corner_to_edge_map(m_corner_to_he, he_to_edge, m_corner_to_edge); // Build edge lengths and charts from the global uv @@ -73,58 +69,40 @@ AffineManifold::AffineManifold(const Eigen::MatrixXi& F, } } -AffineManifold::Index -AffineManifold::num_faces() const -{ - return m_F.rows(); -} +AffineManifold::Index AffineManifold::num_faces() const { return m_F.rows(); } -AffineManifold::Index -AffineManifold::num_vertices() const -{ +AffineManifold::Index AffineManifold::num_vertices() const { return m_vertex_charts.size(); } -Eigen::MatrixXi const& -AffineManifold::get_faces() const -{ - return m_F; -} +Eigen::MatrixXi const &AffineManifold::get_faces() const { return m_F; } -Eigen::MatrixXi const& -AffineManifold::get_F_uv() const -{ - return m_F_uv; -} +Eigen::MatrixXi const &AffineManifold::get_F_uv() const { return m_F_uv; } -VertexManifoldChart const& -AffineManifold::get_vertex_chart(AffineManifold::Index vertex_index) const -{ +VertexManifoldChart const & +AffineManifold::get_vertex_chart(AffineManifold::Index vertex_index) const { return m_vertex_charts[vertex_index]; } -EdgeManifoldChart const& -AffineManifold::get_edge_chart(Index face_index, Index face_vertex_index) const -{ +EdgeManifoldChart const & +AffineManifold::get_edge_chart(Index face_index, + Index face_vertex_index) const { Index edge_index = m_corner_to_edge[face_index][face_vertex_index]; return m_edge_charts[edge_index]; } -FaceManifoldChart const& -AffineManifold::get_face_chart(Index face_index) const -{ +FaceManifoldChart const & +AffineManifold::get_face_chart(Index face_index) const { return m_face_charts[face_index]; } -void -AffineManifold::get_face_corner_charts( - AffineManifold::Index face_index, - std::array& corner_uv_positions) const -{ +void AffineManifold::get_face_corner_charts( + AffineManifold::Index face_index, + std::array &corner_uv_positions) const { for (Eigen::Index i = 0; i < 3; ++i) { // Get the chart for vertex i in the given face int vertex_index = m_F(face_index, i); - VertexManifoldChart const& chart = m_vertex_charts[vertex_index]; + VertexManifoldChart const &chart = m_vertex_charts[vertex_index]; // Iterate over the one ring of face vertex i for (size_t j = 0; j < chart.face_one_ring.size(); ++j) { @@ -137,23 +115,21 @@ AffineManifold::get_face_corner_charts( int first_edge = j; int second_edge = j + 1; corner_uv_positions[i].row(0) = - chart.one_ring_uv_positions.row(first_edge); + chart.one_ring_uv_positions.row(first_edge); corner_uv_positions[i].row(1) = - chart.one_ring_uv_positions.row(second_edge); + chart.one_ring_uv_positions.row(second_edge); break; } } } -void -AffineManifold::get_face_edge_charts( - AffineManifold::Index face_index, - std::array& face_edge_uv_positions) const -{ +void AffineManifold::get_face_edge_charts( + AffineManifold::Index face_index, + std::array &face_edge_uv_positions) const { // Iterate over edges for (size_t i = 0; i < 3; ++i) { // Get the chart for edge jk opposite vertex i in the given face - EdgeManifoldChart const& chart = get_edge_chart(face_index, i); + EdgeManifoldChart const &chart = get_edge_chart(face_index, i); // Break into cases depending on if the face is the top or bottom face in // the chart @@ -171,18 +147,15 @@ AffineManifold::get_face_edge_charts( } } -void -AffineManifold::get_face_global_uv( - AffineManifold::Index face_index, - std::array& face_uv_positions) const -{ +void AffineManifold::get_face_global_uv( + AffineManifold::Index face_index, + std::array &face_uv_positions) const { face_uv_positions = get_face_chart(face_index).face_uv_positions; } double -AffineManifold::compute_curvature(AffineManifold::Index vertex_index) const -{ - VertexManifoldChart const& chart = m_vertex_charts[vertex_index]; +AffineManifold::compute_curvature(AffineManifold::Index vertex_index) const { + VertexManifoldChart const &chart = m_vertex_charts[vertex_index]; // Get zero uv coordinate (location of the central vertex) PlanarPoint zero; @@ -192,9 +165,8 @@ AffineManifold::compute_curvature(AffineManifold::Index vertex_index) const double cone_angle = 0.0; for (size_t j = 0; j < chart.face_one_ring.size(); ++j) { cone_angle += - angle_from_positions<2>(zero, - chart.one_ring_uv_positions.row(j), - chart.one_ring_uv_positions.row(j + 1)); + angle_from_positions<2>(zero, chart.one_ring_uv_positions.row(j), + chart.one_ring_uv_positions.row(j + 1)); } // Compute geodesic curvature for boundary vertices and Gaussian curvature for @@ -206,15 +178,11 @@ AffineManifold::compute_curvature(AffineManifold::Index vertex_index) const } } -bool -AffineManifold::is_boundary(AffineManifold::Index vertex_index) const -{ +bool AffineManifold::is_boundary(AffineManifold::Index vertex_index) const { return get_vertex_chart(vertex_index).is_boundary; } -bool -AffineManifold::is_flat(AffineManifold::Index vertex_index) const -{ +bool AffineManifold::is_flat(AffineManifold::Index vertex_index) const { // All vertices with zero curvature are flat if (float_equal_zero(compute_curvature(vertex_index), 1e-5)) return true; @@ -226,10 +194,8 @@ AffineManifold::is_flat(AffineManifold::Index vertex_index) const return false; } -void -AffineManifold::compute_flat_vertices( - std::vector& flat_vertices) -{ +void AffineManifold::compute_flat_vertices( + std::vector &flat_vertices) { flat_vertices.clear(); flat_vertices.reserve(num_vertices()); @@ -240,26 +206,22 @@ AffineManifold::compute_flat_vertices( } } -void -AffineManifold::compute_cones(std::vector& cones) const -{ +void AffineManifold::compute_cones( + std::vector &cones) const { cones.clear(); cones.reserve(num_vertices()); for (Index vertex_index = 0; vertex_index < num_vertices(); ++vertex_index) { if (!is_flat(vertex_index)) { - spdlog::debug("Getting cone {} of curvature {}", - vertex_index, + spdlog::debug("Getting cone {} of curvature {}", vertex_index, compute_curvature(vertex_index)); cones.push_back(vertex_index); } } } -void -AffineManifold::compute_cones_corners( - std::vector>& is_cone_corner) const -{ +void AffineManifold::compute_cones_corners( + std::vector> &is_cone_corner) const { is_cone_corner.resize(num_faces()); for (Index fi = 0; fi < num_faces(); ++fi) { for (size_t k = 0; k < 3; ++k) { @@ -268,10 +230,8 @@ AffineManifold::compute_cones_corners( } } -void -AffineManifold::compute_cone_points(const MatrixXr& V, - MatrixXr& cone_points) const -{ +void AffineManifold::compute_cone_points(const MatrixXr &V, + MatrixXr &cone_points) const { // Compute the cone indices std::vector cones; compute_cones(cones); @@ -285,18 +245,14 @@ AffineManifold::compute_cone_points(const MatrixXr& V, } } -std::vector -AffineManifold::generate_cones() const -{ +std::vector AffineManifold::generate_cones() const { std::vector cones; compute_cones(cones); return cones; } -void -AffineManifold::compute_boundary_vertices( - std::vector& boundary_vertices) const -{ +void AffineManifold::compute_boundary_vertices( + std::vector &boundary_vertices) const { boundary_vertices.clear(); boundary_vertices.reserve(num_vertices()); @@ -308,29 +264,23 @@ AffineManifold::compute_boundary_vertices( } std::vector -AffineManifold::generate_boundary_vertices() const -{ +AffineManifold::generate_boundary_vertices() const { std::vector boundary_vertices; compute_boundary_vertices(boundary_vertices); return boundary_vertices; } -void -AffineManifold::mark_cone_adjacent_vertex(AffineManifold::Index vertex_index) -{ +void AffineManifold::mark_cone_adjacent_vertex( + AffineManifold::Index vertex_index) { m_vertex_charts[vertex_index].is_cone_adjacent = true; } -void -AffineManifold::mark_cone_adjacent_face(AffineManifold::Index face_index) -{ +void AffineManifold::mark_cone_adjacent_face(AffineManifold::Index face_index) { m_face_charts[face_index].is_cone_adjacent = true; } -void -AffineManifold::cut_cone_edges() -{ - Eigen::MatrixXi const& F = get_faces(); +void AffineManifold::cut_cone_edges() { + Eigen::MatrixXi const &F = get_faces(); // Get cone vertices std::vector cones; @@ -340,14 +290,14 @@ AffineManifold::cut_cone_edges() for (size_t i = 0; i < cones.size(); ++i) { // Get cone vertex chart Index vi = cones[i]; - VertexManifoldChart const& vertex_chart = get_vertex_chart(vi); + VertexManifoldChart const &vertex_chart = get_vertex_chart(vi); // Get edge chart adjacent to the cone edge Index face_index = vertex_chart.face_one_ring[0]; Eigen::Index face_vertex_index = - find_face_vertex_index(F.row(face_index), vi); - EdgeManifoldChart const& edge_chart = - get_edge_chart(face_index, face_vertex_index); + find_face_vertex_index(F.row(face_index), vi); + EdgeManifoldChart const &edge_chart = + get_edge_chart(face_index, face_vertex_index); // Mark edge and endpoints as boundaries Index edge_index = m_corner_to_edge[face_index][face_vertex_index]; @@ -359,23 +309,18 @@ AffineManifold::cut_cone_edges() } } -MatrixXr const& -AffineManifold::get_global_uv() const -{ - return m_global_uv; -} +MatrixXr const &AffineManifold::get_global_uv() const { return m_global_uv; } -void -AffineManifold::add_to_viewer(const MatrixXr& V, Eigen::Matrix color) const -{ +void AffineManifold::add_to_viewer(const MatrixXr &V, + Eigen::Matrix color) const { polyscope::init(); // Add manifold Eigen::MatrixXi const F = get_faces(); polyscope::registerSurfaceMesh("cone_manifold", V, F); polyscope::getSurfaceMesh("cone_manifold") - ->setEdgeWidth(1) - ->setSurfaceColor(glm::vec3(color[0], color[1], color[2])); + ->setEdgeWidth(1) + ->setSurfaceColor(glm::vec3(color[0], color[1], color[2])); // Add cone points MatrixXr cone_points; @@ -384,30 +329,23 @@ AffineManifold::add_to_viewer(const MatrixXr& V, Eigen::Matrix col polyscope::getPointCloud("cones")->setPointColor(glm::vec3(0.5, 0.0, 0.0)); } -void -AffineManifold::view(const MatrixXr& V) const -{ +void AffineManifold::view(const MatrixXr &V) const { add_to_viewer(V); polyscope::show(); } -void -AffineManifold::screenshot(const std::string& filename, - const MatrixXr& V, - SpatialVector camera_position, - SpatialVector camera_target, - bool use_orthographic) const -{ +void AffineManifold::screenshot(const std::string &filename, const MatrixXr &V, + SpatialVector camera_position, + SpatialVector camera_target, + bool use_orthographic) const { // Add the contour network to the surface add_to_viewer(V); // Build the cameras for the viewer - glm::vec3 glm_camera_position = { camera_position[0], - camera_position[1], - camera_position[2] }; - glm::vec3 glm_camera_target = { camera_target[0], - camera_target[1], - camera_target[2] }; + glm::vec3 glm_camera_position = {camera_position[0], camera_position[1], + camera_position[2]}; + glm::vec3 glm_camera_target = {camera_target[0], camera_target[1], + camera_target[2]}; // Set up the cameras polyscope::view::lookAt(glm_camera_position, glm_camera_target); @@ -421,9 +359,7 @@ AffineManifold::screenshot(const std::string& filename, polyscope::removeAllStructures(); } -void -AffineManifold::clear() -{ +void AffineManifold::clear() { m_F.setZero(0, 0); m_corner_to_he.clear(); m_corner_to_edge.clear(); @@ -444,12 +380,9 @@ AffineManifold::clear() // ***************** // Build isometric charts for a surface with a flat metric -void -AffineManifold::build_vertex_charts_from_lengths( - const Eigen::MatrixXi& F, - const std::vector>& l, - std::vector& vertex_charts) const -{ +void AffineManifold::build_vertex_charts_from_lengths( + const Eigen::MatrixXi &F, const std::vector> &l, + std::vector &vertex_charts) const { Index num_vertices = F.maxCoeff() + 1; // Build vertex circulator @@ -467,9 +400,7 @@ AffineManifold::build_vertex_charts_from_lengths( vertex_charts[vertex_index].face_one_ring); // Layout the vertices according to the given flat metric - layout_one_ring(F, - l, - vertex_index, + layout_one_ring(F, l, vertex_index, vertex_charts[vertex_index].vertex_one_ring, vertex_charts[vertex_index].face_one_ring, vertex_charts[vertex_index].one_ring_uv_positions); @@ -485,13 +416,10 @@ AffineManifold::build_vertex_charts_from_lengths( } } -void -AffineManifold::build_edge_charts_from_lengths( - const Eigen::MatrixXi& F, - const Halfedge& halfedge, - const std::vector>& l, - std::vector& edge_charts) const -{ +void AffineManifold::build_edge_charts_from_lengths( + const Eigen::MatrixXi &F, const Halfedge &halfedge, + const std::vector> &l, + std::vector &edge_charts) const { // Build edge charts Index num_edges = halfedge.num_edges(); edge_charts.resize(num_edges); @@ -510,7 +438,7 @@ AffineManifold::build_edge_charts_from_lengths( chart.right_vertex_index = halfedge.halfedge_to_head_vertex(he_top); chart.top_vertex_index = halfedge.halfedge_to_head_vertex(he_top_next); chart.bottom_vertex_index = - halfedge.halfedge_to_head_vertex(he_bottom_next); + halfedge.halfedge_to_head_vertex(he_bottom_next); chart.is_boundary = halfedge.is_boundary_edge(edge_index); // Get lengths of the edges of the top triangle @@ -526,8 +454,8 @@ AffineManifold::build_edge_charts_from_lengths( chart.left_vertex_uv_position = PlanarPoint(0, 0); chart.right_vertex_uv_position = PlanarPoint(1.0, 0); assert(lij > 0); - chart.top_vertex_uv_position = - layout_next_vertex(chart.right_vertex_uv_position, ljk / lij, lki / lij); + chart.top_vertex_uv_position = layout_next_vertex( + chart.right_vertex_uv_position, ljk / lij, lki / lij); // Get center of the target edge for a later shift PlanarPoint center = 0.5 * chart.right_vertex_uv_position; @@ -544,7 +472,7 @@ AffineManifold::build_edge_charts_from_lengths( // Construct the last vertex counterclockwise and then reflect it PlanarPoint uvl_reflected = layout_next_vertex( - chart.right_vertex_uv_position, llj / lij, lil / lij); + chart.right_vertex_uv_position, llj / lij, lil / lij); chart.bottom_vertex_uv_position = reflect_across_x_axis(uvl_reflected); // Shift all vertices so the midpoint is at the origin @@ -567,13 +495,10 @@ AffineManifold::build_edge_charts_from_lengths( } } -void -AffineManifold::build_face_charts( - const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - const Eigen::MatrixXi& F_uv, - std::vector& face_charts) const -{ +void AffineManifold::build_face_charts( + const Eigen::MatrixXi &F, const MatrixXr &global_uv, + const Eigen::MatrixXi &F_uv, + std::vector &face_charts) const { Index num_faces = F.rows(); face_charts.resize(num_faces); for (Index face_index = 0; face_index < num_faces; ++face_index) { @@ -582,18 +507,16 @@ AffineManifold::build_face_charts( ++face_vertex_index) { Index uv_vertex_index = F_uv(face_index, face_vertex_index); face_charts[face_index].face_uv_positions[face_vertex_index] = - global_uv.row(uv_vertex_index); + global_uv.row(uv_vertex_index); } } } // Compose corner to halfedge and halfedge to edge maps -void -AffineManifold::build_corner_to_edge_map( - const std::vector>& corner_to_he, - const std::vector& he_to_edge, - std::vector>& corner_to_edge) const -{ +void AffineManifold::build_corner_to_edge_map( + const std::vector> &corner_to_he, + const std::vector &he_to_edge, + std::vector> &corner_to_edge) const { Index num_faces = corner_to_he.size(); corner_to_edge.resize(num_faces); @@ -609,11 +532,9 @@ AffineManifold::build_corner_to_edge_map( // Layout the next vertex in a triangle with given lengths and the current point // position -PlanarPoint -AffineManifold::layout_next_vertex(const PlanarPoint& current_point, - double next_edge_length, - double prev_edge_length) const -{ +PlanarPoint AffineManifold::layout_next_vertex(const PlanarPoint ¤t_point, + double next_edge_length, + double prev_edge_length) const { // Get the current point and its rotation PlanarPoint p0 = current_point; PlanarPoint p0_perp(-p0[1], p0[0]); @@ -636,14 +557,12 @@ AffineManifold::layout_next_vertex(const PlanarPoint& current_point, } // Layout the one ring around the given vertex from the lengths -void -AffineManifold::layout_one_ring(const Eigen::MatrixXi& F, - const std::vector>& l, - int vertex_index, - const std::vector& vertex_one_ring, - const std::vector& face_one_ring, - MatrixXr& one_ring_uv_positions) const -{ +void AffineManifold::layout_one_ring(const Eigen::MatrixXi &F, + const std::vector> &l, + int vertex_index, + const std::vector &vertex_one_ring, + const std::vector &face_one_ring, + MatrixXr &one_ring_uv_positions) const { one_ring_uv_positions.resize(vertex_one_ring.size(), 2); if (spdlog::get_level() != spdlog::level::info) { SPDLOG_TRACE("Building layout for one ring: {}", @@ -674,16 +593,15 @@ AffineManifold::layout_one_ring(const Eigen::MatrixXi& F, // Layout the next vertex one_ring_uv_positions.row(i + 1) = layout_next_vertex( - one_ring_uv_positions.row(i), next_edge_length, prev_edge_length); + one_ring_uv_positions.row(i), next_edge_length, prev_edge_length); if (spdlog::get_level() != spdlog::level::info) { SPDLOG_TRACE("Next vertex is {}", one_ring_uv_positions.row(i + 1)); } - assert(float_equal( - next_edge_length, - (one_ring_uv_positions.row(i + 1) - one_ring_uv_positions.row(i)) - .norm())); + assert(float_equal(next_edge_length, (one_ring_uv_positions.row(i + 1) - + one_ring_uv_positions.row(i)) + .norm())); assert( - float_equal(prev_edge_length, one_ring_uv_positions.row(i + 1).norm())); + float_equal(prev_edge_length, one_ring_uv_positions.row(i + 1).norm())); } spdlog::trace("Final layout:\n{}", one_ring_uv_positions); @@ -691,12 +609,9 @@ AffineManifold::layout_one_ring(const Eigen::MatrixXi& F, } // Build a corner-indexed metric for a surface with a global parametrization -void -AffineManifold::build_lengths_from_global_uv( - const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - std::vector>& l) const -{ +void AffineManifold::build_lengths_from_global_uv( + const Eigen::MatrixXi &F, const MatrixXr &global_uv, + std::vector> &l) const { Index num_faces = F.rows(); Index face_size = F.cols(); assert(face_size == 3); @@ -718,42 +633,40 @@ AffineManifold::build_lengths_from_global_uv( } // Align local uv charts with the global parametrization -void -AffineManifold::align_local_charts(const MatrixXr& uv, - const Eigen::MatrixXi& F_uv) -{ +void AffineManifold::align_local_charts(const MatrixXr &uv, + const Eigen::MatrixXi &F_uv) { // Rotate and scale local layouts to align with the global layout for (Index vertex_index = 0; vertex_index < num_vertices(); ++vertex_index) { // Get the (transposed) similarity map that maps [1, 0]^T to the first local // uv edge MatrixXr local_layout = - get_vertex_chart(vertex_index).one_ring_uv_positions; + get_vertex_chart(vertex_index).one_ring_uv_positions; PlanarPoint local_edge = local_layout.row(0); MatrixXr local_similarity_map(2, 2); local_similarity_map << local_edge[0], local_edge[1], -local_edge[1], - local_edge[0]; + local_edge[0]; // Get the global uv values corresponding the edge of the face Index edge_face_index = get_vertex_chart(vertex_index).face_one_ring[0]; Index edge_face_vertex_index = - find_face_vertex_index(m_F.row(edge_face_index), vertex_index); + find_face_vertex_index(m_F.row(edge_face_index), vertex_index); Index uv_vertex_index = F_uv(edge_face_index, edge_face_vertex_index); Index uv_edge_vertex_index = - F_uv(edge_face_index, (edge_face_vertex_index + 1) % 3); + F_uv(edge_face_index, (edge_face_vertex_index + 1) % 3); // Get (transposed) similarity map that maps [1, 0]^T to the first global uv // edge PlanarPoint global_edge = - uv.row(uv_edge_vertex_index) - uv.row(uv_vertex_index); + uv.row(uv_edge_vertex_index) - uv.row(uv_vertex_index); MatrixXr global_similarity_map(2, 2); global_similarity_map << global_edge[0], global_edge[1], -global_edge[1], - global_edge[0]; + global_edge[0]; // Apply composite similarity maps to the local uv positions MatrixXr similarity_map = - global_similarity_map * local_similarity_map.inverse(); + global_similarity_map * local_similarity_map.inverse(); m_vertex_charts[vertex_index].one_ring_uv_positions = - m_vertex_charts[vertex_index].one_ring_uv_positions * similarity_map; + m_vertex_charts[vertex_index].one_ring_uv_positions * similarity_map; } // Check validity after direct member variable manipulation @@ -763,17 +676,15 @@ AffineManifold::align_local_charts(const MatrixXr& uv, } // Mark cones and surrounding elements in the vertex and face charts -void -AffineManifold::mark_cones() -{ - Eigen::MatrixXi const& F = get_faces(); +void AffineManifold::mark_cones() { + Eigen::MatrixXi const &F = get_faces(); std::vector cones; compute_cones(cones); for (size_t i = 0; i < cones.size(); ++i) { Index ci = cones[i]; m_vertex_charts[ci].is_cone = true; - VertexManifoldChart const& chart = get_vertex_chart(ci); + VertexManifoldChart const &chart = get_vertex_chart(ci); spdlog::debug("Marking cone at {}", ci); // Mark vertices adjacent to cones @@ -802,11 +713,9 @@ AffineManifold::mark_cones() } } -double -AffineManifold::compute_corner_uv_length( - AffineManifold::Index face_index, - AffineManifold::Index face_vertex_index) const -{ +double AffineManifold::compute_corner_uv_length( + AffineManifold::Index face_index, + AffineManifold::Index face_vertex_index) const { Index vn = m_F_uv(face_index, (face_vertex_index + 1) % 3); Index vp = m_F_uv(face_index, (face_vertex_index + 2) % 3); PlanarPoint next_uv = m_global_uv.row(vn); @@ -816,9 +725,7 @@ AffineManifold::compute_corner_uv_length( } // Check that the manifold is valid and self-consistent -bool -AffineManifold::is_valid_affine_manifold() const -{ +bool AffineManifold::is_valid_affine_manifold() const { // Threshold for length comparisons double length_threshold = 1e-6; @@ -827,7 +734,7 @@ AffineManifold::is_valid_affine_manifold() const zero.setZero(2); // Face containment helper lambda - auto contains_vertex = [](const Eigen::VectorXi& face, Index index) { + auto contains_vertex = [](const Eigen::VectorXi &face, Index index) { for (Eigen::Index i = 0; i < face.size(); ++i) { if (face[i] == index) return true; @@ -837,11 +744,11 @@ AffineManifold::is_valid_affine_manifold() const }; // Edge length check helper lambda - auto edge_has_length = - [&](const PlanarPoint& v0, const PlanarPoint& v1, double length) { - PlanarPoint edge = v1 - v0; - return float_equal(edge.norm(), length, length_threshold); - }; + auto edge_has_length = [&](const PlanarPoint &v0, const PlanarPoint &v1, + double length) { + PlanarPoint edge = v1 - v0; + return float_equal(edge.norm(), length, length_threshold); + }; // Check that the sizes of the member variables are consistent if (static_cast(m_F.rows()) != static_cast(m_l.size())) @@ -856,11 +763,8 @@ AffineManifold::is_valid_affine_manifold() const double edge_uv_length = compute_corner_uv_length(fi, j); if (!float_equal(edge_length, edge_uv_length, length_threshold)) { spdlog::error( - "Inconsistent edge length {} and uv length {} for corner {}, {}", - edge_length, - edge_uv_length, - fi, - j); + "Inconsistent edge length {} and uv length {} for corner {}, {}", + edge_length, edge_uv_length, fi, j); return false; } @@ -874,14 +778,11 @@ AffineManifold::is_valid_affine_manifold() const // Check uvs are the same for the opposite corners double opposite_edge_uv_length = compute_corner_uv_length(fi_opp, j_opp); - if (!float_equal( - edge_length, opposite_edge_uv_length, length_threshold)) { + if (!float_equal(edge_length, opposite_edge_uv_length, + length_threshold)) { spdlog::error( - "Inconsistent opposite uv length for corners {}, {} and {}, {}", - fi, - j, - fi_opp, - j_opp); + "Inconsistent opposite uv length for corners {}, {} and {}, {}", fi, + j, fi_opp, j_opp); return false; } } @@ -889,7 +790,7 @@ AffineManifold::is_valid_affine_manifold() const // Check that each vertex chart is valid for (Index vertex_index = 0; vertex_index < num_vertices(); ++vertex_index) { - auto const& chart = m_vertex_charts[vertex_index]; + auto const &chart = m_vertex_charts[vertex_index]; // Check basic chart indexing and size validity if (chart.vertex_index != vertex_index) @@ -906,7 +807,7 @@ AffineManifold::is_valid_affine_manifold() const for (size_t i = 0; i < chart.face_one_ring.size(); ++i) { Index face_index = chart.face_one_ring[i]; Index face_vertex_index = - find_face_vertex_index(m_F.row(face_index), vertex_index); + find_face_vertex_index(m_F.row(face_index), vertex_index); Index vi = chart.vertex_one_ring[i]; Index vj = chart.vertex_one_ring[i + 1]; @@ -925,35 +826,30 @@ AffineManifold::is_valid_affine_manifold() const spdlog::trace("chart uv positions: {}", chart.one_ring_uv_positions); SPDLOG_TRACE("Face lengths: {}", formatted_vector(m_l[face_index])); } - if (!edge_has_length(zero, - chart.one_ring_uv_positions.row(i), + if (!edge_has_length(zero, chart.one_ring_uv_positions.row(i), m_l[face_index][(face_vertex_index + 2) % 3])) { spdlog::error( - "uv position {} in chart {} does not have expected norm {}", - chart.one_ring_uv_positions.row(i), - vertex_index, - m_l[face_index][(face_vertex_index + 2) % 3]); + "uv position {} in chart {} does not have expected norm {}", + chart.one_ring_uv_positions.row(i), vertex_index, + m_l[face_index][(face_vertex_index + 2) % 3]); return false; } if (!edge_has_length(chart.one_ring_uv_positions.row(i + 1), chart.one_ring_uv_positions.row(i), m_l[face_index][(face_vertex_index + 0) % 3])) { spdlog::error( - "uv positions {} and {} in chart {} do not have expected length {}", - chart.one_ring_uv_positions.row(i + 1), - chart.one_ring_uv_positions.row(i), - vertex_index, - m_l[face_index][(face_vertex_index + 0) % 3]); + "uv positions {} and {} in chart {} do not have expected length {}", + chart.one_ring_uv_positions.row(i + 1), + chart.one_ring_uv_positions.row(i), vertex_index, + m_l[face_index][(face_vertex_index + 0) % 3]); return false; } - if (!edge_has_length(zero, - chart.one_ring_uv_positions.row(i + 1), + if (!edge_has_length(zero, chart.one_ring_uv_positions.row(i + 1), m_l[face_index][(face_vertex_index + 1) % 3])) { spdlog::error( - "uv position {} in chart {} does not have expected norm {}", - chart.one_ring_uv_positions.row(i + 1), - vertex_index, - m_l[face_index][(face_vertex_index + 1) % 3]); + "uv position {} in chart {} does not have expected norm {}", + chart.one_ring_uv_positions.row(i + 1), vertex_index, + m_l[face_index][(face_vertex_index + 1) % 3]); return false; } } @@ -971,23 +867,18 @@ AffineManifold::is_valid_affine_manifold() const // Public Methods // ************** -ParametricAffineManifold::ParametricAffineManifold() -{ +ParametricAffineManifold::ParametricAffineManifold() { assert(is_valid_parametric_affine_manifold()); } -ParametricAffineManifold::ParametricAffineManifold(const Eigen::MatrixXi& F, - const MatrixXr& global_uv) - : AffineManifold(F, global_uv, F) -{ +ParametricAffineManifold::ParametricAffineManifold(const Eigen::MatrixXi &F, + const MatrixXr &global_uv) + : AffineManifold(F, global_uv, F) { assert(is_valid_parametric_affine_manifold()); } -void -ParametricAffineManifold::get_vertex_global_uv( - AffineManifold::Index vertex_index, - PlanarPoint& uv_coords) const -{ +void ParametricAffineManifold::get_vertex_global_uv( + AffineManifold::Index vertex_index, PlanarPoint &uv_coords) const { uv_coords = m_global_uv.row(vertex_index); } @@ -995,26 +886,23 @@ ParametricAffineManifold::get_vertex_global_uv( // Private Methods // *************** -bool -ParametricAffineManifold::is_valid_parametric_affine_manifold() const -{ +bool ParametricAffineManifold::is_valid_parametric_affine_manifold() const { if (m_F_uv != m_F) return false; // Check layout around each vertex is compatible with the global uv for (Index vertex_index = 0; vertex_index < num_vertices(); ++vertex_index) { - auto const& chart = m_vertex_charts[vertex_index]; + auto const &chart = m_vertex_charts[vertex_index]; for (size_t i = 0; i < chart.vertex_one_ring.size(); ++i) { Index vi = chart.vertex_one_ring[i]; PlanarPoint local_uv_difference = chart.one_ring_uv_positions.row(i); PlanarPoint global_uv_difference = - m_global_uv.row(vi) - m_global_uv.row(vertex_index); + m_global_uv.row(vi) - m_global_uv.row(vertex_index); if (!vector_equal(global_uv_difference, local_uv_difference)) { - spdlog::error( - "Global uv coordinates {} and {} do not have expected difference {}", - m_global_uv.row(vi), - m_global_uv.row(vertex_index), - local_uv_difference); + spdlog::error("Global uv coordinates {} and {} do not have expected " + "difference {}", + m_global_uv.row(vi), m_global_uv.row(vertex_index), + local_uv_difference); return false; } } @@ -1024,14 +912,12 @@ ParametricAffineManifold::is_valid_parametric_affine_manifold() const return true; } -void -remove_cones(const Eigen::MatrixXd& V, - const AffineManifold& affine_manifold, - Eigen::MatrixXd& pruned_V, - AffineManifold& pruned_affine_manifold, - std::vector& cones, - std::vector& removed_faces) -{ +void remove_cones(const Eigen::MatrixXd &V, + const AffineManifold &affine_manifold, + Eigen::MatrixXd &pruned_V, + AffineManifold &pruned_affine_manifold, + std::vector &cones, + std::vector &removed_faces) { // Compute the cones affine_manifold.compute_cones(cones); spdlog::debug("Removing cones at {}", formatted_vector(cones, ", ")); @@ -1041,34 +927,34 @@ remove_cones(const Eigen::MatrixXd& V, for (AffineManifold::Index vi = 0; vi < affine_manifold.num_vertices(); ++vi) { is_cone_adjacent_vertex[vi] = - affine_manifold.get_vertex_chart(vi).is_cone_adjacent; + affine_manifold.get_vertex_chart(vi).is_cone_adjacent; } // Create boolean arrays of cone adjacent faces std::vector is_cone_adjacent_face(affine_manifold.num_faces()); for (AffineManifold::Index fi = 0; fi < affine_manifold.num_faces(); ++fi) { is_cone_adjacent_face[fi] = - affine_manifold.get_face_chart(fi).is_cone_adjacent; + affine_manifold.get_face_chart(fi).is_cone_adjacent; } // Remove faces from VF meshes - Eigen::MatrixXi const& F_orig = affine_manifold.get_faces(); - MatrixXr const& global_uv_orig = affine_manifold.get_global_uv(); - Eigen::MatrixXi const& F_uv_orig = affine_manifold.get_F_uv(); + Eigen::MatrixXi const &F_orig = affine_manifold.get_faces(); + MatrixXr const &global_uv_orig = affine_manifold.get_global_uv(); + Eigen::MatrixXi const &F_uv_orig = affine_manifold.get_F_uv(); Eigen::MatrixXi F; MatrixXr global_uv; Eigen::MatrixXi F_uv; - remove_mesh_vertices( - global_uv_orig, F_uv_orig, cones, global_uv, F_uv, removed_faces); + remove_mesh_vertices(global_uv_orig, F_uv_orig, cones, global_uv, F_uv, + removed_faces); remove_mesh_faces(V, F_orig, removed_faces, pruned_V, F); // Remove faces from the cone adjacent arrays std::vector is_cone_adjacent_face_reindexed; std::vector is_cone_adjacent_vertex_reindexed; - remove_vector_values( - removed_faces, is_cone_adjacent_face, is_cone_adjacent_face_reindexed); - remove_vector_values( - cones, is_cone_adjacent_vertex, is_cone_adjacent_vertex_reindexed); + remove_vector_values(removed_faces, is_cone_adjacent_face, + is_cone_adjacent_face_reindexed); + remove_vector_values(cones, is_cone_adjacent_vertex, + is_cone_adjacent_vertex_reindexed); // Make new affine manifold with cones removed pruned_affine_manifold = AffineManifold(F, global_uv, F_uv); @@ -1087,3 +973,213 @@ remove_cones(const Eigen::MatrixXd& V, } } } + +// code added for C1 constraints + +void AffineManifold::compute_edge_global_uv_mappings() { + const auto &F = get_faces(); + const auto &F_uv = get_F_uv(); + const auto &uvs = get_global_uv(); + + for (auto &e : m_edge_charts) { + const int64_t top_f_idx = e.top_face_index; + const int64_t bottom_f_idx = e.bottom_face_index; + + // top face + const auto &f_top = F.row(top_f_idx); + const auto &f_top_uv = F_uv.row(top_f_idx); + + std::array top_local_idx = {{-1, -1, -1}}; // left, right, top + for (int i = 0; i < 3; ++i) { + if (f_top[i] == e.left_vertex_index) { + top_local_idx[0] = i; + } + if (f_top[i] == e.right_vertex_index) { + top_local_idx[1] = i; + } + if (f_top[i] == e.top_vertex_index) { + top_local_idx[2] = i; + } + } + + for (int i = 0; i < 3; ++i) { + assert(top_local_idx[i] > -1); + } + + e.left_global_uv_idx = f_top_uv[top_local_idx[0]]; + e.right_global_uv_idx = f_top_uv[top_local_idx[1]]; + e.top_global_uv_idx = f_top_uv[top_local_idx[2]]; + + e.left_global_uv_position = uvs.row(e.left_global_uv_idx); + e.right_global_uv_position = uvs.row(e.right_global_uv_idx); + e.top_global_uv_position = uvs.row(e.top_global_uv_idx); + + // bottom face + const auto &f_bottom = F.row(bottom_f_idx); + const auto &f_bottom_uv = F_uv.row(bottom_f_idx); + + std::array bottom_local_idx = { + {-1, -1, -1}}; // right (reverse left), left (reverse right), bottom + for (int i = 0; i < 3; ++i) { + if (f_bottom[i] == e.right_vertex_index) { + bottom_local_idx[0] = i; + } + if (f_bottom[i] == e.left_vertex_index) { + bottom_local_idx[1] = i; + } + if (f_bottom[i] == e.bottom_vertex_index) { + bottom_local_idx[2] = i; + } + } + + for (int i = 0; i < 3; ++i) { + assert(bottom_local_idx[i] > -1); + } + + e.reverse_left_global_uv_idx = f_bottom_uv[bottom_local_idx[0]]; + e.reverse_right_global_uv_idx = f_bottom_uv[bottom_local_idx[1]]; + e.bottom_global_uv_idx = f_bottom_uv[bottom_local_idx[2]]; + + e.reverse_left_global_uv_position = uvs.row(e.reverse_left_global_uv_idx); + e.reverse_right_global_uv_position = uvs.row(e.reverse_right_global_uv_idx); + e.bottom_global_uv_position = uvs.row(e.bottom_global_uv_idx); + } +} + +// redundant +void AffineManifold::compute_face_global_uv_mappings() { return; } + +void AffineManifold::generate_lagrange_nodes() { + // b0 b1 b2 b01 b10 b12 b21 b20 b02 b01^c b12^c b20^c b0c bc0 b1c bc1 b2c + // bc2 bc + + const std::array CT_nodes = {{ + PlanarPoint(1., 0.), // b0 + PlanarPoint(0., 1.), // b1 + PlanarPoint(0., 0.), // b2 + PlanarPoint(2. / 3., 1. / 3.), // b01 + PlanarPoint(1. / 3., 2. / 3.), // b10 + PlanarPoint(0., 2. / 3.), // b12 + PlanarPoint(0., 1. / 3.), // b21 + PlanarPoint(1. / 3., 0.), // b20 + PlanarPoint(2. / 3., 0.), // b02 + PlanarPoint(4. / 9., 4. / 9.), // b01^c + PlanarPoint(1. / 9., 4. / 9.), // b12^c + PlanarPoint(4. / 9., 1. / 9.), // b20^c + PlanarPoint(7. / 9., 1. / 9.), // b0c + PlanarPoint(5. / 9., 2. / 9.), // bc0 + PlanarPoint(1. / 9., 7. / 9.), // b1c + PlanarPoint(2. / 9., 5. / 9.), // bc1 + PlanarPoint(1. / 9., 1. / 9.), // b2c + PlanarPoint(2. / 9., 2. / 9.), // bc2 + PlanarPoint(1. / 3., 1. / 3.), // bc + }}; + + // std::map v_to_lagrange_node_map; + const auto &F = get_faces(); + // const auto &F_uv = get_F_uv(); + + // compute cornor vertices + for (size_t i = 0; i < m_face_charts.size(); ++i) { + const auto &f_idx = m_face_charts[i].face_index; + const auto &f = F.row(f_idx); + + for (int k = 0; k < 3; ++k) { + if (v_to_lagrange_node_map.find(f[k]) != v_to_lagrange_node_map.end()) { + // vertex already computed + continue; + } else { + v_to_lagrange_node_map[f[k]] = m_lagrange_nodes.size(); + m_lagrange_nodes.emplace_back(i, CT_nodes[k]); + } + } + } + + // compute nodes on edges and faces + // std::map, std::array> + // m_boundary_edge_to_node_map; + + for (size_t i = 0; i < m_face_charts.size(); ++i) { + const auto &f_idx = m_face_charts[i].face_index; + const auto &f = F.row(f_idx); + + std::array l_vids = {-1}; // node indices for face + + // b0 b1 b2 + for (int k = 0; k < 3; ++k) { + l_vids[k] = v_to_lagrange_node_map[f[k]]; + } + + // b01 b10 b12 b21 b20 b02 + for (int k = 0; k < 3; ++k) { + if (m_boundary_edge_to_node_map.find(std::make_pair( + f[(k + 1) % 3], f[k])) != m_boundary_edge_to_node_map.end()) { + // if already computed its reverse edge + const auto &vs = + m_boundary_edge_to_node_map[std::make_pair(f[(k + 1) % 3], f[k])]; + l_vids[3 + k * 2 + 0] = vs[1]; + l_vids[3 + k * 2 + 1] = vs[0]; + } else { + // create new nodes + l_vids[3 + k * 2 + 0] = m_lagrange_nodes.size(); + m_lagrange_nodes.emplace_back(i, CT_nodes[3 + k * 2 + 0]); + l_vids[3 + k * 2 + 1] = m_lagrange_nodes.size(); + m_lagrange_nodes.emplace_back(i, CT_nodes[3 + k * 2 + 1]); + // add to edge-node map + m_boundary_edge_to_node_map[std::make_pair(f[k], f[(k + 1) % 3])] = { + {l_vids[3 + k * 2 + 0], l_vids[3 + k * 2 + 1]}}; + } + } + + // b01^c b12^c b20^c b0c bc0 b1c bc1 b2c bc2 bc + for (int k = 9; k < 19; ++k) { + l_vids[k] = m_lagrange_nodes.size(); + m_lagrange_nodes.emplace_back(i, CT_nodes[k]); + } + + for (int k = 0; k < 19; ++k) { + assert(l_vids[k] > -1); + } + + // can avoid copy, but fine now + m_face_charts[i].lagrange_nodes = l_vids; + + // assign lagrange node to edge chart + // TODO: + // maybe outside this loop + } + + assert(m_lagrange_nodes.size() == 10 * m_face_charts.size() + + 2 * m_edge_charts.size() + + m_vertex_charts.size()); +} + +/* +get constraint matrices +*/ + +void AffineManifold::get_u_ij(Eigen::SparseMatrix &u_ij_u, + Eigen::SparseMatrix &u_ij_v) const { + const int64_t N_L = m_lagrange_nodes.size(); + assert(size_t(N_L) == 10 * m_face_charts.size() + 2 * m_edge_charts.size() + + m_vertex_charts.size()); + u_ij_u.resize(N_L, N_L); + u_ij_v.resize(N_L, N_L); + + std::vector> triplets_u; + std::vector> triplets_v; + for (const auto &e : m_edge_charts) { + const auto ev0 = e.left_vertex_index; + const auto ev1 = e.right_vertex_index; + const auto ev0_pos = e.left_vertex_uv_position; + const auto ev1_pos = e.right_vertex_uv_position; + + triplets_u.emplace_back(ev0, ev1, (ev1_pos - ev0_pos)[0]); + triplets_u.emplace_back(ev1, ev0, (ev0_pos - ev1_pos)[0]); + triplets_v.emplace_back(ev0, ev1, (ev1_pos - ev0_pos)[1]); + triplets_v.emplace_back(ev1, ev0, (ev0_pos - ev1_pos)[1]); + } + + u_ij_u.setFromTriplets(triplets_u.begin(), triplets_u.end()); + u_ij_v.setFromTriplets(triplets_v.begin(), triplets_v.end()); +} diff --git a/src/core/affine_manifold.h b/src/core/affine_manifold.h index 202feb6..8c507f8 100644 --- a/src/core/affine_manifold.h +++ b/src/core/affine_manifold.h @@ -21,17 +21,16 @@ /// nth vertex is the same as the first. For a boundary vertex, the one ring /// begins as the boundary face to the right of the vertex and ends at the left /// boundary face, and the nth vertex is generally different from the 0th. -struct VertexManifoldChart -{ +struct VertexManifoldChart { Halfedge::Index vertex_index; // Index of the vertex in the affine manifold std::vector - vertex_one_ring; // List of manifold vertex indices in the one ring + vertex_one_ring; // List of manifold vertex indices in the one ring std::vector - face_one_ring; // List of manifold face indices in the one ring + face_one_ring; // List of manifold face indices in the one ring MatrixXr - one_ring_uv_positions; // Local uv coordinates of the one ring vertices - bool is_boundary = false; // Mark boundary vertices - bool is_cone = false; // Mark cone vertices + one_ring_uv_positions; // Local uv coordinates of the one ring vertices + bool is_boundary = false; // Mark boundary vertices + bool is_cone = false; // Mark cone vertices bool is_cone_adjacent = false; // Mark vertices adjacent to a cone }; @@ -43,8 +42,7 @@ struct VertexManifoldChart /// edge, there is only one adjacent face. The bottom vertex and face indices /// are set to an out of range value (e.g., -1), and the bottom vertex is set to /// the empty vector. -struct EdgeManifoldChart -{ +struct EdgeManifoldChart { // Face indices Halfedge::Index top_face_index; Halfedge::Index bottom_face_index; @@ -63,13 +61,31 @@ struct EdgeManifoldChart // True iff the edge is on the boundary bool is_boundary; + + // added for C1 constraints + // global uv vertex mapping + int64_t left_global_uv_idx; + int64_t right_global_uv_idx; + int64_t top_global_uv_idx; + int64_t bottom_global_uv_idx; + int64_t reverse_left_global_uv_idx; + int64_t reverse_right_global_uv_idx; + + // global uv vertex positions + PlanarPoint left_global_uv_position; + PlanarPoint right_global_uv_position; + PlanarPoint top_global_uv_position; + PlanarPoint bottom_global_uv_position; + PlanarPoint reverse_left_global_uv_position; + PlanarPoint reverse_right_global_uv_position; + + std::array lagrange_nodes; }; /// Local layout manifold chart in R2 of a triangle. /// /// This is the same as global uv positions when these are provided. -struct FaceManifoldChart -{ +struct FaceManifoldChart { // Face indices Halfedge::Index face_index; @@ -79,13 +95,20 @@ struct FaceManifoldChart // Global information bool is_boundary = false; // True iff the edge is on the boundary bool is_cone_adjacent = false; // Mark faces adjacent to a cone - std::array is_cone_corner = { false, false, false }; // Mark individual corners adjacent to a cone + std::array is_cone_corner = { + false, false, false}; // Mark individual corners adjacent to a cone + + // added for C1 constraints + // std::array vertex_indices; + // std::array global_uv_indices; + // std::array global_uv_positions; + + std::array lagrange_nodes; }; /// Representation for an affine manifold, which is a topological manifold F /// equipped with a discrete metric l that satisfies the triangle inequality. -class AffineManifold -{ +class AffineManifold { public: typedef int Index; @@ -97,9 +120,8 @@ class AffineManifold /// @param[in] F: faces of the cone manifold /// @param[in] global_uv: global layout of the manifold /// @param[in] F_uv: faces of the global layout - AffineManifold(const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - const Eigen::MatrixXi& F_uv); + AffineManifold(const Eigen::MatrixXi &F, const MatrixXr &global_uv, + const Eigen::MatrixXi &F_uv); /// Get the number of faces in the manifold /// @@ -114,26 +136,25 @@ class AffineManifold /// Get faces for the manifold /// /// @return faces of the manifold - Eigen::MatrixXi const& get_faces() const; + Eigen::MatrixXi const &get_faces() const; /// Get halfedge for the manifold /// /// @return halfedge of the manifold - Halfedge const& get_halfedge() const { return m_halfedge; } + Halfedge const &get_halfedge() const { return m_halfedge; } /// Get halfedge to corner map for the manifold /// /// @return halfedge to corner map of the manifold - std::vector> const& get_he_to_corner() - const - { + std::vector> const & + get_he_to_corner() const { return m_he_to_corner; } /// Get faces for the manifold parametrization /// /// @return faces of the manifold layout - Eigen::MatrixXi const& get_F_uv() const; + Eigen::MatrixXi const &get_F_uv() const; /// Get an isometric chart for the vertex with the given index. /// @@ -145,7 +166,7 @@ class AffineManifold /// /// @param[in] vertex_index: index of the vertex for the chart /// @return chart for the given vertex - VertexManifoldChart const& get_vertex_chart(Index vertex_index) const; + VertexManifoldChart const &get_vertex_chart(Index vertex_index) const; /// Get an isometric chart for the edge opposite the corner with the given /// face index and vertex index within the face. @@ -154,14 +175,14 @@ class AffineManifold /// @param[in] face_vertex_index: index of the corner opposite the edge in the /// face /// @return chart for the given edge - EdgeManifoldChart const& get_edge_chart(Index face_index, + EdgeManifoldChart const &get_edge_chart(Index face_index, Index face_vertex_index) const; /// Get an isometric chart for the given face. /// /// @param[in] face_index: index of a face /// @return chart for the given face - FaceManifoldChart const& get_face_chart(Index face_index) const; + FaceManifoldChart const &get_face_chart(Index face_index) const; /// Get the portions of the isometric vertex charts corresponding to the /// corners of a given face. @@ -173,9 +194,9 @@ class AffineManifold /// /// @param[in] face_index: index of the face for the chart segments /// @param[out] corner_uv_positions: chart uv positions as enumerated above - void get_face_corner_charts( - Index face_index, - std::array& corner_uv_positions) const; + void + get_face_corner_charts(Index face_index, + std::array &corner_uv_positions) const; /// Get the portion of the edge charts contained in the interior of the given /// face. @@ -188,16 +209,16 @@ class AffineManifold /// @param[in] face_index: index of the face for the charts /// @param[out] face_edge_uv_positions: uv positions contained in the given /// face - void get_face_edge_charts( - Index face_index, - std::array& face_edge_uv_positions) const; + void + get_face_edge_charts(Index face_index, + std::array &face_edge_uv_positions) const; /// @brief Get the uv coordinates of the face. /// /// @param[in] face_index: index of the face for the chart /// @param[out] face_edge_uv_positions: global uv positions of the face void get_face_global_uv(Index face_index, - std::array& face_uv_positions) const; + std::array &face_uv_positions) const; /// Compute the curvature curvature at the given vertex. /// @@ -224,24 +245,24 @@ class AffineManifold /// Get list of all flat vertices in the manifold /// /// @param[out] flat_vertices: list of flat vertices - void compute_flat_vertices(std::vector& flat_vertices); + void compute_flat_vertices(std::vector &flat_vertices); /// Get list of all cones in the manifold /// /// @param[out] cones: list of cone vertices - void compute_cones(std::vector& cones) const; + void compute_cones(std::vector &cones) const; /// Get boolean mask of all cones corners in the manifold /// /// @param[out] is_cone_corner: true iff corner i, j is a cone - void compute_cones_corners( - std::vector>& is_cone_corner) const; + void + compute_cones_corners(std::vector> &is_cone_corner) const; /// Compute a matrix of cone point positions from mesh vertex positions. /// /// @param[in] V: mesh vertex positions /// @param[out] cone_points: cone positions w.r.t. V - void compute_cone_points(const MatrixXr& V, MatrixXr& cone_points) const; + void compute_cone_points(const MatrixXr &V, MatrixXr &cone_points) const; /// Return list of all cones in the manifold /// @@ -251,7 +272,7 @@ class AffineManifold /// Get list of all boundary vertices in the manifold /// /// @param[out] boundary_vertices: list of boundary vertices - void compute_boundary_vertices(std::vector& boundary_vertices) const; + void compute_boundary_vertices(std::vector &boundary_vertices) const; /// Return list of all boundary vertices in the manifold /// @@ -271,7 +292,7 @@ class AffineManifold /// Get global uv coordinates /// /// @return global uv coordinates, or the empty matrix if they do not exist - MatrixXr const& get_global_uv() const; + MatrixXr const &get_global_uv() const; /// Cut edges adjacent to cones so that a planar layout is possible around /// them. @@ -282,12 +303,13 @@ class AffineManifold /// /// @param[in] V: mesh vertex positions /// @param[in] color: color for the affine manifold in the viewer - void add_to_viewer(const MatrixXr& V, Eigen::Matrix color = GOLD_YELLOW) const; + void add_to_viewer(const MatrixXr &V, + Eigen::Matrix color = GOLD_YELLOW) const; /// View the cone manifold and its data /// /// @param[in] V: mesh vertex positions - void view(const MatrixXr& V) const; + void view(const MatrixXr &V) const; /// Save an image of the cone manifold and its data to file. /// @@ -296,53 +318,47 @@ class AffineManifold /// @param[in] camera_position: camera position for the screenshot /// @param[in] camera_target: camera target for the screenshot /// @param[in] use_orthographic: use orthographic perspective if true - void - screenshot(const std::string& filename, - const MatrixXr& V, - SpatialVector camera_position, - SpatialVector camera_target, - bool use_orthographic) const; + void screenshot(const std::string &filename, const MatrixXr &V, + SpatialVector camera_position, SpatialVector camera_target, + bool use_orthographic) const; // Clear all internal data for a trivial cone manifold void clear(); -protected: +public: void build_vertex_charts_from_lengths( - const Eigen::MatrixXi& F, - const std::vector>& l, - std::vector& vertex_charts) const; + const Eigen::MatrixXi &F, const std::vector> &l, + std::vector &vertex_charts) const; void build_edge_charts_from_lengths( - const Eigen::MatrixXi& F, - const Halfedge& halfedge, - const std::vector>& l, - std::vector& edge_charts) const; + const Eigen::MatrixXi &F, const Halfedge &halfedge, + const std::vector> &l, + std::vector &edge_charts) const; - void build_face_charts(const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - const Eigen::MatrixXi& F_uv, - std::vector& face_charts) const; + void build_face_charts(const Eigen::MatrixXi &F, const MatrixXr &global_uv, + const Eigen::MatrixXi &F_uv, + std::vector &face_charts) const; void build_corner_to_edge_map( - const std::vector>& corner_to_he, - const std::vector& he_to_edge, - std::vector>& corner_to_edge) const; + const std::vector> &corner_to_he, + const std::vector &he_to_edge, + std::vector> &corner_to_edge) const; - PlanarPoint layout_next_vertex(const PlanarPoint& current_point, + PlanarPoint layout_next_vertex(const PlanarPoint ¤t_point, double next_edge_length, double prev_edge_length) const; - void layout_one_ring(const Eigen::MatrixXi& F, - const std::vector>& l, + void layout_one_ring(const Eigen::MatrixXi &F, + const std::vector> &l, Index vertex_index, - const std::vector& vertex_one_ring, - const std::vector& face_one_ring, - MatrixXr& one_ring_uv_positions) const; + const std::vector &vertex_one_ring, + const std::vector &face_one_ring, + MatrixXr &one_ring_uv_positions) const; - void build_lengths_from_global_uv(const Eigen::MatrixXi& F, - const MatrixXr& global_uv, - std::vector>& l) const; + void build_lengths_from_global_uv(const Eigen::MatrixXi &F, + const MatrixXr &global_uv, + std::vector> &l) const; - void align_local_charts(const MatrixXr& uv, const Eigen::MatrixXi& F_uv); + void align_local_charts(const MatrixXr &uv, const Eigen::MatrixXi &F_uv); void mark_cones(); @@ -351,9 +367,11 @@ class AffineManifold bool is_valid_affine_manifold() const; +public: // Topology information - // TODO: The faces are duplicated in the halfedge. Our halfedge alway retains - // the original VF topology, so there is no need to maintain both separately + // TODO: The faces are duplicated in the halfedge. Our halfedge alway + // retains the original VF topology, so there is no need to maintain both + // separately Eigen::MatrixXi m_F; std::vector> m_corner_to_he; std::vector> m_corner_to_edge; @@ -369,12 +387,40 @@ class AffineManifold std::vector m_vertex_charts; std::vector m_edge_charts; std::vector m_face_charts; + +public: + // code added for C1 constraints + + // edge vectors + void get_u_ij(Eigen::SparseMatrix &u_ij_u, + Eigen::SparseMatrix &u_ij_v) const; + + // reindexing matrices + void get_P_G2F(Eigen::SparseMatrix &P_G2F) const; + void get_P_G2E(Eigen::SparseMatrix &P_G2E) const; + void get_P_d_E(Eigen::SparseMatrix &P_d_E) const; + void get_P_d_M(Eigen::SparseMatrix &P_d_M) const; + + void compute_face_edge_lagrange_node_indices(); + void compute_edge_global_uv_mappings(); + void compute_face_global_uv_mappings(); + void generate_lagrange_nodes(); + + // per face control points indices + // b0 b1 b2 b01 b10 b12 b21 b20 b02 b01^c b12^c b20^c b0c bc0 b1c bc1 b2c + // bc2 bc + std::vector> + m_lagrange_nodes; // (face_idx, barycentric coord) + std::map v_to_lagrange_node_map; // vidx to node idx + + // edge with v_idx to two nodes idx on edge + std::map, std::array> + m_boundary_edge_to_node_map; }; /// Representation for an affine manifold with a global parametrization, which /// yields a flat metric and thus an affine manifold structure. -class ParametricAffineManifold : public AffineManifold -{ +class ParametricAffineManifold : public AffineManifold { public: ParametricAffineManifold(); @@ -383,24 +429,23 @@ class ParametricAffineManifold : public AffineManifold /// /// @param[in] F: faces of the affine manifold /// @param[in] global_uv: affine global layout of the manifold - ParametricAffineManifold(const Eigen::MatrixXi& F, const MatrixXr& global_uv); + ParametricAffineManifold(const Eigen::MatrixXi &F, const MatrixXr &global_uv); /// Get global uv coordinates for a given vertex. /// /// @param[in] vertex_index: index of the vertex for the uv position /// @param[out] uv_coords: global uv position for the given vertex - void get_vertex_global_uv(Index vertex_index, PlanarPoint& uv_coords) const; + void get_vertex_global_uv(Index vertex_index, PlanarPoint &uv_coords) const; -private: +public: bool is_valid_parametric_affine_manifold() const; }; /// Generate an affine manifold with the cone faces removed but cone adjaceny /// information retained -void -remove_cones(const Eigen::MatrixXd& V, - const AffineManifold& affine_manifold, - Eigen::MatrixXd& pruned_V, - AffineManifold& pruned_affine_manifold, - std::vector& cones, - std::vector& removed_faces); \ No newline at end of file +void remove_cones(const Eigen::MatrixXd &V, + const AffineManifold &affine_manifold, + Eigen::MatrixXd &pruned_V, + AffineManifold &pruned_affine_manifold, + std::vector &cones, + std::vector &removed_faces); \ No newline at end of file diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt index 9967452..15956d3 100755 --- a/src/exec/CMakeLists.txt +++ b/src/exec/CMakeLists.txt @@ -15,3 +15,14 @@ target_link_libraries(view_quadratic_spline PRIVATE target_compile_options(view_quadratic_spline PRIVATE -Wall -Wpedantic -Wextra -Werror ) + + +add_executable(generate_cubic_surface generate_cubic_surface.cpp) +target_link_libraries(generate_cubic_surface PRIVATE + QuadraticContoursLib + CloughTocherPatchLib + CLI11::CLI11 +) +target_compile_options(generate_cubic_surface PRIVATE + -Wall -Wpedantic -Wextra -Werror +) diff --git a/src/exec/generate_cubic_surface.cpp b/src/exec/generate_cubic_surface.cpp new file mode 100644 index 0000000..c112bfa --- /dev/null +++ b/src/exec/generate_cubic_surface.cpp @@ -0,0 +1,110 @@ +#include "apply_transformation.h" +#include "common.h" +#include "compute_boundaries.h" +#include "contour_network.h" +#include "generate_transformation.h" +#include "globals.cpp" +#include "twelve_split_spline.h" +#include +#include +#include +#include + +#include "clough_tocher_surface.hpp" + +int main(int argc, char *argv[]) { + // Build maps from strings to enums + std::map log_level_map{ + {"trace", spdlog::level::trace}, {"debug", spdlog::level::debug}, + {"info", spdlog::level::info}, {"warn", spdlog::level::warn}, + {"critical", spdlog::level::critical}, {"off", spdlog::level::off}, + }; + + // Get command line arguments + CLI::App app{"Generate Clough-Tocher cubic surface mesh."}; + std::string input_filename = ""; + std::string output_dir = "./"; + spdlog::level::level_enum log_level = spdlog::level::off; + Eigen::Matrix color = SKY_BLUE; + int num_subdivisions = DISCRETIZATION_LEVEL; + OptimizationParameters optimization_params; + double weight = optimization_params.position_difference_factor; + app.add_option("-i,--input", input_filename, "Mesh filepath") + ->check(CLI::ExistingFile) + ->required(); + app.add_option("--log_level", log_level, "Level of logging") + ->transform(CLI::CheckedTransformer(log_level_map, CLI::ignore_case)); + app.add_option("--num_subdivisions", num_subdivisions, + "Number of subdivisions") + ->check(CLI::PositiveNumber); + app.add_option("-w,--weight", weight, + "Fitting weight for the quadratic surface approximation") + ->check(CLI::PositiveNumber); + CLI11_PARSE(app, argc, argv); + + // Set logger level + spdlog::set_level(log_level); + + // Set optimization parameters + optimization_params.position_difference_factor = weight; + + // Get input mesh + Eigen::MatrixXd V, uv, N; + Eigen::MatrixXi F, FT, FN; + igl::readOBJ(input_filename, V, uv, N, F, FT, FN); + + // Generate quadratic spline + spdlog::info("Computing spline surface"); + // std::vector> face_to_patch_indices; + // std::vector patch_to_face_indices; + Eigen::SparseMatrix fit_matrix; + Eigen::SparseMatrix energy_hessian; + Eigen::CholmodSupernodalLLT> + energy_hessian_inverse; + AffineManifold affine_manifold(F, uv, FT); + + CloughTocherSurface ct_surface(V, affine_manifold, optimization_params, + fit_matrix, energy_hessian, + energy_hessian_inverse); + + ct_surface.m_affine_manifold.generate_lagrange_nodes(); + + ct_surface.write_coeffs_to_obj("test_cubic_points.obj"); + + ct_surface.sample_to_obj("test_sample_cubic_points.obj", 25); + + ct_surface.write_cubic_surface_to_msh_no_conn("test_cubic_sphere.msh"); + // ct_surface.write_cubic_surface_to_msh_with_conn("duck_with_conn"); + + ct_surface.write_cubic_surface_to_msh_with_conn_from_lagrange_nodes( + "icosphere_from_lagrange_nodes"); + + Eigen::SparseMatrix c_f_int; + ct_surface.C_F_int(c_f_int); + Eigen::SparseMatrix C_E_end; + ct_surface.C_E_end(C_E_end); + Eigen::SparseMatrix C_E_mid; + ct_surface.C_E_mid(C_E_mid); + + std::ofstream file("interior_constraint_matrix.txt"); + file << std::setprecision(16) << c_f_int; + std::ofstream file_2("edge_endpoint_constraint_matrix.txt"); + file_2 << std::setprecision(16) << c_f_int; + std::ofstream file_3("edge_midpoint_constraint_matrix.txt"); + file_3 << std::setprecision(16) << c_f_int; + + file.close(); + file_2.close(); + file_3.close(); + + // std::ofstream file_4("interior_constraint_matrix_triplets.txt"); + // const auto trip1 = c_f_int.to_triplets(); + // std::ofstream file_5("edge_endpoint_constraint_matrix_triplets.txt"); + // std::ofstream file_6("edge_midpoint_constraint_matrix_triplets.txt"); + + // file_4.close(); + // file_5.close(); + // file_6.close(); + + return 0; +} diff --git a/src/exec/view_quadratic_spline.cpp b/src/exec/view_quadratic_spline.cpp index 3ad8c41..f4b346d 100755 --- a/src/exec/view_quadratic_spline.cpp +++ b/src/exec/view_quadratic_spline.cpp @@ -1,28 +1,23 @@ // Copyright 2023 Adobe Research. All rights reserved. // To view a copy of the license, visit LICENSE.md. -#include "common.h" -#include "globals.cpp" #include "apply_transformation.h" -#include "generate_transformation.h" +#include "common.h" #include "compute_boundaries.h" #include "contour_network.h" +#include "generate_transformation.h" +#include "globals.cpp" #include "twelve_split_spline.h" +#include #include #include -#include - -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { // Build maps from strings to enums - std::map log_level_map { - {"trace", spdlog::level::trace}, - {"debug", spdlog::level::debug}, - {"info", spdlog::level::info}, - {"warn", spdlog::level::warn}, - {"critical", spdlog::level::critical}, - {"off", spdlog::level::off}, + std::map log_level_map{ + {"trace", spdlog::level::trace}, {"debug", spdlog::level::debug}, + {"info", spdlog::level::info}, {"warn", spdlog::level::warn}, + {"critical", spdlog::level::critical}, {"off", spdlog::level::off}, }; // Get command line arguments @@ -35,14 +30,16 @@ int main(int argc, char *argv[]) OptimizationParameters optimization_params; double weight = optimization_params.position_difference_factor; app.add_option("-i,--input", input_filename, "Mesh filepath") - ->check(CLI::ExistingFile) - ->required(); + ->check(CLI::ExistingFile) + ->required(); app.add_option("--log_level", log_level, "Level of logging") - ->transform(CLI::CheckedTransformer(log_level_map, CLI::ignore_case)); - app.add_option("--num_subdivisions", num_subdivisions, "Number of subdivisions") - ->check(CLI::PositiveNumber); - app.add_option("-w,--weight", weight, "Fitting weight for the quadratic surface approximation") - ->check(CLI::PositiveNumber); + ->transform(CLI::CheckedTransformer(log_level_map, CLI::ignore_case)); + app.add_option("--num_subdivisions", num_subdivisions, + "Number of subdivisions") + ->check(CLI::PositiveNumber); + app.add_option("-w,--weight", weight, + "Fitting weight for the quadratic surface approximation") + ->check(CLI::PositiveNumber); CLI11_PARSE(app, argc, argv); // Set logger level @@ -66,9 +63,14 @@ int main(int argc, char *argv[]) energy_hessian_inverse; AffineManifold affine_manifold(F, uv, FT); TwelveSplitSplineSurface spline_surface( - V, affine_manifold, - optimization_params, face_to_patch_indices, patch_to_face_indices, - fit_matrix, energy_hessian, energy_hessian_inverse); + V, affine_manifold, optimization_params, face_to_patch_indices, + patch_to_face_indices, fit_matrix, energy_hessian, + energy_hessian_inverse); + + // output to obj + spline_surface.write_corner_patch_points_to_obj("sphere_powell_sabon.obj"); + spline_surface.write_cubic_nodes_to_msh("sphere_ps_cubic.msh"); + spline_surface.write_cubic_nodes_to_obj("sphere_ps_cubic_nodes.obj"); // View the mesh spline_surface.view(color, num_subdivisions); diff --git a/src/quadratic_spline_surface/CMakeLists.txt b/src/quadratic_spline_surface/CMakeLists.txt index 789157b..9a57855 100755 --- a/src/quadratic_spline_surface/CMakeLists.txt +++ b/src/quadratic_spline_surface/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(QuadraticSplineSurfaceLib quadratic_spline_surface.cpp quadratic_spline_surface_patch.cpp twelve_split_spline.cpp + quadratic_spline_surface_msh_writer.cpp ) target_include_directories(QuadraticSplineSurfaceLib PUBLIC .) target_link_libraries(QuadraticSplineSurfaceLib PUBLIC diff --git a/src/quadratic_spline_surface/optimize_spline_surface.cpp b/src/quadratic_spline_surface/optimize_spline_surface.cpp index 1188fce..d650e28 100644 --- a/src/quadratic_spline_surface/optimize_spline_surface.cpp +++ b/src/quadratic_spline_surface/optimize_spline_surface.cpp @@ -3,25 +3,25 @@ #include "optimize_spline_surface.h" -#include "powell_sabin_local_to_global_indexing.h" #include "compute_local_twelve_split_hessian.h" #include "planarH.h" +#include "powell_sabin_local_to_global_indexing.h" // Build the hessian for the local fitting energy // // This is a diagonal matrix with special weights for cones and cone adjacent // vertices Eigen::Matrix -build_local_fit_hessian(const std::array& is_cone, - const std::array& is_cone_adjacent, - const OptimizationParameters& optimization_params) -{ +build_local_fit_hessian(const std::array &is_cone, + const std::array &is_cone_adjacent, + const OptimizationParameters &optimization_params) { Eigen::Matrix H_f; H_f.fill(0); // Check for cone collapsing vertices for (size_t i = 0; i < 3; ++i) { - size_t vi = generate_local_vertex_position_variable_index(i, 0, 1); // local vertex index + size_t vi = generate_local_vertex_position_variable_index( + i, 0, 1); // local vertex index // Weights for cone vertices if (is_cone[i]) { @@ -32,33 +32,37 @@ build_local_fit_hessian(const std::array& is_cone, // Add cone gradient fitting term spdlog::info("Weighting cone gradients by {}", - optimization_params.cone_vertex_gradient_difference_factor); - size_t g1i = generate_local_vertex_gradient_variable_index(i, 0, 0, 1); // local first gradient index - size_t g2i = generate_local_vertex_gradient_variable_index(i, 1, 0, 1); // local second gradient index + optimization_params.cone_vertex_gradient_difference_factor); + size_t g1i = generate_local_vertex_gradient_variable_index( + i, 0, 0, 1); // local first gradient index + size_t g2i = generate_local_vertex_gradient_variable_index( + i, 1, 0, 1); // local second gradient index H_f(g1i, g1i) = - optimization_params.cone_vertex_gradient_difference_factor; + optimization_params.cone_vertex_gradient_difference_factor; H_f(g2i, g2i) = - optimization_params.cone_vertex_gradient_difference_factor; + optimization_params.cone_vertex_gradient_difference_factor; } // Weights for cone adjacent vertices (which can be collapsed to the cone) else if (is_cone_adjacent[i]) { // Add increased weight to the cone adjacent position fit spdlog::trace( - "Weighting cone adjacent vertices by {}", - optimization_params.cone_adjacent_position_difference_factor); + "Weighting cone adjacent vertices by {}", + optimization_params.cone_adjacent_position_difference_factor); H_f(vi, vi) = - optimization_params.cone_adjacent_position_difference_factor; + optimization_params.cone_adjacent_position_difference_factor; // Add cone adjacent vertex gradient fitting term spdlog::trace( - "Weighting cone adjacent gradients by {}", - optimization_params.cone_adjacent_vertex_gradient_difference_factor); - size_t g1i = generate_local_vertex_gradient_variable_index(i, 0, 0, 1); // local first gradient index - size_t g2i = generate_local_vertex_gradient_variable_index(i, 1, 0, 1); // local second gradient index + "Weighting cone adjacent gradients by {}", + optimization_params.cone_adjacent_vertex_gradient_difference_factor); + size_t g1i = generate_local_vertex_gradient_variable_index( + i, 0, 0, 1); // local first gradient index + size_t g2i = generate_local_vertex_gradient_variable_index( + i, 1, 0, 1); // local second gradient index H_f(g1i, g1i) = - optimization_params.cone_adjacent_vertex_gradient_difference_factor; + optimization_params.cone_adjacent_vertex_gradient_difference_factor; H_f(g2i, g2i) = - optimization_params.cone_adjacent_vertex_gradient_difference_factor; + optimization_params.cone_adjacent_vertex_gradient_difference_factor; } // Default fitting weight is 1.0 else { @@ -68,16 +72,17 @@ build_local_fit_hessian(const std::array& is_cone, // Check for edges collapsing to a cone and add weight for (size_t i = 0; i < 3; ++i) { - size_t vj = (i + 1) % 3; // next local vertex index - size_t vk = (i + 2) % 3; // prev local vertex index + size_t vj = (i + 1) % 3; // next local vertex index + size_t vk = (i + 2) % 3; // prev local vertex index if ((is_cone_adjacent[vj]) && (is_cone_adjacent[vk])) { // Add cone adjacent adjacent edge gradient fit spdlog::trace( - "Weighting cone edge gradients by {}", - optimization_params.cone_adjacent_edge_gradient_difference_factor); - size_t gjk = generate_local_edge_gradient_variable_index(i, 0, 1); // local first gradient index + "Weighting cone edge gradients by {}", + optimization_params.cone_adjacent_edge_gradient_difference_factor); + size_t gjk = generate_local_edge_gradient_variable_index( + i, 0, 1); // local first gradient index H_f(gjk, gjk) = - optimization_params.cone_adjacent_edge_gradient_difference_factor; + optimization_params.cone_adjacent_edge_gradient_difference_factor; } } @@ -90,13 +95,11 @@ build_local_fit_hessian(const std::array& is_cone, // WARNING: Unlike the other hessians, which are 12x12 matrices assembled per // x,y,z coordinate and combined into a 36x3 block matrix, this Hessian has // mixed coordinate terms and is thus directly 36x36 -Eigen::Matrix -build_planar_constraint_hessian( - const Eigen::Matrix& uv, - const std::array& corner_to_corner_uv_positions, - const std::array& reverse_edge_orientations, - const SpatialVector& normal) -{ +Eigen::Matrix build_planar_constraint_hessian( + const Eigen::Matrix &uv, + const std::array &corner_to_corner_uv_positions, + const std::array &reverse_edge_orientations, + const SpatialVector &normal) { // Build planar hessian array for derived derivative quantities double planarH_array[36][36]; planarHfun(normal[0], normal[1], normal[2], planarH_array); @@ -111,7 +114,7 @@ build_planar_constraint_hessian( // Build C_gl matrix Eigen::Matrix C_gl = - get_C_gl(uv, corner_to_corner_uv_positions, reverse_edge_orientations); + get_C_gl(uv, corner_to_corner_uv_positions, reverse_edge_orientations); // Make block diagonal C_gl matrix Eigen::Matrix C_gl_diag; @@ -123,7 +126,7 @@ build_planar_constraint_hessian( // Build the planar constraint Hessian with indexing so that DoF per // coordinate are contiguous Eigen::Matrix H_p_permuted = - 0.5 * C_gl_diag.transpose() * planarH * C_gl_diag; + 0.5 * C_gl_diag.transpose() * planarH * C_gl_diag; // Reindex so that coordinates per DoF are contiguous Eigen::Matrix H_p; @@ -133,7 +136,7 @@ build_planar_constraint_hessian( for (size_t ci = 0; ci < 12; ++ci) { for (size_t cj = 0; cj < 3; ++cj) { H_p(3 * ri + rj, 3 * ci + cj) = - H_p_permuted(12 * rj + ri, 12 * cj + ci); + H_p_permuted(12 * rj + ri, 12 * cj + ci); } } } @@ -147,30 +150,28 @@ struct LocalHessianData { Eigen::Matrix H_f; // fitting term hessian Eigen::Matrix H_s; // smoothness term hessian Eigen::Matrix H_p; // planarity term hessian - double w_f; // fitting term weight - double w_s; // smoothness term weight - double w_p; // planarity term weight + double w_f; // fitting term weight + double w_s; // smoothness term weight + double w_p; // planarity term weight }; // Structure for the energy quadratic local degree of freedom data struct LocalDOFData { - Eigen::Matrix r_alpha_0; // initial local DOF - Eigen::Matrix r_alpha; // local DOF + Eigen::Matrix r_alpha_0; // initial local DOF + Eigen::Matrix r_alpha; // local DOF Eigen::Matrix r_alpha_flat; // flattened local DOF }; // Assemble the energy quadratic Hessian data -void -generate_local_hessian_data( - const std::array& face_vertex_uv_positions, - const std::array& corner_to_corner_uv_positions, - const std::array& reverse_edge_orientations, - const std::array& is_cone, - const std::array& is_cone_adjacent, - const SpatialVector& face_normal, - const OptimizationParameters& optimization_params, - LocalHessianData& local_hessian_data -) { +void generate_local_hessian_data( + const std::array &face_vertex_uv_positions, + const std::array &corner_to_corner_uv_positions, + const std::array &reverse_edge_orientations, + const std::array &is_cone, + const std::array &is_cone_adjacent, + const SpatialVector &face_normal, + const OptimizationParameters &optimization_params, + LocalHessianData &local_hessian_data) { // Build uv from global positions Eigen::Matrix uv; uv.row(0) = face_vertex_uv_positions[0]; @@ -179,28 +180,25 @@ generate_local_hessian_data( // H_s: local smoothness hessian local_hessian_data.H_s = build_local_smoothness_hessian( - uv, - corner_to_corner_uv_positions, - reverse_edge_orientations); + uv, corner_to_corner_uv_positions, reverse_edge_orientations); // H_f: position fit hessian local_hessian_data.H_f = - build_local_fit_hessian(is_cone, is_cone_adjacent, optimization_params); + build_local_fit_hessian(is_cone, is_cone_adjacent, optimization_params); // H_p: planar fitting term // DEPRECATED if (optimization_params.cone_normal_orthogonality_factor != 0.0) { - local_hessian_data.H_p = build_planar_constraint_hessian(uv, - corner_to_corner_uv_positions, - reverse_edge_orientations, - face_normal); + local_hessian_data.H_p = + build_planar_constraint_hessian(uv, corner_to_corner_uv_positions, + reverse_edge_orientations, face_normal); } else { local_hessian_data.H_p.setZero(); } // w_s: smoothing weight local_hessian_data.w_s = - optimization_params.parametrized_quadratic_surface_mapping_factor; + optimization_params.parametrized_quadratic_surface_mapping_factor; // w_f: fitting weight local_hessian_data.w_f = optimization_params.position_difference_factor; @@ -210,14 +208,12 @@ generate_local_hessian_data( } // Assemble the local degree of freedom data -void -generate_local_dof_data( - const std::array& initial_vertex_positions_T, - const std::array& vertex_positions_T, - const std::array& vertex_gradients_T, - const std::array& edge_gradients_T, - LocalDOFData& local_dof_data -) { +void generate_local_dof_data( + const std::array &initial_vertex_positions_T, + const std::array &vertex_positions_T, + const std::array &vertex_gradients_T, + const std::array &edge_gradients_T, + LocalDOFData &local_dof_data) { // r_alpha_0: fitting values // WARNING: Only fitting to zero implemented for gradients // TODO: Implement fitting for creases @@ -252,32 +248,30 @@ generate_local_dof_data( // Compute the local twelve split energy quadratic from Hessian and local // degree of freedom data -void -compute_local_twelve_split_energy_quadratic( - const LocalHessianData& local_hessian_data, - const LocalDOFData& local_dof_data, - double& local_energy, - TwelveSplitGradient& local_derivatives, - TwelveSplitHessian& local_hessian) -{ +void compute_local_twelve_split_energy_quadratic( + const LocalHessianData &local_hessian_data, + const LocalDOFData &local_dof_data, double &local_energy, + TwelveSplitGradient &local_derivatives, TwelveSplitHessian &local_hessian) { // Extract local hessian data - const Eigen::Matrix& H_f = local_hessian_data.H_f; - const Eigen::Matrix& H_s = local_hessian_data.H_s; - const Eigen::Matrix& H_p = local_hessian_data.H_p; + const Eigen::Matrix &H_f = local_hessian_data.H_f; + const Eigen::Matrix &H_s = local_hessian_data.H_s; + const Eigen::Matrix &H_p = local_hessian_data.H_p; double w_f = local_hessian_data.w_f; double w_s = local_hessian_data.w_s; double w_p = local_hessian_data.w_p; // Extract local degrees of freedom data - const Eigen::Matrix& r_alpha_0 = local_dof_data.r_alpha_0; - const Eigen::Matrix& r_alpha = local_dof_data.r_alpha; - const Eigen::Matrix& r_alpha_flat = local_dof_data.r_alpha_flat; + const Eigen::Matrix &r_alpha_0 = local_dof_data.r_alpha_0; + const Eigen::Matrix &r_alpha = local_dof_data.r_alpha; + const Eigen::Matrix &r_alpha_flat = + local_dof_data.r_alpha_flat; // full local 12x12 hessian (only smoothness and fitting terms) Eigen::Matrix local_hessian_12x12 = - 2 * (w_s * H_s + w_f * H_f); + 2 * (w_s * H_s + w_f * H_f); - // Add smoothness and fitting term blocks to the full local hessian per coordinate + // Add smoothness and fitting term blocks to the full local hessian per + // coordinate local_hessian.fill(0); for (int i = 0; i < 12; i++) { for (int j = 0; j < 12; j++) { @@ -311,7 +305,7 @@ compute_local_twelve_split_energy_quadratic( double smoothness_term = 0.0; for (int i = 0; i < 3; i++) { smoothness_term += - (r_alpha.col(i).transpose() * (w_s * H_s) * r_alpha.col(i))(0, 0); + (r_alpha.col(i).transpose() * (w_s * H_s) * r_alpha.col(i))(0, 0); } spdlog::trace("Smoothness term is {}", smoothness_term); @@ -319,7 +313,7 @@ compute_local_twelve_split_energy_quadratic( double fit_term = 0.0; for (int i = 0; i < 3; i++) { Eigen::Matrix r_alpha_diff = - r_alpha.col(i) - r_alpha_0.col(i); + r_alpha.col(i) - r_alpha_0.col(i); fit_term += (r_alpha_diff.transpose() * (w_f * H_f) * r_alpha_diff)(0, 0); } spdlog::trace("Fit term is {}", fit_term); @@ -335,10 +329,7 @@ compute_local_twelve_split_energy_quadratic( } // Helper function to cyclically shift an array of three elements -template -void -shift_array(std::array& arr, int shift) -{ +template void shift_array(std::array &arr, int shift) { std::array arr_copy = arr; for (int i = 0; i < 3; ++i) { arr[i] = arr_copy[(i + shift) % 3]; @@ -347,21 +338,17 @@ shift_array(std::array& arr, int shift) // Method to cyclically shift the indexing of all energy quadratic data arrays // for triangle vertex values -void -shift_local_energy_quadratic_vertices( - std::array& vertex_positions_T, - std::array& vertex_gradients_T, - std::array& edge_gradients_T, - std::array& initial_vertex_positions_T, - std::array& face_vertex_uv_positions, - std::array& corner_to_corner_uv_positions, - std::array& reverse_edge_orientations, - std::array& is_cone, - std::array& is_cone_adjacent, - std::array& face_global_vertex_indices, - std::array& face_global_edge_indices, - int shift) -{ +void shift_local_energy_quadratic_vertices( + std::array &vertex_positions_T, + std::array &vertex_gradients_T, + std::array &edge_gradients_T, + std::array &initial_vertex_positions_T, + std::array &face_vertex_uv_positions, + std::array &corner_to_corner_uv_positions, + std::array &reverse_edge_orientations, + std::array &is_cone, std::array &is_cone_adjacent, + std::array &face_global_vertex_indices, + std::array &face_global_edge_indices, int shift) { shift_array(vertex_positions_T, shift); shift_array(vertex_gradients_T, shift); shift_array(edge_gradients_T, shift); @@ -376,25 +363,19 @@ shift_local_energy_quadratic_vertices( } // Compute the energy system for a twelve-split spline -void -compute_twelve_split_energy_quadratic( - const std::vector& vertex_positions, - const std::vector& vertex_gradients, - const std::vector>& edge_gradients, - const std::vector& global_vertex_indices, - const std::vector>& global_edge_indices, - const std::vector& initial_vertex_positions, - const Eigen::MatrixXd& initial_face_normals, - const AffineManifold& manifold, - const OptimizationParameters& optimization_params, - double& energy, - VectorXr& derivatives, - Eigen::SparseMatrix& hessian, - int num_variable_vertices, - int num_variable_edges) -{ +void compute_twelve_split_energy_quadratic( + const std::vector &vertex_positions, + const std::vector &vertex_gradients, + const std::vector> &edge_gradients, + const std::vector &global_vertex_indices, + const std::vector> &global_edge_indices, + const std::vector &initial_vertex_positions, + const Eigen::MatrixXd &initial_face_normals, const AffineManifold &manifold, + const OptimizationParameters &optimization_params, double &energy, + VectorXr &derivatives, Eigen::SparseMatrix &hessian, + int num_variable_vertices, int num_variable_edges) { int num_independent_variables = - 9 * num_variable_vertices + 3 * num_variable_edges; + 9 * num_variable_vertices + 3 * num_variable_edges; energy = 0; derivatives.setZero(num_independent_variables); std::vector> hessian_entries; @@ -403,42 +384,42 @@ compute_twelve_split_energy_quadratic( for (AffineManifold::Index face_index = 0; face_index < manifold.num_faces(); ++face_index) { // Get face vertices - Eigen::MatrixXi const& F = manifold.get_faces(); + Eigen::MatrixXi const &F = manifold.get_faces(); int i = F(face_index, 0); int j = F(face_index, 1); int k = F(face_index, 2); // Bundle relevant global variables into per face local vectors std::array initial_vertex_positions_T, vertex_positions_T, - edge_gradients_T; + edge_gradients_T; std::array vertex_gradients_T; build_face_variable_vector(vertex_positions, i, j, k, vertex_positions_T); build_face_variable_vector(vertex_gradients, i, j, k, vertex_gradients_T); edge_gradients_T = edge_gradients[face_index]; - build_face_variable_vector( - initial_vertex_positions, i, j, k, initial_vertex_positions_T); + build_face_variable_vector(initial_vertex_positions, i, j, k, + initial_vertex_positions_T); // Get the global uv values for the face vertices std::array face_vertex_uv_positions; manifold.get_face_global_uv(face_index, face_vertex_uv_positions); // Get corner uv positions for the given face corners. - // NOTE: These may differ from the edge difference vectors computed from the global - // uv by a rotation per vertex due to the local layouts performed at each vertex. - // Since vertex gradients are defined in terms of these local vertex charts, we must - // use these directions when computing edge direction gradients from the vertex uv - // gradients. + // NOTE: These may differ from the edge difference vectors computed from the + // global uv by a rotation per vertex due to the local layouts performed at + // each vertex. Since vertex gradients are defined in terms of these local + // vertex charts, we must use these directions when computing edge direction + // gradients from the vertex uv gradients. std::array corner_to_corner_uv_positions; manifold.get_face_corner_charts(face_index, corner_to_corner_uv_positions); // Get edge orientations - // NOTE: The edge frame is oriented so that one basis vector points along the edge - // counterclockwise and the other points perpendicular into the interior of the - // triangle. If the given face is the bottom face in the edge chart, the sign of - // the midpoint gradient needs to be reversed. + // NOTE: The edge frame is oriented so that one basis vector points along + // the edge counterclockwise and the other points perpendicular into the + // interior of the triangle. If the given face is the bottom face in the + // edge chart, the sign of the midpoint gradient needs to be reversed. std::array reverse_edge_orientations; for (int i = 0; i < 3; ++i) { - EdgeManifoldChart const& chart = manifold.get_edge_chart(face_index, i); + EdgeManifoldChart const &chart = manifold.get_edge_chart(face_index, i); reverse_edge_orientations[i] = (chart.top_face_index != face_index); } @@ -458,31 +439,25 @@ compute_twelve_split_energy_quadratic( // Get global indices of the local vertex and edge DOFs std::array face_global_vertex_indices, face_global_edge_indices; - build_face_variable_vector( - global_vertex_indices, i, j, k, face_global_vertex_indices); + build_face_variable_vector(global_vertex_indices, i, j, k, + face_global_vertex_indices); face_global_edge_indices = global_edge_indices[face_index]; // Check if an edge is collapsing and make sure any collapsing edges have // local vertex indices 0 and 1 // WARNING: This is a somewhat fragile operation that must occur after all // of these arrays are build and before the local to global map is built - // and is not necessary in the current framework used in the paper but is for - // some deprecated experimental methods + // and is not necessary in the current framework used in the paper but is + // for some deprecated experimental methods bool is_cone_adjacent_face = false; for (int i = 0; i < 3; ++i) { if (is_cone[(i + 2) % 3]) { - shift_local_energy_quadratic_vertices(vertex_positions_T, - vertex_gradients_T, - edge_gradients_T, - initial_vertex_positions_T, - face_vertex_uv_positions, - corner_to_corner_uv_positions, - reverse_edge_orientations, - is_cone, - is_cone_adjacent, - face_global_vertex_indices, - face_global_edge_indices, - i); + shift_local_energy_quadratic_vertices( + vertex_positions_T, vertex_gradients_T, edge_gradients_T, + initial_vertex_positions_T, face_vertex_uv_positions, + corner_to_corner_uv_positions, reverse_edge_orientations, is_cone, + is_cone_adjacent, face_global_vertex_indices, + face_global_edge_indices, i); is_cone_adjacent_face = true; break; } @@ -491,6 +466,7 @@ compute_twelve_split_energy_quadratic( // Get normal for the face SpatialVector normal; normal.setZero(); + if (is_cone_adjacent_face) { normal = initial_face_normals.row(face_index); spdlog::trace("Weighting by normal {}", normal.transpose()); @@ -498,54 +474,35 @@ compute_twelve_split_energy_quadratic( // Get local to global map std::vector local_to_global_map; - generate_twelve_split_local_to_global_map(face_global_vertex_indices, - face_global_edge_indices, - num_variable_vertices, - local_to_global_map); + generate_twelve_split_local_to_global_map( + face_global_vertex_indices, face_global_edge_indices, + num_variable_vertices, local_to_global_map); // Compute local hessian data LocalHessianData local_hessian_data; generate_local_hessian_data( - face_vertex_uv_positions, - corner_to_corner_uv_positions, - reverse_edge_orientations, - is_cone, - is_cone_adjacent, - normal, - optimization_params, - local_hessian_data - ); + face_vertex_uv_positions, corner_to_corner_uv_positions, + reverse_edge_orientations, is_cone, is_cone_adjacent, normal, + optimization_params, local_hessian_data); // Compute local degree of freedom data LocalDOFData local_dof_data; - generate_local_dof_data( - initial_vertex_positions_T, - vertex_positions_T, - vertex_gradients_T, - edge_gradients_T, - local_dof_data - ); + generate_local_dof_data(initial_vertex_positions_T, vertex_positions_T, + vertex_gradients_T, edge_gradients_T, + local_dof_data); // Compute the local energy quadratic system for the face double local_energy; TwelveSplitGradient local_derivatives; TwelveSplitHessian local_hessian; compute_local_twelve_split_energy_quadratic( - local_hessian_data, - local_dof_data, - local_energy, - local_derivatives, - local_hessian); + local_hessian_data, local_dof_data, local_energy, local_derivatives, + local_hessian); // Update energy quadratic with the new face energy update_energy_quadratic( - local_energy, - local_derivatives, - local_hessian, - local_to_global_map, - energy, - derivatives, - hessian_entries); + local_energy, local_derivatives, local_hessian, local_to_global_map, + energy, derivatives, hessian_entries); } // Set hessian from the triplets @@ -553,11 +510,9 @@ compute_twelve_split_energy_quadratic( hessian.setFromTriplets(hessian_entries.begin(), hessian_entries.end()); } -void -convert_full_edge_gradients_to_reduced( - const std::vector>& edge_gradients, - std::vector>& reduced_edge_gradients) -{ +void convert_full_edge_gradients_to_reduced( + const std::vector> &edge_gradients, + std::vector> &reduced_edge_gradients) { int num_faces = edge_gradients.size(); reduced_edge_gradients.resize(num_faces); for (int i = 0; i < num_faces; ++i) { @@ -567,21 +522,19 @@ convert_full_edge_gradients_to_reduced( } } -void -convert_reduced_edge_gradients_to_full( - const std::vector>& reduced_edge_gradients, - const std::vector>& corner_data, - const AffineManifold& affine_manifold, - std::vector>& edge_gradients) -{ - Eigen::MatrixXi const& F = affine_manifold.get_faces(); +void convert_reduced_edge_gradients_to_full( + const std::vector> &reduced_edge_gradients, + const std::vector> &corner_data, + const AffineManifold &affine_manifold, + std::vector> &edge_gradients) { + Eigen::MatrixXi const &F = affine_manifold.get_faces(); int num_faces = reduced_edge_gradients.size(); // Compute the first gradient and copy the second for each edge edge_gradients.resize(num_faces); for (int i = 0; i < num_faces; ++i) { for (int j = 0; j < 3; ++j) { - EdgeManifoldChart const& chart = affine_manifold.get_edge_chart(i, j); + EdgeManifoldChart const &chart = affine_manifold.get_edge_chart(i, j); int f_top = chart.top_face_index; if (f_top != i) continue; // Only process top faces of edge charts to prevent redundancy @@ -590,8 +543,7 @@ convert_reduced_edge_gradients_to_full( SpatialVector midpoint; SpatialVector midpoint_edge_gradient; compute_edge_midpoint_with_gradient(corner_data[i][(j + 1) % 3], - corner_data[i][(j + 2) % 3], - midpoint, + corner_data[i][(j + 2) % 3], midpoint, midpoint_edge_gradient); // Copy the gradients @@ -611,24 +563,19 @@ convert_reduced_edge_gradients_to_full( } } -void -build_twelve_split_spline_energy_system( - const MatrixXr& initial_V, - const MatrixXr& initial_face_normals, - const AffineManifold& affine_manifold, - const OptimizationParameters& optimization_params, - double& energy, - VectorXr& derivatives, - Eigen::SparseMatrix& hessian, - Eigen::CholmodSupernodalLLT>& hessian_inverse) -{ +void build_twelve_split_spline_energy_system( + const MatrixXr &initial_V, const MatrixXr &initial_face_normals, + const AffineManifold &affine_manifold, + const OptimizationParameters &optimization_params, double &energy, + VectorXr &derivatives, Eigen::SparseMatrix &hessian, + Eigen::CholmodSupernodalLLT> &hessian_inverse) { int num_vertices = initial_V.rows(); int num_faces = affine_manifold.num_faces(); // Build halfedge std::vector> he_to_corner = - affine_manifold.get_he_to_corner(); - Halfedge const& halfedge = affine_manifold.get_halfedge(); + affine_manifold.get_he_to_corner(); + Halfedge const &halfedge = affine_manifold.get_halfedge(); int num_edges = halfedge.num_edges(); // Assume all vertices and edges are variable @@ -656,49 +603,36 @@ build_twelve_split_spline_energy_system( // Build vertex variable indices std::vector global_vertex_indices; - build_variable_vertex_indices_map( - num_vertices, variable_vertices, global_vertex_indices); + build_variable_vertex_indices_map(num_vertices, variable_vertices, + global_vertex_indices); // Build edge variable indices std::vector> global_edge_indices; - build_variable_edge_indices_map( - num_faces, variable_edges, halfedge, he_to_corner, global_edge_indices); + build_variable_edge_indices_map(num_faces, variable_edges, halfedge, + he_to_corner, global_edge_indices); // Build energy for the affine manifold - compute_twelve_split_energy_quadratic(vertex_positions, - vertex_gradients, - edge_gradients, - global_vertex_indices, - global_edge_indices, - initial_vertex_positions, - initial_face_normals, - affine_manifold, - optimization_params, - energy, - derivatives, - hessian, - num_variable_vertices, - num_variable_edges); + compute_twelve_split_energy_quadratic( + vertex_positions, vertex_gradients, edge_gradients, global_vertex_indices, + global_edge_indices, initial_vertex_positions, initial_face_normals, + affine_manifold, optimization_params, energy, derivatives, hessian, + num_variable_vertices, num_variable_edges); // Build the inverse hessian_inverse.compute(hessian); } -void -optimize_twelve_split_spline_surface( - const MatrixXr& initial_V, - const AffineManifold& affine_manifold, - const Halfedge& halfedge, - const std::vector>& he_to_corner, - const std::vector& variable_vertices, - const std::vector& variable_edges, - const Eigen::SparseMatrix& fit_matrix, - const Eigen::CholmodSupernodalLLT>& - hessian_inverse, - MatrixXr& optimized_V, - std::vector& optimized_vertex_gradients, - std::vector>& optimized_edge_gradients) -{ +void optimize_twelve_split_spline_surface( + const MatrixXr &initial_V, const AffineManifold &affine_manifold, + const Halfedge &halfedge, + const std::vector> &he_to_corner, + const std::vector &variable_vertices, + const std::vector &variable_edges, + const Eigen::SparseMatrix &fit_matrix, + const Eigen::CholmodSupernodalLLT> + &hessian_inverse, + MatrixXr &optimized_V, std::vector &optimized_vertex_gradients, + std::vector> &optimized_edge_gradients) { // Get variable counts int num_vertices = initial_V.rows(); int num_faces = affine_manifold.num_faces(); @@ -717,14 +651,9 @@ optimize_twelve_split_spline_surface( // Build variable values gradient as H VectorXr initial_variable_values; - generate_twelve_split_variable_value_vector(vertex_positions, - vertex_gradients, - edge_gradients, - variable_vertices, - variable_edges, - halfedge, - he_to_corner, - initial_variable_values); + generate_twelve_split_variable_value_vector( + vertex_positions, vertex_gradients, edge_gradients, variable_vertices, + variable_edges, halfedge, he_to_corner, initial_variable_values); spdlog::trace("Initial variable value vector:\n{}", initial_variable_values); // Solve hessian system to get optimized values @@ -735,15 +664,12 @@ optimize_twelve_split_spline_surface( std::vector optimized_vertex_positions = vertex_positions; optimized_vertex_gradients = vertex_gradients; optimized_edge_gradients = edge_gradients; - update_position_variables( - optimized_variable_values, variable_vertices, optimized_vertex_positions); - update_vertex_gradient_variables( - optimized_variable_values, variable_vertices, optimized_vertex_gradients); - update_edge_gradient_variables(optimized_variable_values, - variable_vertices, - variable_edges, - halfedge, - he_to_corner, + update_position_variables(optimized_variable_values, variable_vertices, + optimized_vertex_positions); + update_vertex_gradient_variables(optimized_variable_values, variable_vertices, + optimized_vertex_gradients); + update_edge_gradient_variables(optimized_variable_values, variable_vertices, + variable_edges, halfedge, he_to_corner, optimized_edge_gradients); // Copy variable values to constants @@ -753,22 +679,19 @@ optimize_twelve_split_spline_surface( } } -void -generate_optimized_twelve_split_position_data( - const Eigen::MatrixXd& V, - const AffineManifold& affine_manifold, - const Eigen::SparseMatrix& fit_matrix, - const Eigen::CholmodSupernodalLLT>& - hessian_inverse, - std::vector>& corner_data, - std::vector>& midpoint_data) -{ +void generate_optimized_twelve_split_position_data( + const Eigen::MatrixXd &V, const AffineManifold &affine_manifold, + const Eigen::SparseMatrix &fit_matrix, + const Eigen::CholmodSupernodalLLT> + &hessian_inverse, + std::vector> &corner_data, + std::vector> &midpoint_data) { int num_vertices = V.rows(); // Build halfedge - std::vector> const& he_to_corner = - affine_manifold.get_he_to_corner(); - Halfedge const& halfedge = affine_manifold.get_halfedge(); + std::vector> const &he_to_corner = + affine_manifold.get_he_to_corner(); + Halfedge const &halfedge = affine_manifold.get_halfedge(); int num_edges = halfedge.num_edges(); // Assume all vertices and edges are variable @@ -782,39 +705,29 @@ generate_optimized_twelve_split_position_data( MatrixXr optimized_V; std::vector optimized_vertex_gradients; std::vector> optimized_reduced_edge_gradients; - optimize_twelve_split_spline_surface(V, - affine_manifold, - halfedge, - he_to_corner, - variable_vertices, - variable_edges, - fit_matrix, - hessian_inverse, - optimized_V, - optimized_vertex_gradients, - optimized_reduced_edge_gradients); + optimize_twelve_split_spline_surface( + V, affine_manifold, halfedge, he_to_corner, variable_vertices, + variable_edges, fit_matrix, hessian_inverse, optimized_V, + optimized_vertex_gradients, optimized_reduced_edge_gradients); // Build corner position data from the optimized gradients - generate_affine_manifold_corner_data( - optimized_V, affine_manifold, optimized_vertex_gradients, corner_data); + generate_affine_manifold_corner_data(optimized_V, affine_manifold, + optimized_vertex_gradients, corner_data); // Build the full edge gradients with first gradient determined by the corner // position data std::vector> optimized_edge_gradients; convert_reduced_edge_gradients_to_full(optimized_reduced_edge_gradients, - corner_data, - affine_manifold, + corner_data, affine_manifold, optimized_edge_gradients); // Build midpoint position data from the optimized gradients generate_affine_manifold_midpoint_data( - affine_manifold, optimized_edge_gradients, midpoint_data); + affine_manifold, optimized_edge_gradients, midpoint_data); } -void -generate_zero_vertex_gradients(int num_vertices, - std::vector& gradients) -{ +void generate_zero_vertex_gradients(int num_vertices, + std::vector &gradients) { // Set the zero gradient for each vertex gradients.resize(num_vertices); for (int i = 0; i < num_vertices; ++i) { @@ -822,11 +735,8 @@ generate_zero_vertex_gradients(int num_vertices, } } -void -generate_zero_edge_gradients( - int num_faces, - std::vector>& edge_gradients) -{ +void generate_zero_edge_gradients( + int num_faces, std::vector> &edge_gradients) { // Set the zero gradient for each vertex edge_gradients.resize(num_faces); for (int i = 0; i < num_faces; ++i) { diff --git a/src/quadratic_spline_surface/optimize_spline_surface.h b/src/quadratic_spline_surface/optimize_spline_surface.h index 1d0e6d7..487f0f4 100644 --- a/src/quadratic_spline_surface/optimize_spline_surface.h +++ b/src/quadratic_spline_surface/optimize_spline_surface.h @@ -16,31 +16,30 @@ /// /// Methods to optimize a spline surface -struct OptimizationParameters -{ +struct OptimizationParameters { // Main optimization weight options double position_difference_factor = 1.0; double parametrized_quadratic_surface_mapping_factor = 1.0; // Weights for cone positions, normals, and gradients double cone_position_difference_factor = - 1.0; // fitting weight for cone vertices + 1.0; // fitting weight for cone vertices double cone_vertex_gradient_difference_factor = - 1e6; // fitting weight for cone vertex gradients + 1e6; // fitting weight for cone vertex gradients double cone_adjacent_position_difference_factor = - 1.0; // fitting weight for vertices collapsed to a cone + 1.0; // fitting weight for vertices collapsed to a cone double cone_adjacent_vertex_gradient_difference_factor = - 0.0; // fitting weight for vertex gradients collapsing to a cone + 0.0; // fitting weight for vertex gradients collapsing to a cone double cone_adjacent_edge_gradient_difference_factor = - 0.0; // fitting weight for vertex edge gradients collapsing to a cone + 0.0; // fitting weight for vertex edge gradients collapsing to a cone double cone_normal_orthogonality_factor = - 0.0; // wight for encouraging orthogonality with a normal at a cone + 1.0; // wight for encouraging orthogonality with a normal at a cone // TODO bool compute_final_energy = - false; // Perform one more energy computation for the final value + false; // Perform one more energy computation for the final value bool flatten_cones = false; // Perform final optimization with fixed vertices // and flatten cone constraints int hessian_builder = 1; // 1 for assemble, 0 for autodiff, otherwise assemble @@ -57,16 +56,12 @@ struct OptimizationParameters /// @param[out] derivatives: energy gradient (i.e., linear term) /// @param[out] hessian: energy Hessian (i.e., quadratic term) /// @param[out] hessian_inverse: solver for inverting the Hessian -void -build_twelve_split_spline_energy_system( - const MatrixXr& initial_V, - const MatrixXr& initial_face_normals, - const AffineManifold& affine_manifold, - const OptimizationParameters& optimization_params, - double& energy, - VectorXr& derivatives, - Eigen::SparseMatrix& hessian, - Eigen::CholmodSupernodalLLT>& hessian_inverse); +void build_twelve_split_spline_energy_system( + const MatrixXr &initial_V, const MatrixXr &initial_face_normals, + const AffineManifold &affine_manifold, + const OptimizationParameters &optimization_params, double &energy, + VectorXr &derivatives, Eigen::SparseMatrix &hessian, + Eigen::CholmodSupernodalLLT> &hessian_inverse); /// Compute the optimal per triangle position data for given vertex positions. /// @@ -76,54 +71,51 @@ build_twelve_split_spline_energy_system( /// @param[in] hessian_inverse: solver for inverting the energy Hessian /// @param[out] corner_data: quadratic vertex position and derivative data /// @param[out] midpoint_data: quadratic edge midpoint derivative data -void -generate_optimized_twelve_split_position_data( - const Eigen::MatrixXd& V, - const AffineManifold& affine_manifold, - const Eigen::SparseMatrix& fit_matrix, - const Eigen::CholmodSupernodalLLT>& - hessian_inverse, - std::vector>& corner_data, - std::vector>& midpoint_data); +void generate_optimized_twelve_split_position_data( + const Eigen::MatrixXd &V, const AffineManifold &affine_manifold, + const Eigen::SparseMatrix &fit_matrix, + const Eigen::CholmodSupernodalLLT> + &hessian_inverse, + std::vector> &corner_data, + std::vector> &midpoint_data); /// Generate zero value gradients for a given number of vertices. /// /// @param[in] num_vertices: number of vertices |V| /// @param[out] gradients: |V| trivial vertex gradient matrices -void -generate_zero_vertex_gradients(int num_vertices, - std::vector& gradients); +void generate_zero_vertex_gradients(int num_vertices, + std::vector &gradients); /// Generate zero value gradients for a given number of halfedges. /// /// @param[in] num_faces: number of faces |F| /// @param[out] gradients: 3|F| trivial edge gradient matrices -void -generate_zero_edge_gradients( - int num_faces, - std::vector>& edge_gradients); +void generate_zero_edge_gradients( + int num_faces, std::vector> &edge_gradients); -/// Given edge and opposite corner direction gradients at triangle edge midpoints, -/// extract just the opposite corner direction gradient +/// Given edge and opposite corner direction gradients at triangle edge +/// midpoints, extract just the opposite corner direction gradient /// -/// @param[in] edge_gradients: edge and corner directed gradients per edge midpoints -/// @param[out] reduced_edge_gradients: opposite corner directed gradients per edge midpoints -void -convert_full_edge_gradients_to_reduced( - const std::vector>& edge_gradients, - std::vector>& reduced_edge_gradients); +/// @param[in] edge_gradients: edge and corner directed gradients per edge +/// midpoints +/// @param[out] reduced_edge_gradients: opposite corner directed gradients per +/// edge midpoints +void convert_full_edge_gradients_to_reduced( + const std::vector> &edge_gradients, + std::vector> &reduced_edge_gradients); -/// Given edge direction gradients at triangle edge midpoints, append the gradients in the -/// direction of the opposite triangle corners, which are determined by gradients and -/// position data at the corners. +/// Given edge direction gradients at triangle edge midpoints, append the +/// gradients in the direction of the opposite triangle corners, which are +/// determined by gradients and position data at the corners. /// -/// @param[in] reduced_edge_gradients: opposite corner directed gradients per edge midpoints +/// @param[in] reduced_edge_gradients: opposite corner directed gradients per +/// edge midpoints /// @param[in] corner_data: quadratic vertex position and derivative data /// @param[in] affine_manifold: mesh topology and affine manifold structure -/// @param[out] edge_gradients: edge and corner directed gradients per edge midpoints -void -convert_reduced_edge_gradients_to_full( - const std::vector>& reduced_edge_gradients, - const std::vector>& corner_data, - const AffineManifold& affine_manifold, - std::vector>& edge_gradients); +/// @param[out] edge_gradients: edge and corner directed gradients per edge +/// midpoints +void convert_reduced_edge_gradients_to_full( + const std::vector> &reduced_edge_gradients, + const std::vector> &corner_data, + const AffineManifold &affine_manifold, + std::vector> &edge_gradients); diff --git a/src/quadratic_spline_surface/quadratic_spline_surface.h b/src/quadratic_spline_surface/quadratic_spline_surface.h index 2b27c53..002110b 100644 --- a/src/quadratic_spline_surface/quadratic_spline_surface.h +++ b/src/quadratic_spline_surface/quadratic_spline_surface.h @@ -12,8 +12,7 @@ #include /// Parameters for the discretization of a quadratic spline -struct SurfaceDiscretizationParameters -{ +struct SurfaceDiscretizationParameters { /// Number of subdivisions per triangle of the domain int num_subdivisions = 2; @@ -31,8 +30,7 @@ struct SurfaceDiscretizationParameters /// - visualization /// - (basic) rendering /// - (de)serialization -class QuadraticSplineSurface -{ +class QuadraticSplineSurface { public: // Index type typedef size_t PatchIndex; @@ -43,7 +41,7 @@ class QuadraticSplineSurface /// Constructor from patches /// /// @param[in] patches: quadratic surface patches - QuadraticSplineSurface(std::vector& patches); + QuadraticSplineSurface(std::vector &patches); /// Get the number of patches in the surface /// @@ -53,8 +51,7 @@ class QuadraticSplineSurface /// Get a reference to a spline patch /// /// @return spline patch - QuadraticSplineSurfacePatch const& get_patch(PatchIndex patch_index) const - { + QuadraticSplineSurfacePatch const &get_patch(PatchIndex patch_index) const { return m_patches[patch_index]; } @@ -63,10 +60,9 @@ class QuadraticSplineSurface /// @param[in] patch_index: index of the patch to evaluate /// @param[in] domain_point: point in the patch domain to evaluate /// @param[out] surface_point: output point on the surface - void evaluate_patch(const PatchIndex& patch_index, - const PlanarPoint& domain_point, - SpatialVector& surface_point) const - { + void evaluate_patch(const PatchIndex &patch_index, + const PlanarPoint &domain_point, + SpatialVector &surface_point) const { get_patch(patch_index).evaluate(domain_point, surface_point); } @@ -75,10 +71,9 @@ class QuadraticSplineSurface /// @param[in] patch_index: index of the patch to evaluate /// @param[in] domain_point: point in the patch domain to evaluate /// @param[out] surface_point: output point on the surface - void evaluate_patch_normal(const PatchIndex& patch_index, - const PlanarPoint& domain_point, - SpatialVector& surface_normal) const - { + void evaluate_patch_normal(const PatchIndex &patch_index, + const PlanarPoint &domain_point, + SpatialVector &surface_normal) const { get_patch(patch_index).evaluate_normal(domain_point, surface_normal); } @@ -94,8 +89,8 @@ class QuadraticSplineSurface /// /// @param[in] patch_indices: indices of the patches to keep /// @return subsurface with the given patches - QuadraticSplineSurface subsurface( - const std::vector& patch_indices) const; + QuadraticSplineSurface + subsurface(const std::vector &patch_indices) const; /// Triangulate a given patch /// @@ -104,11 +99,9 @@ class QuadraticSplineSurface /// @param[out] V: vertices of the triangulation /// @param[out] F: faces of the triangulation /// @param[out] N: vertex normals - void triangulate_patch(const PatchIndex& patch_index, - int num_refinements, - Eigen::MatrixXd& V, - Eigen::MatrixXi& F, - Eigen::MatrixXd& N) const; + void triangulate_patch(const PatchIndex &patch_index, int num_refinements, + Eigen::MatrixXd &V, Eigen::MatrixXi &F, + Eigen::MatrixXd &N) const; /// Triangulate the surface. /// @@ -116,10 +109,9 @@ class QuadraticSplineSurface /// @param[out] V: vertices of the triangulation /// @param[out] F: faces of the triangulation /// @param[out] N: vertex normals - void discretize(const SurfaceDiscretizationParameters& surface_disc_params, - Eigen::MatrixXd& V, - Eigen::MatrixXi& F, - Eigen::MatrixXd& N) const; + void discretize(const SurfaceDiscretizationParameters &surface_disc_params, + Eigen::MatrixXd &V, Eigen::MatrixXi &F, + Eigen::MatrixXd &N) const; /// Triangulate the surface. /// @@ -131,20 +123,20 @@ class QuadraticSplineSurface Eigen::MatrixXi, // F Eigen::MatrixXd // N > - discretize(const SurfaceDiscretizationParameters& surface_disc_params) const; + discretize(const SurfaceDiscretizationParameters &surface_disc_params) const; /// Discretize all patch boundaries as polylines /// /// @param[out] points: list of polyline points /// @param[out] polylines: list of lists of polyline edges - void discretize_patch_boundaries( - std::vector& points, - std::vector>& polylines) const; + void + discretize_patch_boundaries(std::vector &points, + std::vector> &polylines) const; /// Save the triangulated surface as an obj /// /// @param[in] filename: filepath to save the obj - void save_obj(const std::string& filename) const; + void save_obj(const std::string &filename) const; /// Add the surface to the viewer /// @@ -166,7 +158,7 @@ class QuadraticSplineSurface /// @param[in] camera_position: camera position for the screenshot /// @param[in] camera_target: camera target for the screenshot /// @param[in] use_orthographic: use orthographic perspective if true - void screenshot(const std::string& filename, + void screenshot(const std::string &filename, SpatialVector camera_position = SpatialVector(0, 0, 2), SpatialVector camera_target = SpatialVector(0, 0, 0), bool use_orthographic = false) const; @@ -174,22 +166,22 @@ class QuadraticSplineSurface /// Serialize the surface /// /// @param[in] out: output stream for the surface - void serialize(std::ostream& out) const; + void serialize(std::ostream &out) const; /// Deserialize a surface /// /// @param[in] in: input stream for the surface - void deserialize(std::istream& in); + void deserialize(std::istream &in); /// Write the surface serialization to file /// /// @param[in] filename: file path for the serialized surface - void write_spline(const std::string& filename) const; + void write_spline(const std::string &filename) const; /// Read a surface serialization from file /// /// @param[in] filename: file path for the serialized surface - void read_spline(const std::string& filename); + void read_spline(const std::string &filename); /// Compute hash tables for the surface void compute_patch_hash_tables(); @@ -198,7 +190,7 @@ class QuadraticSplineSurface /// /// @param[in] point: point in the plane /// @return pair - std::pair compute_hash_indices(const PlanarPoint& point) const; + std::pair compute_hash_indices(const PlanarPoint &point) const; /// Hash table data std::vector hash_table[HASH_TABLE_SIZE][HASH_TABLE_SIZE]; @@ -206,7 +198,7 @@ class QuadraticSplineSurface /// Hash table parameters double patches_bbox_x_min, patches_bbox_x_max, patches_bbox_y_min, - patches_bbox_y_max; + patches_bbox_y_max; double hash_x_interval; double hash_y_interval; @@ -216,4 +208,13 @@ class QuadraticSplineSurface void compute_patches_bbox(); std::vector m_patches; + + //-------------------- code for c1 meshing --------------------// +public: + void write_cubic_nodes_to_msh(std::string filename); + void write_cubic_nodes_to_obj(std::string filename); + + void write_corner_patch_points_to_obj(std::string filename); + + //-------------------------------------------------------------// }; diff --git a/src/quadratic_spline_surface/quadratic_spline_surface_msh_writer.cpp b/src/quadratic_spline_surface/quadratic_spline_surface_msh_writer.cpp new file mode 100644 index 0000000..e917ac3 --- /dev/null +++ b/src/quadratic_spline_surface/quadratic_spline_surface_msh_writer.cpp @@ -0,0 +1,117 @@ +#include "quadratic_spline_surface.h" + +void QuadraticSplineSurface::write_cubic_nodes_to_msh(std::string filename) { + std::ofstream file(filename); + + /* + $MeshFormat + 4.1 0 8 MSH4.1, ASCII + $EndMeshFormat + */ + + file << "$MeshFormat\n" + << "4.1 0 8\n" + << "$EndMeshFormat\n"; + + // msh 10 node 3rd-order triangle nodes + const std::array normalized_vs = { + {PlanarPoint(0, 0), PlanarPoint(1., 0), PlanarPoint(0, 1.), + PlanarPoint(1. / 3., 0), PlanarPoint(2. / 3., 0), + PlanarPoint(2. / 3., 1. / 3.), PlanarPoint(1. / 3., 2. / 3.), + PlanarPoint(0, 2. / 3.), PlanarPoint(0, 1. / 3.), + PlanarPoint(1. / 3., 1. / 3.)}}; + + file << "$Nodes\n"; + + const size_t node_size = m_patches.size() * 10; + file << "1 " << node_size << " 1 " << node_size << "\n"; + file << "2 1 0 " << node_size << "\n"; + + for (size_t i = 1; i <= node_size; ++i) { + file << i << "\n"; + } + + for (const auto &patch : m_patches) { + for (const auto &normalized_v : normalized_vs) { + PlanarPoint nv = normalized_v; + auto domain_v = patch.denormalize_domain_point(nv); + SpatialVector surface_point; + patch.evaluate(domain_v, surface_point); + file << surface_point(0, 0) << " " << surface_point(0, 1) << " " + << surface_point(0, 2) << "\n"; + } + } + + file << "$EndNodes\n"; + + // write elements + const size_t element_size = m_patches.size(); + + file << "$Elements\n"; + file << "1 " << element_size << " 1 " << element_size << "\n"; + file << "2 1 21 " << element_size << "\n"; + for (size_t i = 0; i < element_size; ++i) { + file << i + 1 << " " << i * 10 + 1 << " " << i * 10 + 2 << " " << i * 10 + 3 + << " " << i * 10 + 4 << " " << i * 10 + 5 << " " << i * 10 + 6 << " " + << i * 10 + 7 << " " << i * 10 + 8 << " " << i * 10 + 9 << " " + << i * 10 + 10 << "\n"; + } + + file << "$EndElements\n"; +} + +void QuadraticSplineSurface::write_cubic_nodes_to_obj(std::string filename) { + std::ofstream file(filename); + + // msh 10 node 3rd-order triangle nodes + const std::array normalized_vs = { + {PlanarPoint(0, 0), PlanarPoint(1., 0), PlanarPoint(0, 1.), + PlanarPoint(1. / 3., 0), PlanarPoint(2. / 3., 0), + PlanarPoint(2. / 3., 1. / 3.), PlanarPoint(1. / 3., 2. / 3.), + PlanarPoint(0, 2. / 3.), PlanarPoint(0, 1. / 3.), + PlanarPoint(1. / 3., 1. / 3.)}}; + + for (const auto &patch : m_patches) { + for (const auto &normalized_v : normalized_vs) { + PlanarPoint nv = normalized_v; + auto domain_v = patch.denormalize_domain_point(nv); + SpatialVector surface_point; + patch.evaluate(domain_v, surface_point); + file << "v " << surface_point(0, 0) << " " << surface_point(0, 1) << " " + << surface_point(0, 2) << "\n"; + } + } + + // write elements + const size_t element_size = m_patches.size(); + + for (size_t i = 0; i < element_size; ++i) { + file << "f " << " " << i * 10 + 1 << " " << i * 10 + 2 << " " << i * 10 + 3 + << "\n"; + } +} + +void QuadraticSplineSurface::write_corner_patch_points_to_obj( + std::string filename) { + std::ofstream file(filename); + + const std::array normalized_vs = { + {PlanarPoint(0, 0), PlanarPoint(1, 0), PlanarPoint(0, 1)}}; + + for (const auto &patch : m_patches) { + for (const auto &normalized_v : normalized_vs) { + PlanarPoint nv = normalized_v; + auto domain_v = patch.denormalize_domain_point(nv); + SpatialVector surface_point; + patch.evaluate(domain_v, surface_point); + file << "v " << surface_point(0, 0) << " " << surface_point(0, 1) << " " + << surface_point(0, 2) << std::endl; + } + } + for (size_t i = 0; i < m_patches.size(); ++i) { + file << "f " << i * 3 + 1 << " " << i * 3 + 2 << " " << i * 3 + 3 + << std::endl; + } + + file.close(); +} \ No newline at end of file