Calabi Yau and Hanson's Manifold in Blender (with add-ons)
OverView
I wrote about creating Calabi Yau Manifold with python in my previous post1 . After that, I found an article2 about another version of this manifold having torus-knot-like shape.
In my previous entry, I made the original manifold with python in Jupyter Notebook environment, but in this entry, I'll make this manifold using 3d modeling software Blender with its add-ons (like sverchok3 and animation nodes4), which have node editors including scripting nodes.
Difference from previous Calabi Yau Manifold
Exponents of z1 and z2 may not always equal
When I wrote my previous entry, In the equations of Calabi Yau Manifold, exponents of z1 and z2 are equal ( both has a same value, n). But for this manifold, exponents of z1 (n1) and z2 (n2) are not always equal. As a result, the range of k1 and k2 may also be different.
Using sinh/cosh instead of sin/cos for z1 and z2 parameter
Compare with the previous one, the Calabi Yau and Hanson's Manifold uses sinh and cosh instead of sin and cos for z1 and z2.
Making the manifold with Blender add-ons
Considering these differences, I'll implement the manifold with Blender add-ons. Both add-ons (sverchok and animation nodes) have script nodes, so I'll write some scripts and use them in each environments.
Making the manifold with sverchok
To make the surface, I wrote a script and load it with sverchok's 'Script Node Lite'. The code is here:
""" in n1_dimension s d=2 n=2 in n2_dimension s d=2 n=2 in a_radian s d=0.4 n=2 in x_dim s d=20 n=2 in y_dim s d=20 n=2 out verts v out edges s out faces s """ import cmath import numpy as np from math import sin, cos, sinh, cosh, pi from mathutils import Vector def calcZ1(x, y, k, n): return cmath.exp(1j*(2*cmath.pi*k/n)) * (cmath.cosh(x+y*1j))**(2/n) def calcZ2(x, y, k, n): return cmath.exp(1j*(2*cmath.pi*k/n)) * (1 / 1j) * (cmath.sinh(x+y*1j))**(2/n) def calcZ1Real(x, y, k, n): return (calcZ1(x, y, k, n)).real def calcZ2Real(x, y, k, n): return (calcZ2(x, y, k, n)).real def calcZ(x, y, k1_, k2_, n1_, n2_, a_): z1 = calcZ1(x, y, k1, n1_) z2 = calcZ2(x, y, k2, n2_) return z1.imag * cos(a_) + z2.imag*sin(a_) # x = np.linspace(0, pi/2, x_dim) x = np.linspace(-1, 1, x_dim) y = np.linspace(0, pi/2, y_dim) x, y = np.meshgrid(x, y) verts = [[]] edges = [[]] edge_set = [] faces = [[]] face_set = [] n1 = n1_dimension n2 = n2_dimension for i in range(n1*n2): edge_set.append(set()) face_set.append(set()) count = 0 for k1 in range(n1): for k2 in range(n2): # calc X, Y, Z values X = np.frompyfunc(calcZ1Real, 4, 1)(x, y, k1, n1).astype('float32') Y = np.frompyfunc(calcZ2Real, 4, 1)(x, y, k2, n2).astype('float32') Z = np.frompyfunc(calcZ, 7, 1)(x, y, k1, k2, n1, n2, a_radian).astype('float32') X_ = X.flatten() Y_ = Y.flatten() Z_ = Z.flatten() v = [] for x1, y1, z1 in zip(X_, Y_, Z_): v.append(((float(x1), float(y1), float(z1)))) verts[0].extend(v) for i in range(x_dim * y_dim): y_index = i / y_dim x_index = i % y_dim j = i + count * x_dim * y_dim if (y_index < y_dim - 1) and (x_index < x_dim - 1): edge_set[count].add(tuple(sorted([j, j+y_dim]))) edge_set[count].add(tuple(sorted([j+y_dim, j+y_dim+1]))) edge_set[count].add(tuple(sorted([j+y_dim+1, j+1]))) edge_set[count].add(tuple(sorted([j+1, j]))) face_set[count].add(tuple(([j, j+y_dim, j+y_dim+1, j+1]))) count += 1 for i in range(n1*n2): edges[0].extend(list(edge_set[i])) faces[0].extend(list(face_set[i]))
This code also has been uploaded in my gist: https://gist.github.com/asahidari/16da5b0d35f0197dba2a9e13aa1af29a
Here is the output of this script.
Making the manifold with animation nodes
Animation nodes also have 'Script Node'. Some data types are different from sverchok, but the code is very similar.
""" in n1_dimension s d=2 n=2 in n2_dimension s d=2 n=2 in a_radian s d=0.4 n=2 in x_dim s d=20 n=2 in y_dim s d=20 n=2 out verts v out edges s out faces s """ import cmath import numpy as np from math import sin, cos, sinh, cosh, pi from mathutils import Vector def calcZ1(x, y, k, n): return cmath.exp(1j*(2*cmath.pi*k/n)) * (cmath.cosh(x+y*1j))**(2/n) def calcZ2(x, y, k, n): return cmath.exp(1j*(2*cmath.pi*k/n)) * (1 / 1j) * (cmath.sinh(x+y*1j))**(2/n) def calcZ1Real(x, y, k, n): return (calcZ1(x, y, k, n)).real def calcZ2Real(x, y, k, n): return (calcZ2(x, y, k, n)).real def calcZ(x, y, k1_, k2_, n1_, n2_, a_): z1 = calcZ1(x, y, k1, n1_) z2 = calcZ2(x, y, k2, n2_) return z1.imag * cos(a_) + z2.imag*sin(a_) # x = np.linspace(0, pi/2, x_dim) x = np.linspace(-1, 1, x_dim) y = np.linspace(0, pi/2, y_dim) x, y = np.meshgrid(x, y) verts = [[]] edges = [[]] edge_set = [] faces = [[]] face_set = [] n1 = n1_dimension n2 = n2_dimension for i in range(n1*n2): edge_set.append(set()) face_set.append(set()) count = 0 for k1 in range(n1): for k2 in range(n2): # calc X, Y, Z values X = np.frompyfunc(calcZ1Real, 4, 1)(x, y, k1, n1).astype('float32') Y = np.frompyfunc(calcZ2Real, 4, 1)(x, y, k2, n2).astype('float32') Z = np.frompyfunc(calcZ, 7, 1)(x, y, k1, k2, n1, n2, a_radian).astype('float32') X_ = X.flatten() Y_ = Y.flatten() Z_ = Z.flatten() v = [] for x1, y1, z1 in zip(X_, Y_, Z_): v.append(((float(x1), float(y1), float(z1)))) verts[0].extend(v) for i in range(x_dim * y_dim): y_index = i / y_dim x_index = i % y_dim j = i + count * x_dim * y_dim if (y_index < y_dim - 1) and (x_index < x_dim - 1): edge_set[count].add(tuple(sorted([j, j+y_dim]))) edge_set[count].add(tuple(sorted([j+y_dim, j+y_dim+1]))) edge_set[count].add(tuple(sorted([j+y_dim+1, j+1]))) edge_set[count].add(tuple(sorted([j+1, j]))) face_set[count].add(tuple(([j, j+y_dim, j+y_dim+1, j+1]))) count += 1 for i in range(n1*n2): edges[0].extend(list(edge_set[i])) faces[0].extend(list(face_set[i]))
This code is also uploaded to my gist: https://gist.github.com/asahidari/48a09b76000dca35f89eb8613f158169
Following image is how to connect nodes.
The output image is here.
Environments
- Blender 2.8.3
- Sverchok add-on 0.6.0.0
- Animation nodes add-on 2.7.1