Skip to content

Instantly share code, notes, and snippets.

@wschutzer
Created November 27, 2024 02:16
Show Gist options
  • Save wschutzer/b19c79ccb24acd77759eec753ccd0d1b to your computer and use it in GitHub Desktop.
Save wschutzer/b19c79ccb24acd77759eec753ccd0d1b to your computer and use it in GitHub Desktop.
/* Love Equation
* ---------------------
*
* Copyright (C) 2024 Waldeck Schutzer (@infinitymathart)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
final boolean recording = false;
final int duration = 8; // Seconds
final int fps = 60; // Frames Per Second
final int num_frames = fps*duration;
// Motion blur:
final int samples_per_frame = 10; // How many frames to average?
final float shutter_angle = 1.5; // How distant the frames are in time?
final float aspect = 1920.0/1080;
final int frameWidth = recording ? 2160 : 400;
final int frameHeight = (int)(aspect*frameWidth);
final float fac = 1.0*frameWidth/400;
float t = 0; // Normalized animation time in the interval 0 - 1.
float k = 0;
PShader motionBlur;
PGraphics gtex0; // Each frame is drawn here
// Vertex shader code
String[] motionBlurVertShader = {
"#version 100",
"#define PROCESSING_TEXTURE_SHADER",
"uniform mat4 transformMatrix;",
"uniform mat4 texMatrix;",
"attribute vec4 position;",
"attribute vec4 color;",
"attribute vec2 texCoord;",
"varying vec4 vertColor;",
"varying vec4 vertTexCoord;",
"void main() {",
"gl_Position = transformMatrix * position;",
"vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);",
"vertColor = color;",
"}"};
String[] motionBlurFragShader = {
"#version 100",
"#ifdef GL_ES",
"precision mediump float;",
"precision mediump int;",
"#endif",
"#define PROCESSING_TEXTURE_SHADER",
"uniform sampler2D texture; // Source and result frame, works as an accumulator",
"uniform sampler2D tex0; // Currently drawn frame to be added",
"uniform float alpha;",
"uniform float beta;",
"varying vec4 vertColor; // Seems to be white",
"varying vec4 vertTexCoord;",
"void main()",
"{",
" vec4 curColor = texture2D(tex0, vertTexCoord.xy); // Color of current pixel ",
" vec4 accColor = texture2D(texture, vertTexCoord.xy); // Color in accumulator",
" gl_FragColor = beta*(alpha*accColor + curColor); // Blending",
"}"};
void settings()
{
size(frameWidth, frameHeight, P2D); // Use P2D renderer for shader support
smooth();
}
void setup()
{
gtex0 = createGraphics(width, height, P2D);
gtex0.smooth();
motionBlur = new PShader(this, motionBlurVertShader, motionBlurFragShader);
formula = loadShape("formula.svg");
}
void draw()
{
background(0);
for(int i=0; i<samples_per_frame; i++)
{
t = map((recording ? 1.0*(frameCount-1): 1.0*mouseX/width*num_frames) + i*shutter_angle/samples_per_frame, 0, num_frames, 0, 1) % 1;
draw_(gtex0,t); // Draw on gtex1
motionBlur.set("tex0", gtex0);
motionBlur.set("alpha", (float)(i));
motionBlur.set("beta", (float)(1.0/(i+1)));
filter(motionBlur); // Averages the frames with a moving average
}
if (recording)
{
println(frameCount+"/"+num_frames);
saveFrame("/tmp/f/frame_####.png");
if (frameCount >= num_frames)
exit();
}
}
// ---------------------------------------------------------------------------------
final int num_points = (int)(10000*fac);
final float hsize = 70.0*fac;
final float fsize = 1.3*fac;
float y0 = 10*fac;
PShape formula;
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 easeOutElastic(float x)
{
float c4 = (2*PI)/3;
if(x<=0) return 0;
if(x>=1) return 1;
return pow(2, -13 * x) * sin((x * 10 - 0.75) * c4) + 1;
}
// the heart curve
PVector rheart(float theta, float r)
{
r*=0.9;
float s = sin(theta);
float x = r*s*s*s;
float y = r*(13*cos(theta)-5*cos(2*theta)-2*cos(3*theta)-cos(4*theta))/16;
return new PVector(x, -y);
}
float f(float x)
{
if (x <= -sqrt(5) || x>= sqrt(5)) return pow(5,1.0/3);
return pow(x*x,1.0/3)+0.9*sqrt(5-x*x)*sin(k*PI*x);
}
void draw_(PGraphics g,float t)
{
if (t < 0.7)
k = 80/PI*ease(t/0.7,5.2);
else
k = 80/PI*((1-easeOutElastic(ease((t-0.7)/0.3,3.2))));
g.beginDraw();
g.push();
g.background(0); // Clear the canvas
g.noStroke();
g.fill(255, 0, 0);
g.translate(width/2,height/2);
PVector p = rheart(TAU*t, 3.5*hsize);
g.ellipse(p.x,p.y-hsize-y0,8*fac,8*fac);
g.stroke(255,0,0);
g.strokeWeight(1.5*fac);
g.noFill();
g.beginShape();
float y2 = -hsize*f(-sqrt(5))-y0;
g.vertex(-width/2,y2);
g.vertex(-sqrt(5)*hsize,y2);
for(int i=0; i<num_points; i++)
{
float tt = 2.0*sqrt(5)*i/num_points - sqrt(5);
float x = tt*hsize;
float y = -hsize*f(tt)-y0;
g.vertex(x,y);
}
g.vertex(sqrt(5)*hsize,y2);
g.vertex(width/2,y2);
g.endShape();
formula.fill(255);
formula.stroke(255);
g.shape(formula, -fsize*formula.width/2, height/3-fsize*formula.height+10*fac, fsize*formula.width, fsize*formula.height);
g.fill(255,0,0);
g.textSize(36*fac);
g.textAlign(CENTER);
g.text(String.format("k = %-8.5f", k), 0, height/3+fsize*formula.height);
g.fill(255);
g.textSize(15*fac);
g.text("@infinitymathart | 2024", 0, height/2-25*fac);
g.pop();
g.endDraw();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment