├── README.md └── code ├── fluidsphereobstacle ├── README.md └── fluidsphereobstacle.pde ├── fractalsliding2d ├── README.md └── fractalsliding2d.pde ├── hilbertcurvetransforms ├── README.md └── hilbertcurvetransforms.pde ├── moorecurvequeue ├── README.md └── moorecurvequeue.pde ├── mooremiddlecurves ├── README.md └── mooremiddlecurves.pde ├── moorevoronoi ├── README.md ├── moore.pde ├── moorevoronoi.pde └── voronoi.frag ├── permutationpatternspropagation ├── README.md └── permutationpatternspropagation.pde ├── pusheddigitssphere ├── README.md └── pusheddigitssphere.pde ├── radialcollapse ├── README.md ├── os.pde └── radialcollapse.pde ├── scattereddots ├── README.md └── scattereddots.pde ├── sierpinskiloop ├── README.md └── sierpinskiloop.pde ├── sierpinskiloop_simple └── sierpinskiloop_simple.pde ├── sinusoidspacking ├── README.md └── sinusoidspacking.pde ├── sphereimpacts ├── README.md └── sphereimpacts.pde ├── spherewave ├── README.md ├── os.pde └── spherewave.pde ├── spiralmagic ├── README.md └── spiralmagic.pde ├── spiralssphere ├── README.md └── spiralssphere.pde ├── spiralwave ├── README.md └── spiralwave.pde ├── sunconnections ├── README.md ├── os.pde └── sunconnections.pde ├── toruscurve ├── README.md └── toruscurve.pde ├── twolevelssliding ├── README.md └── twolevelssliding.pde └── unfoldedcubestiling ├── README.md ├── data └── ChakraPetch-Medium.ttf └── unfoldedcubestiling.pde /README.md: -------------------------------------------------------------------------------- 1 | # Processing animations source code list 2 | 3 | This repo has the source codes of some of my animations. For more context and insights about this project, please read the text below. 4 | 5 | 6 | | | Year | 7 | |-----------------|------| 8 | | [**scattered dots**](https://github.com/Bleuje/processing-animations-code/blob/main/code/scattereddots/) | 2018 | 9 | | [**Sierpinski triangle loop**](https://github.com/Bleuje/processing-animations-code/blob/main/code/sierpinskiloop/) | 2017,2021 | 10 | | [**spiral wave**](https://github.com/Bleuje/processing-animations-code/blob/main/code/spiralwave/) | 2021 | 11 | | [**fluid sphere obstacle**](https://github.com/Bleuje/processing-animations-code/blob/main/code/fluidsphereobstacle/) | 2017 | 12 | | [**sun connections**](https://github.com/Bleuje/processing-animations-code/blob/main/code/sunconnections/) | 2018 | 13 | | [**radial collapse**](https://github.com/Bleuje/processing-animations-code/blob/main/code/radialcollapse/) | 2020 | 14 | | [**moore curve queue**](https://github.com/Bleuje/processing-animations-code/blob/main/code/moorecurvequeue/) | 2021,2023 | 15 | | [**moore middle curves**](https://github.com/Bleuje/processing-animations-code/blob/main/code/mooremiddlecurves/) | 2024 | 16 | | [**sphere impacts**](https://github.com/Bleuje/processing-animations-code/blob/main/code/sphereimpacts/) | 2021 | 17 | | [**two levels sliding**](https://github.com/Bleuje/processing-animations-code/blob/main/code/twolevelssliding/) | 2021 | 18 | | [**sinusoids packing**](https://github.com/Bleuje/processing-animations-code/blob/main/code/sinusoidspacking/) | 2018,2024 | 19 | | [**hilbert curve transforms**](https://github.com/Bleuje/processing-animations-code/blob/main/code/hilbertcurvetransforms/) | 2022 | 20 | | [**sphere wave**](https://github.com/Bleuje/processing-animations-code/blob/main/code/spherewave/) | 2022 | 21 | | [**unfolded cubes tiling**](https://github.com/Bleuje/processing-animations-code/tree/main/code/unfoldedcubestiling) | 2024 | 22 | | [**moore voronoi**](https://github.com/Bleuje/processing-animations-code/tree/main/code/moorevoronoi) | 2024 | 23 | | [**2D fractal sliding squares**](https://github.com/Bleuje/processing-animations-code/blob/main/code/fractalsliding2d/) | 2023 | 24 | | [**spiral magic**](https://github.com/Bleuje/processing-animations-code/blob/main/code/spiralmagic/) | 2021 | 25 | | [**permutation patterns propagation**](https://github.com/Bleuje/processing-animations-code/blob/main/code/permutationpatternspropagation/) | 2021 | 26 | | [**torus curve**](https://github.com/Bleuje/processing-animations-code/blob/main/code/toruscurve/) | 2023 | 27 | | [**pushed digits sphere**](https://github.com/Bleuje/processing-animations-code/blob/main/code/pusheddigitssphere/) | 2023 | 28 | | [**spirals sphere**](https://github.com/Bleuje/processing-animations-code/blob/main/code/spiralssphere/) | 2023 | 29 | 30 | 31 | I'm trying to sort the list from simpler to more complex or time-consuming to fully understand, but this is highly subjective. 32 | 33 | This set of animations is [watchable on my site here](https://bleuje.com/gifanimationsite/single/spiralssphere/) using the bottom arrows/links to navigate. 34 | 35 | Unfortunately I had to give them some kind of names, which is something I don't really like. 36 | 37 | --- 38 | 39 | ## Motivation and information 40 | 41 | In my early animation experiences with [Processing](https://processing.org/), I came across captivating gifs by [beesandbombs (Dave)](https://beesandbombs.com/). Studying even a fraction of the [code he has shared](https://gist.github.com/beesandbombs) greatly enhanced my skills and approach. Over time, I've shared quite raw source codes from my animations, valuing visual outcome over code aesthetics. But with positive feedback and people asking me how the animations are made, I see value in sharing the code with more care. 42 | 43 | This project compiles noteworthy animation source codes, aiming for clarity through commented code. It's a work in progress, aiming to surpass my previous releases despite the imperfections. 44 | 45 | For newcomers or those seeking context, I recommend [**tutorials**](https://bleuje.com/tutorials/) on my website, especially the "[Replacement Technique](https://bleuje.com/tutorial4/)" and the one about beesandbombs' [motion blur template](https://bleuje.com/tutorial6/). 46 | 47 | I hope this collection serves both those diving into Processing-based animations and the simply curious, potentially inspiring others just as beesandbombs inspired me. 48 | 49 | ## Acknowledgments and Licensing 50 | 51 | This repository includes animations that utilize a [motion blur template](https://bleuje.com/tutorial6/) created by Dave. We have permission from him to use this template in our work. 52 | 53 | Please note that while the motion blur template is available for use, the rest of the source code in this repository is generally protected under my copyright, and all rights are reserved. If you wish to use any part of my source code for your own projects or any other purpose, please contact me to obtain permission. 54 | -------------------------------------------------------------------------------- /code/fluidsphereobstacle/README.md: -------------------------------------------------------------------------------- 1 | ## Fluid sphere obstacle 2 | 3 | ![fluid sphere obstacle gif](https://bleuje.com/gifset/2017/2017_22_sphereagainsflow_v2.gif) 4 | 5 | ## [code](https://github.com/Bleuje/processing-animations-code/blob/main/code/fluidsphereobstacle/fluidsphereobstacle.pde) 6 | 7 | #### some used techniques 8 | 9 | simulation, replacement technique 10 | 11 | ### other comments 12 | 13 | It's using a simulation of particles following a flow field. 14 | 15 | The flow field is defined by the sum of: 16 | - a constant speed field 17 | - a repulsive field (might be the most technical part of the code) 18 | - some perlin noise 19 | 20 | Once the particle paths are computed, [replacement technique](https://bleuje.com/tutorial4/) is used to show particles following them. It's also using interpolation between computed positions of the simulation [(tutorial)](https://bleuje.com/tutorial7/). 21 | 22 | A black sphere is then simply drawn, its position and size have been adjusted experimentally. 23 | 24 | ### links 25 | 26 | On bleuje site: https://bleuje.com/gifanimationsite/single/fluidsphereobstacle/ 27 | 28 | On social media: 29 | - tumblr: https://necessary-disorder.tumblr.com/post/164452980553 30 | - twitter: https://twitter.com/etiennejcb/status/1439309996471341059 31 | -------------------------------------------------------------------------------- /code/fluidsphereobstacle/fluidsphereobstacle.pde: -------------------------------------------------------------------------------- 1 | // Processing code by Etienne Jacob 2 | // motion blur template by beesandbombs, explanation/article: https://bleuje.com/tutorial6/ 3 | // See the license information at the end of this file. 4 | // result here : https://bleuje.com/gifanimationsite/single/fluidsphereobstacle/ 5 | 6 | int[][] result; // pixel colors buffer for motion blur 7 | float t; // time global variable in [0,1[ 8 | float c; // other global variable for testing things, controlled by mouse 9 | 10 | void draw() 11 | { 12 | if (!recording) // test mode... 13 | { 14 | t = (mouseX*1.3/width)%1; 15 | c = mouseY*1.0/height; 16 | if (mousePressed) 17 | println(c); 18 | draw_(); 19 | } 20 | else // render mode... 21 | { 22 | for (int i=0; i positions = new ArrayList(); // list of recorded positions with simulation 103 | 104 | float sw = random(1,2); // strokeWeight parameter 105 | int numberOfParticlesOnPath = 3; 106 | float tOffset = random(1); // particles of different paths don't start at the same time thanks to this offset 107 | 108 | Path() 109 | { 110 | positions.add(new PVector(currentX,currentY)); 111 | } 112 | 113 | void update() 114 | { 115 | PVector velocity = field(currentX,currentY); 116 | currentX += timeStep*velocity.x; 117 | currentY += timeStep*velocity.y; 118 | positions.add(new PVector(currentX,currentY)); 119 | } 120 | 121 | void show() 122 | { 123 | strokeWeight(sw); 124 | float tt = (t+tOffset)%1; // particles don't start at the same time on different paths 125 | int arrayLength = positions.size(); 126 | 127 | // replacement technique on path with numberOfParticlesOnPath particles 128 | // and linear interpolation between positions recorded during simulation 129 | for(int i=0;i [0,1], with a parameter g: 18 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 19 | double ease(double p, double g) { 20 | if (p < 0.5) 21 | return 0.5 * Math.pow(2*p, g); 22 | else 23 | return 1 - 0.5 * Math.pow(2*(1 - p), g); 24 | } 25 | 26 | double c01(double x) 27 | { 28 | return Math.min(1,Math.max(0,x)); 29 | } 30 | 31 | void draw() 32 | { 33 | if (!recording) // test mode... 34 | { 35 | t = (mouseX*1.3/width)%1; 36 | c = mouseY*1.0/height; 37 | if (mousePressed) 38 | println(c); 39 | draw_(); 40 | } 41 | else // render mode... 42 | { 43 | for (int i=0; i=0) // if true, stop recursion 169 | { 170 | if(childIndex==0) // only show if it's a child 0 (not clean :( ) 171 | { 172 | // drawing a dot... 173 | float reduce = constrain(map(param,0.1,0,1,0),0,1)*1.15; // size change parameter if we're close to splitting 174 | strokeWeight(0.95*L*reduce); 175 | stroke(255); 176 | point(0,0); 177 | } 178 | } 179 | else 180 | { 181 | float growth = pow(constrain(map(param,0,-0.1,0,1),0,1),1.7); // size change parameter if we're close to splitting 182 | PVector pos = path2(p); 183 | pos.mult(0.5*L); 184 | push(); 185 | translate(pos.x,pos.y); 186 | 187 | scale(0.5); // with this scale change during recursions, we don't have to adjust pixel positions with factors 188 | scale(growth); // just for splitting, generally at one, no scale change here 189 | 190 | // to show yourself you show your children "one loop time ago" (p-1) 191 | for(int i=1;i [0,1], with a parameter g: 14 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 15 | float ease(float p, float g) { 16 | if (p < 0.5) 17 | return 0.5 * pow(2*p, g); 18 | else 19 | return 1 - 0.5 * pow(2*(1 - p), g); 20 | } 21 | 22 | void draw() 23 | { 24 | if (!recording) // test mode... 25 | { 26 | t = (mouseX*1.3/width)%1; 27 | c = mouseY*1.0/height; 28 | if (mousePressed) 29 | println(c); 30 | draw_(); 31 | } 32 | else // render mode... 33 | { 34 | for (int i=0; i y swap) 160 | if(level%2==0) positions[level-1] = new PVector(positions[level-1].y,positions[level-1].x); 161 | } 162 | } 163 | } 164 | 165 | Point [] array = new Point[n]; 166 | 167 | // go from v1 to v2 with rotation around their middle 168 | PVector rotater(PVector v1,PVector v2,float p,boolean orientation) // p is the progress in this interpolation, in [0,1] 169 | { 170 | PVector middle = v1.copy().add(v2).mult(0.5); 171 | PVector middleToV1 = v1.copy().sub(middle); 172 | 173 | float angle = atan2(middleToV1.y,middleToV1.x); 174 | float o = (orientation?-1:1); 175 | float r = middleToV1.mag(); 176 | 177 | return new PVector(middle.x+r*cos(angle+o*PI*p),middle.y+r*sin(angle+o*PI*p)); 178 | } 179 | 180 | // easing function taken from https://easings.net/#easeOutElastic 181 | float easeOutElastic(float x) 182 | { 183 | float c4 = (2*PI)/3; 184 | if(x<=0) return 0; 185 | if(x>=1) return 1; 186 | return pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1; 187 | } 188 | 189 | void drawCurve(float p) // p (in [0,1)) will simply be the time t 190 | { 191 | p = (p+12345-0.05)%1; // keep p in [0,1[, classic 12345 to make sure it's positive before modulo 192 | 193 | p = constrain(map(p,0,0.88,0,1),0,1); // transform p to do nothing for some time 194 | 195 | stroke(255); 196 | strokeWeight(1.4); 197 | noFill(); 198 | 199 | beginShape(); 200 | for(int i=0;i=1) return 1; 171 | return pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1; 172 | } 173 | 174 | void showDot(float p0, float vertexPositionOffset) 175 | { 176 | float p = p0 * numberOfStopPositions; // map p0 input from [0,1] to stop position index range 177 | float frc = (p+1234)%1; // fractional part indicating progress between 2 stop positions 178 | 179 | float moveProgression = constrain(3.8*frc, 0, 1); // speedup + constrain for blocked movement, makes the dots stop 180 | 181 | float q = (floor(p) + moveProgression)/numberOfStopPositions; // q in [0,1] indicates position on the curve 182 | PVector pos = position(q, vertexPositionOffset); 183 | 184 | // dot drawing parameters 185 | float sz = 10; 186 | float intensity = pow(sin(PI*moveProgression), 5.0); 187 | intensity = easeOutElastic(intensity); 188 | 189 | push(); 190 | translate(pos.x,pos.y); 191 | stroke(255); 192 | strokeWeight(1.0); 193 | fill(0); 194 | ellipse(0, 0, sz - 5*intensity, sz - 5*intensity); 195 | 196 | if(vertexPositionOffset == 0) // if first type of dot, show white dot in circle 197 | { 198 | strokeWeight(2.0+(sz-5)*intensity); 199 | point(0,0); 200 | } 201 | pop(); 202 | } 203 | 204 | // replacement technique (https://bleuje.com/tutorial4/) 205 | void showDotsReplacement() 206 | { 207 | int K = numberOfStopPositions - 7; // number of drawn dots, implies there will be 7 moving areas of changes :) 208 | 209 | for(int i=0;i [0,1], with a parameter g: 16 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 17 | float ease(float p, float g) { 18 | if (p < 0.5) 19 | return 0.5 * pow(2*p, g); 20 | else 21 | return 1 - 0.5 * pow(2*(1 - p), g); 22 | } 23 | 24 | // defines a map function variant to constrain or not in target interval (exists in openFrameworks) 25 | float map(float x, float a, float b, float c, float d, boolean constr) 26 | { 27 | return constr ? constrain(map(x,a,b,c,d),min(c,d),max(c,d)) : map(x,a,b,c,d); 28 | } 29 | 30 | // short one to map an x from [a,b] to [0,1] and constrain 31 | float mp01(float x, float a, float b) 32 | { 33 | return map(x,a,b,0,1,true); 34 | } 35 | 36 | //----------------------------------- 37 | 38 | void draw() 39 | { 40 | if (!recording) // test mode... 41 | { 42 | t = (mouseX*1.3/width)%1; 43 | c = mouseY*1.0/height; 44 | if (mousePressed) 45 | println(c); 46 | draw_(); 47 | } 48 | else // render mode... 49 | { 50 | for (int i=0; i [0,1], with a parameter g: 19 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 20 | float ease(float p, float g) { 21 | if (p < 0.5) 22 | return 0.5 * pow(2*p, g); 23 | else 24 | return 1 - 0.5 * pow(2*(1 - p), g); 25 | } 26 | 27 | //----------------------------------- 28 | 29 | void draw() { 30 | if (!recording) { 31 | t = (mouseX*1.3/width)%1; 32 | c = mouseY*1.0/height; 33 | if (mousePressed) 34 | println(c); 35 | draw_(); 36 | } else { 37 | for (int i=0; i1.0 ) 65 | minDist = min( minDist, dot( 0.5*(vToClosestPoint+vToPoint), normalize(vToPoint-vToClosestPoint) ) ); 66 | } 67 | 68 | // now something specific to this animation: 69 | // also using distance to "border points" for nicer design on the borders of the image 70 | int numberOfBorderPoints = curveSize*4; 71 | for (int i = 0; i < numberOfBorderPoints; i++) { 72 | vec2 point = getBorderPoint(i); 73 | vec2 vToPoint = point - uv; 74 | if( dot(vToClosestPoint-vToPoint,vToClosestPoint-vToPoint)>1.0 ) 75 | minDist = min( minDist, dot( 0.5*(vToClosestPoint+vToPoint), normalize(vToPoint-vToClosestPoint) ) ); 76 | } 77 | 78 | 79 | float stripes = 0.47*min(abs(minDist-4),abs(minDist-10)); // function of shape \/\/, for 2 stripes 80 | float brightness = smoothstep(0.7,0.3,stripes); 81 | vec3 color = vec3(brightness); 82 | 83 | gl_FragColor = vec4(color, 1.0); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /code/permutationpatternspropagation/README.md: -------------------------------------------------------------------------------- 1 | ## Permutation patterns propagation 2 | 3 | ![permutation patterns propagation gif](https://bleuje.com/gifset/2021/2021_11_cyclespropagation2.gif) 4 | 5 | ## [code](https://github.com/Bleuje/processing-animations-code/blob/main/code/permutationpatternspropagation/permutationpatternspropagation.pde) 6 | 7 | #### some used techniques 8 | 9 | simulation 10 | 11 | ### links 12 | 13 | On bleuje site: https://bleuje.com/gifanimationsite/single/permutationpatternspropagation/ 14 | 15 | On social media: 16 | - tumblr: https://necessary-disorder.tumblr.com/post/656611026798772224 17 | - twitter: https://twitter.com/etiennejcb/status/1655991294940807208 18 | -------------------------------------------------------------------------------- /code/permutationpatternspropagation/permutationpatternspropagation.pde: -------------------------------------------------------------------------------- 1 | // Processing code by Etienne Jacob 2 | // motion blur template by beesandbombs, explanation/article: https://bleuje.com/tutorial6/ 3 | // See the license information at the end of this file. 4 | // View the rendered result at: https://bleuje.com/gifanimationsite/single/permutationpatternspropagation/ 5 | 6 | ////////////////////////////////////////////////////////////////////////////// 7 | // Start of template 8 | 9 | int[][] result; // pixel colors buffer for motion blur 10 | float t; // time global variable in [0,1[ 11 | float c; // other global variable for testing things, controlled by mouse 12 | 13 | // ease in and out, [0,1] -> [0,1], with a parameter g: 14 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 15 | float ease(float p, float g) { 16 | if (p < 0.5) 17 | return 0.5 * pow(2*p, g); 18 | else 19 | return 1 - 0.5 * pow(2*(1 - p), g); 20 | } 21 | 22 | void draw() 23 | { 24 | if (!recording) // test mode... 25 | { 26 | t = (mouseX*1.3/width)%1; 27 | c = mouseY*1.0/height; 28 | if (mousePressed) 29 | println(c); 30 | draw_(); 31 | } 32 | else // render mode... 33 | { 34 | for (int i=0; i=-HALF_PI-QUARTER_PI-eps)&&(angleToLocalCenter<=-HALF_PI+QUARTER_PI-eps)) return new PVector(i+1,j); 119 | if((angleToLocalCenter>=-HALF_PI+QUARTER_PI-eps)&&(angleToLocalCenter<=QUARTER_PI-eps)) return new PVector(i,j+1); 120 | if((angleToLocalCenter>=QUARTER_PI-eps)&&(angleToLocalCenter<=QUARTER_PI+HALF_PI-eps)) return new PVector(i-1,j); 121 | else return new PVector(i,j-1); 122 | } 123 | else // same thing as previous if branch but opposite direction 124 | { 125 | if((angleToLocalCenter>=-HALF_PI-QUARTER_PI+eps)&&(angleToLocalCenter<=-HALF_PI+QUARTER_PI+eps)) return new PVector(i-1,j); 126 | if((angleToLocalCenter>=-HALF_PI+QUARTER_PI+eps)&&(angleToLocalCenter<=QUARTER_PI+eps)) return new PVector(i,j-1); 127 | if((angleToLocalCenter>=QUARTER_PI+eps)&&(angleToLocalCenter<=QUARTER_PI+HALF_PI+eps)) return new PVector(i+1,j); 128 | else return new PVector(i,j+1); 129 | } 130 | } 131 | return new PVector(0,0); // dummy value that will never be returned because we will always have a mainType of 0, 1 or 2 and fall into previous ifs 132 | } 133 | 134 | 135 | 136 | // "slices" pattern : 137 | /* the slices looks like this, the rotation direction changes every other slice 138 | 139 | -- -- 140 | || || 141 | || || 142 | || || 143 | || || 144 | -- -- 145 | 146 | */ 147 | // a and b are "local" i and j, i and j are the global position 148 | PVector sliceNextPosition(int a,int b,int i, int j,int Height) 149 | { 150 | if((i/2)%2==0) // "every other slice" 151 | { 152 | // in the 4 following lines, hard coded next positions on the 4 corner positions (top and bottom of the slice) 153 | if((b==0)&&(a%2==0)) return new PVector(i+1,j); 154 | if((b==0)&&(a%2==1)) return new PVector(i,j+1); 155 | if((b==Height-1)&&(a%2==0)) return new PVector(i,j-1); 156 | if((b==Height-1)&&(a%2==1)) return new PVector(i-1,j); 157 | // for other positions just go down or up depending on the side we're on 158 | if(a%2==0) return new PVector(i,j-1); 159 | if(a%2==1) return new PVector(i,j+1); 160 | } 161 | else // same as previous case but other direction 162 | { 163 | if((b==0)&&(a%2==0)) return new PVector(i,j+1); 164 | if((b==0)&&(a%2==1)) return new PVector(i-1,j); 165 | if((b==Height-1)&&(a%2==0)) return new PVector(i+1,j); 166 | if((b==Height-1)&&(a%2==1)) return new PVector(i,j-1); 167 | 168 | if(a%2==0) return new PVector(i,j+1); 169 | if(a%2==1) return new PVector(i,j-1); 170 | } 171 | return new PVector(0,0); 172 | } 173 | 174 | int numPositionsPerMove = 3; // 3 means we go 2 positions further using the current next positions pattern 175 | int numberOfPatterns = 3; 176 | float transitionTime = 0.27; // fraction of time used for each particle move. Because we have 3 patterns so 3 moves, it can't be larger than 1/3 = 0.333333 177 | // (also has to be smaller than 1/3 due to position change that can create a move request earlier than the one on position before move) 178 | 179 | float iterationsPerCycle = 2000; // simulation quality. timeStep = 1.0/iterationsPerCycle later; 180 | int numberOfRepeats = 4; // repeating the loop many times to try to achieve stability and perfect looping 181 | float time = 0; // will be incremented during simulation 182 | 183 | 184 | // class to have the list of positions of each move, 185 | // and a function ("unMappedPosition") to give the continuous change of position from continuous time 186 | class Move 187 | { 188 | int start_i; 189 | int start_j; 190 | int type; 191 | float startTime,endTime; 192 | PVector endPos; 193 | 194 | PVector [] path = new PVector[numPositionsPerMove]; 195 | 196 | Move(PVector startPos,int type_,float tm) 197 | { 198 | start_i = round(startPos.x); 199 | start_j = round(startPos.y); 200 | type = type_; 201 | startTime = tm; 202 | endTime = startTime+transitionTime; 203 | 204 | 205 | // finding the move's path iteratively, using the next positions field... 206 | int curi = start_i; 207 | int curj = start_j; 208 | for(int k=0;k positions = new ArrayList(); // not in pixels, just with grid indices 267 | 268 | void update() // simulation update 269 | { 270 | // if we're not moving we check is the waves trigger a new move 271 | if(!isMoving && time >= triggerer(currentPos,doneMoves)) 272 | { 273 | currentMove = new Move(currentPos,doneMoves%numberOfPatterns,time); 274 | isMoving = true; 275 | doneMoves++; 276 | } 277 | else if(isMoving && time >= currentMove.endTime) // if we were moving we check if we're now past the move's endTime, in this case we set isMoving to false 278 | { 279 | isMoving = false; 280 | currentPos = currentMove.endPos; 281 | } 282 | 283 | // now adding the current position to the list 284 | if(!isMoving) 285 | { 286 | positions.add(currentPos); 287 | } 288 | else 289 | { 290 | positions.add(currentMove.unMappedPosition(time)); // when we're moving, the Move class has a member function to give position in function of time 291 | } 292 | } 293 | 294 | 295 | 296 | // show() using the list of positions from simulation to get position at any time of the loop, 297 | // with linear interpolation, 298 | // using positions of the last cycle of the simulation 299 | void show() 300 | { 301 | float t2 = (t+(numberOfRepeats-1))*0.9999999; 302 | float ifl = iterationsPerCycle*t2; 303 | int i1 = floor(ifl); 304 | int i2 = i1+1; 305 | float lp = ifl-i1; 306 | 307 | PVector v1 = positions.get(i1); 308 | PVector v2 = positions.get(i2); 309 | 310 | PVector u = v1.copy().lerp(v2,lp); 311 | 312 | PVector pixelPos = convertToPixelPosition(u.x,u.y); 313 | 314 | // design : drawing the particle with an ellipse with a smaller white dot inside it 315 | stroke(255); 316 | strokeWeight(1.5); 317 | fill(0); 318 | ellipse(pixelPos.x,pixelPos.y,12,12); 319 | 320 | strokeWeight(2.6); 321 | point(pixelPos.x,pixelPos.y); 322 | } 323 | } 324 | 325 | Particle [][] array = new Particle[gridSize][gridSize]; 326 | 327 | void simulate() 328 | { 329 | println("Starting simulation..."); 330 | float timeStep = 1.0/iterationsPerCycle; 331 | for(int k=0;k 0) 128 | { 129 | // 3D -> 2D projection formula 130 | float x2D = projectionFactor * position.x / zDistanceFromCamera; 131 | float y2D = projectionFactor * position.y / zDistanceFromCamera; 132 | 133 | PVector pixelPosition = new PVector(x2D,y2D); 134 | 135 | if(isDigit) 136 | { 137 | float textSz = 29; 138 | float scl = 0.1 * sizeFactor * projectionFactor / zDistanceFromCamera; // size change due to the 3D -> 2D projection 139 | 140 | push(); 141 | translate(pixelPosition.x -0.5*textSz/2, pixelPosition.y + 0.5*textSz/2); // slight correction translate 142 | scale(scl); 143 | rotate(0.03 * sin(TAU*(t-0.3)) * PI); // slight 2D rotation 144 | 145 | fill(255,alphaFactor2 * 275); 146 | noStroke(); 147 | textSize(textSz); 148 | 149 | text(digitValue,0,0); 150 | 151 | pop(); 152 | } 153 | else // simple dot, used for dashed curve drawing 154 | { 155 | float sz = sizeFactor * projectionFactor / zDistanceFromCamera; // size change due to the 3D -> 2D projection 156 | 157 | stroke(255,21 * alphaFactor2); 158 | strokeWeight(sz); 159 | 160 | point(pixelPosition.x,pixelPosition.y); 161 | } 162 | } 163 | } 164 | 165 | void showDashedCurve() 166 | { 167 | int dashParam = 20; // number of dots on each segment of the curve 168 | 169 | float R2 = 1.05*R; // larger sphere radius for dashed curve 170 | 171 | int m = 1000; 172 | for(int i=0;i dashParam) continue; // technique to skip drawing half of the time... 175 | 176 | // spherical coordinates 177 | float theta = map((i + 38 * t * float(dashParam)) % m, 0, m, 0, PI); // theta movement, with loop (%m) 178 | float phi = (t + 0.006) * TAU; 179 | 180 | // cartesian coordiantes from spherical coordinates 181 | float x = R2*sin(theta)*cos(phi); 182 | float y = R2*cos(theta); 183 | float z = R2*sin(theta)*sin(phi); 184 | 185 | PVector pos = new PVector(x,y,z); 186 | float sizeFactor = 0.73; 187 | boolean isDigit = false; 188 | float alphaFactor = 14.0; 189 | 190 | showParticle(pos, sizeFactor, isDigit, 0, alphaFactor); 191 | } 192 | 193 | 194 | // drawing the big dots at the sphere poles... 195 | PVector pole1Position = new PVector(0,R2,0); 196 | PVector pole2Position = new PVector(0,-R2,0); 197 | showParticle(pole1Position, 4, false, 0, 1000); 198 | showParticle(pole2Position, 4, false, 0, 1000); 199 | } 200 | 201 | class Digit 202 | { 203 | PVector position0; 204 | int digitValue; 205 | 206 | Digit(int i) 207 | { 208 | digitValue = i%10; 209 | 210 | // setting (x,y,z) position on black sphere (position at start and end) 211 | // using special formulas to get evenly distributed positions 212 | // see https://stackoverflow.com/a/26127012 213 | float phi = PI * (3. - sqrt(5.)); 214 | float theta = phi*i; 215 | float y = map(i,0,numberOfDigits-1,1,-1); 216 | float radius = sqrt(1 - y * y); 217 | y *= R; 218 | float x = cos(theta) * R * radius; 219 | float z = sin(theta) * R * radius; 220 | 221 | position0 = new PVector(x,y,z); 222 | } 223 | 224 | void show(float p) // p is the progress parameter in [0,1] 225 | { 226 | float delayFromAngle = atan2(position0.z,position0.x)/TAU; 227 | 228 | // without the replacement technique, the wave must pass 2 times 229 | // so the previous delay (delayFromAngle) must be divided by 2 230 | float delay = delayFromAngle / 2; 231 | 232 | float delayedP = (1234 + p - delay)%1; 233 | float angleChangeProgress = -pow(1-delayedP,2.0); // sudden angle change, and stops moving at the end 234 | // this formula was found with pen and paper, for it to match the wave speed 235 | 236 | // getting the position on sphere 237 | PVector position = rotY(position0, angleChangeProgress * TAU); 238 | 239 | 240 | // digit size style... 241 | float wo = (1234 - 4*p + delayFromAngle)%1; 242 | float wv = pow(c01(sin(PI*wo)),3.3); 243 | float sizeFactor = 2.8 + 1.3*wv; 244 | // size bump style stuff when starting to move 245 | float bumpMax = 0.5; 246 | float bump = 1 + bumpMax*pow(1-c01(27*delayedP),2.3); 247 | float bump2 = 1 + bumpMax*pow(1-c01(100*(1-delayedP)),2.0); 248 | 249 | float sizeFactor2 = sizeFactor * bump * bump2; 250 | boolean isDigit = true; 251 | float alphaFactor = 1.0; 252 | 253 | showParticle(position, sizeFactor2, isDigit, digitValue, alphaFactor); 254 | } 255 | 256 | // replacement technique (https://bleuje.com/tutorial4/) 257 | void show() 258 | { 259 | int K = 2; 260 | for(int i=0;i digitsArray = new ArrayList(); 268 | 269 | void setup() 270 | { 271 | size(600,600,P2D); 272 | result = new int[width*height][3]; 273 | smooth(8); 274 | 275 | // the font that was used, deactivated here so that it runs without the font file 276 | //mono = createFont("Manrope-Medium.ttf", 128); 277 | //textFont(mono); 278 | 279 | for(int i=0;i code here : https://gist.github.com/Bleuje/fce86ef35b66c4a2b6a469b27163591e 5 | // See the license information at the end of this file. 6 | // View the rendered result at: https://bleuje.com/gifanimationsite/single/radialcollapse/ 7 | 8 | // A lot of hidden blocks are drawn, I don't really care, but it's very slow 9 | 10 | ////////////////////////////////////////////////////////////////////////////// 11 | // Start of template 12 | 13 | int[][] result; // pixel colors buffer for motion blur 14 | float t; // time global variable in [0,1[ 15 | float c; // other global variable for testing things, controlled by mouse 16 | 17 | // ease in and out, [0,1] -> [0,1], with a parameter g: 18 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 19 | float ease(float p, float g) { 20 | if (p < 0.5) 21 | return 0.5 * pow(2*p, g); 22 | else 23 | return 1 - 0.5 * pow(2*(1 - p), g); 24 | } 25 | 26 | void draw() 27 | { 28 | if (!recording) // test mode... 29 | { 30 | t = (mouseX*1.3/width)%1; 31 | c = mouseY*1.0/height; 32 | if (mousePressed) 33 | println(c); 34 | draw_(); 35 | } 36 | else // render mode... 37 | { 38 | for (int i=0; i=offset which is the same as pp>=0 145 | float pp = p - offset; 146 | float q = max(0,0.5*pp); // no fall while q=0 147 | 148 | float zFall = pow(RATIO,p)*1700*pow(q,2.0); // fall length 149 | float blockHeight = DZ*pow(RATIO,p); // block height, changing with fractal zoom factor 150 | float z = -zFall-l*blockHeight; // fall + height change because of height index 151 | 152 | push(); 153 | translate(0,0,z); 154 | 155 | // changing stroke weight with p and noise 156 | float f = 0.5+5*pow(map((float)noise.eval(seed+1.0*p,0),-1,1,0,1),3.0); 157 | strokeWeight(sw*f); 158 | stroke(col+0.34*z); 159 | fill(0); 160 | 161 | beginShape(); 162 | vertex(x1,y1,0); 163 | vertex(x2,y2,0); 164 | vertex(x3,y3,0); 165 | vertex(x4,y4,0); 166 | endShape(CLOSE); 167 | beginShape(); 168 | vertex(x1,y1,0); 169 | vertex(x2,y2,0); 170 | vertex(x2,y2,-blockHeight); 171 | vertex(x1,y1,-blockHeight); 172 | endShape(CLOSE); 173 | beginShape(); 174 | vertex(x3,y3,0); 175 | vertex(x4,y4,0); 176 | vertex(x4,y4,-blockHeight); 177 | vertex(x3,y3,-blockHeight); 178 | endShape(CLOSE); 179 | beginShape(); 180 | vertex(x1,y1,0); 181 | vertex(x4,y4,0); 182 | vertex(x4,y4,-blockHeight); 183 | vertex(x1,y1,-blockHeight); 184 | endShape(CLOSE); 185 | beginShape(); 186 | vertex(x2,y2,0); 187 | vertex(x3,y3,0); 188 | vertex(x3,y3,-blockHeight); 189 | vertex(x2,y2,-blockHeight); 190 | endShape(CLOSE); 191 | beginShape(); 192 | vertex(x1,y1,-blockHeight); 193 | vertex(x2,y2,-blockHeight); 194 | vertex(x3,y3,-blockHeight); 195 | vertex(x4,y4,-blockHeight); 196 | endShape(CLOSE); 197 | pop(); 198 | } 199 | 200 | // replacement technique : 201 | int K = 5; 202 | void show() 203 | { 204 | for(int ii=-2*K;ii [0,1], with a parameter g: 14 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 15 | float ease(float p, float g) { 16 | if (p < 0.5) 17 | return 0.5 * pow(2*p, g); 18 | else 19 | return 1 - 0.5 * pow(2*(1 - p), g); 20 | } 21 | 22 | void draw() 23 | { 24 | if (!recording) // test mode... 25 | { 26 | t = (mouseX*1.3/width)%1; 27 | c = mouseY*1.0/height; 28 | if (mousePressed) 29 | println(c); 30 | draw_(); 31 | } 32 | else // render mode... 33 | { 34 | for (int i=0; i positions = new ArrayList(); 93 | ArrayList sizes = new ArrayList(); 94 | 95 | DotPath() 96 | { 97 | float x = startX; 98 | float y = startY; 99 | positions.add(new PVector(x,y)); 100 | 101 | sizes.add(0.0); // start with a size 0 102 | 103 | for(int i=0;iwidth-emptyMargin){ 139 | x -= 2*jumpLength; 140 | } 141 | if(xheight-emptyMargin){ 145 | y -= 2*jumpLength; 146 | } 147 | if(y [0,1], with a parameter g: 14 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 15 | float ease(float p, float g) { 16 | if (p < 0.5) 17 | return 0.5 * pow(2*p, g); 18 | else 19 | return 1 - 0.5 * pow(2*(1 - p), g); 20 | } 21 | 22 | void draw() 23 | { 24 | if (!recording) // test mode... 25 | { 26 | t = (mouseX*1.3/width)%1; 27 | c = mouseY*1.0/height; 28 | if (mousePressed) 29 | println(c); 30 | draw_(); 31 | } 32 | else // render mode... 33 | { 34 | for (int i=0; i=DEPTH) return; 94 | 95 | float sw = map(iterationsIndex+p,0,DEPTH,SWMAX,0); 96 | strokeWeight(sw); 97 | 98 | triangle(v1.x,v1.y,v2.x,v2.y,v3.x,v3.y); 99 | 100 | // now let's draw the triangles inside... 101 | 102 | PVector u1 = v1.copy().add(v2).mult(0.5); // position at the middle between v1 and v2 103 | PVector u2 = v2.copy().add(v3).mult(0.5); // position at the middle between v2 and v3 104 | PVector u3 = v3.copy().add(v1).mult(0.5); // position at the middle between v3 and v1 105 | 106 | drawFractal(p,v1,u1,u3,iterationsIndex+1); 107 | drawFractal(p,u1,v2,u2,iterationsIndex+1); 108 | drawFractal(p,u3,u2,v3,iterationsIndex+1); 109 | } 110 | 111 | // first level with some new fractal triangles coming in 112 | void drawThing(float p,PVector v1,PVector v2,PVector v3) 113 | { 114 | PVector u1 = v1.copy().add(v2).mult(0.5); // position at the middle between v1 and v2 115 | PVector u2 = v2.copy().add(v3).mult(0.5); // position at the middle between v2 and v3 116 | PVector u3 = v3.copy().add(v1).mult(0.5); // position at the middle between v3 and v1 117 | 118 | PVector w1 = v2.copy().lerp(u1,p); // go from v2 to u1 with time 119 | PVector w2 = v2.copy().lerp(u2,p); // go from v2 to u2 with time 120 | PVector w3 = v3.copy().lerp(u2,p); // go from v3 to u2 with time 121 | PVector w4 = v3.copy().lerp(u3,p); // go from v3 to u3 with time 122 | 123 | drawFractal(p,v1,w1,w4,0); 124 | drawFractal(p,w1,v2,w2,0); 125 | drawFractal(p,w4,w3,v3,0); 126 | } 127 | 128 | void draw_(){ 129 | background(0); 130 | push(); 131 | translate(width/2,height/2+50); 132 | 133 | // defining the main triangle vertices' positions 134 | float a1 = TWO_PI*0.0/3.0-HALF_PI; 135 | PVector v1 = new PVector(R*cos(a1),R*sin(a1)); 136 | float a2 = TWO_PI*1.0/3.0-HALF_PI; 137 | PVector v2 = new PVector(R*cos(a2),R*sin(a2)); 138 | float a3 = TWO_PI*2.0/3.0-HALF_PI; 139 | PVector v3 = new PVector(R*cos(a3),R*sin(a3)); 140 | 141 | noFill(); 142 | 143 | blendMode(ADD); // to add layers of red, green and blue drawings, for chromatic aberration style 144 | 145 | for(int col=0;col<3;col++) // RGB chromatic aberration loop 146 | { 147 | stroke(255*int(col==0),255*int(col==1),255*int(col==2)); // draw in red, green or blue depending on col 148 | 149 | strokeWeight(SWMAX); 150 | triangle(v1.x,v1.y,v2.x,v2.y,v3.x,v3.y); // drawing the main triangle 151 | 152 | int N = 16; 153 | for(int i=0;i [0,1], with a parameter g: 13 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 14 | float ease(float p, float g) { 15 | if (p < 0.5) 16 | return 0.5 * pow(2*p, g); 17 | else 18 | return 1 - 0.5 * pow(2*(1 - p), g); 19 | } 20 | 21 | void draw() 22 | { 23 | if (!recording) // test mode... 24 | { 25 | t = (mouseX*1.3/width)%1; 26 | c = mouseY*1.0/height; 27 | if (mousePressed) 28 | println(c); 29 | draw_(); 30 | } 31 | else // render mode... 32 | { 33 | for (int i=0; i=DEPTH) return; 93 | 94 | float sw = map(iterationsIndex+p,0,DEPTH,SWMAX,0); 95 | strokeWeight(sw); 96 | 97 | triangle(v1.x,v1.y,v2.x,v2.y,v3.x,v3.y); 98 | 99 | // now let's draw the triangles inside... 100 | 101 | PVector u1 = v1.copy().add(v2).mult(0.5); // position at the middle between v1 and v2 102 | PVector u2 = v2.copy().add(v3).mult(0.5); // position at the middle between v2 and v3 103 | PVector u3 = v3.copy().add(v1).mult(0.5); // position at the middle between v3 and v1 104 | 105 | drawFractal(p,v1,u1,u3,iterationsIndex+1); 106 | drawFractal(p,u1,v2,u2,iterationsIndex+1); 107 | drawFractal(p,u3,u2,v3,iterationsIndex+1); 108 | } 109 | 110 | // first level with some new fractal triangles coming in 111 | void drawThing(float p,PVector v1,PVector v2,PVector v3) 112 | { 113 | PVector u1 = v1.copy().add(v2).mult(0.5); // position at the middle between v1 and v2 114 | PVector u2 = v2.copy().add(v3).mult(0.5); // position at the middle between v2 and v3 115 | PVector u3 = v3.copy().add(v1).mult(0.5); // position at the middle between v3 and v1 116 | 117 | PVector w1 = v2.copy().lerp(u1,p); // go from v2 to u1 with time 118 | PVector w2 = v2.copy().lerp(u2,p); // go from v2 to u2 with time 119 | PVector w3 = v3.copy().lerp(u2,p); // go from v3 to u2 with time 120 | PVector w4 = v3.copy().lerp(u3,p); // go from v3 to u3 with time 121 | 122 | drawFractal(p,v1,w1,w4,0); 123 | drawFractal(p,w1,v2,w2,0); 124 | drawFractal(p,w4,w3,v3,0); 125 | } 126 | 127 | void draw_(){ 128 | background(0); 129 | push(); 130 | translate(width/2,height/2+60*800/600); 131 | scale(800.0/600); 132 | 133 | // defining the main triangle vertices' positions 134 | float a1 = TWO_PI*0.0/3.0-HALF_PI; 135 | PVector v1 = new PVector(R*cos(a1),R*sin(a1)); 136 | float a2 = TWO_PI*1.0/3.0-HALF_PI; 137 | PVector v2 = new PVector(R*cos(a2),R*sin(a2)); 138 | float a3 = TWO_PI*2.0/3.0-HALF_PI; 139 | PVector v3 = new PVector(R*cos(a3),R*sin(a3)); 140 | 141 | noFill(); 142 | stroke(255); 143 | strokeWeight(SWMAX); 144 | 145 | triangle(v1.x,v1.y,v2.x,v2.y,v3.x,v3.y); // drawing the main triangle 146 | 147 | float t2 = 3*t; 148 | 149 | float rotationIndex = floor(t2)%3; // we draw the scene with 3 different successive rotations 150 | float q = t2%1; // fractional part for time inside current rotation 151 | 152 | push(); 153 | rotate(TWO_PI*rotationIndex/3); 154 | drawThing(ease(constrain(q,0,1),1.75),v1,v2,v3); 155 | pop(); 156 | 157 | 158 | pop(); 159 | } 160 | 161 | 162 | /* License: 163 | * 164 | * Copyright (c) 2021, 2024 Etienne Jacob 165 | * 166 | * All rights reserved. 167 | * 168 | * This code after the template and the related animations are the property of the 169 | * copyright holder. Any reproduction, distribution, or use of this material, 170 | * in whole or in part, without the express written permission of the copyright 171 | * holder is strictly prohibited. 172 | */ 173 | -------------------------------------------------------------------------------- /code/sinusoidspacking/README.md: -------------------------------------------------------------------------------- 1 | ## Sinusoids packing 2 | 3 | ![sinusoids packing gif](https://bleuje.com/gifset/2024/2024_sinusoidspacking.gif) 4 | 5 | ## [code](https://github.com/Bleuje/processing-animations-code/blob/main/code/sinusoidspacking/sinusoidspacking.pde) 6 | 7 | #### some used techniques 8 | 9 | circle packing, lighting from normal 10 | 11 | ### links 12 | 13 | On bleuje site: https://bleuje.com/gifanimationsite/single/sinusoidspacking/ 14 | 15 | On social media: 16 | - twitter: https://x.com/etiennejcb/status/1845186249377587605 17 | - instagram: https://www.instagram.com/p/DBGBT2ttI9Z/ 18 | -------------------------------------------------------------------------------- /code/sinusoidspacking/sinusoidspacking.pde: -------------------------------------------------------------------------------- 1 | // Processing code by Etienne Jacob 2 | // motion blur template by beesandbombs, explanation/article: https://bleuje.com/tutorial6/ 3 | // See the license information at the end of this file. 4 | 5 | ////////////////////////////////////////////////////////////////////////////// 6 | // Start of template 7 | 8 | int[][] result; // pixel colors buffer for motion blur 9 | float t; // time global variable in [0,1[ 10 | float c; // other global variable for testing things, controlled by mouse 11 | 12 | //----------------------------------- 13 | 14 | void draw() { 15 | if (!recording) // test mode... 16 | { 17 | t = (mouseX*1.3/width)%1; 18 | c = mouseY*1.0/height; 19 | if (mousePressed) 20 | println(c); 21 | draw_(); 22 | } else // render mode... 23 | { 24 | for (int i=0; i code here : https://gist.github.com/Bleuje/fce86ef35b66c4a2b6a469b27163591e 5 | // See the license information at the end of this file. 6 | // View the rendered result at: https://bleuje.com/gifanimationsite/single/spherewave/ 7 | 8 | ////////////////////////////////////////////////////////////////////////////// 9 | // Start of template 10 | 11 | int[][] result; // pixel colors buffer for motion blur 12 | float t; // time global variable in [0,1[ 13 | float c; // other global variable for testing things, controlled by mouse 14 | 15 | //----------------------------------- 16 | // some generally useful functions... 17 | 18 | float c01(float x) 19 | { 20 | return constrain(x,0,1); 21 | } 22 | 23 | // ease in and out, [0,1] -> [0,1], with a parameter g: 24 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 25 | float ease(float p, float g) { 26 | if (p < 0.5) 27 | return 0.5 * pow(2*p, g); 28 | else 29 | return 1 - 0.5 * pow(2*(1 - p), g); 30 | } 31 | 32 | // defines a map function variant to constrain or not in target interval (exists in openFrameworks) 33 | float map(float x, float a, float b, float c, float d, boolean constr) 34 | { 35 | return constr ? constrain(map(x,a,b,c,d),min(c,d),max(c,d)) : map(x,a,b,c,d); 36 | } 37 | 38 | // short one to map an x from [a,b] to [0,1] and constrain 39 | float mp01(float x, float a, float b) 40 | { 41 | return map(x,a,b,0,1,true); 42 | } 43 | 44 | // reversed pow that does some kind of ease out, [0,1] -> [0,1], with a parameter g 45 | float pow_(float p,float g) 46 | { 47 | return 1-pow(1-p,g); 48 | } 49 | //----------------------------------- 50 | 51 | void draw() 52 | { 53 | if (!recording) // test mode... 54 | { 55 | t = (mouseX*1.3/width)%1; 56 | c = mouseY*1.0/height; 57 | if (mousePressed) 58 | println(c); 59 | draw_(); 60 | } 61 | else // render mode... 62 | { 63 | for (int i=0; i=1) return 1; 162 | return pow(2, -7 * x) * sin((x * 10 - 0.75) * c4) + 1; 163 | } 164 | 165 | class Particle 166 | { 167 | PVector pos1,pos2; 168 | float delay1; 169 | PVector orientation; 170 | float seed = random(10,1000); 171 | 172 | Particle(int i) 173 | { 174 | // setting (x,y,z) position on black sphere (position at start and end) 175 | // using special formulas to get evenly distributed positions 176 | // see https://stackoverflow.com/a/26127012 177 | float phi = PI * (3. - sqrt(5.)); 178 | float theta = phi*i; 179 | float y = map(i,0,n-1,1,-1); 180 | float radius = sqrt(1 - y * y); 181 | y *= blackSphereRadius; 182 | float x = cos(theta) * blackSphereRadius * radius; 183 | float z = sin(theta) * blackSphereRadius * radius; 184 | 185 | pos1 = new PVector(x,y,z); 186 | 187 | // orientation of rotation effect 188 | orientation = orienterField(pos1); 189 | 190 | float radius2 = blackSphereRadius+DR*pow(random(0.75)+0.25,1.4); 191 | pos2 = pos1.copy().normalize().mult(radius2); // position when particle left the black sphere 192 | // but so far without random displacement 193 | 194 | float pw = 2.0; 195 | float rd = random(1); 196 | float rd1 = mp01(rd,0,0.5); 197 | float rd2 = mp01(rd,0.5,1); 198 | float delay0 = 0.5*pow_(rd1,pw)+0.5*pow(rd2,pw); 199 | // delay for particles returning on the sphere with this offset, the above code defines a desired random distribution 200 | delay1 = delay0*0.45; 201 | } 202 | 203 | float sphereSize(float p) 204 | { 205 | float noiseRadius = 6.0; 206 | float ns = map((float)noise.eval(seed+noiseRadius*cos(TWO_PI*p),noiseRadius*sin(TWO_PI*p)),-1,1,0,1); 207 | return pow(ns,3.0)*2+0.5; 208 | } 209 | 210 | // noisy displacement 211 | PVector displacement(float p) 212 | { 213 | float noiseRadius = 1.4; 214 | float amplitude = 8; 215 | float noiseSpaceX = noiseRadius*cos(TWO_PI*p); 216 | float noiseSpaceY = noiseRadius*sin(TWO_PI*p); 217 | float dx = amplitude*(float)noise.eval(2*seed+noiseSpaceX,noiseSpaceY); 218 | float dy = amplitude*(float)noise.eval(3*seed+noiseSpaceX,noiseSpaceY); 219 | float dz = amplitude*(float)noise.eval(4*seed+noiseSpaceX,noiseSpaceY); 220 | // (looping noise but not a necessary property because the displacement is 0 at the begining) 221 | return new PVector(dx,dy,dz); 222 | } 223 | 224 | void show(float p) // p will just be the time t in [0,1] 225 | { 226 | p = (12345+p)%1; 227 | 228 | // IMPORTANT : time is reversed because I experimented and tried reversed time at some point (sorry about this) 229 | // after this line of code the progress with p is actually to go from sphere surface to displaced position and then doing the elastic easing to come back to the sphere's surface 230 | p = 1-p; // maybe try without this line of code to understand it better 231 | 232 | // q is how much the particle left the black sphere 233 | float q = mp01(p-delay1,0,0.25); 234 | q = ease(q,3.0); 235 | 236 | // go from position on sphere to displaced position (when time is not reversed) 237 | PVector pos = pos1.copy().lerp(pos2.copy().add(displacement(p)),q); 238 | 239 | // diagonal delay for main effect 240 | float delay2 = 0.3 - map(0.5*pos.y-0.5*pos.x,-150,150,0,0.3); 241 | 242 | float q2 = mp01(p-delay2,0.3,0.7); 243 | q2 = 1-easeOutElastic(pow(1-q2,2.0)); 244 | 245 | // use rotater function and orientation to go back to position on sphere (when time is not reversed) 246 | PVector v = rotater(pos,pos1,q2,orientation); 247 | 248 | float sz = lerp(1.0,sphereSize(p),0.2+0.8*pow(c01(sin(PI*p)),1.5)); // controlling particle size through time 249 | 250 | v = bounder(v); // keep position outside of black sphere 251 | 252 | push(); 253 | translate(v.x,v.y,v.z); 254 | 255 | sphereDetail(5); 256 | fill(255); 257 | noStroke(); 258 | 259 | sphere(sz*0.82); 260 | 261 | pop(); 262 | } 263 | 264 | } 265 | 266 | Particle [] particlesArray = new Particle[n]; 267 | 268 | void setup(){ 269 | size(600,600,P3D); 270 | result = new int[width*height][3]; 271 | 272 | noise = new OpenSimplexNoise(1234); 273 | 274 | for(int i=0;i [0,1], with a parameter g: 14 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 15 | float ease(float p, float g) { 16 | if (p < 0.5) 17 | return 0.5 * pow(2*p, g); 18 | else 19 | return 1 - 0.5 * pow(2*(1 - p), g); 20 | } 21 | 22 | void draw() 23 | { 24 | if (!recording) // test mode... 25 | { 26 | t = (mouseX*1.3/width)%1; 27 | c = mouseY*1.0/height; 28 | if (mousePressed) 29 | println(c); 30 | draw_(); 31 | } 32 | else // render mode... 33 | { 34 | for (int i=0; inewCameraZ) 94 | { 95 | float dotDepthFromCamera = v.z-newCameraZ; 96 | 97 | // 3D -> 2D projection formulas : 98 | float x = viewZoom*v.x/dotDepthFromCamera; 99 | float y = viewZoom*v.y/dotDepthFromCamera; 100 | float sw = 400*sizeFactor/dotDepthFromCamera; 101 | 102 | strokeWeight(sw); 103 | point(x,y); 104 | } 105 | } 106 | 107 | // 2D spiral path with easing 108 | PVector spiralPath(float p) 109 | { 110 | p = constrain(1.2*p,0,1); // for large p we keep the same position at the end of the spiral 111 | p = ease(p,1.8); 112 | int numberOfSpiralTurns = 3; 113 | float theta = TWO_PI*numberOfSpiralTurns*sqrt(p); // sqrt to have smooth spiral curve parametrization 114 | float r = 170*sqrt(p); 115 | // x y from previous polar coordinates 116 | float x = r*cos(theta); 117 | float y = r*sin(theta); 118 | y += startDotYOffset; 119 | return new PVector(x,y); 120 | } 121 | 122 | // one particle 123 | class Star 124 | { 125 | // the random x and y displacements of the star on 2D screen 126 | float dx = 30*random(-1,1); 127 | float dy = 30*random(-1,1); 128 | 129 | float spiralLocation; // parameter for location on the spiral curve 130 | float strokeWeightFactor = pow(random(1),2.0); // random strokeWeight factor 131 | 132 | float z; // fixed z of the star 133 | 134 | Star() 135 | { 136 | z = random(0.5*cameraZ,cameraTravelDistance+cameraZ); // random star z between two bounds 137 | spiralLocation = (1-pow(1-random(1),3.0))/1.3; // location on the spiral curve parameter, with some nice random distribution 138 | z = lerp(z,cameraTravelDistance/2,0.3*spiralLocation); // changing z so that it's further from camera as the parameter of the spiral increases 139 | } 140 | 141 | void show(float p) // p (in [0,1]) increases with time 142 | { 143 | PVector spiralPos = spiralPath(spiralLocation); 144 | float q = p-spiralLocation; 145 | if(q>0) // show only if the time is further than spiral location 146 | { 147 | float displacementProgress = constrain(5*q,0,1); // progress to go to displaced positions, reaches maximum progress of 1 quickly in function of q 148 | float easing = 1-pow(1-displacementProgress,4.0); // easing for the travel to displaced postion (start fast then slow down) 149 | 150 | float screenX = lerp(spiralPos.x,spiralPos.x+dx,easing); 151 | float screenY = lerp(spiralPos.y,spiralPos.y+dy,easing); 152 | // this gave us 2D screen positions we want 153 | 154 | // now we want to find position in 3D space, to be at the star's z 155 | // to do that we invert the projection formulas 156 | float vx = (z-cameraZ)*screenX/viewZoom; 157 | float vy = (z-cameraZ)*screenY/viewZoom; 158 | // this is our 3D position 159 | PVector u = new PVector(vx,vy,z); 160 | 161 | float dotSize = 8.5*strokeWeightFactor; 162 | showProjectedDot(u,dotSize); // now we can project this 3D position to 2D screen and show it 163 | } 164 | } 165 | } 166 | 167 | void drawStartDot() 168 | { 169 | if(t>changeEventTime){ 170 | float dy = cameraZ*startDotYOffset/viewZoom; // some inverted projection again to get y position in 3D space 171 | PVector v = new PVector(0,dy,cameraTravelDistance); // 3D position 172 | showProjectedDot(v,2.5); 173 | } 174 | } 175 | 176 | Star [] array = new Star[numberOfStars]; 177 | 178 | void setup(){ 179 | size(500,500,P2D); 180 | result = new int[width*height][3]; 181 | 182 | randomSeed(1234); 183 | 184 | for(int i=0;i code here : https://gist.github.com/Bleuje/fce86ef35b66c4a2b6a469b27163591e 5 | // See the license information at the end of this file. 6 | // View the rendered result at: https://bleuje.com/gifanimationsite/single/sunconnections/ 7 | 8 | ////////////////////////////////////////////////////////////////////////////// 9 | // Start of template 10 | 11 | int[][] result; // pixel colors buffer for motion blur 12 | float t; // time global variable in [0,1[ 13 | float c; // other global variable for testing things, controlled by mouse 14 | 15 | void draw() 16 | { 17 | if (!recording) // test mode... 18 | { 19 | t = (mouseX*1.3/width)%1; 20 | c = mouseY*1.0/height; 21 | if (mousePressed) 22 | println(c); 23 | draw_(); 24 | } 25 | else // render mode... 26 | { 27 | for (int i=0; i [0,1], with a parameter g: 23 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 24 | float ease(float p, float g) { 25 | if (p < 0.5) 26 | return 0.5 * pow(2*p, g); 27 | else 28 | return 1 - 0.5 * pow(2*(1 - p), g); 29 | } 30 | 31 | void draw() 32 | { 33 | if (!recording) // test mode... 34 | { 35 | t = (mouseX*1.3/width)%1; 36 | c = mouseY*1.0/height; 37 | if (mousePressed) 38 | println(c); 39 | draw_(); 40 | } 41 | else // render mode... 42 | { 43 | for (int i=0; i [0,1], with a parameter g: 16 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 17 | float ease(float p, float g) { 18 | if (p < 0.5) 19 | return 0.5 * pow(2*p, g); 20 | else 21 | return 1 - 0.5 * pow(2*(1 - p), g); 22 | } 23 | 24 | void draw() 25 | { 26 | if (!recording) // test mode... 27 | { 28 | t = (mouseX*1.3/width)%1; 29 | c = mouseY*1.0/height; 30 | if (mousePressed) 31 | println(c); 32 | draw_(); 33 | } 34 | else // render mode... 35 | { 36 | for (int i=0; i mainPath = new ArrayList(); 84 | 85 | int largeGridSize = 7; 86 | int numberOfSimulationSteps = 1700; // simulation steps for small squares movements 87 | int smallSquareGridSize = 6; 88 | 89 | // small square 90 | class SmallSquare 91 | { 92 | PVector [] positions = new PVector[numberOfSimulationSteps+1]; 93 | } 94 | 95 | // an instance of this shows the whole thing, and there's only one instance so a class was not necessary 96 | class System 97 | { 98 | SmallSquare [] smallSquares = new SmallSquare[smallSquareGridSize*smallSquareGridSize]; 99 | 100 | int [][] indexAtPos = new int[smallSquareGridSize][smallSquareGridSize]; 101 | 102 | PVector holePos; 103 | 104 | System() 105 | { 106 | holePos = new PVector(floor(random(smallSquareGridSize)),floor(random(smallSquareGridSize))); 107 | 108 | int k = 0; 109 | for(int i=0;i0.001 || abs(holePos.y-j)>0.001) 114 | { 115 | smallSquares[k] = new SmallSquare(); 116 | smallSquares[k].positions[0] = new PVector(i,j); // initial position in grid 117 | indexAtPos[i][j] = k; // storing that at (i,j) we have the k-th smallSquare 118 | k++; 119 | } 120 | } 121 | } 122 | 123 | PVector comingFrom = new PVector(-100,-100); // dummy initial value for an (i,j) position in grid 124 | // this is used to avoid to have the small hole go back to its previous position 125 | 126 | // simulation in which we move the small hole of the grid and update the small squares positions 127 | for(int stepIndex=0;stepIndex=smallSquareGridSize) 148 | continue; 149 | // (continue implies going dreictly to next wile loop step) 150 | found = true; 151 | } 152 | if(direction==1) 153 | { 154 | target = new PVector(holePos.x+1,holePos.y); 155 | if(round(target.x)==round(comingFrom.x)&&round(target.y)==round(comingFrom.y)) 156 | continue; 157 | if(target.x<0||target.x>=smallSquareGridSize) 158 | continue; 159 | found = true; 160 | } 161 | if(direction==2) 162 | { 163 | target = new PVector(holePos.x,holePos.y-1); 164 | if(round(target.x)==round(comingFrom.x)&&round(target.y)==round(comingFrom.y)) 165 | continue; 166 | if(target.y<0||target.y>=smallSquareGridSize) 167 | continue; 168 | found = true; 169 | } 170 | if(direction==3) 171 | { 172 | target = new PVector(holePos.x,holePos.y+1); 173 | if(round(target.x)==round(comingFrom.x)&&round(target.y)==round(comingFrom.y)) 174 | continue; 175 | if(target.y<0||target.y>=smallSquareGridSize) 176 | continue; 177 | found = true; 178 | } 179 | 180 | if(found) 181 | { 182 | PVector aux = holePos; 183 | holePos = target; 184 | previousHolePos = aux; 185 | availablePositionWasNotFound = false; 186 | } 187 | } 188 | 189 | int smallSquareIndexAtHole = indexAtPos[round(holePos.x)][round(holePos.y)]; 190 | // (we haven't moved the small squares yet) 191 | 192 | // we update indexAtPos because this small square takes the position of the previous hole position 193 | indexAtPos[round(previousHolePos.x)][round(previousHolePos.y)] = smallSquareIndexAtHole; 194 | // we update the position of the hole at the simulation step 195 | smallSquares[smallSquareIndexAtHole].positions[stepIndex+1] = previousHolePos; 196 | // we update the positions of the other small squares (it stays the same) 197 | for(int e=0;e<(smallSquareGridSize*smallSquareGridSize-1);e++) 198 | { 199 | if(e!=smallSquareIndexAtHole) 200 | { 201 | smallSquares[e].positions[stepIndex+1] = smallSquares[e].positions[stepIndex]; 202 | } 203 | } 204 | 205 | comingFrom = previousHolePos; 206 | } 207 | 208 | } 209 | 210 | 211 | 212 | void showGrid(float p) // p progressing in range [0,1] 213 | { 214 | p = lerp(p,ease(p,1.8),0.6); // easing mixed with no easing 215 | // this easing makes holes move faster in the center 216 | 217 | // preparing linear interpolation for small squares movement (but most won't move because we interpolate between two same positions) 218 | float floatIndex = 0.9999*numberOfSimulationSteps*p; 219 | int stepIndex1 = floor(floatIndex); 220 | int stepIndex2 = stepIndex1+1; 221 | float frac = floatIndex-stepIndex1; 222 | 223 | for(int e=0;e<(smallSquareGridSize*smallSquareGridSize-1);e++) 224 | { 225 | PVector pos1 = smallSquares[e].positions[stepIndex1]; 226 | PVector pos2 = smallSquares[e].positions[stepIndex2]; 227 | PVector pos = pos1.copy().lerp(pos2,ease(frac,1.7)); // interpolation done, easing for smooth movement 228 | 229 | // converting to pixel position 230 | pos.add(new PVector(-smallSquareGridSize/2.0+0.5,-smallSquareGridSize/2.0+0.5)); 231 | float A = 14.25; 232 | pos.mult(A); 233 | 234 | // drawing one small square 235 | push(); 236 | translate(pos.x,pos.y); 237 | fill(0); 238 | rectMode(CENTER); 239 | stroke(255); 240 | strokeWeight(1.75); 241 | rect(0,0,9,9); 242 | pop(); 243 | } 244 | } 245 | 246 | // interpolation between two main path positions, with easing 247 | PVector mainPathPos(float p) // here p is in range [0,1] 248 | { 249 | float floatIndex = p*(mainPath.size()-1)*0.999999; 250 | int ind1 = floor(floatIndex); 251 | int ind2 = ind1+1; 252 | float frac = floatIndex-ind1; 253 | 254 | PVector pos1 = mainPath.get(ind1); 255 | PVector pos2 = mainPath.get(ind2); 256 | 257 | float interp = ease(constrain(9*frac,0,1),2.2); // we stop for a long time after moving 258 | 259 | return pos1.copy().lerp(pos2,interp); 260 | } 261 | 262 | PVector mainPathPosToPixelPos(PVector integerPos) 263 | { 264 | float px = map(0.5+integerPos.x,0,largeGridSize,-width/2,width/2); 265 | float py = map(0.5+integerPos.y,0,largeGridSize,0-height/2,height/2); 266 | return new PVector(px,py); 267 | } 268 | 269 | void show(float p) 270 | { 271 | PVector pos = mainPathPos(p); 272 | PVector pixelPos = mainPathPosToPixelPos(pos); // (i,j) to pixel position 273 | 274 | push(); 275 | translate(pixelPos.x,pixelPos.y); 276 | showGrid(p); 277 | pop(); 278 | } 279 | 280 | // replacement technique 281 | int numberOfDrawnGrids = mainPath.size()-5; // (with replacement technique) 282 | void show() 283 | { 284 | for(int i=0;i [0,1], with a parameter g: 25 | // https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing 26 | float ease(float p, float g) { 27 | if (p < 0.5) 28 | return 0.5 * pow(2*p, g); 29 | else 30 | return 1 - 0.5 * pow(2*(1 - p), g); 31 | } 32 | 33 | // defines a map function variant to constrain or not in target interval (exists in openFrameworks) 34 | float map(float x, float a, float b, float c, float d, boolean constr) 35 | { 36 | return constr ? constrain(map(x,a,b,c,d),min(c,d),max(c,d)) : map(x,a,b,c,d); 37 | } 38 | 39 | //----------------------------------- 40 | 41 | void draw() 42 | { 43 | if (!recording) // test mode... 44 | { 45 | t = (mouseX*1.3/width)%1; 46 | c = mouseY*1.0/height; 47 | if (mousePressed) 48 | println(c); 49 | draw_(); 50 | } 51 | else // render mode... 52 | { 53 | for (int i=0; i cubes; 327 | 328 | PFont chosenFont; 329 | 330 | void setup() 331 | { 332 | size(800,800,P3D); 333 | result = new int[width*height][3]; 334 | smooth(8); 335 | ortho(); // isometric view activated 336 | 337 | chosenFont = createFont("ChakraPetch-Medium.ttf", 128); 338 | textFont(chosenFont); 339 | 340 | cubes = new ArrayList(); 341 | 342 | for(int i=-nCubesGrid;i<=nCubesGrid;i++) 343 | { 344 | for(int j=-nCubesGrid;j<=nCubesGrid;j++) 345 | { 346 | cubes.add(new Cube(i,j)); 347 | } 348 | } 349 | } 350 | 351 | 352 | void draw_() 353 | { 354 | background(0); 355 | push(); 356 | translate(width/2,height/2,-1000); // drawing far from camera to avoid glitch when stuff is too close to camera 357 | // (because of ortho(), it doesn't affect drawing other than correcting glitch) 358 | 359 | scale(800.0/600); // it was coded for width and height = 600, now rescaling for other resolution (800) 360 | 361 | // camera view... 362 | rotateX(0.307*PI); // angle that works for the good "optical illusion"/clean cubes layout that blends with floor grid 363 | rotateZ(0.75*PI); 364 | 365 | drawFloorGrid(); 366 | 367 | for(Cube cube : cubes) 368 | { 369 | cube.show(); 370 | } 371 | 372 | pop(); 373 | } 374 | 375 | 376 | /* License: 377 | * 378 | * Copyright (c) 2024 Etienne Jacob 379 | * 380 | * All rights reserved. 381 | * 382 | * This code after the template and the related animations are the property of the 383 | * copyright holder. Any reproduction, distribution, or use of this material, 384 | * in whole or in part, without the express written permission of the copyright 385 | * holder is strictly prohibited. 386 | */ 387 | --------------------------------------------------------------------------------