Category Archives: 11-Lenticular-GIF

Joel Simon

20 Jan 2014

For this project I wrote several js scripts using the THREE.js (it’s amazing, try it!) javascript webgl wrapper. I am primarily a 3d thinker so thats where my mind when when thinking about short gif-able animations. I also had a high-poly 3d model of my head for playing around with.

GIFGIF

rhinogif2

In the above gifs I applied a randomized parallel graph contraction algorithm to a 3d model of my head. The head I mostly made and the rhino I did not. What I mean by ‘mostly made’ is  that I took a scan and added eyes hair and ears. Scans have unworkable topology and are by no means finished models.

I was looking for a way to collapse the model and began by randomly picking edges to collapse. It was clear this was cool; however, the number of frames would have to be linear to the number of vertices and half the time the change would be hidden (on the other side of the model). Then I looked for ways to collapse multiple groups of vertices. It didn’t take long to realize that this is what we just did in 15-210 last semester in order to find Minimum Spanning Trees (mst’s) of graphs. A 3d mesh is nothing more than a graph so this was perfect. After overcoming my shock of finding a real world application for 210 I wrote the script that takes any mesh and graph reduces it. The main challenges were overcoming the fact that THREE.js does not natively support dynamic geometry and me being dumb with js pointers (yes, js has pointers).

My main critic of this (which I could have anticipated) was that the frames jump too much from one to another. Thus not making a smooth gif and failing the main objective. Having started with single edge contraction I was imagining a more slow and gradual transformation. This graph contraction takes on average O(logV) rounds so it is no surprise it goes so fast. As I type this I realize I can vary the probably that each head gets assigned a H or T and easily vary to rate of contractions. That will wait until tomorrow.

In response to the above I made the below gif which is a cos wave that rides around a spherical head. This was achieved by normalizing each vertex every frame. My critic on this would be that it continues to abuse the coolness of normal mapping, also the wave isn’t very interesting and has that awkward area in between the two waves.

(sphere Joel looks super grumpy)

sphere2

 

Ok I just made this.

scream

 

This is the code which uses concurrent randomized star contraction on a THREE.js mesh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
// custom global variables
var mainObj, mainGeo, adjacencyList;
 
init();
animate();
 
function init() {
	// SCENE
	scene = new THREE.Scene();
	// CAMERA
	var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
	var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
	camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
	scene.add(camera);
	camera.position.set(250, -20, 442.0931145267771);
	// camera.position.set(0,150,400);
	camera.lookAt(scene.position);	
	// RENDERER
	if ( Detector.webgl )
		renderer = new THREE.WebGLRenderer( {antialias:true, preserveDrawingBuffer: true } );
	else
		renderer = new THREE.CanvasRenderer(); 
	renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
	container = document.getElementById( 'ThreeJS' );
	container.appendChild( renderer.domElement );
	// EVENTS
	THREEx.WindowResize(renderer, camera);
	THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
	// CONTROLS
	controls = new THREE.OrbitControls( camera, renderer.domElement );
 
	// LIGHT
	var light = new THREE.PointLight(0xffffff);
	light.position.set(0,250,0);
	scene.add(light);
 
	// scene.add(floor);
	// SKYBOX/FOG
	var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
	var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
	var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
	// scene.add(skyBox);
	scene.fog = new THREE.FogExp2( 0x9999ff, 0.00025 );
 
	////////////
	// CUSTOM //
	////////////
	var materialNormal = new THREE.MeshNormalMaterial();
	var loader = new THREE.JSONLoader();
	loader.load( "./models/elephant.js", function( geo, head_materials ) {
			mainObj = new THREE.Mesh(
				// new THREE.CubeGeometry(50, 50, 50, 2, 1, 1), 
				// new THREE.IcosahedronGeometry( 120, 2 ),
				geo,
				// new THREE.MeshBasicMaterial( { color: 0x00ee00, wireframe: true, transparent: true } ) 
				materialNormal
				// new THREE.MeshNormalMaterial({side: THREE.DoubleSide })
				// new THREE.MeshFaceMaterial( head_materials )
			);
			mainObj.scale = new THREE.Vector3(300,300,300 );
 
			mainObj.position.set(-90,-20,0);
			mainObj.rotation.set(0,4*Math.PI/4,0);
			// mainObj.rotation.set(0,Math.PI/2,0);//face
			scene.add( mainObj );
			mainGeo = mainObj.geometry;
 
			adjacencyList = makeAdjacencyList(mainObj.geometry);
			mainGeo.vList = {};
			for (var i = 0; i < mainGeo.vertices.length; i++) { 				mainGeo.vList[i] = Math.random() > .5 ? 'T' : 'H';
			}
			var bar = 10;
			animate();
			screenshot(function(){
				main();
			});
			function main () {
				if (bar > 0) {
					starContract();
					animate();
					console.log('Contracted', i);
					screenshot(function(){
						bar--;
						main();
					});
				}
			}			
	});
}
	var mapping = {};
function starContract() {
	var neighbours, mappedTo;
	var c = 0;
	//For every vertex give it a H or T
	//Init a mapping from v -> v
	for (var v in mainGeo.vList) {
		mainGeo.vList[v] = Math.random() > .5 ? 'T' : 'H';
		mapping[v] = v;
	}
	//For ever T vertex pick one adjacent H to contract to.
	for (var v in mainGeo.vList) {
		if (mainGeo.vList[v] == 'T') {
			// map to and return a adjacent vertex
			mappedTo = contractToHead(v);
			//record where we mapped to.
			mapping[v] = mappedTo;
			if (mappedTo != v) delete mainGeo.vList[v];
			// console.log(v, mappedTo);
			c++;
		}
	}
	var oldN;
	var newN;
	// Update adjacency list to account for contracted vertices.
	for (var vert in adjacencyList) {
		for (var neigh in adjacencyList[vert]) {
			newN = mapping[neigh];
			delete adjacencyList[vert][neigh];
			adjacencyList[vert][newN] = true;
		}
	}
	for (var i = 0; i < mainGeo.vertices.length; i++) {
		mainGeo.vertices[i].copy(mainGeo.vertices[mapping[i]]);
	};
	mainGeo.verticesNeedUpdate = true;
	return;
}
function contractToHead(i) {
	neighbours = adjacencyList[i];
	if (Object.keys(neighbours).length == 0) {
		return console.log('no neighbors.');
	}
	for (var n in neighbours) {
		// attach to first H.
 
		if (mainGeo.vList[n] == 'H') {
			//contract the T vert to H vert
			mainGeo.vertices[i] = mainGeo.vertices[n];
			adjacencyList[n] = mergeObjs(adjacencyList[i], adjacencyList[n]);
			delete adjacencyList[n][n];
			delete adjacencyList[n][i];
			delete adjacencyList[i];
			return n;
		}	
	}
	return i;
}
 
function makeAdjacencyList (geometry) {
	var m = {};
	var f;
	for (var i = 0; i < geometry.faces.length; i++) {
		f = geometry.faces[i];
		if (!m[f.a]) m[f.a] = {};
		if (!m[f.b]) m[f.b] = {};
		if (!m[f.c]) m[f.c] = {};
		m[f.a][f.b] = true;
		m[f.a][f.c] = true;
		m[f.b][f.a] = true;
		m[f.b][f.c] = true;
		m[f.c][f.a] = true;
		m[f.c][f.b] = true;
	}
	return m;
}
var rdy = true;
function update() {
	if (keyboard.pressed("z") && rdy) {
		starContract();
		rdy = false;
		setTimeout(function() {
			rdy = true;
		}, 500);
	}
	// controls.update();
}
function randomInt(n, i) {
	i = i || 0;
	return Math.floor((Math.random()*n)+i);
}
function randomProperty(obj) {
  var result;
  var count = 0;
  for (var prop in obj) {
    if (Math.random() < 1/++count) {
			result = prop;
		}
	}
  return result;
}
function render() {
	renderer.render( scene, camera );
}
function animate() {
  requestAnimationFrame( animate );
	render();		
	update();
}
 
function mergeObjs(obj1,obj2) {
  var obj3 = {};
  for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
  for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
  return obj3;
}
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
}
 
/*** ADDING SCREEN SHOT ABILITY ***/
function screenshot (callback) {
	img = document.getElementById('embedImage');
  var imgNode;
  //Listen to 'P' key 
  try {
    img.src = renderer.domElement.toDataURL(); 
    img.download = 'foo.png';
    img.onload = function () {
    	var myWindow;
			myWindow = window.open(img.src.replace('image/png', 'image/octet-stream'), "width=512,height=512");
    	console.log('saved');
    	callback();
	  };
  } 
  catch(e) {
    console.log("Browser does not support taking screenshot of 3d context");
    return;
  }
}

Nastassia Barber

20 Jan 2014

Before I started this project, I had never heard of Processing or made anything like this GIF.  I looked at some of the examples for a while, but crazy trippy optical tricks aren’t really my thing, so I decided to do something figurative.  I wanted it to still look simple and only have a couple of colors so it would look good on the card.  A minimalist design was also ideal for someone just figuring out how to use Processing.  I decided to pick an object from everyday life that was easy to simplify into something pretty, so I somehow arrived at blinds in an empty room.

blindssmall

 

Here are some pages from my largely unintelligible sketchbook (I’m not sure why I get 1000% worse at drawing when I’m thinking).

sketches1 sketches 2

 

I like the way this looks, so I’m glad that I managed to make something nice as a beginner. However, it’s still totally obvious that someone didn’t take years of experience to figure out how to make this GIF, so in the future I’m sure I could do better.   If I had taken less time to do simple things, I would have maybe added some more detail to the blinds and made them more believably 3-dimensional.  It also would have been nice to be able to loop in 10 frames, but it looked terrible so I chose not to.

(I’m not sure why the code below doesn’t look quite right; I followed the directions…)
 


//Nastassia Barber
//IACD 2014
//Professor Golan Levin

Blind blinds;
Cord cord;
float framerate=10;
//some stuff for saving frames
boolean bRecording;
int nElapsedFrames;
String myName="blindsyay";
int nFramesInLoop=20;

void setup()
{
size(1500,1500);
blinds =new Blind();
cord =new Cord();
}

void keyPressed()
{ 
// Press a key to export frames to the output folder
bRecording = true;
nElapsedFrames = 0;
}

class Blind
//the blinds and also the light are in here
{
float wide;
float front_height;
float side_height;
float ypos;
float lightlen;
float opacity;

Blind()
{
wide=600;
front_height=50;
side_height=0;
ypos=100;
lightlen=0;
opacity=100;
}
void rotateBlind()
//to decrease the height of front slat while increasing the height of the visible part of the side slat so it appears to rotate
{
if (front_height>10)
{
front_height=front_height-(40/framerate);
side_height=side_height+(10/framerate);
}
else
{
front_height=48;
side_height=0;
}
}
void growLight()
//increases size of light patch on floor as blinds open
{
if (lightlen<20)
{
lightlen=lightlen+(20/framerate);
opacity=opacity+(150/framerate);
}
else
{
lightlen=0;
opacity=100;
}
}
}
class Cord
//a separate class for the blinds' pull cord just because
{
float cordl;

Cord()
{
cordl=400;
}
void pullCord()
//increases length of cord as blind opens
{
if (cordl<500)
{
cordl=cordl+100/framerate;
}
else cordl=400;
}
}

void draw()
{
background(30,30,30);
//fill(121,53,211); 
//rect(100,100,600,500);
float xpos=100;
float starty;
for (int i = 0; i<10; i=i+1)
//draw blinds themselves
{
starty=blinds.ypos+(50*i);
fill(#FFFFFF);
//rect(xpos,blinds.ypos,blinds.wide,blinds.front_height);
quad(xpos,starty,xpos+blinds.wide,starty,xpos+blinds.wide+5,starty+blinds.front_height, xpos+5, starty+blinds.front_height);
fill(#868686);
quad(xpos+5,starty+blinds.front_height,xpos+blinds.wide+5, starty+blinds.front_height,xpos+blinds.wide+6,starty+blinds.front_height+blinds.side_height, xpos+6, starty+blinds.front_height+blinds.side_height);
}
//draw cord on blinds
strokeWeight(2);
line(680,100,680,100+cord.cordl);
fill(#FFFFFF);
strokeWeight(1);
ellipse(680,100+cord.cordl,8,11);
float start_y=650;
float start_x=120;
float lightl=600;
float opacity=blinds.opacity;
for (int i = 0; i<10; i=i+1)
//draw light on floor
{
float slope_up=150;
fill(107,63,165,opacity);
quad(start_x,start_y, start_x+lightl-20,start_y, start_x+lightl+blinds.lightlen,start_y+blinds.lightlen, start_x+blinds.lightlen,start_y+blinds.lightlen);
start_y=start_y+20;
start_x=start_x+20;
lightl=lightl+20;
opacity=opacity-20;
}
blinds.rotateBlind();
blinds.growLight();
cord.pullCord();

// If we're recording the output, save the frame to a file. 
if (bRecording)
{
saveFrame("output/"+ myName + "-loop-" + nf(nElapsedFrames, 4) + ".png");
nElapsedFrames++; 
if (nElapsedFrames == nFramesInLoop)
{
bRecording = false;
}
}
}

Ticha Sethapakdi

19 Jan 2014

This is actually a modified version of an old abstract Processing animation I made for giggles, which was the result of playing with sine waves. I remember spending a frustrating amount of time last semester trying to get the form to move in a specific way, and I eventually just gave up on it. But because I was so fond of its form and simple sophistication, I decided to struggle with it another time and attempt to get it to work the way I initially intended it to–that is, make the planes move in the same direction. Fortunately, after arbitrarily long amounts of time pulling my hair out, I was finally able to figure out how things worked and all was well.

Of course, it still doesn’t work perfectly–although the animation technically does ‘loop’, it does not do so seamlessly and requires many iterations to revert back to the original state. Regardless, I am very pleased that I was able to at least accomplish the movement and I am quite satisfied with the color scheme.

I have two different designs:

This version is closer to my original design, with some obvious minor adjustments like the background color and typeface. I always found it fascinating how it’s possible to construct some words using hexadecimal codes and thought it was an interesting bridge between language and color. Then, when I remembered that #C0FFEE was a hex code, I decided to make something that was dedicated to the color of coffee.

 

This design was partly inspired by Mr. Div, who has created many beautiful animations with very tasteful color schemes. When I finished the first design, I couldn’t help but feel like there was something missing, so I decided spend some more time thinking about how I could improve the aesthetics. I really wanted to add a glowing effect to the shape by applying blur filters and strokes, but they caused Processing to lag significantly and still did not produce the desired result. When I couldn’t find anything online either, I began to feel desperate–so what I ended up doing was exporting the ‘normal’ sequence of frames from Processing and then adding a Color Dodge effect to each layer in Photoshop. Every. Single. Layer. It was painstakingly tedious work, but I am satisfied with what came out of it.

 

Code for the first one:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* Credit to Golan Levin for the recording option */
 
int nFrames = 10;
int nElapsedFrames = 0;
int quadCount = 30;
Quad[] quads = new Quad[quadCount];
boolean bRecording;
PFont font;
 
void setup() 
{
  frameRate(10);
  font = loadFont("Minecraftia-80.vlw");
  textFont(font, 80);
  smooth();
  noStroke();
  size(1500, 1500);
  float f = -(float) quadCount/2;
 
  for (int i = 0; i < quadCount; i++) { 
    quads[i] = new Quad(f);
    f++;
  }
}
 
void draw() {
  if (nElapsedFrames < nFrames) {
    translate(width/2, height/2); 
    background(#A32121);
 
    for (int i = 0; i < quadCount; i++) { 
      Quad quad = (Quad) quads[i];
      quad.display();
    }
    fill(255);
    text("#C0FFEE", -210, 680);
 
    saveFrame("c0ffee-###.png");
 
    nElapsedFrames++;
  }
  else {
    noLoop();
    exit();
  }
}
 
class Quad {
  float rad;
  float angle;
  float dec;
 
  //particle constructor
  Quad(float angle_) {
    angle = angle_;
    dec = 200 * 0.00004; 
 
  }
 
  void display () {
    //for GIF POP version only
    pushMatrix();
    scale(3.0, 3.0);
 
    pushMatrix();
    rotate(radians(90));
 
    fill(#C0FFEE, 45);
 
    quad(20*sin(angle), sin(angle), 180*cos(angle), sin(angle), 
    120*cos(angle), 100*sin(angle)+100, sin(angle), abs(30*sin(angle)));
 
    pushMatrix();
    scale(1.0, -1.0);
    quad(20*sin(angle), sin(angle), 180*cos(angle), sin(angle), 
    120*cos(angle), (100*sin(angle)+100), sin(angle), abs(30*sin(angle)));
    popMatrix();
 
    angle+=dec;
    popMatrix();
 
    popMatrix();
  }
}

Kevan Loney

19 Jan 2014

Howdy yall!

So for those who may not know me all that well, I’m am a bonafide coffee addict. So it seemed like a good starting point for the first project to include something that shows a bit more about me. Over the break my good friend went to London and got me a “Coffee-Journal” that even (get ready) includes a flavor wheel for every cup of joe that you may intake. This inspired my Lenticular Animation. Do not fret, much coffee was drank during the making of this Gif for reference sake.

The making of it was a bit of a re-learning curve at the beginning. It has been a while since I’ve hard coded anything so it took me a while to get it off the ground and running. But the act of creating the cup vertex by vertex was probably the funnest part for me. It was almost as if I was drawing in my head on a graph and then translating that to the computer. How fun!

Sketches:

Example Sketches

Example Sketches

Lenticular Animation:

Loney_Coffee_Drip

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Kevan Loney
// January 2014
// CMU - IACD - Project 1_ Lenticular Animation - Coffee
// Prof. Golan Levin
 
//======================================================================================
 
// Global variables. 
 
int     nFramesInLoop = 30;
int     nElapsedFrames;
boolean bRecording; 
float   percentCompleteFraction;
 
//======================================================================================
 
void setup() {
  size (500, 500); 
  bRecording = false;
  nElapsedFrames = 0;
  frameRate (nFramesInLoop);
}
//======================================================================================
 
void keyPressed() { 
  // Press a key to export frames to the output folder
  bRecording = true;
  nElapsedFrames = 0;
}
 
//======================================================================================
 
void draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } 
  else {
    float modFrame = (float) (frameCount % nFramesInLoop);
    percentCompleteFraction = modFrame / (float)nFramesInLoop;
  }
 
  // Render the design, based on that percentage. 
  globalWorld (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  if (bRecording) {
    saveFrame("output/" + "-loop-" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames == nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//======================================================================================
 
void globalWorld(float percentCompleteFraction) {
background (#E0E0E0);
smooth();
 
  pushMatrix();
  translate(140,0);
  CoffeeDrips(percentCompleteFraction);
  popMatrix();
  pushMatrix();
  CoffeeCup();
  popMatrix();
 
}
 
void CoffeeCup () {
  //background(255);
  translate(500/3.8, 500-300);
  scale(1.1, 1.1);
  fill(#F2ECDF);
  stroke(255);
  strokeWeight(0);
 
  //-------------------------------------------------
  fill(#EDEDED);
  stroke(1);
  strokeWeight(0);
  beginShape(); //World Floor
  vertex(-118,240);
  vertex(340, 240);
  vertex(335, 272);
  vertex(-120, 272);
 
  endShape();
 
  //-------------------------------------------------
  beginShape(); // The base cup
  vertex(50, 250);
  vertex(0, 0);
  vertex(-10, 0);
  vertex(-10, -10);
  vertex(210, -10);
  vertex(210, 0);
  vertex(200, 0);
  vertex(150, 250);
  endShape();
  //-------------------------------------------------
  fill(40, 32, 17, 70); // Graphic Shadow
  stroke(255);
  strokeWeight(0);
  beginShape(); 
  vertex(50, 250);
  vertex(0, 0);
  vertex(200, 0);
  vertex(100, 20);
  vertex(75, 40);
  vertex(75,150);
  vertex(170,150);
  vertex(168, 160);
  vertex(75, 160);
  vertex(75, 250);
  endShape();
  //-------------------------------------------------
  fill(255, 255 , 255, 190); // Graphic Highlight
  stroke(255);
  strokeWeight(0);
  beginShape();
  vertex(140,250);
  vertex(146, 250);
  vertex(193, 10);
  vertex(187, 10);
  endShape();
  beginShape();
  vertex(-5, -6);
  vertex(-5, -8);
  vertex(205, -8);
  vertex(205, -6);
  endShape();
  //-------------------------------------------------
  fill(#A27926); // Graphic CupHolder
  stroke(1);
  strokeWeight(0);
  beginShape();
  vertex(195,65);
  vertex(5 ,65);
  vertex(25, 150);
  vertex(175, 150);
  endShape();
  //-------------------------------------------------
  fill(#195211); //Graphic SBucks Logo
  stroke(1);
  strokeWeight(0);
  ellipse(98, 108, 70, 70);
  //-------------------------------------------------
  fill(46, 87, 22, 90); // Graphic Holder Shadow
  stroke(1);
  strokeWeight(0);
  beginShape();
  vertex(5, 65);
  vertex(25, 150);
  vertex(50, 150);
  vertex(35, 70);
  vertex(193, 70);
  vertex(195,65);
  endShape();
  //-------------------------------------------------
  fill(#898989); //CUP shadow
  stroke(1);
  strokeWeight(0);
  beginShape();
  vertex(50, 250);
  vertex(-120, 272);
  vertex(335, 272);
  vertex(150, 250);
  endShape();
  //-------------------------------------------------
  fill(#646464); // Cup Shadow number2
  stroke(1);
  strokeWeight(0);
  beginShape();
  vertex(50,250);
  vertex(20,255);
  vertex(180, 255);
  vertex(150, 250);
  endShape();
 
}
 
void CoffeeDrips (float percent){
 
  float DripSize = -15;
  float changeY = DripSize - (percent*-30);
 
  int CoffeeDripAnim = 7;
  for (int i=0; i &lt;= CoffeeDripAnim; i++) {
    float dripSpace = DripSize + (i*changeY);
    translate( 0, dripSpace);
 
  }
  fill(#552907); // Drip Base Shape/Color
  stroke(1);
  strokeWeight(0);
  translate (100,0);
  scale(3,3);
  beginShape();
  vertex(0,0);
  vertex(-3,10);
  vertex(-6, 15);
  vertex(-7, 19);
  vertex(-6.5,20);
  vertex(-4, 23);
  vertex(0, 25);
  vertex(4, 23);
  vertex(6.5, 20);
  vertex(7, 19);
  vertex(6, 15);
  vertex(3,10);
  endShape();
  //-------------------------------------------------
  fill(#EAC09F); // Drip Highlight
  stroke(1);
  strokeWeight(0);
  beginShape();
  vertex(1,8);
  vertex(2,12);
  vertex(4,15);
  vertex(5, 18);
  vertex(4, 21);
  vertex(2, 23);
  endShape();
 
}

 

Cheers,

Kevan

Chanamon Ratanalert

19 Jan 2014

This project was a nice challenge for me. I had never used Processing before (and I’ve heard of it maybe twice), so I didn’t know what I would be capable of. This kind of limited my choices. Looking through many other Processing gifs (mostly on beesandbombs.tumblr.com), I discovered that a lot would be possible. Of course I wasn’t going to set myself up to try to shoot for such complicated pieces. Additionally, this project was given to us pretty opened, so I limited myself by deciding to want to create something that would continuously loop (instead of have hacked cuts) and would still look good when a portion of it was made into a lenticular print.

After a wide range of exploration (see sketches below), I decided to stick to my roots of simple geometric design. The designs that my eye has been keen to for a while now in clothing, images, my own doodles, etc. have been geometric shapes and very clean designs. And, of course, symmetry has always been a favorite of mine. After coming up with a design I liked, I combined it with a couple hours of browsing processing tutorials and got an animation working (with great help from Golan’s template). After numerous tweaks that practically burned my retinas fromt staring at spinning objects for so long, I came up with this final gif.

chanamon-LenticularAnimation
*The code is set at 30 frames per loop. This image was comprised of 90 frames taken from a continuous run of the code.

I’m pretty content with it. For my first crack at Processing, I’d say I tackled this project well. I didn’t shoot too far beyond what I thought I could accomplish and the gif is pretty nice. How I eventually dwindled down from outlandish sketches impressed me as well, since I usually try to shoot farther than I know is good for me. I’m kind of bored of the gif though. I don’t know if it’s because I’m disappointed in myself for not being able to develop something bigger and better, but I wish I could have done something more elaborate. But the amount of time and experience I had to complete this project didn’t lend me much more complexity. I will definitely create more gifs with Processing in the future and improve my skills.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// This is was based off a template by Prof. Golan Levin, Jan. 2014 CMU IACD
// When you press a key, this program will export a series of images
// into an "output" directory located in its sketch folder.
// These can then be combined into an animated GIF.
// Chanamon Ratanalert, January 2014 - CMU IACD
 
//===================================================
// Global variables.
 
int nFramesInLoop = 30; // for lenticular export, change this to 10!
int nElapsedFrames;
boolean bRecording;
float radius;
float wdth; //variable width of center diamonds
float maxWdth; //maximum width of center diamonds
float pointy; //variable height of points around large circle
float maxPointY; //max height of points around large circle
float tinting; //variable for color of small diamonds
float maxTinting; //max color change of small diamonds
int bkgd;
String myName = "chanamon";
//===================================================
void setup() {
size (500, 500);
radius = 140;
wdth = 100;
maxWdth = 100;
pointy = 30;
maxPointY = 30;
bkgd = 51;
tinting = 130;
maxTinting = 128;
 
bRecording = false;
// bRecording = true; //use true if want to immediately start recording
nElapsedFrames = 0;
frameRate (nFramesInLoop);
}
//===================================================
void keyPressed() {
// Press a key to export frames to the output folder
bRecording = true;
nElapsedFrames = 0;
}
 
//===================================================
void draw() {
 
// Compute a percentage (0...1) representing where we are in the loop.
float percentCompleteFraction = 0;
if (bRecording) {
percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
}
else {
float modFrame = (float) (frameCount % nFramesInLoop);
percentCompleteFraction = modFrame / (float)nFramesInLoop;
}
 
// Render the design, based on that percentage.
renderInner (percentCompleteFraction);
renderOuter (percentCompleteFraction);
 
// If we're recording the output, save the frame to a file.
if (bRecording) {
saveFrame("output/"+ myName + "-loop-" + nf(nElapsedFrames, 4) + ".png");
nElapsedFrames++;
// if (nElapsedFrames == nFramesInLoop) { //uncomment if want to record nFramesInLoop # of imgs
if (percentCompleteFraction == 3){
bRecording = false;
}
}
}
 
//===================================================
void renderInner (float percent) {
//this method draws the inner diamond spokes and two circles
 
//changes made as time passes
float angle = percent * (TWO_PI/8);
wdth = wdth - 1; //width of center diamonds
if (abs(wdth) &gt;= maxWdth){
wdth = maxWdth;
}
 
//begin imaging
background(bkgd);
smooth();
translate(width/2, height/2);
rotate(angle);
stroke(bkgd + 90);
ellipse(0, 0, 10, 10); //center circle
ellipse(0, 0, radius*2, radius*2); //outer circle
noFill();
strokeWeight(2);
//draw inner diamond spokes
for (int i = 0; i &lt;= 8; i++) {
stroke(255);
rotate(radians(45));
float topx = 0;
float topy = 0;
float dx = wdth/2;
float dy = radius/2;
beginShape();
vertex(topx, topy);
vertex(topx-dx, topy+dy);
vertex(topx, topy+radius);
vertex(topx+dx, topy+dy);
endShape(CLOSE);
}
}
 
//===================================================
void renderOuter (float percent) {
//this method draws the outer points and small diamonds
 
//changes made as time passes
float angle = percent * (TWO_PI/8);
wdth = wdth - 1; //width of center diamonds
if (abs(wdth) &gt; maxWdth){
wdth = maxWdth;
}
 
tinting = tinting - 2; //changing color of small outer diamonds
if (abs(tinting) &gt; maxTinting){
tinting = maxTinting;
}
 
//begin imaging
smooth();
rotate(angle*(-2));
noFill();
strokeWeight(2);
for (int i = 0; i &lt;= 8; i++) {
rotate(radians(45));
stroke(bkgd + 90);
line(0,radius,radius/2,radius+pointy);
line(0,-radius,radius/2,-radius-pointy);
 
//draw diamonds
float topx = 0;
float topy = 0 + radius + pointy;
float r = pointy;
float w = 2*pointy/3;
float dx = w/2;
float dy = r/3;
stroke(bkgd + abs(tinting));
beginShape();
vertex(topx, topy);
vertex(topx-dx, topy+dy);
vertex(topx, topy+r);
vertex(topx+dx, topy+dy);
endShape(CLOSE);
}
}

Jeff’s Lenticular Animation

jeffcrossman-lentloop

 

This lenticular animation was inspired by the oscillation of the balance wheel found within mechanical timepieces. My initial plan was to do a very literal modeling of this mechanic in processing and my initial results were promising. Using a YouTube video that had a slow motion view of the wheel I was able to extract a lot of good detail about the movement, for example, the wheel rotated about 495 degrees before switching directions, and the spring expanded as the wheel moved counter-clockwise and contracted during clockwise movement.

crossman-lenticular-sketch

Once I had the simple harmonic motion working I realized that with the 10 frame limit for Gifpop, there was no way to make a very literal animation of the balance wheel look acceptable. So I went more abstract using what I had as a base for expansion. The animation isn’t very visually complex or compelling so I chose a pop art color palette to make the piece more visually engaging.

Code on GitHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// This is a template for creating a looping animation in Processing. 
// When you press a key, this program will export a series of images
// into an "output" directory located in its sketch folder. 
// These can then be combined into an animated GIF. 
// Prof. Golan Levin, January 2014 - CMU IACD
// Author: Jeff Crossman
 
//===================================================
// Global variables. 
 
int     nFramesInLoop = 30; // for lenticular export, change this to 10!
int     nScreenResolution = 500; // n X n pixels
int     nElapsedFrames;
boolean bRecording; 
 
String  myName = "jeffcrossman";
 
//===================================================
void setup() {
  size (nScreenResolution, nScreenResolution); 
  bRecording = false;
  nElapsedFrames = 0;
  frameRate (nFramesInLoop); 
  rectMode(CENTER);
}
//===================================================
void keyPressed() { 
  // Press a key to export frames to the output folder
  bRecording = true;
  nElapsedFrames = 0;
}
 
//===================================================
void draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  float percentCompleteFraction = 0; 
  if (bRecording) {
    percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop;
  } 
  else {
    float modFrame = (float) (frameCount % nFramesInLoop);
    percentCompleteFraction = modFrame / (float)nFramesInLoop;
  }
 
  // Render the design, based on that percentage. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  if (bRecording) {
    saveFrame("output/"+ myName + "-loop-" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames == nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
void renderMyDesign (float percent) {
 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // This example uses two different graphical techniques. 
  // Use or delete whatever you prefer from this example. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  // here, I set the background and some other graphical properties
  background (0, 153, 255); 
  stroke (229, 6, 123); 
  fill(0, 0, 0);
  strokeWeight (42); 
  smooth();
 
  //----------------------
  // Here, I assign some handy variables. 
  float amplitude = radians(55);
  float period = 240;
  float x = amplitude * cos(TWO_PI * percent);
 
  if (x &gt; radians(45))
    x = radians(45);
  else if ( x &lt; -radians(45))
    x = -radians(45);
 
  //text (x, 200, 20);
 
  scale(nScreenResolution / 500);  // Scale drawing to screen size
 
  translate(250, 250);
  rotate(-radians(percent*90));
  translate(-250, -250);
 
  pushMatrix();
  translate(250, 250);
  rotate(x);
  rect(0, 0, 100, 100);
  popMatrix();
 
  pushMatrix();
  translate(151, 151);
  rotate(-x);
  rect(0, 0, 100, 100);
  popMatrix();
 
  pushMatrix();
  translate(349, 151);
  rotate(-x);
  rect(0, 0, 100, 100);
  popMatrix();
 
  pushMatrix();
  translate(349, 349);
  rotate(-x);
  rect(0, 0, 100, 100);
  popMatrix();
 
  pushMatrix();
  translate(151, 349);
  rotate(-x);
  rect(0, 0, 100, 100);
  popMatrix();
}