## Project 08: Generative Portrait

For this project, I chose to render my brother. I originally had wanted to use particle systems to create a water-like simulation on top of the loaded pixels using a mutual repulsion force. In this sketch, however, I used a gaussian distribution to create a black hole in which the array of tiny pixel ellipses would form and disappear. The sketch did not turn out as I had hoped since I did not use a gradually added velocity and just layered the gaussian particles on top of the grid, and used too many ellipses in the for loops. This made the transition abrupt and the program slow.

Project08Face

## sayers and sdmack Project 08

We have created a slide puzzle using still images of Gidon Orelowitz and Ivan Weis-Palacios (our boyfriends). (The Ivan version is posted here- visit Sydney’s page to play the Gidon version!: http://cmuems.com/2015c/sayers/10/29/sayers-sdmack-project-08/)  Creating this puzzle involved several smaller puzzles within the big puzzle (such as how to shuffle all the tiles, switching tiles, edge cases etc.) and a lot of caffeine and hard work.  The code allows for any imgur.jpg image to become the puzzle (400×400).  The work was divided evenly with collaborative brainstorming for each step.  This was definitely a challenge, but we are very happy with the result! Thank you so much to Gidon Orelowitz for helping us greatly with the logic of this puzzle (and knowing about group theory and the 15 puzzle and edge cases).

Go to the very end for a Spooky Surprise!

Screenshots (Ivan):

Screenshots (Gidon):

Project: (This is the Ivan version- visit Sydney’s page to play the Gidon version!) :)

sketch

HERRREEEEEE’S JOHNNY!

PS: The opening credits of The Shining are teal.  The scariest color.

## Matthew Lin – Project 08

sketch

For this project, I chose to draw the portrait in a similar manner to a printer. The dots are enlarged circles at random y-values for each x-value. Each new line that moves across the canvas adds more detail to the image and adds more definition to facial features. In terms of viewer interaction, holding down the left or right arrow key will color the image with a different overall shade. Pressing the left arrow key will create a new line of dots that move left across the canvas, while pressing the right arrow key will produce the opposite result. The line of dots that move left have a green shade to them, and if held long enough, the overall image will take on a green color. Similarly, the dots created by pressing the right arrow key have a red shade. Having the three rgb values allows for multiple possibilities in terms of overall color.

## Project-08-Portrait

**Accidentally submitted late because I waited to take a screenshot of it :(

For this project, I chose my brother as the subject. I wanted to explore the use of lines with different sizes and thicknesses in order to make up the portrait. The viewer can control the width of the middle lines based on their mouse position.

## Jitaekim-Project08

sketch

I made a computational self portrait that incorporates the curve function I used from one of the past project. I realized though that, waiting for this curve to fill the canvas would take too long and if the circles are too big it makes it hard to see the photo, so I made a spray can to see more details about the portrait.

## Project-08-Portrait-Ben Snell

Multidimensional Image Extrusions

I chose to explore the visualization of color photography as a multidimensional form of data capture. Using the form of a tetrahedron, which is well suited for interfacing with four-dimensional data sets, I projected the individual R, G, and B channels from a single face outward onto their own separate faces. This can be thought of, in simple terms, as a three dimensional bar chart, where different colored bars originate at the same location but are extruded at different angles. The result is a particularly interesting form exhibiting crystal-like formations, that allows us to view and understand color photography in a whole new light.

The portrait is of myself, and has, strangely enough, turned my hair white. Could this be a manifestation of my future self living within a purely digital world?

Here are some of my process sketches and screengrabs:

And here is the final piece:

I used Three.js and a few functions written by the illustrious Mr.Doob. If the above visualization is not displaying correctly, try loading the page, or clicking / dragging on the image.

```

Tetrahedron using Three.js revision 73

body {
background-color: #ffffff;
margin: 0;
overflow: hidden;
}
canvas { width: 100%; height: 100% }

// Some of the following functions were explicitly written by Mr. Doob, specifically those retrieving image data.

// Adjusted color mapping so height of projections corresponds to the amount of that color present below. alpha values also map to the opacity of the final tetrahedron, with transparency = 0 alpha and full opacity = somewhat transparent

// holds location of the mouse
var mouse = new THREE.Vector2();

// called every time mouse is moved
function onMouseMove( event ) {

// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// console.log(mouse.x);
}

var camera, controls, scene, renderer;

// attributes of tetrahedrons
var nLevels = 35;
var tetraEdge = tetraRadius * Math.pow(8/3, 1/2);
var tetraFaceHt = tetraRadius * Math.pow(2, 1/2);
var tetraHeight = tetraRadius * 4/3;

// scene center
var sceneX = 0;
var sceneY = nLevels * tetraHeight / 2 + 100;
var sceneZ = 500;

var centerOfScene = new THREE.Vector3(0, sceneY, 0);

var imageColors = new Array(); // holds colors of top tetras
var imageTransparencies = new Array();

function drawImage(imageObj) {

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var imageX = 0;
var imageY = 0;
var imageWidth = imageObj.width;
var imageHeight = imageObj.height;

context.drawImage(imageObj, imageX, imageY);

var imageData = context.getImageData(imageX, imageY, imageWidth, imageHeight);
var data = imageData.data;

// interpolate pixels of top tetrahedron surface
// first, determine whether the height or width will be the constraining factor
var HtoW = imageHeight / imageWidth;
var imgCenterX = imageWidth / 2;
var imgCenterY = imageHeight / 2;
var imgTetraEdgeSize;
var imgTetraFaceHt;
if (HtoW > (Math.pow(3, 1/2) / 2)) {
// taller than wider for an equilateral triangle --> constrain by width
imgTetraEdgeSize = imageWidth;
imgTetraFaceHt = imgTetraEdgeSize * Math.pow(3, 1/2) / 2;
} else {
// wider than taller for eq. triangle -- >constrain by height
imgTetraFaceHt = imageHeight;
imgTetraEdgeSize = imgTetraFaceHt * 2 / Math.pow(3, 1/2);
}
var imgTopY = imgCenterY - imgTetraFaceHt / 2;

// for each row
var nRows = nLevels;
var stepY = imgTetraFaceHt / (nRows - 1);
for (var i = 0; i < nRows; i++) {
var nTetras = i + 1;

var rowColors = new Array();
var rowTransparencies = new Array();

// for each tetra in the row, find the closest pixel color
for (var j = 0; j < nTetras; j++) {
var py = imgTopY + i * stepY; // can't have only 1 level

var stepX = imgTetraEdgeSize / (nRows - 1);
var rowWidth = i * stepX;
var startX = imgCenterX - rowWidth / 2;
var divisor = nTetras - 1;
// if (divisor == 0) divisor = 1;
// var stepX = rowWidth / divisor;

var px = startX + stepX * j;

px = Math.round(px);
// px = Math.constrain(0, imageWidth);
py = Math.round(py);
// py = Math.constrain(0, imageHeight);

var red = data[((imageWidth * py) + px) * 4];
var green = data[((imageWidth * py) + px) * 4 + 1];
var blue = data[((imageWidth * py) + px) * 4 + 2];
var alpha = data[((imageWidth * py) + px) * 4 + 3];

var thisColor = new THREE.Color();
thisColor.setRGB( red / 255, green / 255, blue / 255);

// FOR VIEWING TOP IMAGE:
// var newTetra = new tetra(tetraRadius, thisColor, px, py, 0);

var thisAlpha = alpha / 255;

rowColors.push(thisColor);
rowTransparencies.push(thisAlpha);
}
imageColors.push(rowColors);
imageTransparencies.push(rowTransparencies);
}
}

function tetra(size, color, px, py, pz, transparency) {
this.size = size;
this.px = px;
this.py = py;
this.pz = pz;
this.col = color;
this.trans = transparency;

// make the geometry
this.geometry = new THREE.TetrahedronGeometry( this.size );
// rotate the geometry into correct orientation-
this.geometry.applyMatrix( new THREE.Matrix4().makeRotationAxis( new THREE.Vector3( 1, 0, -1 ).normalize(), Math.atan( Math.sqrt(2)) + Math.PI) );
this.geometry.applyMatrix( new THREE.Matrix4().makeRotationY( Math.PI / 12 )); // 15 degrees
// translate geometry to proper location
this.geometry.applyMatrix( new THREE.Matrix4().makeTranslation(this.px, this.py, this.pz)); // 15 degrees

// make the material
this.solidMaterial = new THREE.MeshBasicMaterial( { color: this.col, transparent: true, opacity: this.trans } );
this.wireMaterial = new THREE.MeshBasicMaterial( { color: this.col, transparent: true, opacity: this.trans, wireframe: true, wireframeLinewidth: 1 } );

// attach material to geometry to make mesh
this.solidMesh = new THREE.Mesh( this.geometry, this.solidMaterial );
this.wireMesh = new THREE.Mesh( this.geometry, this.wireMaterial );
}

var makeTetras = function(nLevels, tetraRadius, tetraEdge, tetraHeight) {

// iterating backwards layers transparencies correctly

// for each level, create a triangular grid of tetrahedrons
for (var i = nLevels - 1; i >= 0; i--) {
// for (var i = 0; i < nLevels; i++) {

// each level has the same y coorindate
var levelY = i * tetraHeight;

// calculate number of rows in this level
var nRows = i + 1;
// for each row, create a certain number of tetrahedrons in a line
for (var j = nRows - 1; j >= 0; j--) {
// for (var j = 0; j < nRows; j++) {

// each row has the same z coordinate
var rowZ = -(- i * tetraEdge / Math.pow(3, 1/2) + j * tetraEdge * Math.pow(3, 1/2)/2);

// caluclate number of tetrahedrons in this row
var nTetras = j + 1;

var startX = - (j * tetraEdge) / 2;
var stepX = tetraEdge;

// create nTetras
for (var k = nTetras - 1; k >= 0; k--) {
// for (var k = 0; k < nTetras; k++) {

var px = startX + stepX * k;
var py = levelY;
var pz = rowZ;
var thisColor = new THREE.Color();

var tr = imageColors[j][k].r;
// console.log("i: " + i + "  j: " + j + "  k: " + k + "   ratio: " + (k / i) + "   red: " + tr);
var tg = imageColors[j + nLevels - 1 - i][k + nLevels - 1 - i].g;
var tb = imageColors[j + nLevels - 1 - i][k].b;

var alphaR = tr * imageTransparencies[j][k];
var alphaG = tg * imageTransparencies[j + nLevels - 1 - i][k + nLevels - 1 - i];
var alphaB = tb * imageTransparencies[j + nLevels - 1 - i][k];

// scale colors to projection distance from origin to target wall
// (remove the "1 -" to have colors "grow" from the canvas
// Note: This isn't a perfect solution, but it's pretty close

var scaleR = (1 - (nLevels - i) / (nLevels - i + j));
tr *= scaleR;
alphaR *= scaleR;

var scaleG  = (1 - (nLevels - i) / (nLevels - i + j));
tg *= scaleG;
alphaG *= scaleG;

var scaleB = (1 - (nLevels - i) / (nLevels - i + j));
tb *= scaleB;
alphaB *= scaleB;

// var thisAlpha = Math.max(alphaR, alphaG, alphaB);
// OR: Remove out some of the transparency noise:
var thisAlpha = Math.max(alphaR, alphaG, alphaB);
var alphaThreshold = 0.0;
if (thisAlpha < alphaThreshold) thisAlpha = 0.0;

// scale colors to be proportional of the max intensity (prevents washed out colors in areas of low but present transparency)
maxIntensity = Math.max(tr, tg, tb);
var adjTR = tr / maxIntensity;
var adjTG = tg / maxIntensity;
var adjTB = tb / maxIntensity;

var newTetra = new tetra(tetraRadius, thisColor, px, py, pz, thisAlpha);

var black = false;

// add black corner guide tetras (optional)
// if (i == 0) {
// 	var black = new THREE.Color(0, 0, 0);
// 	var opaque = 1.0;
// 	var boundingTetra = new tetra(tetraRadius, black, px, py, pz, opaque);
// 	black = true;
// } else if (i == (nLevels - 1)) {
// 	if (j == 0) {
// 		var black = new THREE.Color(0, 0, 0);
// 		var opaque = 1.0;
// 		var boundingTetra = new tetra(tetraRadius, black, px, py, pz, opaque);
// 		black = true;
// 	} else if (j == (nRows - 1)) {
// 		if (k == 0) {
// 			var black = new THREE.Color(0, 0, 0);
// 			var opaque = 1.0;
// 			var boundingTetra = new tetra(tetraRadius, black, px, py, pz, opaque);
// 			black = true;
// 		} else if (k == (nTetras - 1)) {
// 			var black = new THREE.Color(0, 0, 0);
// 			var opaque = 1.0;
// 			var boundingTetra = new tetra(tetraRadius, black, px, py, pz, opaque);
// 			black = true;
// 		}
// 	}
// }

// if not the corner cubes, fill with color
if (black == false) {
}
}
}
}
}

init();
animate();

function init() {

camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.x = sceneX;
camera.position.y = sceneY;
camera.position.z = sceneZ;

// ---------------

controls = new THREE.TrackballControls( camera );

controls.target = centerOfScene;

controls.rotateSpeed = 2.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;

controls.noZoom = true;
controls.noPan = true;

controls.staticMoving = false;
controls.enableDamping = false;
// controls.dynamicDampingFactor = 0.3;

controls.keys = [ 65, 83, 68 ];

// --------------

scene = new THREE.Scene();
scene.position = centerOfScene;

// --------------

var imageObj = new Image();
drawImage(this);

};
imageObj.crossOrigin = "anonymous";
imageObj.src = 'http://i.imgur.com/vO0sNpn.png?1'; //'http://i.imgur.com/ybPwhEJ.png?1'; //'http://i.imgur.com/ybPwhEJ.png'; //'http://i.imgur.com/BVRzlPn.png'; //'http://i.imgur.com/iEr8YIz.png'; //'http://i.imgur.com/deGu5WQ.png?3'; //'http://i.imgur.com/deGu5WQ.png?1'; //'http://i.imgur.com/kHcRALD.png?1;' // 'http://i.imgur.com/5W12yQW.png?1'; //'http://i.imgur.com/5W12yQW.png'; //'http://i.imgur.com/gKSTRFl.png';

// ^ ONLY USE 100 px square images

// --------------

// DRAW (0, 0, 0) for debugging:
// var geometry = new THREE.SphereGeometry( 40, 32, 32 );
// var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
// var sphere = new THREE.Mesh( geometry, material );

//---------------

var light = new THREE.AmbientLight( 0xffffff ); // soft white light

renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false; // ??
document.body.appendChild( renderer.domElement );

// window.addEventListener( 'resize', onWindowResize, false );

render();
}

// function onWindowResize() {

// 	camera.aspect = window.innerWidth / window.innerHeight;
// 	camera.updateProjectionMatrix();

// 	renderer.setSize( window.innerWidth, window.innerHeight );

// 	controls.handleResize();

// 	render();

// }

function animate() {

camera.position.x = 0.2 * (mouse.x * 500) + 0.8 * (camera.position.x);
camera.position.y = 0.2 * (mouse.y * 600 + 300) + 0.8 * (camera.position.y);
// camera.lookAt(centerOfScene);

requestAnimationFrame( animate );
controls.update();
}

function render() {

renderer.render( scene, camera );
// stats.update();
}

```

## Project-08-Aereed

sketch

For this project I focused more on creating a piece that would draw the eye around, then developing the final image at the end. It took me a while to figure out how to make the paths change direction randomly and in the end it wasn’t exactly what I wanted. If I were to return to this project I would try and make it so that the pixels traveled up and down with no trail and continued to travel even when the next one was called. I also think that pixel size in that project would be the same in stead of random.

//Alex E. S. Reed
//15-104 MWF 12:30
//aereed@andrew.cmu.edu
//Assignment-Project-08

var underlyingImage;
var size; //size of the pixels
var ix, iy;
var theColorAtLocationXY;
var spotX; // location on x
var spotY; // location on y
var count = 0; //number of clicks
var speed;
var speed2;

var myImageURL = ‘https://i.imgur.com/YLbb1wr.jpg?1’;
}

function setup() {
createCanvas(370, 320);
background(30);
}

function draw() {

if (count % 2 == 0) { // if the count is even set the y speed
spotY = (spotY + speed) % 200;
spotX = spotX + speed2;
} else {
spotX = (spotX + speed) % 200; // if the count is odd set the x speed
spotY = spotY + speed2;
}
spots();
}

function spots () {
sizeA = random(6, 20);
sizeB = random(6, 20);
ix = constrain(floor(spotX), 0, width-1);
iy = constrain(floor(spotY), 0, height-1);
theColorAtLocationXY = underlyingImage.get(ix, iy);

noStroke();
fill(theColorAtLocationXY);

rectMode(CENTER);

if (count % 2 == 0) {
rect(spotX, spotY, sizeA, sizeB); // if the count is even draw a rect
} else {
ellipse(spotX, spotY, sizeA, sizeB); // odd draw a circle
}
}

function crossHairs() {
strokeWeight(0.5);
stroke(255);
line(mouseX, 0, mouseX, height);
line(0, mouseY, width, mouseY);
}

function mousePressed () { // this function places a cross hair and a pixel at the correct place
crossHairs();

spotX = mouseX;
spotY = mouseY;

spots();

var m = constrain(mouseX, 0, width); //x boundaries
var p = constrain(mouseY, 0, height); //y boundaries

if (mouseX == m && mouseY == p) { // if the mouse is clicked in here
count ++; //increase count

speed = floor(random()*3 – 1);
speed2 = floor(random()*3 – 1);
}
}

## Johanna McAllister Project-08

This is blatantly a small adjustment to Golan’s notes. Instead of plain squares and circles, the pixels are the image itself, modified by the tint function.

Jo Project 08

After spending a good amount of time trying to debug the following code, this non-functional program below was my original idea. Inspired by Roy Liechtenstein’s layered works, I had planned to isolate shapes (or blobs), determined by whether or not the brightness of their pixels are in a given range of brightnesses. If I were to adjust the range of brightnesses, different shapes would appear. The prospective end result would have been a time-based series of these shapes. In rapid succession, these different frames would have been able to show my sister’s face.

Here is the code.

## JooYun Han – Project08

The animation “Inside Out” was impressive to me, and I would like to make kinds of emotion balls which is pile of myself – soul balls. Once it is clicked, it will create a ball, it will fall down and there will be a hole where my soul comes out. When “H” is typed, the last hall will be disappeared. Using self-portrait makes me a little bit sentimental and I think it is good practice for reflecting myself with writing code and design it.

sketch