Skip to content

Instantly share code, notes, and snippets.

@wschutzer
Created December 4, 2024 19:41
Show Gist options
  • Save wschutzer/8e44337385b27362713b2bdd46379856 to your computer and use it in GitHub Desktop.
Save wschutzer/8e44337385b27362713b2bdd46379856 to your computer and use it in GitHub Desktop.
delayedcurves7
/*
* delayedcurves7
* --------------
* This code creates an animation consisting of colored balls moving according to random noise (OpenSimplexNoise)
* The balls are connected with flexible lines that follow the movement with some delay.
*
* 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/>.
*/
#include <array>
#include <vector>
// Requires OpenSimplexNoise.h and OpenSimplexNoise.cpp available here: https://gist.github.com/wschutzer/4f1905567fa1c4d4fe1b61ffc61df608
#include "ofApp.h"
bool recording = false;
int FPS = 60;
int duration = 8;
int numFrames = FPS*duration;
float aspect = 1.0; // 1920.0/1080;
int frameWidth = recording ? 2160 : 500;
int frameHeight = frameWidth*aspect;
float t = 0;
float fac = frameWidth/500.0;
int samplesPerFrame = 7;
float shutterAngle = 1.5;
int width = frameWidth; // Compatibility with Processing
int height = frameHeight;
int m = recording ? 2000 : 500;
// float delay_factor = 1.0;
OpenSimplexNoise::Noise noise(1337);
float fontSize = 10*fac;
ofTrueTypeFont font;
ofFbo largeCanvas;
#define lerp(a,b,t) ofLerp((a),(b),(t))
float thereAndBack(float t)
{
return 4*t*(1-t);
}
float easeInOutCubic(float x)
{
return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
}
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 easeInQuart(float t)
{
return t*t*t*t;
}
float easeThereAndBack(float t)
{
return easeInQuart(thereAndBack(t));
}
float distance(float x0, float y0, float x1, float y1)
{
x0 -= x1;
y0 -= y1;
return sqrt(x0*x0+y0*y0);
}
class thing
{
float delay_factor = 0.5;
float motion_radius = 0.5;
float xseed = 1337;
float yseed = 1515;
float radius = 150;
float x0 = 0;
float y0 = 0;
float bradius = 3*fac;
float lradius = 0.5*fac;
float offset = 0;
float g = 3.2;
ofColor color;
public:
thing() {} // Do nothing constructor
thing(float x0_, float y0_, float radius_,float mrad_, float _xseed, float _yseed)
{
x0 = x0_; y0 = y0_;
radius = radius_;
motion_radius = mrad_;
xseed = _xseed; yseed = _yseed;
offset = ofRandom(0, 1);
color = ofColor::fromHsb(ofRandom(0,255),200,255);
}
float x(float t)
{
t = fmod(10+t+offset,1.0);
t = ease(t, g);
return x0 + radius*noise.eval(xseed + motion_radius*cos(TWO_PI*t),motion_radius*sin(TWO_PI*t));
}
float y(float t)
{
t = fmod(10+t+offset,1.0);
t = ease(t, g);
return y0 + radius*noise.eval(yseed + motion_radius*cos(TWO_PI*t),motion_radius*sin(TWO_PI*t));
}
void draw(float t)
{
ofPushStyle();
ofSetColor(color); // Set color to white
ofFill(); // Set to fill mode to ensure the circle is filled
ofDrawCircle(x(t), y(t), bradius);
ofPopStyle();
}
void interp(thing &other, float t)
{
ofPushStyle();
ofSetLineWidth(2); // Set stroke weight equivalent to strokeWeight(2)
ofSetColor(color, 60); // Set stroke color with alpha (255, 100)
for (int i = 0; i <= m; i++) {
float tt = 1.0 * i / m;
float _x = lerp(x(t - delay_factor*tt),other.x(t - delay_factor*(1-tt)),tt);
float _y = lerp(y(t - delay_factor*tt),other.y(t - delay_factor*(1-tt)),tt);
ofColor c = color.getLerped(other.color, tt);
ofSetColor(c, 60);
ofDrawCircle(_x, _y, lradius); // Draw point equivalent (small circle)
}
ofPopStyle();
}
};
const int numThings = 12;
thing th[numThings];
//--------------------------------------------------------------
void ofApp::setup()
{
ofSeedRandom(1337);
if (recording)
largeCanvas.allocate(frameWidth, frameHeight, GL_RGBA);
// ofSetWindowShape(frameWidth, frameHeight);
ofSetFrameRate(FPS);
ofSetWindowTitle("Delayed Curves");
for(int i=0; i<numThings; i++)
{
float angle = TWO_PI/numThings;
float r = 0.25*height;
float mr = fac*ofMap(noise.eval(angle, r),-1,1,120,180);
th[i] = thing(r*cos(angle*i)/aspect,r*sin(angle*i),mr,ofRandom(0.5,1),ofRandom(100,1000),ofRandom(100,1000));
}
font.load("Courier New", fontSize, true, true, true);
}
//--------------------------------------------------------------
void ofApp::update()
{
}
//--------------------------------------------------------------
void ofApp::draw()
{
// Actual draw
std::vector<std::array<int, 3>> pixbuf(recording ? frameWidth*frameHeight : 0); // Pixel buffer, used when recording
if ( recording)
{
for (int k=0; k<samplesPerFrame; k++)
{
t = std::fmod(ofMap(ofGetFrameNum()-1 + k*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1), 1.0);
largeCanvas.begin();
ofPushStyle();
ofPushMatrix();
draw_();
ofPopMatrix();
ofPopStyle();
largeCanvas.end();
ofPixels pixels;
largeCanvas.readToPixels(pixels);
for (int i=0; i<width*height; i++)
{
pixbuf[i][0] += pixels[4*i];
pixbuf[i][1] += pixels[4*i+1];
pixbuf[i][2] += pixels[4*i+2];
}
}
// largeCanvas.draw(0, 0, ofGetWidth(), ofGetHeight());
ofPixels pixels;
pixels.allocate(width, height, OF_PIXELS_RGB);
for(int i=0; i<width*height; i++)
{
pixels[3*i] = ofClamp(1.0*pixbuf[i][0]/samplesPerFrame,0,255);
pixels[3*i+1] = ofClamp(1.0*pixbuf[i][1]/samplesPerFrame,0,255);
pixels[3*i+2] = ofClamp(1.0*pixbuf[i][2]/samplesPerFrame,0,255);
}
ofImage image;
image.setFromPixels(pixels);
image.draw(0, 0, ofGetWidth(), ofGetHeight());
int start = 1;
if (ofGetFrameNum() > start)
{
ostringstream os;
os << setw(4) << setfill('0') << ofGetFrameNum() - start;
// ofSaveImage(pixels, "/tmp/r/frame_" + os.str() + ".png"); // OF_IMAGE_QUALITY_BEST);
image.save("/tmp/r/frame_" + os.str() + ".png"); // OF_IMAGE_QUALITY_BEST);
std::cout << (ofGetFrameNum()-start) << "/" << numFrames << std::endl;
//image.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
//image.save("/tmp/r/frame_" + os.str() + ".png");
if (ofGetFrameNum() - start >= numFrames)
{
std::exit(1);
}
}
}
else
{
t = fmod(1.0*ofGetMouseX()/width,1.0);
ofPushStyle();
ofPushMatrix();
draw_();
ofPopMatrix();
ofPopStyle();
}
}
//--------------------------------------------------------------
void ofApp::draw_()
{
ofBackground(255);
ofTranslate(width/2, height/2);
for(int i=0; i<numThings; i++)
for(int j=i+1; j<numThings; j++)
th[i].interp(th[j], t);
ofSetColor(250);
for(int i=0; i<numThings; i++)
th[i].draw(t);
ofSetColor(0);
font.drawString("@infinitymathart | 2024", -10.5*fontSize, height/2-fontSize);
}
//--------------------------------------------------------------
void ofApp::exit()
{
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key)
{
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key)
{
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y )
{
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button)
{
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button)
{
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button)
{
}
//--------------------------------------------------------------
void ofApp::mouseScrolled(int x, int y, float scrollX, float scrollY)
{
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y)
{
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y)
{
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h)
{
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg)
{
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo)
{
}
//========================================================================
int main(int argc, char **argv)
{
if (argc>1)
if (argv[1][0] == '-' && argv[1][1] == 'r')
recording = true;
//Use ofGLFWWindowSettings for more options like multi-monitor fullscreen
//ofGLWindowSettings settings;
//settings.setSize(frameWidth, frameHeight);
//settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN
//auto window = ofCreateWindow(settings);
ofSetupOpenGL(500, 500*aspect, OF_WINDOW);
ofRunApp(new ofApp());
//ofRunApp(window, make_shared<ofApp>());
//ofRunMainLoop();
}
/*
* delayedcurves7
* --------------
* This code creates an animation consisting of colored balls moving according to random noise (OpenSimplexNoise)
* The balls are connected with flexible lines that follow the movement with some delay.
*
* 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/>.
*/
#pragma once
#include "ofMain.h"
#include "OpenSimplexNoise.h"
// OpenSimplexNoise can be found here: https://gist.github.com/wschutzer/4f1905567fa1c4d4fe1b61ffc61df608
class ofApp : public ofBaseApp
{
public:
void setup() override;
void update() override;
void draw() override;
void exit() override;
void draw_();
void keyPressed(int key) override;
void keyReleased(int key) override;
void mouseMoved(int x, int y ) override;
void mouseDragged(int x, int y, int button) override;
void mousePressed(int x, int y, int button) override;
void mouseReleased(int x, int y, int button) override;
void mouseScrolled(int x, int y, float scrollX, float scrollY) override;
void mouseEntered(int x, int y) override;
void mouseExited(int x, int y) override;
void windowResized(int w, int h) override;
void dragEvent(ofDragInfo dragInfo) override;
void gotMessage(ofMessage msg) override;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment