Save Images From Flickr

 

So you want to steal images from Flickr? Flickr tries to protect it’s copyrighted images by disallowing the user to “right click” and download the image. However, in order to see the image in your browser, the image had to served from somewhere…

Above you can see that the common user wouldn’t be able to save this image to their desktop. However, if we inspect the element in firebug we realize the image is actually contained in a ‘div’ just a few lines above. So we can copy that image URL and save our image that way! See below…

As you can see below, I copied the image url to my browser, visited the page and I can save my Image…

That’s it!

HTML5 and CSS3 Porthole

UPDATE: Here’s the finished version…

Show an image behind another image on your site…

By no means is this new, noteworthy or otherwise important. However, my solution was fairly simple, so it’s a good candidate for a tutorial.

First off, here’s the demo (I know it doesn’t look good in safari/opera and probably won’t work at all in IE…but it works on the browsers I use):

DEMO

When the user loads the page, they see one image. When they hover over “#zoomer” they see the other image, inside the magnifying glass. It works by masking the second image to a small circle, making the circle follow your mouse and overlaying a magnifying glass (which also follows your mouse). Aight, let’s get into it…

The HTML…

Make a wrapper called “zoomer” and put two of the same sized image wrapped in divs inside. we’ll monitor “zoomer” for mousemove events. Then when someone is moving the mouse over it we’ll adjust the positions of the second image and the magnifying glass.

<div id="zoomer">

                <div class="small-window">
                    <img class="small-image" src="images/small.jpg" alt="small lorizzle" />
                </div>

                <div class="big-window">
                    <img class="big-image" src="images/big.jpg" alt="big lorizzle" />
                </div>

                <img src="images/magnifying-glass.png" alt="" id="mag" />

            </div>

The CSS…

We’ll set the width and height of “#zoomer” to the width and height of our images. Make sure to set the width and height of the second image as well. Then set “#zoomer” to position: relative so we can keep everything contained. Set the images to position: absolute to get them to overlay eachother. Set the second image window to an opacity: zero so we can make sure it doesn’t show on top of the base image. Since we want to set a border radius to the second image, but we also need to set it to position: absolute, we’ll need an image mask because this breaks in chrome (don’t ask how I know that, it just takes years of experimentation). We’ll set the magnifying glass to position: absolute so we can move it around easily with javascript and we’ll add some transitions to the two images so they fade in and out. As the user hovers off “#zoomer” we’ll set the opacity: 0 with javascript and when they hover on, we’ll fade the images and magnifying glass in by setting opacity to 1. The transitions will make it look like its fading in and out.

#zoomer {
    position: relative;
    width: 440px;
    height: 600px;
    margin: 0 auto;
}

.small-image {
    position: absolute;
    top: 0;
    left: 0;
}

.big-image {
    position: absolute;
    top: 0;
    left: 0;
    width: 440px;
    height: 600px;
}

.big-window {
    position: absolute;  /* this breaks overflow: hidden in webkit (chrome/safari)*/
    opacity: 0;

    -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);

    overflow: hidden;

    transition: opacity .4s;
    -moz-transition: opacity .4s;
    -webkit-transition: opacity .4s;
    -o-transition: opacity .4s;

}

#mag {
    opacity: 0;
    width: 219px;
    height: 139px;
    position: absolute;

    transition: opacity .4s;
    -moz-transition: opacity .4s;
    -webkit-transition: opacity .4s;
    -o-transition: opacity .4s;
}

Now for the fun part (Javascript)…

Follow the comments to see what’s going on, I tried to explain it the best I can. Basically, we grab some elements, set up out radius and border-radius based on our radius setting (size of the porthole or viewport). Then we listen for a mouse moving over our “#zoomer” element and grab the position of the mouse. We get the page offset so we can tell where the mouse is relative to our element. We detect if the mouse is still between the bounds of #zoomer and if it is we adjust the magnifying glass and our porthole to follow the mouse. Finally, if the mouse leaves, we fade everything out.

// grab the elements and do some calculations on the size of the "porthole", then set the porthole
                var zoomer = $('#zoomer');
                var porthole = $('.big-window');
                var secondImage = $('.big-image');
                var magImage = $('#mag');
                var diameter = 100;
                var radius = diameter / 2;

                porthole.css({
                    width: diameter,
                    height: diameter,
                    'border-radius': radius + 'px',
                    '-moz-border-radius': radius + 'px',
                    '-webkit-border-radius': radius + 'px',
                    '-o-border-radius': radius + 'px'
                });

                // as soon as someone starts moving the mouse over #zoomer
                zoomer.on('mousemove', function(evt) {

                    // get the page offsets (since we centered our #zoomer)
                    var oLeft = evt.currentTarget.offsetLeft;
                    var oTop = evt.currentTarget.offsetTop;

                    // get the position of the mouse over #zoomer
                    var posLeft = (evt.pageX - oLeft);
                    var posTop = (evt.pageY - oTop);

                    // detect whether or not the mouse has moved past the left or right barriers of #zoomer
                    var limitLeft = ((posLeft - radius) > - radius) ? false : true;
                    var limitRight = ((posLeft + radius) < (440 + radius)) ? false : true;

                    // if we're in bounds of #zoomer
                    if(!limitLeft && !limitRight) {

                        // account for the radius of the "porthole"
                        var posTop = posTop - radius;
                        var posLeft = posLeft - radius;

                        // move the porthole to the mouse's position
                        porthole.css({
                            top: posTop,
                            left: posLeft,
                            opacity: 1
                        });

                        // adjust the second image so it moves opposite the mouse movement (so it looks like its staying still)
                        secondImage.css({
                            top: -1 * (posTop),
                            left: -1 * (posLeft)
                        });

                        // move the magnifying glass with the mouse
                        magImage.css({
                            top: posTop,
                            left: posLeft,
                            opacity: 1
                        });

                    } else {
                        // fade the porthole out
                        porthole.css({
                            opacity: 0
                        });

                        // fade the magnifying glass out
                        magImage.css({
                            opacity: 0
                        });
                    }

                });

That’s it!

Feel free to download the zip file or view the demo (link at the top of the page).

Download

 

 

 

 

WiFly Shield Ruining Your Weekend?

It’s because, most likely…you’ve updated your arduino IDE to 1.0 and all of a sudden your scripts stop compiling. We’ll I have a fix…

So it took me all day to realize this, track down all the libraries, restart everything and the rest of it but I have a process. Let’s say you’ve just updated to 1.0 and removed your old Arduino library. You’re starting from scratch:

  1. Download the PString library.
  2. Download the Streaming library.
  3. Download the Time library, and install just the “Time” sub directory like in the photo below:

4. Finally, Install the latest fork of the Arduino-Shield library. NOTE: at the time of writing this, the link has the latest, but you should check Github to make sure (https://github.com/sparkfun/WiFly-Shield/network).

You should have something that looks like this in your arduino folder:

Make sure to rename your downloaded WiFly-Shield library to “WiFly”. Try one of the examples and it should work. Like I said, I spent all day figuring this out, so if you run into trouble let me know and I’ll try to help. Chances are I did the same thing.

 

Blobify Yourself with Kinect and Bubble Boy Part 2

NOTE: If you’re looking for the older version please refer here.

This script takes point cloud data from the kinect, marches over the points using a marching cubes algorithm and renders a polygonal mesh. As far as I’m aware, it was the first live 3D (actual) rendering done on a kinect.

You’ll need…

Installation…

  1. I wrote an installation guide here, follow it to install open kinect.
  2. Download the latest toxiclibs library and unzip it.
  3. Move the unzipped toxiclibs folder into ~/Documents/Processing/libraries/
  4. Copy the code below and paste it in a new sketch in processing.
  5. Save the sketch as bubbleBoy.
  6. Restart processing so it knows to include the new library.

Code…

This is rather long. Basically, instead of including .java files in the sketch directory, I copied the contents into the sketch itself. Processing allows you to do this because it executes and compiles as Java anyway. All that’s needed is a minor tweak involving making marchingCubeTables a static method to allow for static fields. I also don’t include the processing.core files as you don’t need them.

NOTE: If you don’t feel like copying and pasting, I have a zip file with everything you’ll need at the bottom of this page. Just download, copy the toxiclibs library to your “libraries” folder in ~/Documents/Processing/ and copy the bubbleBoy2 directory to you “sketches” folder in ~/Documents/sketches .

import toxi.geom.Vec3D;
import processing.opengl.*;
//import rui.marchingCubes.*;

// kinect
import org.openkinect.*;
import org.openkinect.processing.*;

//import processing.core.PApplet;

import java.lang.Math;
import java.util.ArrayList;
import toxi.geom.Vec3D;

MarchingCubes mc;
Vec3D rotationAxis;

Boolean bUseFill;

// kinect
Kinect kinect;
float a = 0;
// Size of kinect image
int w = 640;
int h = 480;
int kWidth  = 640;
int kHeight = 480;
// depth mapping and tilt
boolean depth = true;
boolean rgb = false;
boolean ir = false;
float deg = 8; // Start at 15 degrees
PImage depthImg;
int minDepth =  40;
int maxDepth = 860;
// set initial record to false
boolean record = false;
int counter = 0;
// print custom file
boolean printFile = false;
ArrayList points;
PrintWriter output;
// We'll use a lookup table so that we don't have to repeat the math over and over
float[] depthLookUp = new float[2048];

void setup(){
  size(1024, 600, OPENGL);
  Vec3D aabbMin = new Vec3D(-width/2, -height/2, -250);
  Vec3D aabbMax = new Vec3D(width/2, height/2, 250);
  Vec3D numPoints = new Vec3D(50,50,50);
  float isoLevel = 1;
  mc = new MarchingCubes(this, aabbMin, aabbMax, numPoints, isoLevel);

  rotationAxis = new Vec3D();

  bUseFill = false;

  // kinect
  kinect = new Kinect(this);
  kinect.start();
  kinect.enableDepth(true);
  kinect.tilt(deg);
  // We don't need the grayscale image in this example
  // so this makes it more efficient
  kinect.processDepthImage(false);
  // get depthImg to constrain
  depthImg = new PImage(kWidth, kHeight);
  // Lookup table for all possible depth values (0 - 2047)
  for (int i = 0; i < depthLookUp.length; i++) {
    depthLookUp[i] = rawDepthToMeters(i);
  }
  points = new ArrayList();
  output = createWriter("points.txt");

}

void draw(){
  background(255);
  lights();

  // kinect
  int[] depth = kinect.getRawDepth();
  int skip = 50;
  //translate(width/750,height/750,-50);
    mc.reset();

    // original for loop
    println("entering loop");
    int nBalls = 0;
    for(int x=0; x<w; x+=skip) {
      for(int y=0; y<h; y+=skip) {
        int offset = x+y*w;
        int rawDepth = depth[offset];

        if(rawDepth >= minDepth && rawDepth <= maxDepth) {
          PVector v = depthToWorld(x,y,rawDepth);
          Vec3D metaBallPos = new Vec3D(v.x * 500, v.y * 300, v.z*300);
          mc.addMetaBall(metaBallPos, 100, 1);
          nBalls++;
        }

      }
    }
    println("done with loop, " + nBalls + " balls");
    // end original for loop

  mc.createMesh();
  if(bUseFill){
    fill(0,255,0);
    noStroke();
  }
  else {
    noFill();
    stroke(127);
  }

  pushMatrix();
  translate(width/2, height/2, 0);
  rotateX(rotationAxis.x);
  rotateY(rotationAxis.y);
  mc.renderMesh();
  popMatrix();
}

PVector depthToWorld(int x, int y, int depthValue) {

  final double fx_d = 1.0 / 5.9421434211923247e+02;
  final double fy_d = 1.0 / 5.9104053696870778e+02;
  final double cx_d = 3.3930780975300314e+02;
  final double cy_d = 2.4273913761751615e+02;

  PVector result = new PVector();
  double depth =  depthLookUp[depthValue];//rawDepthToMeters(depthValue);
  result.x = (float)((x - cx_d) * depth * fx_d);
  result.y = (float)((y - cy_d) * depth * fy_d);
  result.z = (float)(depth);
  return result;
}

float rawDepthToMeters(int depthValue) {
  if (depthValue < 2047) {
    return (float)(1.0 / ((double)(depthValue) * -0.0030711016 + 3.3309495161));
  }
  return 0.0f;
}

void keyPressed(){
  if(key == CODED){
    if(keyCode == LEFT) rotationAxis.y += 0.05;
    if(keyCode == RIGHT) rotationAxis.y -= 0.05;
    if(keyCode == UP) rotationAxis.x -= 0.05;
    if(keyCode == DOWN) rotationAxis.x += 0.05;
  }
  else {
    if(key == ' '){
      bUseFill = !bUseFill;
    }
    if(key == 'r' || key == 'R'){
      mc.reset();
      rotationAxis.set(0,0,0);
    }
  }
}

void stop() {
  kinect.quit();
  super.stop();
}

// This is because we can't find fuck all for libraries, so we'll just drop java in here as it's converted at runtime...

/**
 *
 *
 * simple class implementing the Marching Cubes algorithm to create 3d volumetric meshes.
 * based on the code and explanations by Paul Bourke that can be found here:
 * http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
 *
 * its dependent on Processing's PApplet and Karsten Schmidt's Vec3D class,
 * you can find processing here: www.processing.org
 * and you can find the Vec3D class here: code.google.com/p/toxiclibs
 *
 * @author ruimadeira
 *
 */
public class MarchingCubes {

	PApplet p5;

	public float voxelValues[][][];
	protected Vec3D voxels[][][];
	protected Vec3D numPoints, aabbMin, aabbMax;
	protected Vec3D cubeSize;
	protected Vec3D worldSize;
	protected float isoLevel;

	private Vec3D vertList[];

	protected ArrayList<MCTriangle> triangles;

	/**
	 * constructor:
	 * you must define the world bounds, the number of points that will make the grid (in a Vec3D),
	 * and the isoLevel.
	 * @param _p5
	 * @param _aabbMin
	 * @param _aabbMax
	 * @param _numPoints
	 * @param _isoLevel
	 */

	public MarchingCubes(PApplet _p5, Vec3D _aabbMin, Vec3D _aabbMax, Vec3D _numPoints, float _isoLevel){
		p5 = _p5;
		aabbMin = new Vec3D(_aabbMin);
		aabbMax = new Vec3D(_aabbMax);
		worldSize = aabbMax.sub(aabbMin);
		numPoints = new Vec3D(_numPoints);
		cubeSize = new Vec3D(worldSize.x / (numPoints.x-1), worldSize.y / (numPoints.y-1), worldSize.z / (numPoints.z-1));
		voxelValues = new float[(int)numPoints.x][(int)numPoints.y][(int)numPoints.z];
		voxels = new Vec3D[(int)numPoints.x][(int)numPoints.y][(int)numPoints.z];

		_internalReset();
		isoLevel = _isoLevel;

		vertList = new Vec3D[12];
		triangles = new ArrayList<MCTriangle>();

	}

	/**
	 * creates the mesh
	 */
	public void createMesh(){
		triangles = new ArrayList<MCTriangle>();
		for(int i=0; i<numPoints.x-1; i++){
			for(int j=0; j<numPoints.y-1; j++){
				for(int k=0; k<numPoints.z-1; k++){
					polygonise(i, j, k);
				}
			}
		}
	}

	/**
	 * returns an ArrayList of MCTriangles with all the triangles that make up the mesh
	 * @return
	 */
	public ArrayList<MCTriangle> getMesh(){
		return triangles;
	}

	/**
	 * copies the mesh triangles into an array and returns
	 * @return
	 */
	public MCTriangle[] getMeshToArray(){
		MCTriangle _triArray[] = new MCTriangle[triangles.size()];
		triangles.toArray(_triArray);
		return _triArray;
	}

	/**
	 * default rendering, renders the mesh
	 */
	public void renderMesh(){
		MCTriangle tri;
		p5.beginShape(PApplet.TRIANGLES);
		for(int i=0; i<triangles.size(); i++){
			tri = triangles.get(i);
			p5.vertex(tri.a.x, tri.a.y, tri.a.z);
			p5.vertex(tri.b.x, tri.b.y, tri.b.z);
			p5.vertex(tri.c.x, tri.c.y, tri.c.z);
		}
		p5.endShape();
	}

	/**
	 * renders the iso grid.
	 * its useful for debuging.
	 */
	public void renderGrid(){
		p5.noFill();
		p5.stroke(127);
		p5.beginShape(PApplet.LINES);
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z-1; k++){
					p5.vertex(voxels[i][j][k].x, voxels[i][j][k].y, voxels[i][j][k].z);
					p5.vertex(voxels[i][j][k+1].x, voxels[i][j][k+1].y, voxels[i][j][k+1].z);
				}
			}
		}
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y-1; j++){
				for(int k=0; k<numPoints.z; k++){
					p5.vertex(voxels[i][j][k].x, voxels[i][j][k].y, voxels[i][j][k].z);
					p5.vertex(voxels[i][j+1][k].x, voxels[i][j+1][k].y, voxels[i][j+1][k].z);
				}
			}
		}

		for(int i=0; i<numPoints.x-1; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z; k++){
					p5.vertex(voxels[i][j][k].x, voxels[i][j][k].y, voxels[i][j][k].z);
					p5.vertex(voxels[i+1][j][k].x, voxels[i+1][j][k].y, voxels[i+1][j][k].z);
				}
			}
		}
		p5.endShape();
	}

	/**
	 * returns a tridimensional array of the values that each voxel has.
	 * you can use this to define the value of each voxel
	 * @return
	 */
	public float[][][] getValues(){
		return voxelValues;
	}

	/**
	 * return the voxel grid that makes up the iso space, in a three dimensional array.
	 * @return
	 */
	public Vec3D[][][] getVoxels(){
		return voxels;
	}

	/**
	 * sets the iso value of a voxel
	 *
	 * @param posX
	 * @param posY
	 * @param posZ
	 * @param value
	 */
	public void setValue(int indexX, int indexY, int indexZ, float value){
		if(indexX > -1 && indexX < numPoints.x &&
		   indexY > -1 && indexY < numPoints.y &&
		   indexZ > -1 && indexZ < numPoints.z){
			voxelValues[indexX][indexY][indexZ] = value;
		}
	}

	/**
	 * gets the value of the specified voxel
	 * @param posX
	 * @param posY
	 * @param posZ
	 * @return
	 */
	public float getValue(int posX, int posY, int posZ){
		if(posX > -1 && posX < numPoints.x &&
		   posY > -1 && posY < numPoints.y &&
		   posZ > -1 && posZ < numPoints.z){
			return voxelValues[posX][posY][posZ];
		}
		return 0;
	}

	/**
	 * returns the a specific voxel of the iso space
	 * @param posX
	 * @param posY
	 * @param posZ
	 * @return
	 */
	public Vec3D getVoxel(int posX, int posY, int posZ){
		if(posX > -1 && posX < numPoints.x &&
		   posY > -1 && posY < numPoints.y &&
		   posZ > -1 && posZ < numPoints.z){
			return voxels[posX][posY][posZ];
		}
		return new Vec3D(0,0,0);
	}

	/**
	 * checks if the specified point is inside a voxel cube and returns the voxel.
	 * returns a new Vec3D if point is outside the grid.
	 * @param pos
	 * @return
	 */
	public Vec3D getVoxelAtWorldCoord(Vec3D point){
		for(int i=0; i<voxels.length-1; i++){
			for(int j=0; j<voxels[i].length-1; j++){
				for(int k=0; k<voxels[i][j].length-1; k++){
					if(point.x >= voxels[i][j][k].x &&
					   point.y >= voxels[i][j][k].y &&
					   point.z >= voxels[i][j][k].z &&
					   point.x <= voxels[i+1][j+1][k+1].x &&
					   point.y <= voxels[i+1][j+1][k+1].y &&
					   point.z <= voxels[i+1][j+1][k+1].z){
						return voxels[i][j][k];
					}
				}
			}
		}
		return new Vec3D();
	}

	/**
	 *  adds a metaball, with the specified radius, the grid points
	 *  inside the radius will be added the "metaValue"
	 * @param pos
	 * @param radius
	 * @param metaValue
	 */
	public void addMetaBall(Vec3D pos, float radius, float metaValue){
		float radiusSQ = radius*radius;
		float distSQ;

		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=0; k<voxels[i][j].length; k++){
					distSQ = voxels[i][j][k].distanceToSquared(pos);
					if(distSQ < radiusSQ){
						voxelValues[i][j][k] += (1-distSQ / radiusSQ) * metaValue;
					}
				}
			}
		}
	}

	public void addMetaBox(Vec3D aabbMin, Vec3D aabbMax, float metaValue){
		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=0; k<voxels[i][j].length; k++){
					if(voxels[i][j][k].x > aabbMin.x && voxels[i][j][k].y > aabbMin.y &&
					   voxels[i][j][k].z > aabbMin.z && voxels[i][j][k].x < aabbMax.x &&
					   voxels[i][j][k].y < aabbMax.y && voxels[i][j][k].z < aabbMax.z){
						PApplet.println("added");
						voxelValues[i][j][k] += metaValue;
					}
				}
			}
		}
	}

	/**
	 * returns the maximum voxel value
	 * @return
	 */
	public float getMax(){
		float _max = voxelValues[0][0][0];
		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=1; k<voxels[i][j].length; k++){
					if(_max < voxelValues[i][j][k])_max = voxelValues[i][j][k];
				}
			}
		}
		return _max;
	}

	/**
	 * returns the lowest voxel value
	 * @return
	 */
	public float getMin(){
		float _min = voxelValues[0][0][0];
		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=1; k<voxels[i][j].length; k++){
					if(_min > voxelValues[i][j][k])_min = voxelValues[i][j][k];
				}
			}
		}

		return _min;
	}

	/**
	 * multiplies all grid values with _val
	 * @param _val
	 */
	public void scale(float _val){
		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=0; k<voxels[i][j].length; k++){
					voxelValues[i][j][k] *= _val;
				}
			}
		}
	}

	/**
	 * sets all grid values with _val
	 * @param _val
	 */
	public void set(float _val){
		for(int i=0; i<voxels.length; i++){
			for(int j=0; j<voxels[i].length; j++){
				for(int k=0; k<voxels[i][j].length; k++){
					voxelValues[i][j][k] = _val;
				}
			}
		}
	}

	/**
	 * sets the grid point with specified index with the value
	 * @param indexX
	 * @param indexY
	 * @param indexZ
	 * @param val
	 */
	public void set(int indexX, int indexY, int indexZ, float val){
		if(indexX >-1 && indexX < numPoints.x &&
		   indexY >-1 && indexY < numPoints.y &&
		   indexZ >-1 && indexZ < numPoints.z){
			voxelValues[indexX][indexY][indexZ] = val;
		}
	}

	/**
	 * normalizes the voxel values
	 */
	public void normalize(){
		float maxVal = 0;
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z; k++){
					if(voxelValues[i][j][k] > maxVal) maxVal = voxelValues[i][j][k];
				}
			}
		}
		float invertMaxVal = 1.0f/maxVal;
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z; k++){
					voxelValues[i][j][k] *= invertMaxVal;
				}
			}
		}
	}

	/**
	 * resets the voxel values to zero
	 */
	public void reset(){
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z; k++){
					voxelValues[i][j][k] = 0;
				}
			}
		}
	}

	/**
	 * redefines the minimum bounds of the iso space
	 * @param _aabbMin
	 */
	public void setAABBMin(Vec3D _aabbMin){
		aabbMin.set(_aabbMin);
		_internalReset();
	}

	/**
	 * returns the minimum bound of the iso space
	 * @return
	 */
	public Vec3D getAABBMin(){
		return aabbMin;
	}

	/**
	 * redefines the maximum bound of the iso space
	 * @param _aabbMax
	 */
	public void setAABBMax(Vec3D _aabbMax){
		aabbMax.set(_aabbMax);
		_internalReset();
	}

	/**
	 * returns the maximum bound of the iso space
	 * @return
	 */
	public Vec3D getAABBMax(){
		return aabbMax;
	}

	/**
	 * returns the number of triangles that make up the mesh
	 * @return
	 */
	public int getNumTriangles(){
		return triangles.size();
	}

	/**
	 * returns the iso level
	 * @return
	 */
	public float getIsoLevel(){
		return isoLevel;
	}

	/**
	 * sets the iso level
	 * @param _isoLevel
	 */
	public void setIsoLevel(float _isoLevel){
		isoLevel = _isoLevel;
	}

	/**
	 * returns the number of vertexes that make up the iso space
	 * in a Vec3D: the x value represents the number of elements along the X axis,
	 * the y value the number of elements along the Y axis and the z value the number
	 * of elements along the Z axis
	 * @return
	 */
	public Vec3D getNumVoxels(){
		return numPoints;
	}

	/**
	 * redefines the number of voxels that make up the grid
	 * @param _numPoints
	 */
	public void setNumVoxels(Vec3D _numPoints){
		numPoints.set(_numPoints.x, _numPoints.y, _numPoints.z);
		voxels = new Vec3D[(int)numPoints.x][(int)numPoints.y][(int)numPoints.z];
		voxelValues = new float[(int)numPoints.x][(int)numPoints.y][(int)numPoints.z];
		_internalReset();
	}

	/**
	 * returns the size of a single cube of the iso space
	 * @return
	 */
	public Vec3D getCubeSize(){
		return cubeSize;
	}

	/**
	 * returns the total size of the iso space
	 * @return
	 */
	public Vec3D getWorldSize(){
		return worldSize;
	}

	//Internals
	protected void _internalReset(){
		for(int i=0; i<numPoints.x; i++){
			for(int j=0; j<numPoints.y; j++){
				for(int k=0; k<numPoints.z; k++){
					voxels[i][j][k] = new Vec3D(cubeSize.x * i, cubeSize.y * j, cubeSize.z * k);
					voxels[i][j][k].x += aabbMin.x;
					voxels[i][j][k].y += aabbMin.y;
					voxels[i][j][k].z += aabbMin.z;
					voxelValues[i][j][k] = 0;

				}
			}
		}
	}

	protected void polygonise(int i, int j, int k){
		int cubeIndex = 0;
		if (voxelValues[i][j][k] < isoLevel) cubeIndex |= 1;
		if (voxelValues[i+1][j][k] < isoLevel) cubeIndex |= 2;
		if (voxelValues[i+1][j+1][k] < isoLevel) cubeIndex |= 4;
		if (voxelValues[i][j+1][k] < isoLevel) cubeIndex |= 8;
		if (voxelValues[i][j][k+1] < isoLevel) cubeIndex |= 16;
		if (voxelValues[i+1][j][k+1] < isoLevel) cubeIndex |= 32;
		if (voxelValues[i+1][j+1][k+1] < isoLevel) cubeIndex |= 64;
		if (voxelValues[i][j+1][k+1] < isoLevel) cubeIndex |= 128;
		/* Cube is entirely in/out of the surface */
		if (MarchingCubesTables.edgeTable[cubeIndex] == 0){
			return;
		}

		/* Find the vertices where the surface intersects the cube */

		if ((MarchingCubesTables.edgeTable[cubeIndex] & 1) > 0){
			vertList[0] = vertexInterp(isoLevel, voxels[i][j][k], voxels[i+1][j][k], voxelValues[i][j][k] ,voxelValues[i+1][j][k]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 2) > 0){
			vertList[1] = vertexInterp(isoLevel, voxels[i+1][j][k], voxels[i+1][j+1][k], voxelValues[i+1][j][k], voxelValues[i+1][j+1][k]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 4) > 0){
			vertList[2] = vertexInterp(isoLevel, voxels[i+1][j+1][k], voxels[i][j+1][k], voxelValues[i+1][j+1][k], voxelValues[i][j+1][k]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 8  ) > 0){
			vertList[3] = vertexInterp(isoLevel, voxels[i][j+1][k], voxels[i][j][k], voxelValues[i][j+1][k], voxelValues[i][j][k]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 16) > 0){
			vertList[4] = vertexInterp(isoLevel, voxels[i][j][k+1], voxels[i+1][j][k+1], voxelValues[i][j][k+1], voxelValues[i+1][j][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 32) > 0){
			vertList[5] = vertexInterp(isoLevel, voxels[i+1][j][k+1], voxels[i+1][j+1][k+1], voxelValues[i+1][j][k+1], voxelValues[i+1][j+1][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 64) > 0){
			vertList[6] = vertexInterp(isoLevel, voxels[i+1][j+1][k+1], voxels[i][j+1][k+1], voxelValues[i+1][j+1][k+1], voxelValues[i][j+1][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 128) > 0){
			vertList[7] = vertexInterp(isoLevel, voxels[i][j+1][k+1], voxels[i][j][k+1], voxelValues[i][j+1][k+1], voxelValues[i][j][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 256) > 0){
			vertList[8] = vertexInterp(isoLevel, voxels[i][j][k], voxels[i][j][k+1], voxelValues[i][j][k], voxelValues[i][j][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 512) > 0){
			vertList[9] = vertexInterp(isoLevel, voxels[i+1][j][k], voxels[i+1][j][k+1], voxelValues[i+1][j][k], voxelValues[i+1][j][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 1024) > 0){
			vertList[10] = vertexInterp(isoLevel, voxels[i+1][j+1][k], voxels[i+1][j+1][k+1], voxelValues[i+1][j+1][k], voxelValues[i+1][j+1][k+1]);
		}
		if ((MarchingCubesTables.edgeTable[cubeIndex] & 2048) > 0){
			vertList[11] = vertexInterp(isoLevel,	voxels[i][j+1][k], voxels[i][j+1][k+1], voxelValues[i][j+1][k], voxelValues[i][j+1][k+1]);
		}

		Vec3D vecA;
		Vec3D vecB;
		Vec3D normalVec = new Vec3D();
		for(i=0; MarchingCubesTables.triTable[cubeIndex][i] != -1; i+=3){

			vecA = vertList[MarchingCubesTables.triTable[cubeIndex][i+1]].sub(vertList[MarchingCubesTables.triTable[cubeIndex][i]]);
			vecB = vertList[MarchingCubesTables.triTable[cubeIndex][i+2]].sub(vertList[MarchingCubesTables.triTable[cubeIndex][i+1]]);
			normalVec = vecA.cross(vecB);

			Vec3D triA = new Vec3D(vertList[MarchingCubesTables.triTable[cubeIndex][i]].x, vertList[MarchingCubesTables.triTable[cubeIndex][i]].y, vertList[MarchingCubesTables.triTable[cubeIndex][i]].z);
			Vec3D triB = new Vec3D(vertList[MarchingCubesTables.triTable[cubeIndex][i+1]].x, vertList[MarchingCubesTables.triTable[cubeIndex][i+1]].y, vertList[MarchingCubesTables.triTable[cubeIndex][i+1]].z);
			Vec3D triC = new Vec3D(vertList[MarchingCubesTables.triTable[cubeIndex][i+2]].x, vertList[MarchingCubesTables.triTable[cubeIndex][i+2]].y, vertList[MarchingCubesTables.triTable[cubeIndex][i+2]].z);
			triangles.add(new MCTriangle(triA, triB, triC, normalVec));
		}
	}

	protected Vec3D vertexInterp(float _isoLevel, Vec3D vertice, Vec3D vertice2, float valP1, float valP2){
		 float mu;
		 Vec3D p = new Vec3D();

		   if (Math.abs(isoLevel-valP1) < 0.00001)
		      return(vertice);
		   if (Math.abs(isoLevel-valP2) < 0.00001)
		      return(vertice2);
		   if (Math.abs(valP1-valP2) < 0.00001)
		      return(vertice);
		   mu = (isoLevel - valP1) / (valP2 - valP1);
		   p.x = vertice.x + mu * (vertice2.x - vertice.x);
		   p.y = vertice.y + mu * (vertice2.y - vertice.y);
		   p.z = vertice.z + mu * (vertice2.z - vertice.z);

		   return p;
	}

}

/**
 *
 * tables, dont mess with these :)
 * @author ruimadeira
 *
 */
public static final class MarchingCubesTables {
	public static final int edgeTable[] = {
		0x0  , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
		0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
		0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
		0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
		0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
		0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
		0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
		0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
		0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
		0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
		0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
		0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
		0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
		0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
		0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
		0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
		0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
		0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
		0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
		0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
		0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
		0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
		0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
		0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
		0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
		0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
		0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
		0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
		0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
		0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
		0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
		0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0
		};

	public static final int triTable[][] = {
		{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
		{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
		{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
		{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
		{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
		{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
		{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
		{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
		{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
		{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
		{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
		{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
		{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
		{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
		{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
		{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
		{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
		{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
		{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
		{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
		{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
		{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
		{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
		{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
		{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
		{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
		{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
		{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
		{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
		{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
		{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
		{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
		{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
		{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
		{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
		{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
		{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
		{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
		{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
		{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
		{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
		{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
		{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
		{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
		{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
		{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
		{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
		{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
		{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
		{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
		{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
		{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
		{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
		{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
		{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
		{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
		{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
		{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
		{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
		{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
		{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
		{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
		{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
		{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
		{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
		{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
		{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
		{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
		{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
		{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
		{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
		{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
		{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
		{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
		{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
		{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
		{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
		{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
		{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
		{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
		{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
		{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
		{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
		{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
		{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
		{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
		{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
		{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
		{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
		{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
		{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
		{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
		{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
		{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
		{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
		{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
		{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
		{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
		{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
		{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
		{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
		{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
		{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
		{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
		{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
		{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
		{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
		{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
		{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
		{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
		{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
		{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
		{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
		{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
		{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
		{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
		{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
		{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
		{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
		{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
		{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
		{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
		{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
		{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
		{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
		{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
		{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
		{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
		{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
		{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
		{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
		{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
		{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
		{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
		{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
		{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
		{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
		{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
		{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
		{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
		{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
		{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
		{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
		{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
		{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
		{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
		{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
		{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
		{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
		{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
		{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
		{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
		{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
		{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
		{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
		{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
		{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
		{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
		{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
		{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
		{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
		{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
		{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
		{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
		{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
		{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
		{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
		{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
		{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
		{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
		{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
		{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
		{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
		{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
		{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
		{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
		{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
		{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
		{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
		{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
		{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
		};
}

import toxi.geom.Vec3D;

/**
 * simple container for triangle vertices and normal
 * @author ruimadeira
 *
 */

public class MCTriangle {
	public Vec3D a, b, c, normal;

	MCTriangle(){

	}
	MCTriangle(Vec3D _a, Vec3D _b, Vec3D _c){
		a = new Vec3D(_a);
		b = new Vec3D(_b);
		c = new Vec3D(_c);
		normal = new Vec3D();
	}
	MCTriangle(Vec3D _a, Vec3D _b, Vec3D _c, Vec3D _norm){
		a = new Vec3D(_a);
		b = new Vec3D(_b);
		c = new Vec3D(_c);
		normal = new Vec3D(_norm);
	}
}

Phew! I know, it’s a lot of code, but look how much you have to play with now!

Download…

Serving Correct Mimes with Node and Express

This weekend I was looking for a dynamic way to serve mime types with nodeJS using the Express framework.First we’ll set up our server to include a couple libraries and an external javascript file that we’ll write ourselves:

require(__dirname  + '/lib/helper.js');

var express = require('express'),
    url = require('url');
    
var app = express.createServer();

The process should be fairly straight forward, grab any dynamic request:

 

app.get('/*.*', function(req, res) {

    var options = url.parse(req.url, true);

    var mime = Helper.getMime(options);
    
    serveFile(res, options.pathname, mime);

});

Express will pull in any get request that queries for something deeper than “/” in the url. It then we’ll use our url library to parse the request and get the last part of the file with no “?id=…&whatever=example…” and no base path like “http://www.example.com”. All we want is “/css/style.css” or whatever. Next we send the option string to our helper to figure out our mime type. I’ve done this by making Helper a global variable. You’ll see in the next code example. We’ll create a folder called “lib,” then we’ll place a file called “helper.js” in it and paste the following inside of it:

/* 
 * @author - Based of a file from Gist here: https://gist.github.com/1757658
 * 
 * @modified - Mike Newell - it was on Gist so I figure I can use it
 * 
 * @Description -   Added support for a few more mime types including the new
 *                  .ogv, .webm, and .mp4 file types for HTML5 video.
 * 
 */


Helper = {
    
    types: {
        "3gp" : "video/3gpp"
        , "a" : "application/octet-stream"
        , "ai" : "application/postscript"
        , "aif" : "audio/x-aiff"
        , "aiff" : "audio/x-aiff"
        , "asc" : "application/pgp-signature"
        , "asf" : "video/x-ms-asf"
        , "asm" : "text/x-asm"
        , "asx" : "video/x-ms-asf"
        , "atom" : "application/atom+xml"
        , "au" : "audio/basic"
        , "avi" : "video/x-msvideo"
        , "bat" : "application/x-msdownload"
        , "bin" : "application/octet-stream"
        , "bmp" : "image/bmp"
        , "bz2" : "application/x-bzip2"
        , "c" : "text/x-c"
        , "cab" : "application/vnd.ms-cab-compressed"
        , "cc" : "text/x-c"
        , "chm" : "application/vnd.ms-htmlhelp"
        , "class" : "application/octet-stream"
        , "com" : "application/x-msdownload"
        , "conf" : "text/plain"
        , "cpp" : "text/x-c"
        , "crt" : "application/x-x509-ca-cert"
        , "css" : "text/css"
        , "csv" : "text/csv"
        , "cxx" : "text/x-c"
        , "deb" : "application/x-debian-package"
        , "der" : "application/x-x509-ca-cert"
        , "diff" : "text/x-diff"
        , "djv" : "image/vnd.djvu"
        , "djvu" : "image/vnd.djvu"
        , "dll" : "application/x-msdownload"
        , "dmg" : "application/octet-stream"
        , "doc" : "application/msword"
        , "dot" : "application/msword"
        , "dtd" : "application/xml-dtd"
        , "dvi" : "application/x-dvi"
        , "ear" : "application/java-archive"
        , "eml" : "message/rfc822"
        , "eps" : "application/postscript"
        , "exe" : "application/x-msdownload"
        , "f" : "text/x-fortran"
        , "f77" : "text/x-fortran"
        , "f90" : "text/x-fortran"
        , "flv" : "video/x-flv"
        , "for" : "text/x-fortran"
        , "gem" : "application/octet-stream"
        , "gemspec" : "text/x-script.ruby"
        , "gif" : "image/gif"
        , "gz" : "application/x-gzip"
        , "h" : "text/x-c"
        , "hh" : "text/x-c"
        , "htm" : "text/html"
        , "html" : "text/html"
        , "ico" : "image/vnd.microsoft.icon"
        , "ics" : "text/calendar"
        , "ifb" : "text/calendar"
        , "iso" : "application/octet-stream"
        , "jar" : "application/java-archive"
        , "java" : "text/x-java-source"
        , "jnlp" : "application/x-java-jnlp-file"
        , "jpeg" : "image/jpeg"
        , "jpg" : "image/jpeg"
        , "js" : "application/javascript"
        , "json" : "application/json"
        , "log" : "text/plain"
        , "m3u" : "audio/x-mpegurl"
        , "m4v" : "video/mp4"
        , "man" : "text/troff"
        , "mathml" : "application/mathml+xml"
        , "mbox" : "application/mbox"
        , "mdoc" : "text/troff"
        , "me" : "text/troff"
        , "mid" : "audio/midi"
        , "midi" : "audio/midi"
        , "mime" : "message/rfc822"
        , "mml" : "application/mathml+xml"
        , "mng" : "video/x-mng"
        , "mov" : "video/quicktime"
        , "mp3" : "audio/mpeg"
        , "mp4" : "video/mp4"
        , "mp4v" : "video/mp4"
        , "mpeg" : "video/mpeg"
        , "mpg" : "video/mpeg"
        , "ms" : "text/troff"
        , "msi" : "application/x-msdownload"
        , "odp" : "application/vnd.oasis.opendocument.presentation"
        , "ods" : "application/vnd.oasis.opendocument.spreadsheet"
        , "odt" : "application/vnd.oasis.opendocument.text"
        , "ogg" : "application/ogg"
        , "ogv" : "video/ogg"
        , "p" : "text/x-pascal"
        , "pas" : "text/x-pascal"
        , "pbm" : "image/x-portable-bitmap"
        , "pdf" : "application/pdf"
        , "pem" : "application/x-x509-ca-cert"
        , "pgm" : "image/x-portable-graymap"
        , "pgp" : "application/pgp-encrypted"
        , "pkg" : "application/octet-stream"
        , "pl" : "text/x-script.perl"
        , "pm" : "text/x-script.perl-module"
        , "png" : "image/png"
        , "pnm" : "image/x-portable-anymap"
        , "ppm" : "image/x-portable-pixmap"
        , "pps" : "application/vnd.ms-powerpoint"
        , "ppt" : "application/vnd.ms-powerpoint"
        , "ps" : "application/postscript"
        , "psd" : "image/vnd.adobe.photoshop"
        , "py" : "text/x-script.python"
        , "qt" : "video/quicktime"
        , "ra" : "audio/x-pn-realaudio"
        , "rake" : "text/x-script.ruby"
        , "ram" : "audio/x-pn-realaudio"
        , "rar" : "application/x-rar-compressed"
        , "rb" : "text/x-script.ruby"
        , "rdf" : "application/rdf+xml"
        , "roff" : "text/troff"
        , "rpm" : "application/x-redhat-package-manager"
        , "rss" : "application/rss+xml"
        , "rtf" : "application/rtf"
        , "ru" : "text/x-script.ruby"
        , "s" : "text/x-asm"
        , "sgm" : "text/sgml"
        , "sgml" : "text/sgml"
        , "sh" : "application/x-sh"
        , "sig" : "application/pgp-signature"
        , "snd" : "audio/basic"
        , "so" : "application/octet-stream"
        , "svg" : "image/svg+xml"
        , "svgz" : "image/svg+xml"
        , "swf" : "application/x-shockwave-flash"
        , "t" : "text/troff"
        , "tar" : "application/x-tar"
        , "tbz" : "application/x-bzip-compressed-tar"
        , "tcl" : "application/x-tcl"
        , "tex" : "application/x-tex"
        , "texi" : "application/x-texinfo"
        , "texinfo" : "application/x-texinfo"
        , "text" : "text/plain"
        , "tif" : "image/tiff"
        , "tiff" : "image/tiff"
        , "torrent" : "application/x-bittorrent"
        , "tr" : "text/troff"
        , "txt" : "text/plain"
        , "vcf" : "text/x-vcard"
        , "vcs" : "text/x-vcalendar"
        , "vrml" : "model/vrml"
        , "war" : "application/java-archive"
        , "wav" : "audio/x-wav"
        , "webm" : "video/webm"
        , "wma" : "audio/x-ms-wma"
        , "wmv" : "video/x-ms-wmv"
        , "wmx" : "video/x-ms-wmx"
        , "wrl" : "model/vrml"
        , "wsdl" : "application/wsdl+xml"
        , "xbm" : "image/x-xbitmap"
        , "xhtml" : "application/xhtml+xml"
        , "xls" : "application/vnd.ms-excel"
        , "xml" : "application/xml"
        , "xpm" : "image/x-xpixmap"
        , "xsl" : "application/xml"
        , "xslt" : "application/xslt+xml"
        , "yaml" : "text/yaml"
        , "yml" : "text/yaml"
        , "zip" : "application/zip"
    },
    
    getMime: function(u) {
        
        var ext = this.getExt(u.pathname).replace('.', '');
        
        return this.types[ext.toLowerCase()] || 'application/octet-stream';
        
    },
    
    getExt: function(path) {
        var i = path.lastIndexOf('.');
        
        return (i < 0) ? '' : path.substr(i);
    }
    
};

Basically, this file sets up a really large array of objects. For each of the items in the array we store a file extension and corresponding mime type. We make a function at the bottom called “getExt()” which parses the url string we pass to it and returns the file extension. This function is called by our main function called “getMime()” which will be called from our original script. It strips the period off the file extension, then returns the mime type associated with that extension in the array.

Lastly, we create a function that will server the file dynamically and report any errors if we can’t read the file. To use the file system reader we’ll also need to require it. At the top of your server.js file add this line:

fs = require('fs')

So your file should look something like:

 require(__dirname  + '/lib/helper.js');

var express = require('express'),
    fs = require('fs'),
    url = require('url');
    
var app = express.createServer();

Now we should have a server.js file that looks like this:

require(__dirname  + '/lib/helper.js');

var express = require('express'),
    fs = require('fs'),
    url = require('url');
    
var app = express.createServer();

app.get('/*.*', function(req, res) {

    var options = url.parse(req.url, true);

    var mime = Helper.getMime(options);
    
    serveFile(res, options.pathname, mime);

});

function serveFile(res, pathName, mime) {
    
    mime = mime || 'text/html';
    
    fs.readFile(__dirname + '/' + pathName, function (err, data) {
        if (err) {
            res.writeHead(500, {"Content-Type": "text/plain"});
            return res.end('Error loading ' + pathName + " with Error: " + err);
        }
        res.writeHead(200, {"Content-Type": mime});
        res.end(data);
    });
}

And a helper.js file in the /lib folder that looks like this:

/* 
 * @author - Based of a file from Gist here: https://gist.github.com/1757658
 * 
 * @modified - Mike Newell - it was on Gist so I figure I can use it
 * 
 * @Description -   Added support for a few more mime types including the new
 *                  .ogv, .webm, and .mp4 file types for HTML5 video.
 * 
 */


Helper = {
    
    types: {
        "3gp" : "video/3gpp"
        , "a" : "application/octet-stream"
        , "ai" : "application/postscript"
        , "aif" : "audio/x-aiff"
        , "aiff" : "audio/x-aiff"
        , "asc" : "application/pgp-signature"
        , "asf" : "video/x-ms-asf"
        , "asm" : "text/x-asm"
        , "asx" : "video/x-ms-asf"
        , "atom" : "application/atom+xml"
        , "au" : "audio/basic"
        , "avi" : "video/x-msvideo"
        , "bat" : "application/x-msdownload"
        , "bin" : "application/octet-stream"
        , "bmp" : "image/bmp"
        , "bz2" : "application/x-bzip2"
        , "c" : "text/x-c"
        , "cab" : "application/vnd.ms-cab-compressed"
        , "cc" : "text/x-c"
        , "chm" : "application/vnd.ms-htmlhelp"
        , "class" : "application/octet-stream"
        , "com" : "application/x-msdownload"
        , "conf" : "text/plain"
        , "cpp" : "text/x-c"
        , "crt" : "application/x-x509-ca-cert"
        , "css" : "text/css"
        , "csv" : "text/csv"
        , "cxx" : "text/x-c"
        , "deb" : "application/x-debian-package"
        , "der" : "application/x-x509-ca-cert"
        , "diff" : "text/x-diff"
        , "djv" : "image/vnd.djvu"
        , "djvu" : "image/vnd.djvu"
        , "dll" : "application/x-msdownload"
        , "dmg" : "application/octet-stream"
        , "doc" : "application/msword"
        , "dot" : "application/msword"
        , "dtd" : "application/xml-dtd"
        , "dvi" : "application/x-dvi"
        , "ear" : "application/java-archive"
        , "eml" : "message/rfc822"
        , "eps" : "application/postscript"
        , "exe" : "application/x-msdownload"
        , "f" : "text/x-fortran"
        , "f77" : "text/x-fortran"
        , "f90" : "text/x-fortran"
        , "flv" : "video/x-flv"
        , "for" : "text/x-fortran"
        , "gem" : "application/octet-stream"
        , "gemspec" : "text/x-script.ruby"
        , "gif" : "image/gif"
        , "gz" : "application/x-gzip"
        , "h" : "text/x-c"
        , "hh" : "text/x-c"
        , "htm" : "text/html"
        , "html" : "text/html"
        , "ico" : "image/vnd.microsoft.icon"
        , "ics" : "text/calendar"
        , "ifb" : "text/calendar"
        , "iso" : "application/octet-stream"
        , "jar" : "application/java-archive"
        , "java" : "text/x-java-source"
        , "jnlp" : "application/x-java-jnlp-file"
        , "jpeg" : "image/jpeg"
        , "jpg" : "image/jpeg"
        , "js" : "application/javascript"
        , "json" : "application/json"
        , "log" : "text/plain"
        , "m3u" : "audio/x-mpegurl"
        , "m4v" : "video/mp4"
        , "man" : "text/troff"
        , "mathml" : "application/mathml+xml"
        , "mbox" : "application/mbox"
        , "mdoc" : "text/troff"
        , "me" : "text/troff"
        , "mid" : "audio/midi"
        , "midi" : "audio/midi"
        , "mime" : "message/rfc822"
        , "mml" : "application/mathml+xml"
        , "mng" : "video/x-mng"
        , "mov" : "video/quicktime"
        , "mp3" : "audio/mpeg"
        , "mp4" : "video/mp4"
        , "mp4v" : "video/mp4"
        , "mpeg" : "video/mpeg"
        , "mpg" : "video/mpeg"
        , "ms" : "text/troff"
        , "msi" : "application/x-msdownload"
        , "odp" : "application/vnd.oasis.opendocument.presentation"
        , "ods" : "application/vnd.oasis.opendocument.spreadsheet"
        , "odt" : "application/vnd.oasis.opendocument.text"
        , "ogg" : "application/ogg"
        , "ogv" : "video/ogg"
        , "p" : "text/x-pascal"
        , "pas" : "text/x-pascal"
        , "pbm" : "image/x-portable-bitmap"
        , "pdf" : "application/pdf"
        , "pem" : "application/x-x509-ca-cert"
        , "pgm" : "image/x-portable-graymap"
        , "pgp" : "application/pgp-encrypted"
        , "pkg" : "application/octet-stream"
        , "pl" : "text/x-script.perl"
        , "pm" : "text/x-script.perl-module"
        , "png" : "image/png"
        , "pnm" : "image/x-portable-anymap"
        , "ppm" : "image/x-portable-pixmap"
        , "pps" : "application/vnd.ms-powerpoint"
        , "ppt" : "application/vnd.ms-powerpoint"
        , "ps" : "application/postscript"
        , "psd" : "image/vnd.adobe.photoshop"
        , "py" : "text/x-script.python"
        , "qt" : "video/quicktime"
        , "ra" : "audio/x-pn-realaudio"
        , "rake" : "text/x-script.ruby"
        , "ram" : "audio/x-pn-realaudio"
        , "rar" : "application/x-rar-compressed"
        , "rb" : "text/x-script.ruby"
        , "rdf" : "application/rdf+xml"
        , "roff" : "text/troff"
        , "rpm" : "application/x-redhat-package-manager"
        , "rss" : "application/rss+xml"
        , "rtf" : "application/rtf"
        , "ru" : "text/x-script.ruby"
        , "s" : "text/x-asm"
        , "sgm" : "text/sgml"
        , "sgml" : "text/sgml"
        , "sh" : "application/x-sh"
        , "sig" : "application/pgp-signature"
        , "snd" : "audio/basic"
        , "so" : "application/octet-stream"
        , "svg" : "image/svg+xml"
        , "svgz" : "image/svg+xml"
        , "swf" : "application/x-shockwave-flash"
        , "t" : "text/troff"
        , "tar" : "application/x-tar"
        , "tbz" : "application/x-bzip-compressed-tar"
        , "tcl" : "application/x-tcl"
        , "tex" : "application/x-tex"
        , "texi" : "application/x-texinfo"
        , "texinfo" : "application/x-texinfo"
        , "text" : "text/plain"
        , "tif" : "image/tiff"
        , "tiff" : "image/tiff"
        , "torrent" : "application/x-bittorrent"
        , "tr" : "text/troff"
        , "txt" : "text/plain"
        , "vcf" : "text/x-vcard"
        , "vcs" : "text/x-vcalendar"
        , "vrml" : "model/vrml"
        , "war" : "application/java-archive"
        , "wav" : "audio/x-wav"
        , "webm" : "video/webm"
        , "wma" : "audio/x-ms-wma"
        , "wmv" : "video/x-ms-wmv"
        , "wmx" : "video/x-ms-wmx"
        , "wrl" : "model/vrml"
        , "wsdl" : "application/wsdl+xml"
        , "xbm" : "image/x-xbitmap"
        , "xhtml" : "application/xhtml+xml"
        , "xls" : "application/vnd.ms-excel"
        , "xml" : "application/xml"
        , "xpm" : "image/x-xpixmap"
        , "xsl" : "application/xml"
        , "xslt" : "application/xslt+xml"
        , "yaml" : "text/yaml"
        , "yml" : "text/yaml"
        , "zip" : "application/zip"
    },
    
    getMime: function(u) {
        
        var ext = this.getExt(u.pathname).replace('.', '');
        
        return this.types[ext.toLowerCase()] || 'application/octet-stream';
        
    },
    
    getExt: function(path) {
        var i = path.lastIndexOf('.');
        
        return (i < 0) ? '' : path.substr(i);
    }
    
};

So in a couple hundred lines of code we have something that serves dynamic files, anything from a correctly mimed javascript file to a .webm video.

Anyway.

Local Version Control

Of course, one should use Git for everything, but…

If you do a lot of revisions and are constantly breaking something, I’ve got a simple albeit important trick.

Memory on computers, cloud, even thumb drives is virtually limitless, with that in mind, we no longer worry about copying root folders. I like to make a container folder with the project name just below that are encapsulated root folders containing everything the project needs to run. Something like this:

Naming individual files with version numbers gets out of control quick. Especially if you have a major branch change, naming whole project folders according to version allows you to track/change branches much more easily and has the added advantage of making file uploads/code changes much easier.

That’s it for today, just thought I’d share.

Pulling Circles Out of Videos with Canvas

I’ll keep this short. Canvas will render pixel data as fast as a video can play (up to certain dimension 640×380 or around there). What’s more, once you have that pixel data rendered to a canvas element you can manipulate things pixel by pixel on another canvas tag.

It goes something like this:

  1. Play video and while it’s playing, listen for each frame.
  2. Render that frame to the canvas and wait for the next frame.
  3. Once you render to canvas, grab that pixel data, manipulate it and render that data to another canvas.

What it looks like is this:

DEMO

So all I’m doing here is grabbing video data, rendering to the first canvas. Then taking that pixel data and rendering to a second canvas. Then I’m taking a click event and rendering pixel data from your mouse origin to a third canvas that follows your mouse.

That’s it.

Here’s the code:

var App = {

    init: function() {

        // find the video
        this.video = document.getElementById('cVideo');

        // get canvas one
        this.c1 = document.getElementById('c1');
        this.ctx1 = this.c1.getContext('2d');

        // get canvas 2
        this.c2 = document.getElementById('c2');
        this.ctx2 = this.c2.getContext('2d');
        this.ctx2.fillStyle = 'white';

        // get mouse following canvas
        this.c3 = document.getElementById('c3');
        this.ctx3 = this.c3.getContext('2d');

        // define global so it works in the setTimout, setInterval and window
        // events
        var self = this;

        // set up a listener for the video when it's playing
        this.video.addEventListener('playing', function(evt) {

            self.width = self.video.videoWidth;
            self.height = self.video.videoHeight;
            self.callback();

        }, false);

        // loop the video (firefox)
        this.video.addEventListener('ended', function(evt) {
            self.video.play();
        });

        // listen for mouse down and show the third canvas
        this.video.addEventListener('mousedown', function(evt) {
            self.c3.style.display = 'block';
            self.clientX = evt.clientX;
            self.clientY = evt.clientY;
            self.clicked = true;
        }, false);

        // listen for mouse down and show the third canvas
        this.c2.addEventListener('mousedown', function(evt) {
            self.c3.style.display = 'block';
            self.clientX = evt.clientX;
            self.clientY = evt.clientY;
            self.clicked = true;
        }, false);

        // listen for mouse movement and track it for canvas 3
        window.addEventListener('mousemove', function(evt) {
            self.mouseX = evt.clientX;
            self.mouseY = evt.clientY;
        }, false);

        // listen for mouse release and hide canvas 3
        window.addEventListener('mouseup', function(evt) {
            self.c3.style.display = 'none';
        }, false);

    },

    // draw to canvases and call this function again as fast as you can
    callback: function() {
        this.computeFrame();
        var self = this;
        setTimeout(function() {
            self.callback();
        }, 0);

    },

    // draw video frame by frame on canvas elements
    computeFrame: function() {

        this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
        var frame = this.ctx1.getImageData(0, 0, this.width, this.height);

        var box = this.ctx1.getImageData(this.clientX - 50, this.clientY - 50, 100, 100);

        this.ctx2.putImageData(frame, 0, 0);

        if(this.clicked) {

            this.c2.style.position = 'absolute';
            this.c2.style.top = '0';
            this.c2.style.left = '0';

            this.ctx3.putImageData(box, 0, 0);
            this.c3.style.position = 'absolute';
            this.c3.style.top = (this.mouseY - 50) + 'px';
            this.c3.style.left = (this.mouseX - 50) + 'px';
            this.ctx2.beginPath();
            this.ctx2.arc(this.clientX, this.clientY, 50, 0, Math.PI*2, true);
            this.ctx2.fill();

        }

        return;
    }

}

Basically, we get the canvas and video elements. Set up some listeners. Then monitor video playback, and each frame of the video, render that to different canvas elements.

NOTE: works best in firefox, chrome is second, safari sucks and of course IE doesn’t work at all probably (I didn’t even test it).

Flatten and Count Uniques in a Multidimensional Array

I know this is super easy for all you PHP nerds out there, but it’s these sorts of posts I enjoy the most. Super simple, short and easy to understand…

Let’s say we have this:

$example = array(
                array("client1", "client2", "client3"),
                array("client1", "client2", "client4"),
                array("client4", "client5", "client6")
            );

We have a multidimensional array of clients. We want to flatten the array and then see how many times each client is duplicated. Here’s a function to do just that:

function flatten_count($multi) {

        $objTmp = (object) array('aFlat' => array());

        array_walk_recursive($multi, create_function('&$v, $k, &$t', '$t->aFlat[] = $v;'), $objTmp);

        return array_count_values($objTmp->aFlat);

    }

It walks through the array, posting each value to a new array using references and storing them in an object temporary. Then PHP provides us with a handy little function to count all duplicate values in an array and return the values themselves as keys. We run that on the flattened array and return the sorted + counted array.

Easy!

UPDATE: the above code may not work on some setups. I had a problem between PHP 5.2.7 and 5.3.1. If you’re looking for a platform agnostic way:

$values = array();
        $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($example));
        foreach($it as $v) {
            $values[] = $v;
        }

Anyway…

Transparent Border with CSS

So Chris Coyier over at CSS-Tricks.com has an interesting method for creating transparent borders using border clip. I found this method to be a little unreliable in some browsers with some border widths. This is my version, it’s totally manual so if you don’t know what you’re doing with CSS this probably won’t help much.

Basically, I’m setting a container to be positioned absolutely. Then I set a box inside to contain all the other divs. I named this “dialog container”, then I created three boxes inside of that and stacked them on top of eachother. the container is positioned relatively, so if you position all three boxes inside as absolute they will stack over top of eachother. I put the drop shadow on bottom. This has no background or anything, its just dropshadow. I sandwich semi-opaque dive in the middle and make it 1 pixel wider than the rest of the divs. Then I layer on a third box which contains content and has a background color. In this case, a gradient.

This allows us to have a dropshadowed semi-transparent div without making the content change opacity, all in css3 and html5. Whabam bitches!

Check out the demo if you do shit like that…

The CSS…

/*******************************************************************************
                                    DIALOG
*******************************************************************************/

.dialog-positioner {
    position: absolute;
}

.dialog-container {
    position: relative;
}

.dialog-shadow {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

    -moz-box-shadow: 0px 2px 8px #000; /* Firefox */
    -webkit-box-shadow: 0px 2px 8px #000; /* Safari, Chrome */
    box-shadow: 0px 2px 8px #000; /* CSS3 */

    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;

    z-index: 1;

}

.dialog-border {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

    padding: 1px;

    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;

    zoom: 1;
    filter: alpha(opacity=50);
    opacity: 0.5;
    background-color: white;

    z-index: 2;
}

.dialog {
    position: relative;
    top: 1px;
    left: 1px;
    width: 332px;
    padding: 30px 22px;

    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;

    background: #3f3b3d;
    background: -webkit-gradient(linear, left top, left bottom, from(#3f3b3d), to(#2c292b));
    background: -webkit-linear-gradient(top, #3f3b3d, #2c292b);
    background: -moz-linear-gradient(top, #3f3b3d, #2c292b);
    background: -ms-linear-gradient(top, #3f3b3d, #2c292b);
    background: -o-linear-gradient(top, #3f3b3d, #2c292b);

    z-index: 3;

}

The HTML…

<div class="dialog-positioner" id="agency-positioner">
                    <div class="dialog-container">

                        <article class="dialog">
                            <h2>HEY, IT'S YOUR AGENCY.</h2>
                            <p>What would you like to see in the building over the next year?</p>
                            <p>Take a look at our current client roster and tell us who you think is missing.</p>
                            <h3>Mouse Over to Explore the Agency</h3>
                        </article>
                        <div class="dialog-border"></div>
                        <div class="dialog-shadow"></div>
                    </div>
                </div>

 

DEMO

NOTE: something that would make this bitchin’ cool, is if you put a svg in the middle of this layer and put a gaussian blur or something on it so it filtered things behind it… I didn’t have time for that though.