Created
December 4, 2024 19:41
-
-
Save wschutzer/8e44337385b27362713b2bdd46379856 to your computer and use it in GitHub Desktop.
delayedcurves7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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