Skip to content

Instantly share code, notes, and snippets.

@wschutzer
Last active October 1, 2024 15:37
Show Gist options
  • Save wschutzer/b26d5f979d0ee5453c4ea45ef94fe6c9 to your computer and use it in GitHub Desktop.
Save wschutzer/b26d5f979d0ee5453c4ea45ef94fe6c9 to your computer and use it in GitHub Desktop.
Mobius tunnel
// The tunnel is a mobius ring with digits seen from the inside.
//
// Mathematics and Processing code by Waldeck Schützer (@infinitymathart)
// Motion blur template by @davidbeesandbombs, explanation/article: https://bleuje.com/tutorial6/
// modified by @infinitymathart to add the chromatic aberration effect.
// Idea and concept for flipping digits on a surface by @etinjcb
// First million digits of pi from: https://pi2e.ch/blog/2017/03/10/pi-digits-download/ file: pi_dec_1m.txt
//
//
import peasy.*;
import processing.core.PMatrix3D;
PeasyCam cam;
boolean recording = true;
int frame_size = recording ? 2160 : 800;
float fac = frame_size/800.0;
int numSegments = 200; // Number of segments along the Möbius ring (higher means smoother)
float ringRadius = 200*fac; // Radius of the Möbius ring
float ringWidth = 180*fac; // Width of the rectangular cross-section
float ringThickness = 180*fac; // Thickness of the rectangular cross-section
float maxOffset = 5; // Maximum amount of chromatic aberration at the edges
int n_chars = 35;
float lm = -0.94;
float csp = 1.96/n_chars;
float gscl = 0.24/fac; // Proportional font scaling
int FPS = 60;
int samplesPerFrame = 7;
int numFrames = 90*FPS;
float shutterAngle = 2.1;
float bht = 1.4;
color c_face1 = color(255);
color c_face1_flip = color(200);
color c_face2 = color(255);
color c_face2_flip = color(200);
PFont courier;
int[][] result;
float t, c;
int rows = 10;
int cols = 10;
BufferedReader piReader;
String piFilePath;
thing[] things_t;
thing[] things_b;
thing[] things_l;
thing[] things_r;
// -----------------------------------------------------------------------------------------
// Modified @davebeesandbombs motion blur template to add lens chromatic aberration
float ease(float p) {
return 3*p*p - 2*p*p*p;
}
float ease(float p, float g) {
if (p < 0.5)
return 0.5 * pow(2*p, g);
else
return 1 - 0.5 * pow(2*(1 - p), g);
}
float ease_wide(float p, float g) // p in range -1 to 1 instead of 0-1
{
return 2*ease((p+1)/2,g)-1;
}
void push() {
pushMatrix();
pushStyle();
}
void pop() {
popStyle();
popMatrix();
}
void draw() {
if (!recording) {
t = 1 - mouseX*1.0/width;
c = mouseY*1.0/height;
// if (mousePressed)
// println(c);
draw_();
}
else
{
for (int i=0; i<width*height; i++)
for (int a=0; a<3; a++)
result[i][a] = 0;
c = 0;
for (int sa=0; sa<samplesPerFrame; sa++)
{
t = 1-map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
push();
draw_();
loadPixels();
for (int i=0; i<pixels.length; i++)
{
result[i][0] += pixels[i] >> 16 & 0xff;
result[i][1] += pixels[i] >> 8 & 0xff;
result[i][2] += pixels[i] & 0xff;
}
pop();
}
loadPixels();
for (int i=0; i<pixels.length; i++)
{
// Lens chromatic aberration effect: offsets the pixel indices according to the
// normalized distance from the center of the image.
int x = i % width;
int y = i / width;
// Compute distance from center (normalized between 0 and 1)
float dx = (x - width / 2.0) / (width / 2.0);
float dy = (y - height / 2.0) / (height / 2.0);
float dist = sqrt(dx * dx + dy * dy); // Distance from center (0 to 1)
// Offset based on distance, stronger near the edges
int rx_ofs = (int)(maxOffset * dist);
int ry_ofs = -(int)(maxOffset * dist);
int gx_ofs = -(int)(maxOffset * dist);
int gy_ofs = (int)(maxOffset * dist);
int bx_ofs = (int)(maxOffset * dist / 2);
int by_ofs = -(int)(maxOffset * dist / 2);
// Calculate the offset index for each color channel
int ri = constrain(i + rx_ofs + ry_ofs * width, 0, pixels.length - 1);
int gi = constrain(i + gx_ofs + gy_ofs * width, 0, pixels.length - 1);
int bi = constrain(i + bx_ofs + by_ofs * width, 0, pixels.length - 1);
// Pixel averaging implementing the motion blur. Notice the pixel indices
// computed according to the chromatic aberration effect.
int r = int(constrain(1.0*result[ri][0]/samplesPerFrame*bht,0,255));
int g = int(constrain(1.0*result[bi][1]/samplesPerFrame*bht,0,255));
int b = int(constrain(1.0*result[gi][2]/samplesPerFrame*bht,0,255));
pixels[i] = 0xff << 24 |
r << 16 |
g << 8 |
b;
}
updatePixels();
saveFrame("/tmp/t/frame_####.png");
println(frameCount,"/",numFrames);
if (frameCount==numFrames)
exit();
}
}
char readNextChar() {
try {
// Read the next character from the file
int nextChar = piReader.read();
if (nextChar == -1) {
// End of file reached, return null character
return '\0';
} else {
return (char) nextChar;
}
} catch (IOException e) {
e.printStackTrace();
return '\0';
}
}
void settings()
{
size(frame_size,frame_size,P3D);
pixelDensity(1);
smooth(8);
}
void setup()
{
randomSeed(1337);
result = new int[width*height][3];
// Tunnel view 1
//cam = new PeasyCam(this, 181.10555 *fac, 27.147495 *fac, 1.1629903 *fac, 79.32707977294922 *fac);
//cam.setRotations( -1.4146475 , -0.10396168 , 0.03245295 );
// Tunnel view 2 - viral on instagram
// cam = new PeasyCam(this, 219.31699 *fac, 30.588377 *fac, 6.93263 *fac, 79.32707977294922 *fac);
// cam.setRotations( -1.3788422 , 0.55727357 , -0.027554117 );
// Tunnel inner wall perspective - cool
cam = new PeasyCam(this, 219.31699 *fac, 30.588377 *fac, 6.93263 *fac, 129.02213214848877 *fac);
cam.setRotations( -0.66477895 , 1.3625605 , -0.8804048 );
if (recording)
cam.setActive(false);
courier = createFont("Courier New",24*fac,true);
piFilePath = dataPath("") + "/pi_dec_1m.txt";
piReader = createReader(piFilePath);
// Things are random digits to be drawn on each of the 4 faces of the surface
// (actually there are only two faces)
things_t = new thing[n_chars*numSegments];
things_b = new thing[n_chars*numSegments];
things_l = new thing[n_chars*numSegments];
things_r = new thing[n_chars*numSegments];
// The digits of pi fill each individual face, begining with the top
for(int j=0; j<numSegments;j++)
{
int k = n_chars*j;
for(int i=0; i<n_chars; i++)
things_t[k+i] = new thing(lm+i*csp, 0, readNextChar());
}
for(int j=0; j<numSegments;j++)
{
int k = n_chars*j;
for(int i=0; i<n_chars; i++)
things_b[k+i] = new thing(lm+i*csp, 0, readNextChar());
}
for(int j=0; j<numSegments;j++)
{
int k = n_chars*j;
for(int i=0; i<n_chars; i++)
things_l[k+i] = new thing(lm+i*csp, 0, readNextChar());
}
for(int j=0; j<numSegments;j++)
{
int k = n_chars*j;
for(int i=0; i<n_chars; i++)
things_r[k+i] = new thing(lm+i*csp, 0, readNextChar());
}
}
void draw_()
{
background(0);
pushMatrix();
noStroke();//stroke(128); // Draw lines around the rectangles
fill(0); //fill(150*lvl, 200*lvl, 255*lvl);
lights();
directionalLight(128, 128, 128, 0, 0, 1);
drawMobiusRing();
popMatrix();
// texts
pushMatrix();
float[] rot = cam.getRotations();
rotateX(rot[0]);
rotateY(rot[1]);
rotateZ(rot[2]);
translate(0,0,(float)cam.getDistance()-260*fac);
fill(255);stroke(255);
textAlign(CENTER, CENTER);
textFont(courier);
textSize(5*fac);
text("@infinitymathart • 2024",0.0*width,0.18*height);
popMatrix();
}
// Rendering the Möbius ring using the segment class for each segment
//
void drawMobiusRing()
{
for (int i = 0; i < numSegments; i++)
{
float u1 = map(i, 0, numSegments, 0, TWO_PI); // u parameter for current segment
float u2 = map(i + 1, 0, numSegments, 0, TWO_PI); // u parameter for next segment
// Create ring for u1 and u2
ring f = new ring(u1, u2, 0);
f.set_index(i);
f.shift(-2*TAU*t);
f.draw();
}
}
// Function to draw the debugging visuals
//
void drawFaceNormals(ring f)
{
pushStyle();
PVector[] centers = {f.tf.p, f.bf.p, f.rf.p, f.lf.p};
PVector[] normals = {f.tf.n, f.bf.n, f.rf.n, f.lf.n};
for (int i = 0; i < centers.length; i++) {
PVector center = centers[i];
PVector normalEnd = PVector.add(center, normals[i].copy().mult(30)); // Scale normal for visibility
// Draw the normal vector (line)
stroke(0,0,255); // White line
strokeWeight(1);
line(center.x, center.y, center.z, normalEnd.x, normalEnd.y, normalEnd.z);
// Draw a green dot at the center of each face
stroke(0, 255, 0);
strokeWeight(6);
point(center.x, center.y, center.z);
// Draw a red dot at the end of each normal vector
stroke(255, 0, 0);
strokeWeight(6);
point(normalEnd.x, normalEnd.y, normalEnd.z);
}
popStyle();
}
// Parametric surface of the Möbius strip
//
PVector s(float u, float v, float r) {
float x = r * (1 + v / 2 * cos(u / 2)) * cos(u);
float y = r * (1 + v / 2 * cos(u / 2)) * sin(u);
float z = r * v / 2 * sin(u / 2);
return new PVector(x, y, z);
}
// Partial derivative with respect to v
//
PVector dsdv(float u, float v, float r)
{
float x = 1.0 / 2 * cos(u / 2) * cos(u);
float y = 1.0 / 2 * cos(u / 2) * sin(u);
float z = 1.0 / 2 * sin(u / 2);
return new PVector(x, y, z).mult(r);
}
// Partial derivative with respect to u
//
PVector dsdu(float u, float v, float r)
{
float x = -v / 4 * sin(u / 2) * cos(u) - (1 + v / 2 * cos(u / 2)) * sin(u);
float y = -v / 4 * sin(u / 2) * sin(u) + (1 + v / 2 * cos(u / 2)) * cos(u);
float z = v / 4 * cos(u / 2);
return new PVector(x, y, z).mult(r);
}
// Normal vector at the point
//
PVector normal_vector(float u, float v, float r) {
PVector du = dsdu(u, v, r);
PVector dv = dsdv(u, v, r);
PVector n = dv.cross(du);
return n.normalize();
}
// System of coordinates at a point on the surface
//
Coords surfCoords(float u, float v, float r)
{
PVector du = dsdu(u,v,r).normalize();
PVector dv = dsdv(u,v,r).normalize();
PVector n = dv.cross(du).normalize();
return new Coords( s(u,v,r), du, dv, n );
}
// Coords class to represent the coordinate system at a point on the surface
//
class Coords {
PVector p; // The point on the surface
PVector du; // Tangent vector along u
PVector dv; // Tangent vector along v
PVector n; // Normal vector
Coords(PVector p_, PVector du_, PVector dv_, PVector n_) {
p = p_.copy();
du = du_.copy();
dv = dv_.copy();
n = n_.copy();
}
PMatrix3D basisChange(Coords other) {
PMatrix3D inv = other.getBasisMatrix();
inv.invert(); // Get the inverse of the other system's basis
PMatrix3D result = getBasisMatrix();
result.apply(inv); // Apply inverse to get change of basis
return result;
}
PMatrix3D getBasisMatrix() {
return new PMatrix3D(
du.x, dv.x, n.x, 0,
du.y, dv.y, n.y, 0,
du.z, dv.z, n.z, 0,
0, 0, 0, 1
);
}
}
// Surface segment. It consists of 4 lines (top,left,bottom,right)
// forming a quadrilateral with center at the point (u, v)
// (tipically, v = 0).
//
class segment {
float u, v;
Coords c; // Center and basis of the segment
PVector tl; // Top left vertex
PVector tr; // Top right vertex
PVector bl; // Bottom left vertex
PVector br; // Bottom right vertex
Coords tf; // Top line coordinate system
Coords bf; // Bottom line coordinate system
Coords rf; // Right line coordinate system
Coords lf; // Left line coordinate system
segment(float u, float v)
{
this.u = u;
this.v = v;
// Central coordinate system
c = surfCoords(u, v, ringRadius);
// Compute the four vertices for the rectangular segment
tl = c.p.copy().add(c.dv.copy().mult(-ringWidth / 2)).add(c.n.copy().mult(ringThickness / 2));
tr = c.p.copy().add(c.dv.copy().mult(ringWidth / 2)).add(c.n.copy().mult(ringThickness / 2));
bl = c.p.copy().add(c.dv.copy().mult(-ringWidth / 2)).add(c.n.copy().mult(-ringThickness / 2));
br = c.p.copy().add(c.dv.copy().mult(ringWidth / 2)).add(c.n.copy().mult(-ringThickness / 2));
// Coordinate systems for each face:
tf = new Coords(tl.copy().add(tr).mult(0.5), c.du, c.dv, c.n); // Top face
bf = new Coords(bl.copy().add(br).mult(0.5), c.du, c.dv.copy().mult(-1), c.n.copy().mult(-1)); // Bottom face
rf = new Coords(tr.copy().add(br).mult(0.5), c.du, c.n.copy().mult(-1), c.dv); // Right face
lf = new Coords(tl.copy().add(bl).mult(0.5), c.du, c.n.copy(), c.dv.copy().mult(-1)); // Left face
}
void draw_tl() // Draws the vertex at tl
{
vertex(tl.x, tl.y, tl.z);
}
void draw_tr()
{
vertex(tr.x, tr.y, tr.z);
}
void draw_bl()
{
vertex(bl.x, bl.y, bl.z);
}
void draw_br()
{
vertex(br.x, br.y, br.z);
}
}
// Each pair of segments define 4 faces (top,left,bottom,right) which form
// a ring.
//
class ring
{
float u1, u2, v;
float u_shift; // Shift to be applied to u1 and u2 during animation
segment s1; // First segment
segment s2; // Next segment
Coords tf; // System of coordinates for top face
Coords bf; // System of coordinates for bottom face
Coords rf; // System of coordinates for right face
Coords lf; // System of coordinates for left face
int index;
ring(float u1_, float u2_, float v_)
{
u_shift = 0;
index = 0;
u1 = u1_;
u2 = u2_;
v = v_;
update_coords();
}
void update_coords()
{
// Create segment objects for u1 and u2
s1 = new segment(u1+u_shift, v);
s2 = new segment(u2+u_shift, v);
tf = new Coords(s1.tf.p.copy().add(s2.tf.p).mult(0.5), s1.tf.du.copy().add(s2.tf.du).mult(0.5), s1.tf.dv.copy().add(s2.tf.dv).mult(0.5), s1.tf.n.copy().add(s2.tf.n).mult(0.5));
bf = new Coords(s1.bf.p.copy().add(s2.bf.p).mult(0.5), s1.bf.du.copy().add(s2.bf.du).mult(0.5), s1.bf.dv.copy().add(s2.bf.dv).mult(0.5), s1.bf.n.copy().add(s2.bf.n).mult(0.5));
lf = new Coords(s1.lf.p.copy().add(s2.lf.p).mult(0.5), s1.lf.du.copy().add(s2.lf.du).mult(0.5), s1.lf.dv.copy().add(s2.lf.dv).mult(0.5), s1.lf.n.copy().add(s2.lf.n).mult(0.5));
rf = new Coords(s1.rf.p.copy().add(s2.rf.p).mult(0.5), s1.rf.du.copy().add(s2.rf.du).mult(0.5), s1.rf.dv.copy().add(s2.rf.dv).mult(0.5), s1.rf.n.copy().add(s2.rf.n).mult(0.5));
}
void shift(float u_shift_)
{
u_shift = u_shift_;
update_coords();
}
void set_index(int i_)
{
index = i_;
}
// Draws the faces of a ring and things on the faces
//
void draw()
{
// Draw the top and bottom faces of the segment
beginShape(QUADS);
s1.draw_tl();
s1.draw_bl();
s2.draw_bl();
s2.draw_tl();
endShape(CLOSE);
beginShape(QUADS);
s1.draw_tr();
s2.draw_tr();
s2.draw_br();
s1.draw_br();
endShape(CLOSE);
// Draw the left face (connecting top-left and bottom-left)
beginShape(QUADS);
s1.draw_tl();
s2.draw_tl();
s2.draw_tr();
s1.draw_tr();
endShape(CLOSE);
// Draw the right face (connecting bottom-left and bottom-right)
beginShape(QUADS);
s1.draw_bl();
s2.draw_bl();
s2.draw_br();
s1.draw_br();
endShape(CLOSE);
// Text things on the faces of the ring
pushStyle();
textAlign(CENTER, CENTER); // Center the text
textSize(10*fac); // Adjust text size as needed
fill(255); // Text color
stroke(255);
for(int i=0;i<n_chars;i++)
{
thing tg = things_t[n_chars*index+i];
if (tg.flipping()) fill(c_face1_flip); else fill(c_face1);
drawTextOnTopFace(this, tg.get(), tg.u, tg.v);
tg = things_b[n_chars*index+i];
if (tg.flipping()) fill(c_face1_flip); else fill(c_face1);
drawTextOnBottomFace(this, tg.get(), tg.u, tg.v);
tg = things_l[n_chars*index+i];
if (tg.flipping()) fill(c_face2_flip); else fill(c_face2);
drawTextOnLeftFace(this, tg.get(), tg.u, tg.v);
tg = things_r[n_chars*index+i];
if (tg.flipping()) fill(c_face2_flip); else fill(c_face2);
drawTextOnRightFace(this, tg.get(), tg.u, tg.v);
}
popStyle();
}
}
float dv_top(ring f, float u3, float v3)
{
float h = 0.01;
return s_top_face(f, u3, v3+h).sub(s_top_face(f, u3, v3-h)).mult(0.5/h).mag();
}
// Parametric function for the top face using bilinear interpolation
PVector s_top_face(ring f, float u3, float v3)
{
// Interpolate between the four corner points (tl, tr, bl, br)
PVector tl = f.s1.tl; // Top-left
PVector tr = f.s1.tr; // Top-right
PVector bl = f.s2.tl; // Bottom-left
PVector br = f.s2.tr; // Bottom-right
// Adjust u3 and v3 to range from -1 to 1, making sure the center corresponds to u3 = 0, v3 = 0
float uRatio = (u3 + 1) / 2.0; // Map u3 from [-1,1] to [0,1]
float vRatio = (v3 + 1) / 2.0; // Map v3 from [-1,1] to [0,1]
// Bilinear interpolation to calculate the point on the surface
PVector point = tl.copy().mult((1 - uRatio) * (1 - vRatio))
.add(tr.copy().mult(uRatio * (1 - vRatio)))
.add(bl.copy().mult((1 - uRatio) * vRatio))
.add(br.copy().mult(uRatio * vRatio));
return point;
}
// Parametric function for the bottom face using bilinear interpolation
PVector s_bottom_face(ring f, float u3, float v3) {
PVector tl = f.s1.bl; // Bottom-left
PVector tr = f.s1.br; // Bottom-right
PVector bl = f.s2.bl; // Top-left
PVector br = f.s2.br; // Top-right
float uRatio = (u3 + 1) / 2.0; // Map u3 from [-1,1] to [0,1]
float vRatio = (v3 + 1) / 2.0; // Map v3 from [-1,1] to [0,1]
PVector point = tl.copy().mult((1 - uRatio) * (1 - vRatio))
.add(tr.copy().mult(uRatio * (1 - vRatio)))
.add(bl.copy().mult((1 - uRatio) * vRatio))
.add(br.copy().mult(uRatio * vRatio));
return point;
}
float dv_bottom(ring f, float u3, float v3)
{
float h = 0.01;
return s_bottom_face(f, u3, v3+h).sub(s_bottom_face(f, u3, v3-h)).mult(0.5/h).mag();
}
// Parametric function for the left face using bilinear interpolation
PVector s_left_face(ring f, float u3, float v3) {
PVector tl = f.s1.tl; // Top-left
PVector tr = f.s1.bl; // Top-right
PVector bl = f.s2.tl; // Bottom-left
PVector br = f.s2.bl; // Bottom-right
float uRatio = (u3 + 1) / 2.0; // Map u3 from [-1,1] to [0,1]
float vRatio = (v3 + 1) / 2.0; // Map v3 from [-1,1] to [0,1]
PVector point = tl.copy().mult((1 - uRatio) * (1 - vRatio))
.add(tr.copy().mult(uRatio * (1 - vRatio)))
.add(bl.copy().mult((1 - uRatio) * vRatio))
.add(br.copy().mult(uRatio * vRatio));
return point;
}
float dv_left(ring f, float u3, float v3)
{
float h = 0.01;
return s_left_face(f, u3, v3+h).sub(s_left_face(f, u3, v3-h)).mult(0.5/h).mag();
}
// Parametric function for the right face using bilinear interpolation
PVector s_right_face(ring f, float u3, float v3) {
PVector tl = f.s1.tr; // Top-left
PVector tr = f.s1.br; // Top-right
PVector bl = f.s2.tr; // Bottom-left
PVector br = f.s2.br; // Bottom-right
float uRatio = (u3 + 1) / 2.0; // Map u3 from [-1,1] to [0,1]
float vRatio = (v3 + 1) / 2.0; // Map v3 from [-1,1] to [0,1]
PVector point = tl.copy().mult((1 - uRatio) * (1 - vRatio))
.add(tr.copy().mult(uRatio * (1 - vRatio)))
.add(bl.copy().mult((1 - uRatio) * vRatio))
.add(br.copy().mult(uRatio * vRatio));
return point;
}
float dv_right(ring f, float u3, float v3)
{
float h = 0.01;
return s_right_face(f, u3, v3+h).sub(s_right_face(f, u3, v3-h)).mult(0.5/h).mag();
}
// General function to draw text on a face using basis transformation
void drawTextOnFace(PVector position, PMatrix3D basisChangeMatrix, String txt, float scl)
{
pushMatrix(); // Save the current transformation matrix
// Move to the position where the text should be placed
translate(position.x, position.y, position.z);
// Apply the basis change matrix to align the text correctly
applyMatrix(basisChangeMatrix);
// Draw the text
translate(0,0,-fac); // move text up and away from the surface slightly
scale(scl);
text(txt, 0, 0); // Draw text at the origin
popMatrix(); // Restore the original transformation matrix
}
// Function to draw text on the top face
void drawTextOnTopFace(ring f, String label, float u, float v) {
Coords globalCoords = new Coords(
new PVector(0, 0, 0),
new PVector(0, 1, 0),
new PVector(-1, 0, 0),
new PVector(0, 0, 1)
); // Global coordinate system (identity matrix)
// Compute the change of basis matrix
PMatrix3D basisChangeMatrix = f.tf.basisChange(globalCoords);
// Get the point on the face where to draw
PVector center = s_top_face(f, u, -v);
float g = dv_top(f, u, -v);
// Draw the text on the top face
drawTextOnFace(center, basisChangeMatrix, label, g*gscl);
}
// Function to draw text on the bottom face
void drawTextOnBottomFace(ring f, String label, float u, float v)
{
Coords globalCoords = new Coords(
new PVector(0, 0, 0),
new PVector(0, 1, 0),
new PVector(-1, 0, 0),
new PVector(0, 0, 1)
); // Global coordinate system for the bottom face
// Compute the change of basis matrix
PMatrix3D basisChangeMatrix = f.bf.basisChange(globalCoords);
// Get the center of the bottom face
PVector center = s_bottom_face(f, -u, -v);
float g = dv_bottom(f, -u, -v);
// Draw the text on the bottom face
drawTextOnFace(center, basisChangeMatrix, label, g*gscl);
}
// Function to draw text on the left face
void drawTextOnLeftFace(ring f, String label, float u, float v)
{
Coords globalCoords = new Coords(
new PVector(0, 0, 0),
new PVector(0, 1, 0), // dv (horizontal) for the left face
new PVector(-1, 0, 0), // du (vertical) for the left face
new PVector(0, 0, 1)
); // Global coordinate system for the left face
// Compute the change of basis matrix
PMatrix3D basisChangeMatrix = f.lf.basisChange(globalCoords);
// Get the center of the left face
PVector center = s_left_face(f, -u, -v);
float g = dv_left(f, -u, -v);
// Draw the text on the left face
drawTextOnFace(center, basisChangeMatrix, label, g*gscl);
}
// Function to draw text on the right face
void drawTextOnRightFace(ring f, String label, float u, float v)
{
Coords globalCoords = new Coords(
new PVector(0, 0, 0),
new PVector(0, 1, 0), // dv (horizontal) for the right face
new PVector(-1, 0, 0), // du (vertical) for the right face
new PVector(0, 0, 1)
); // Global coordinate system for the right face
// Compute the change of basis matrix
PMatrix3D basisChangeMatrix = f.rf.basisChange(globalCoords);
// Get the center of the right face
PVector center = s_right_face(f, u, -v);
float g = dv_right(f, u, -v);
// Draw the text on the right face
drawTextOnFace(center, basisChangeMatrix, label, g*gscl);
}
void mousePressed()
{
float[] position = cam.getPosition();
println("\n\nPosition (", position[0], ", ", position[1], ", ", position[2], ")\n");
float[] rotations = cam.getRotations();
float[] look = cam.getLookAt();
double dist = cam.getDistance();
println("..:");
println(" cam = new PeasyCam(this, ", look[0], "*fac, ", look[1], "*fac, ", look[2], "*fac, ", dist,"*fac);" );
println(" cam.setRotations(", rotations[0], ", ", rotations[1], ", ", rotations[2],");\n\n");
}
class thing
{
float u, v; // (u,v) coordinates relative to the surface face
char c; // First digit
char c1; // Second digit
float offset; // Offset at which it begins to flip
boolean flips; // Does this thing flip or not?
thing(float u_, float v_)
{
u = u_;
v = v_;
c = "0123456789".charAt(int(random(0,9)));
c1 = "0123456789".charAt(int(random(0,9)));
flips = random(0,1) > 0.6;
offset = random(0, 1);
}
thing(float u_, float v_, char d)
{
u = u_;
v = v_;
c = d;
// c1 = "×∞∅∂∮∿⋌⋉⪯⊕⊗⋍⟡∴⇒√𝛼±0123456789".charAt(int(random(0,28)));
c1 = "0123456789".charAt(int(random(0,9)));
flips = random(0,1) > 0.8;
offset = random(0, 1);
}
boolean flipping()
{
if (flips)
{
float tt = (t+offset)%1;
if ( (tt > 0.38 && tt < 0.40) || (tt > 0.68 && tt < 0.70 ) )
return true;
}
return false;
}
// Returns c, unless it flips and it's time to flip, in which case it returns c1
String get()
{
return flipping() ? ""+c1 : ""+c;
}
}
/* License:
*
* Copyright (c) 2024 Waldeck Schützer
*
* All rights reserved.
*
* This code after the template and the related animations are the property of the
* copyright holder. Any reproduction, distribution, or use of this material,
* in whole or in part, without the express written permission of the copyright
* holder is strictly prohibited.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment