Continuation of previous commit, forgot to add files.
Continuation of previous commit, forgot to add files.

--- a/src/js/circuit/PCB/Element.js
+++ b/src/js/circuit/PCB/Element.js
@@ -44,26 +44,12 @@
 		};
 	
 		Element.prototype.render = function(ctx, color, mirror,	pins_only) {
-	
+
 			var i, sym, rot = 0;
 	
 			ctx.save();
 	
 			ctx.translate(this.mx, this.my);
-	
-			for (i = 0; i < this.parts.length; i++) {
-				if (pins_only) {
-					if (this.parts[i] instanceof Pin)
-						this.parts[i].render(ctx, color);
-				} else {
-					this.parts[i].render(ctx, color);
-				}
-			}
-	
-			if (pins_only) {
-				ctx.restore();
-				return;
-			}
 
 			if(!this.flags.hidename){
 
@@ -100,7 +86,6 @@
 			}
 	
 			ctx.restore();
-	
 		};
 
 		Element.prototype.renderText = function(gl, shaderProgram){

--- a/src/js/circuit/PCB/ElementArc.js
+++ b/src/js/circuit/PCB/ElementArc.js
@@ -20,11 +20,21 @@
 		};
 	
 		ElementArc.prototype.render = function(ctx, color) {
-	
+
+			if(!this._cache){
+				this._cache = {}
+				this._cache.x = this.x;
+				this._cache.y = this.y;
+				if(this.parent){
+					this._cache.x += this.parent.mx;
+					this._cache.y += this.parent.my;
+				}
+			}
+
 			ctx.beginPath();
 			ctx.arc(
-				this.x,
-				this.y,
+				this._cache.x,
+				this._cache.y,
 				this.r1,
 				(Math.PI * 2) - (Math.PI * this.start / 180.0),
 				(Math.PI * 2) - (Math.PI * (this.start + this.sweep) / 180.0),

--- a/src/js/circuit/PCB/ElementLine.js
+++ b/src/js/circuit/PCB/ElementLine.js
@@ -12,9 +12,18 @@
 
 	ElementLine.prototype.render = function(ctx, color) {
 
+		var x1 = this.x1, y1 = this.y1, x2 = this.x2, y2 = this.y2;
+		
+		if(this.parent){
+			x1 += this.parent.mx;
+			y1 += this.parent.my;
+			x2 += this.parent.mx;
+			y2 += this.parent.my;
+		}
+
 		ctx.beginPath();
-		ctx.moveTo(this.x1, this.y1);
-		ctx.lineTo(this.x2, this.y2);
+		ctx.moveTo(x1, y1);
+		ctx.lineTo(x2, y2);
 		ctx.lineCap = 'round';
 		ctx.lineWidth = this.thick;
 		ctx.strokeStyle = color;

--- a/src/js/circuit/PCB/Layer.js
+++ b/src/js/circuit/PCB/Layer.js
@@ -28,9 +28,13 @@
 			this.parts.push(part);
 	
 		};
-	
+
+		Layer.prototype.isEmpty = function(){
+			return this.parts.length == 0;
+		}
+
 		Layer.prototype.render = function(ctx, color){
-	
+
 			if(!color) color = this.pcbv.getLayerColors()[this.number-1];
 	
 			for(var p = 0; p < this.parts.length; p++)
@@ -41,7 +45,7 @@
 		Layer.prototype.renderGL = function(gl, shaderProgram, oMatrix, mvMatrix, color, pins, elements){
 	
 			gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
-	
+
 			gl.viewport(0, 0, this.framebuffer.width, this.framebuffer.height);
 			gl.clearColor(0.0, 0.0, 0.0, 0.0);
 			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

--- a/src/js/circuit/PCB/PCBViewer.js
+++ b/src/js/circuit/PCB/PCBViewer.js
@@ -20,13 +20,8 @@
 	 	"./ElementArc",
 	 	"./Polygon",
 	 	"./Symbol",
-	 	"./LayerManager",
-	 	"Graphics/glMatrix",
-	 	"Graphics/GLHelper",
-	 	"text!../../../data/shaders/2D.fs",
-	 	"text!../../../data/shaders/2D.vs",
-	 	"text!../../../data/shaders/Texture.fs",
-	 	"text!../../../data/shaders/Texture.vs"
+	 	"./Renderers/TwoDRenderer",
+	 	"./Renderers/GLRenderer"
 	],
 	function(
 		Line,
@@ -40,18 +35,11 @@
 		ElementArc,
 		Polygon,
 		Symbol,
-		LayerManager,
-		glMatrix,
-		GLHelper,
-		FragmentShader2Dtxt,
-		VertexShader2Dtxt,
-		FragmentShaderTextxt,
-		VertexShaderTextxt
+		TwoDRenderer,
+		GLRenderer
 	){
 
 		function PCBV(canvas, attach, mode) {
-
-			var gl;
 
 			this._layerColors = PCBV._defaultLayerColors;
 
@@ -75,93 +63,16 @@
 					this.mode = "Normal";
 					this.ctx = canvas.getContext('2d');
 
-					this.buffer_layer = document.createElement('canvas');
-					this.buffer_layer.width = this.canvas.width;
-					this.buffer_layer.height = this.canvas.height;
-			
-					this.buffer_ctx = this.buffer_layer.getContext('2d');
-
 				} else {
 
 					this.mode = "Accelerated";
-
-					gl = this.ctx;
-
-					gl.viewportWidth = canvas.width;
-					gl.viewportHeight = canvas.height;
-
-					// 2D Shader
-
-					this.shaderProgram = GLHelper.createProgram(gl, 
-							GLHelper.createShader(gl, gl.VERTEX_SHADER, VertexShader2Dtxt),
-							GLHelper.createShader(gl, gl.FRAGMENT_SHADER, FragmentShader2Dtxt));
-
-					this.shaderProgram.vertexPositionAttribute = gl.getAttribLocation(this.shaderProgram, "aVertexPosition");
-			        gl.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute);
-
-			        this.shaderProgram.pMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uPMatrix");
-			        this.shaderProgram.mvMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uMVMatrix");
-			        this.shaderProgram.vColorUniform = gl.getUniformLocation(this.shaderProgram, "vColor");
-
-			        this.shaderProgram.pointsizeUniform = gl.getUniformLocation(this.shaderProgram, "pointsize");
-			        this.shaderProgram.innerRadiusUniform = gl.getUniformLocation(this.shaderProgram, "innerRadius");
-			        this.shaderProgram.roundPointsUniform = gl.getUniformLocation(this.shaderProgram, "roundPoints");
-			        this.shaderProgram.startAngleUniform = gl.getUniformLocation(this.shaderProgram, "startAngle");
-			        this.shaderProgram.sweepUniform = gl.getUniformLocation(this.shaderProgram, "sweep");
-			        this.shaderProgram.arcEnabledUniform = gl.getUniformLocation(this.shaderProgram, "arcEnabled");
-			        this.shaderProgram.invertedUniform = gl.getUniformLocation(this.shaderProgram, "inverted");
-
-			        // Texture Shader
-
-					this.texShaderProgram = GLHelper.createProgram(gl, 
-							GLHelper.createShader(gl, gl.VERTEX_SHADER, VertexShaderTextxt),
-							GLHelper.createShader(gl, gl.FRAGMENT_SHADER, FragmentShaderTextxt));
-
-			        this.texShaderProgram.vertexPositionAttribute = gl.getAttribLocation(this.texShaderProgram, "aVertexPosition");
-			        gl.enableVertexAttribArray(this.texShaderProgram.vertexPositionAttribute);
-
-			        this.texShaderProgram.textureCoordAttribute = gl.getAttribLocation(this.texShaderProgram, "aTextureCoord");
-			        gl.enableVertexAttribArray(this.texShaderProgram.textureCoordAttribute);
-
-			        this.texShaderProgram.pMatrixUniform = gl.getUniformLocation(this.texShaderProgram, "uPMatrix");
-			        this.texShaderProgram.mvMatrixUniform = gl.getUniformLocation(this.texShaderProgram, "uMVMatrix");
-			        this.texShaderProgram.samplerUniform = gl.getUniformLocation(this.texShaderProgram, "uSampler");
-
-			        // Viewport setup
-			        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
 
 				}
 
 				this.last_time = 0;
 				this.end_time = 0;
 		
-				this.clicking = false;
-			
-				this.mouse_x = canvas.width/2;
-				this.mouse_y = canvas.height/2;
-		
-				this.mouseUpFunction = function(t){return function(e){if(e.which == 1) t.clicking = false;};}(this);
-				this.mouseDownFunction = function(t){return function(e){if(e.which == 1) t.clicking = true;};}(this)
-				this.keyDownFunction = function(t){return function(e){t.update_key(e);};}(this);
-				this.mouseMoveFunction = function(t){return function(e){
-					if(t.clicking)
-						t.update_mouse_drag(t.mouse_x-e.pageX, t.mouse_y-e.pageY);		
-					t.mouse_x = e.pageX;
-					t.mouse_y = e.pageY;
-				};}(this);
-				this.mouseWheelFunction = function(t){return function(e){t.wheel(e);};}(this);
-
-				if(window.addEventListener){
-					document.addEventListener("mouseup", this.mouseUpFunction, false);
-					this.canvas.addEventListener("mousewheel", this.mouseWheelFunction, false);
-					this.canvas.addEventListener('DOMMouseScroll', this.mouseWheelFunction, false);
-				} else {
-					document.attachEvent("mouseup", this.mouseUpFunction);
-					this.canvas.attachEvent("onmousewheel", this.mouseWheelFunction);
-				}
-				canvas.onmousedown = this.mouseDownFunction;
-				canvas.onkeydown = this.keyDownFunction;
-				canvas.onmousemove = this.mouseMoveFunction;
+				this._setupEventHandlers();
 
 			};
 
@@ -171,45 +82,7 @@
 
 		PCBV.prototype.destroy = function(){
 
-			var i;
-
-			if(this.mode == "Accelerated"){
-
-				var gl = this.ctx;
-
-				for(i = 0; i < this.vias.length; i++)
-					this.vias[i].cleanupGL(gl);
-				for(i = 0; i < this.layers.length; i++)
-					this.layers[i].cleanupGL(gl);
-				for(i = 0; i < this.elements.length; i++)
-					this.elements[i].cleanupGL(gl);
-				for(i in this.symbols)
-					this.symbols[i].cleanupGL(gl);
-
-				// Restore GL Defaults
-				gl.disable(gl.BLEND);
-				gl.useProgram(null);
-				gl.blendFunc(gl.ONE, gl.ZERO);
-				gl.clearColor(0, 0, 0, 0);
-
-				// Unbind all buffers
-				gl.bindTexture(gl.TEXTURE_2D, null);
-				gl.bindBuffer(gl.ARRAY_BUFFER, null);
-				gl.bindRenderbuffer(gl.RENDERBUFFER, null);
-				gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-
-				// Clear viewport
-				gl.viewport(0, 0, gl.viewportWidth, gl.viewportheight);
-				gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-
-				// Clear errors
-				while(gl.getError());
-
-			} else {
-
-				this.ctx = null;
-
-			}
+			this.renderer.destroy();
 
 			if(window.addEventListener){
 				document.removeEventListener("mouseup", this.mouseUpFunction, false);
@@ -223,7 +96,7 @@
 			this.canvas.onkeydown = null;
 			this.canvas.onmousemove = null;
 
-		}
+		};
 
 		// TODO: maybe make this layer named based!
 		PCBV._defaultLayerColors = ['#8B2323', '#3A5FCD', '#104E8B', '#CD3700',
@@ -251,292 +124,81 @@
 			return false;
 		}
 
-		PCBV.prototype.getLayerColors = function(){
-			return this._layerColors;
+		PCBV.prototype._buildLayers = function(){
+
+			var top = [], solder = [], pins = [];
+
+			var i, j;
+			for(i = 0; i < this.elements.length; i++){
+
+				if(!this.elements[i].onsolder()){
+					this.renderer.bottomSilk.parts.push(this.elements[i]);
+				} else {
+					this.renderer.topSilk.parts.push(this.elements[i]);
+				}
+
+				for(j = 0; j < this.elements[i].parts.length; j++){
+					if(this.elements[i].parts[j] instanceof Pin)
+						pins.push(this.elements[i].parts[j]);
+					else if(
+					  this.elements[i].parts[j] instanceof ElementLine ||
+					  this.elements[i].parts[j] instanceof ElementArc ||
+					  this.elements[i].parts[j] instanceof Text
+					 ){
+						if(!this.elements[i].onsolder())
+							this.renderer.bottomSilk.parts.push(this.elements[i].parts[j]);
+						else
+							this.renderer.topSilk.parts.push(this.elements[i].parts[j]);
+					} else if(this.elements[i].onsolder())
+						solder.push(this.elements[i].parts[j]);
+					else
+						top.push(this.elements[i].parts[j]);
+				}
+			}
+
+			for(i = 0; i < this.vias.length; i++)
+				pins.push(this.vias[i]);
+
+			this.renderer.addLayer(new Layer(this, -1, "top_component", top));
+			this.renderer.addLayer(new Layer(this, -2, "solder_component", solder));
+			this.renderer.addLayer(new Layer(this, -3, "pins", pins));
+
+		};
+
+		PCBV.prototype._setupEventHandlers = function(){
+
+			this.clicking = false;
+			
+			this.mouseX = this.canvas.width / 2;
+			this.mouseY = this.canvas.height/ 2;
+	
+			this.mouseUpFunction = function(t){return function(e){if(e.which == 1) t.clicking = false;};}(this);
+			this.mouseDownFunction = function(t){return function(e){if(e.which == 1) t.clicking = true;};}(this)
+			this.keyDownFunction = function(t){return function(e){t._updateKey(e);};}(this);
+			this.mouseMoveFunction = function(t){return function(e){
+				if(t.clicking)
+					t._updateMouseDrag(t.mouseX-e.pageX, t.mouseY-e.pageY);		
+				t.mouseX = e.pageX;
+				t.mouseY = e.pageY;
+			};}(this);
+			this.mouseWheelFunction = function(t){return function(e){t._wheel(e);};}(this);
+
+			if(window.addEventListener){
+				document.addEventListener("mouseup", this.mouseUpFunction, false);
+				this.canvas.addEventListener("mousewheel", this.mouseWheelFunction, false);
+				this.canvas.addEventListener('DOMMouseScroll', this.mouseWheelFunction, false);
+			} else {
+				document.attachEvent("mouseup", this.mouseUpFunction);
+				this.canvas.attachEvent("onmousewheel", this.mouseWheelFunction);
+			}
+			this.canvas.onmousedown = this.mouseDownFunction;
+			this.canvas.onkeydown = this.keyDownFunction;
+			this.canvas.onmousemove = this.mouseMoveFunction;
+
 		}
 
-		PCBV.prototype.resize = function(){
-
-			if(this.mode == "Normal"){
-
-				this.buffer_layer.width = this.canvas.width;
-				this.buffer_layer.height = this.canvas.height;
-
-			} else {
-
-				this.ctx.viewportWidth = this.canvas.width;
-				this.ctx.viewportHeight = this.canvas.height;
-
-				this.resized = true;
-
-			}
-
-		};
-	
-		PCBV.prototype.parse_data = function(data){
-	
-			var lines, l, splt, new_obj, sub_obj, xmi, xma;
-	
-			lines = data.split('\n');
-	
-			l = 0;
-			while(l < lines.length){
-				line = lines[l];
-	
-				if(line.substr(0,3) == "PCB"){
-	
-					line = line.substr(4, line.length-2);
-					splt = line.split(' ');
-	
-					this.name = splt[0].split('"')[1];
-					this.width = parseInt(splt[1]);
-					this.height = parseInt(splt[2]);
-	
-				} else if(line.substr(0,3) == "Via"){
-	
-					line = line.substr(4, line.length-2);
-					splt = line.split(' ');
-	
-					new_obj = new Via(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1], splt[7].split('"')[1]);
-	
-					this.vias.push(new_obj);
-	
-				} else if(line.substr(0, 7) == "Element"){
-	
-					line = line.substr(8,line.length-2);
-					splt = line.match(/[^\s"]+|"([^"]*)"/gi); // TODO: change all space splits with text to this regex
-	
-					new_obj = new Element(this, splt[0].split('"')[1], splt[1].split('"')[1], splt[2].split('"')[1], splt[3].split('"')[1], parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]), parseInt(splt[7]), parseInt(splt[8]), parseInt(splt[9]), splt[7].split('"')[10]);
-	
-					while(line[0] != ')'){
-						l++;
-						line = lines[l];
-						line = line.trim();
-	
-						if(line.substr(0, 3) == 'Pad'){
-	
-							line = line.substr(4, line.length-2);
-							splt = line.split(' ');
-	
-							sub_obj = new Pad(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]), splt[7].split('"')[1], splt[8].split('"')[1], splt[9].split('"')[1]);	
-							sub_obj.parent = new_obj;
-
-							new_obj.parts.push(sub_obj);
-	
-						} else if(line.substr(0, 3) == 'Pin'){
-	
-							line = line.substr(4, line.length-2);
-							splt = line.match(/[^\s"]+|"([^"]*)"/gi);
-	
-							sub_obj = new Pin(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1], splt[7].split('"')[1], splt[8].split('"')[1]);
-							sub_obj.parent = new_obj;
-
-							new_obj.parts.push(sub_obj);
-	
-						} else if(line.substr(0, 11) == 'ElementLine'){
-	
-							line = line.substr(13, line.length-2);
-							splt = line.split(' ');
-	
-							sub_obj = new ElementLine(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]));
-							sub_obj.parent = new_obj;
-
-							new_obj.parts.push(sub_obj);
-	
-						} else if(line.substr(0, 10) == 'ElementArc'){
-	
-							line = line.substr(12, line.length-2);
-							splt = line.split(' ');
-	
-							sub_obj = new ElementArc(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]));
-							sub_obj.parent = new_obj;
-
-							new_obj.parts.push(sub_obj);
-	
-						};
-	
-					}
-	
-					this.elements.push(new_obj);
-	
-				} else if(line.substr(0, 5) == "Layer"){
-	
-					line = line.substr(6,line.length-2);
-					splt = line.split(' ');
-	
-					new_obj = new Layer(this, parseInt(splt[0]), splt[1].split('"')[1]);
-	
-					while(line[0] != ')'){
-						l++;
-						line = lines[l];
-						line = line.trim();
-	
-						if(line.substr(0, 4) == 'Line'){
-	
-							line = line.substr(5, line.length-2);
-							splt = line.split(' ');
-	
-							sub_obj = new Line(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1]);
-	
-							new_obj.parts.push(sub_obj);
-	
-						} else if(line.substr(0, 4) == 'Text'){
-	
-							line = line.substr(5, line.length-2);
-							splt = line.match(/[^\s"]+|"([^"]*)"/gi);
-	
-							sub_obj = new Text(this, parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), splt[4].split('"')[1], splt[5].split('"')[1]);
-	
-							new_obj.parts.push(sub_obj);
-	
-						} else if(line.substr(0, 7) == "Polygon"){
-
-							var flags = line.substr(9,line.length-11), points = [];
-
-							line = '';
-							while(line.indexOf(')') == -1){
-
-								l++;
-								line = lines[l];
-
-								var bracket_idx = line.indexOf('[');
-								while(bracket_idx != -1){
-
-									var space_idx = line.indexOf(' ', bracket_idx);
-
-									var newPoint = {
-										x: parseFloat(line.substring(bracket_idx + 1, space_idx)),
-										y: parseFloat(line.substring(space_idx + 1, line.indexOf(']', space_idx))),
-									};
-									points.push(newPoint);
-									bracket_idx++;
-									bracket_idx = line.indexOf('[', bracket_idx);
-
-								}
-
-							}
-
-							sub_obj = new Polygon(flags, points);
-							new_obj.parts.push(sub_obj);
-
-						}
-
-					}
-	
-					this.layers.push(new_obj);
-	
-				} else if(line.substr(0, 6) == "Symbol"){
-	
-					line = line.substr(7, line.length-2);
-	
-					new_obj = new Symbol(line.substr(1,1), parseInt(line.substr(4,line.length-2).replace(']', '')));
-
-					xmi = undefined;
-					xma = undefined;
-	
-					while(line[0] != ')'){
-						l++;
-						line = lines[l];
-						line = line.trim();
-	
-						if(line.substr(0, 10) == 'SymbolLine'){
-	
-							line = line.substr(11, line.length-2);
-							splt = line.split(' ');
-	
-							sub_obj = new Line(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]));
-	
-							if(xmi == undefined) xmi = sub_obj.x1;					
-							else if(xmi > sub_obj.x1) xmi = sub_obj.x1;
-							if(xmi > sub_obj.x2) xmi = sub_obj.x2;
-							if(xma == undefined) xma = sub_obj.x1;
-							else if(xma < sub_obj.x1) xma = sub_obj.x1;
-							if(xma < sub_obj.x2) xma = sub_obj.x2;
-
-							new_obj.lines.push(sub_obj);
-
-						};
-	
-					}
-	
-					if(new_obj.name == ' ') new_obj.width = 1800;
-					else new_obj.width = Math.abs(xma-xmi);
-	
-					this.symbols[new_obj.name] = new_obj;
-	
-				} else {}
-	
-				l++;
-	
-			}
-	
-			this.offset = {};	// Board offset to render at
-			this.offset.x = 0;
-			this.offset.y = 0;
-	
-			this.side = Layer.SOLDER;
-	
-			this.scale = 1.0;
-
-			if(this.mode == "Accelerated"){
-
-				var top = [], solder = [], pins = [];
-
-				this.layerManager = new LayerManager(this.layers);
-
-				var i, j;
-				for(i = 0; i < this.elements.length; i++){
-
-					if(!this.elements[i].onsolder()){
-						this.layerManager.bottomSilk.parts.push(this.elements[i]);
-					} else {
-						this.layerManager.topSilk.parts.push(this.elements[i]);
-					}
-
-					for(j = 0; j < this.elements[i].parts.length; j++){
-						if(this.elements[i].parts[j] instanceof Pin)
-							pins.push(this.elements[i].parts[j]);
-						else if(
-						  this.elements[i].parts[j] instanceof ElementLine ||
-						  this.elements[i].parts[j] instanceof ElementArc ||
-						  this.elements[i].parts[j] instanceof Text
-						 ){
-							if(!this.elements[i].onsolder())
-								this.layerManager.bottomSilk.parts.push(this.elements[i].parts[j]);
-							else
-								this.layerManager.topSilk.parts.push(this.elements[i].parts[j]);
-						} else if(this.elements[i].onsolder())
-							solder.push(this.elements[i].parts[j]);
-						else
-							top.push(this.elements[i].parts[j]);
-					}
-				}
-
-				for(i in this.symbols){
-					this.symbols[i].init3DArrays(this.ctx);
-				}
-
-				for(i = 0; i < this.vias.length; i++){
-					pins.push(this.vias[i]);
-				}
-
-				this.layerManager.top = new Layer(this, -1, "top_component", top);
-				this.layerManager.solder = new Layer(this, -2, "solder_component", solder);
-				this.layerManager.pins = new Layer(this, -3, "pins", pins);
-				this.layerManager.layers.push(this.layerManager.top);
-				this.layerManager.layers.push(this.layerManager.solder);
-				this.layerManager.layers.push(this.layerManager.pins);
-
-				this.layerManager.setupFramebuffers(this.ctx);
-				this.layerManager.init3DArrays(this.ctx);
-				this.layerManager.setupGL(this.ctx, this.width, this.height);
-
-			}
+		PCBV.prototype._wheel = function(e) {
 			
-		};
-		
-		PCBV.prototype.wheel = function(e) {
-	
 			var	ev = window.event || e,
 				d,
 				elem = this.canvas,
@@ -554,12 +216,12 @@
 	
 			d = Math.max(-1, Math.min(1, (ev.wheelDelta || -ev.detail)));
 	
-			this.update_mouse_scroll(this.mouse_x-off.left, this.mouse_y-off.top, d);
+			this._updateMouseScroll(this.mouseX - off.left, this.mouseY - off.top, d);
 	
 		};
 	
 		// Key event handler
-		PCBV.prototype.update_key = function(e){
+		PCBV.prototype._updateKey = function(e){
 	
 			var which = e.which, prevent = true;
 	
@@ -608,7 +270,7 @@
 		};
 	
 		// Drag event handler
-		PCBV.prototype.update_mouse_drag = function(x, y){
+		PCBV.prototype._updateMouseDrag = function(x, y){
 
 			var dx, dy, min;
 
@@ -626,7 +288,7 @@
 		};
 	
 		// Scroll event handler
-		PCBV.prototype.update_mouse_scroll = function(x, y, s){
+		PCBV.prototype._updateMouseScroll = function(x, y, s){
 
 			if(s > 0){ // Zoom in
 				if(this.side) this.offset.y += (((this.canvas.height-(this.canvas.height/1.1))/2)*((this.canvas.height-y)/this.canvas.height*2))/(Math.min(this.canvas.width/this.width, this.canvas.height/this.height)*this.scale);
@@ -642,107 +304,249 @@
 			this.render();
 		};
 
-		PCBV.prototype.render_layers = function(){
-	
-			this.buffer_ctx.clearRect(0, 0, this.buffer_layer.width, this.buffer_layer.height);
-		
-			var bot_silk = null, top_silk = null,
-				i;
-	
-			for(i = 0; i < this.layers.length; i++){
-				if(this.layers[i].name == 'silk'){
-					if(top_silk) bot_silk = this.layers[i];
-					else top_silk = this.layers[i];
-				};
-			}
-		
-			if(this.side) bot_silk.render(this.buffer_ctx, '#FFFFFF');
-			else top_silk.render(this.buffer_ctx, '#FFFFFF');
-		
-			if(this.side){
-				for(i = 0; i < this.layers.length; i++)
-					if(this.layers[i].name != 'silk') this.layers[i].render(this.buffer_ctx);
-			} else {
-				for(i = this.layers.length-1; i >= 0; i--)
-					if(this.layers[i].name != 'silk') this.layers[i].render(this.buffer_ctx);
-			}
-		
-			if(this.side) top_silk.render(this.buffer_ctx);
-			else bot_silk.render(this.buffer_ctx);
-		
-			this.ctx.drawImage(this.buffer_layer, 0, 0);
-		
-		};
-
-		PCBV.prototype._do_render = function(){
+		PCBV.prototype._doRender = function(){
 
 			// Calculate how much we need to scale based on size of the
 			// pcb vs canvas size and how zoomed in we are
 			scalef = Math.min(this.canvas.width/this.width, this.canvas.height/this.height)*this.scale;
 
-			if(this.mode == "Accelerated"){
-
-				if(this.resized){
-					for(var i = 0; i < this.layers.length; i++){
-						this.layers[i].resizeFrameBuffer(this.ctx);
+			if(this.resized){
+				this.renderer.resize();
+				this.resized = false;
+			}
+
+			this.renderer.render(this.side, this.offset.x, this.offset.y, scalef);
+
+			this.end_time = (new Date()).getTime();
+
+		};
+
+		PCBV.prototype.getLayerColors = function(){
+			return this._layerColors;
+		};
+
+		PCBV.prototype.resize = function(){
+			this.resized = true;
+		};
+
+		PCBV.prototype.setup = function(){
+
+			this.offset = {};	// Board offset to render at
+			this.offset.x = 0;
+			this.offset.y = 0;
+	
+			this.side = Layer.SOLDER;
+	
+			this.scale = 1.0;
+
+			if(this.mode == "Accelerated")
+				this.renderer = new GLRenderer(this.ctx, this.canvas, this.symbols, this.layers, this.width, this.height);
+			else
+				this.renderer = new TwoDRenderer(this.ctx, this.canvas, this.symbols, this.layers, this.width, this.height);
+
+			this._buildLayers();
+			this.renderer.setup();
+
+		};
+
+		PCBV.prototype.parse = function(data){
+	
+			var lines, l, splt, new_obj, sub_obj, xmi, xma;
+	
+			lines = data.split('\n');
+	
+			l = 0;
+			while(l < lines.length){
+				line = lines[l];
+	
+				if(line.substr(0,3) == "PCB"){
+	
+					line = line.substr(4, line.length-2);
+					splt = line.split(' ');
+	
+					this.name = splt[0].split('"')[1];
+					this.width = parseInt(splt[1]);
+					this.height = parseInt(splt[2]);
+	
+				} else if(line.substr(0,3) == "Via"){
+	
+					line = line.substr(4, line.length-2);
+					splt = line.split(' ');
+	
+					new_obj = new Via(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1], splt[7].split('"')[1]);
+	
+					this.vias.push(new_obj);
+	
+				} else if(line.substr(0, 7) == "Element"){
+	
+					line = line.substr(8,line.length-2);
+					splt = line.match(/[^\s"]+|"([^"]*)"/gi); // TODO: change all space splits with text to this regex
+	
+					new_obj = new Element(this, splt[0].split('"')[1], splt[1].split('"')[1], splt[2].split('"')[1], splt[3].split('"')[1], parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]), parseInt(splt[7]), parseInt(splt[8]), parseInt(splt[9]), splt[7].split('"')[10]);
+	
+					while(line[0] != ')'){
+						l++;
+						line = lines[l];
+						line = line.trim();
+	
+						if(line.substr(0, 3) == 'Pad'){
+	
+							line = line.substr(4, line.length-2);
+							splt = line.split(' ');
+	
+							sub_obj = new Pad(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]), splt[7].split('"')[1], splt[8].split('"')[1], splt[9].split('"')[1]);	
+							sub_obj.parent = new_obj;
+
+							new_obj.parts.push(sub_obj);
+	
+						} else if(line.substr(0, 3) == 'Pin'){
+	
+							line = line.substr(4, line.length-2);
+							splt = line.match(/[^\s"]+|"([^"]*)"/gi);
+	
+							sub_obj = new Pin(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1], splt[7].split('"')[1], splt[8].split('"')[1]);
+							sub_obj.parent = new_obj;
+
+							new_obj.parts.push(sub_obj);
+	
+						} else if(line.substr(0, 11) == 'ElementLine'){
+	
+							line = line.substr(13, line.length-2);
+							splt = line.split(' ');
+	
+							sub_obj = new ElementLine(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]));
+							sub_obj.parent = new_obj;
+
+							new_obj.parts.push(sub_obj);
+	
+						} else if(line.substr(0, 10) == 'ElementArc'){
+	
+							line = line.substr(12, line.length-2);
+							splt = line.split(' ');
+	
+							sub_obj = new ElementArc(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), parseInt(splt[6]));
+							sub_obj.parent = new_obj;
+
+							new_obj.parts.push(sub_obj);
+	
+						};
+	
 					}
-					this.resized = false;
-				}
-
-				this.layerManager.renderGL(this.ctx, this.shaderProgram, this.texShaderProgram, this.side, this.offset.x, this.offset.y, scalef);
-
-			} else {
-
-				// Fill canvas background Dark Grey
-				this.buffer_ctx.fillStyle = '#CCCCCC';
-				this.buffer_ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
-			
-				// Save default tranform
-				this.buffer_ctx.save();
-			
-				// Flip canvas if solder side
-				if(this.side){
-					this.buffer_ctx.scale(1, -1);
-					this.buffer_ctx.translate(0, -this.canvas.height);
-				}
-			
-				// Scale and shift
-				this.buffer_ctx.scale(scalef, scalef);
-				this.buffer_ctx.translate(-this.offset.x, -this.offset.y);	
-
-				// Fill board space Grey
-				this.buffer_ctx.fillStyle = '#E5E5E5';
-				this.buffer_ctx.fillRect(0, 0, this.width, this.height);
-
-				this.ctx.globalAlpha = 1.0;
-			
-				this.ctx.drawImage(this.buffer_layer, 0, 0);
-			
-				this.ctx.globalAlpha = 0.5;
-			
-				this.buffer_ctx.clearRect(0, 0, this.buffer_layer.width, this.buffer_layer.height);
-				for(i = 0; i < this.elements.length; i++) if(!this.elements[i].onsolder() != !this.side) this.elements[i].render(this.buffer_ctx, '#FFFFFF', !this.side);
-				this.ctx.drawImage(this.buffer_layer, 0, 0);
-			
-				this.render_layers();
-			
-				this.buffer_ctx.clearRect(0, 0, this.buffer_layer.width, this.buffer_layer.height);
-				for(i = 0; i < this.elements.length; i++) if(this.elements[i].onsolder() == this.side) this.elements[i].render(this.buffer_ctx, '#000000', this.side);
-				for(i = 0; i < this.elements.length; i++) if(!this.elements[i].onsolder() != !this.side) this.elements[i].render(this.buffer_ctx, '#000000', this.side, true);
-				this.ctx.drawImage(this.buffer_layer, 0, 0);
-			
-				this.buffer_ctx.clearRect(0, 0, this.buffer_layer.width, this.buffer_layer.height);
-				for(i = 0; i < this.vias.length; i++) this.vias[i].render(this.buffer_ctx);
-				this.ctx.drawImage(this.buffer_layer, 0, 0);
-			
-				// Restore default tranform
-				this.buffer_ctx.restore();
-
-			}
-
-			this.end_time = (new Date()).getTime();
-
-		}
+	
+					this.elements.push(new_obj);
+	
+				} else if(line.substr(0, 5) == "Layer"){
+	
+					line = line.substr(6,line.length-2);
+					splt = line.split(' ');
+	
+					new_obj = new Layer(this, parseInt(splt[0]), splt[1].split('"')[1]);
+	
+					while(line[0] != ')'){
+						l++;
+						line = lines[l];
+						line = line.trim();
+	
+						if(line.substr(0, 4) == 'Line'){
+	
+							line = line.substr(5, line.length-2);
+							splt = line.split(' ');
+	
+							sub_obj = new Line(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]), splt[6].split('"')[1]);
+	
+							new_obj.parts.push(sub_obj);
+	
+						} else if(line.substr(0, 4) == 'Text'){
+	
+							line = line.substr(5, line.length-2);
+							splt = line.match(/[^\s"]+|"([^"]*)"/gi);
+	
+							sub_obj = new Text(this, parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), splt[4].split('"')[1], splt[5].split('"')[1]);
+	
+							new_obj.parts.push(sub_obj);
+	
+						} else if(line.substr(0, 7) == "Polygon"){
+
+							var flags = line.substr(9,line.length-11), points = [];
+
+							line = '';
+							while(line.indexOf(')') == -1){
+
+								l++;
+								line = lines[l];
+
+								var bracket_idx = line.indexOf('[');
+								while(bracket_idx != -1){
+
+									var space_idx = line.indexOf(' ', bracket_idx);
+
+									var newPoint = {
+										x: parseFloat(line.substring(bracket_idx + 1, space_idx)),
+										y: parseFloat(line.substring(space_idx + 1, line.indexOf(']', space_idx))),
+									};
+									points.push(newPoint);
+									bracket_idx++;
+									bracket_idx = line.indexOf('[', bracket_idx);
+
+								}
+
+							}
+
+							sub_obj = new Polygon(flags, points);
+							new_obj.parts.push(sub_obj);
+
+						}
+
+					}
+	
+					this.layers.push(new_obj);
+	
+				} else if(line.substr(0, 6) == "Symbol"){
+	
+					line = line.substr(7, line.length-2);
+	
+					new_obj = new Symbol(line.substr(1,1), parseInt(line.substr(4,line.length-2).replace(']', '')));
+
+					xmi = undefined;
+					xma = undefined;
+	
+					while(line[0] != ')'){
+						l++;
+						line = lines[l];
+						line = line.trim();
+	
+						if(line.substr(0, 10) == 'SymbolLine'){
+	
+							line = line.substr(11, line.length-2);
+							splt = line.split(' ');
+	
+							sub_obj = new Line(parseInt(splt[0]), parseInt(splt[1]), parseInt(splt[2]), parseInt(splt[3]), parseInt(splt[4]), parseInt(splt[5]));
+	
+							if(xmi == undefined) xmi = sub_obj.x1;					
+							else if(xmi > sub_obj.x1) xmi = sub_obj.x1;
+							if(xmi > sub_obj.x2) xmi = sub_obj.x2;
+							if(xma == undefined) xma = sub_obj.x1;
+							else if(xma < sub_obj.x1) xma = sub_obj.x1;
+							if(xma < sub_obj.x2) xma = sub_obj.x2;
+
+							new_obj.lines.push(sub_obj);
+
+						};
+	
+					}
+	
+					if(new_obj.name == ' ') new_obj.width = 1800;
+					else new_obj.width = Math.abs(xma-xmi);
+	
+					this.symbols[new_obj.name] = new_obj;
+	
+				} else {}
+	
+				l++;
+	
+			}
+
+		};
 
 		PCBV.prototype.render = function(force, timeout){
 		
@@ -758,7 +562,7 @@
 			}
 
 			// Using requestAnimFrame prevents Screen Tearing
-			window.requestAnimFrame(function(){t._do_render();});
+			window.requestAnimFrame(function(){t._doRender();});
 
 		};
 

--- a/src/js/circuit/PCB/Pad.js
+++ b/src/js/circuit/PCB/Pad.js
@@ -18,16 +18,38 @@
 
 	Pad.prototype.render = function(ctx, color) {
 
+		if(!this._cache){
+			
+			var x1, y2, x2, y2;
+
+			this._cache = {};
+
+			x1 = this.x1;
+			y1 = this.y1;
+			x2 = this.x2;
+			y2 = this.y2;
+			if(this.parent){
+				x1 += this.parent.mx;
+				y1 += this.parent.my;
+				x2 += this.parent.mx;
+				y2 += this.parent.my;
+			}
+			this._cache.rx1 = x1 - (this.thick / 2);
+			this._cache.ry1 = y1 - (this.thick / 2);
+			this._cache.rx2 = x2 - x1 + (this.thick);
+			this._cache.ry2 = y2 - y1 + (this.thick);
+		}
+
 		if (color == '#FFFFFF')
 			ctx.fillStyle = color;
 		else
 			ctx.fillStyle = '#4D4D4D'; // TODO: global color
 
 		ctx.fillRect(
-			this.x1 - (this.thick / 2),
-			this.y1 - (this.thick / 2),
-			this.x2 - this.x1 + (this.thick),
-			this.y2 - this.y1 + (this.thick));
+			this._cache.rx1,
+			this._cache.ry1,
+			this._cache.rx2,
+			this._cache.ry2);
 
 	};
 

--- a/src/js/circuit/PCB/Pin.js
+++ b/src/js/circuit/PCB/Pin.js
@@ -11,34 +11,48 @@
 		this.drill = drill;
 		this.name = name;
 		this.num = num;
-		this.flags = flags;
+		this.flags = {
+			square: false
+		};
+
+		var i, split = flags.split(',');
+		for(i = 0; i < split.length; i++)
+			this.flags[split[i]] = true;
 
 	};
 
 	Pin.prototype.render = function(ctx, color) {
 
-		if (color == '#FFFFFF')
+		if(!this._cache){
+
+			this._cache = {};
+
+			this._cache.x = this.x;
+			this._cache.y = this.y;
+			if(this.parent){
+				this._cache.x += this.parent.mx;
+				this._cache.y += this.parent.my;
+			}
+			this._cache.rx = this._cache.x - (this.thick / 2);
+			this._cache.ry = this._cache.y - (this.thick / 2);
+
+		}
+
+		if(color == '#FFFFFF')
 			return;
 
-		var i, splt = this.flags.split(','), square = false;
-
-		for (i = 0; i < splt.length; i++)
-			if (splt[i] == 'square')
-				square = true;
-
 		ctx.beginPath();
-		if (square)
-			ctx.rect(this.x - this.thick / 2, this.y - this.thick / 2,
-					this.thick, this.thick);
+		if(this.flags.square)
+			ctx.rect(this._cache.rx, this._cache.ry, this.thick, this.thick);
 		else
-			ctx.arc(this.x, this.y, this.thick / 2, 0, Math.PI * 2, true);
+			ctx.arc(this._cache.x, this._cache.y, this.thick / 2, 0, Math.PI * 2, true);
 
 		ctx.closePath();
 		ctx.fillStyle = '#4D4D4D'; // TODO: global color
 		ctx.fill();
 
 		ctx.beginPath();
-		ctx.arc(this.x, this.y, this.drill / 2, 0, Math.PI * 2, true);
+		ctx.arc(this._cache.x, this._cache.y, this.drill / 2, 0, Math.PI * 2, true);
 		ctx.closePath();
 		ctx.fillStyle = '#E5E5E5'; // TODO: set colors to global option
 		ctx.fill();
@@ -46,15 +60,8 @@
 	};
 
 	Pin.prototype.renderGL = function(gl, shaderProgram){
-
-		var splt = this.flags.split(','), square = true;
-
-		for (i = 0; i < splt.length; i++)
-			if (splt[i] == 'square')
-				square = false;
-
 		
-		gl.uniform1f(shaderProgram.roundPointsUniform, square);
+		gl.uniform1f(shaderProgram.roundPointsUniform, !this.flags.square);
 
 		gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
 		gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
@@ -76,14 +83,7 @@
 
 	Pin.prototype.clearGL = function(gl, shaderProgram){
 
-		var splt = this.flags.split(','), square = true;
-
-		for (i = 0; i < splt.length; i++)
-			if (splt[i] == 'square')
-				square = false;
-
-		
-		gl.uniform1f(shaderProgram.roundPointsUniform, square);
+		gl.uniform1f(shaderProgram.roundPointsUniform, !this.flags.square);
 
 		gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
 		gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);

--- /dev/null
+++ b/src/js/circuit/PCB/Renderers/GLRenderer.js
@@ -1,1 +1,318 @@
-
+define(
+	[
+	 	"./Renderer",
+	 	"../Layer",
+	 	"Graphics/glMatrix",
+	 	"Graphics/GLHelper",
+	 	"Util/Class",
+	 	"text!shaders/2D.fs",
+	 	"text!shaders/2D.vs",
+	 	"text!shaders/Texture.fs",
+	 	"text!shaders/Texture.vs"
+	],
+	function(
+		Renderer,
+		Layer,
+		glMatrix,
+		GLHelper,
+		Class,
+		FragmentShader2Dtxt,
+		VertexShader2Dtxt,
+		FragmentShaderTextxt,
+		VertexShaderTextxt
+	){
+
+		var GLRenderer = function(ctx, canvas, symbols, layers, boardWidth, boardHeight){
+
+			Renderer.call(this, ctx, canvas, symbols, layers, boardWidth, boardHeight);
+
+		}
+
+		Class.extend(Renderer, GLRenderer);
+
+		GLRenderer.prototype._drawLayer = function(layer){
+			if(layer && !layer.isEmpty()){
+				this.ctx.bindTexture(this.ctx.TEXTURE_2D, layer.texture);
+				this.ctx.drawElements(this.ctx.TRIANGLES, this.layerVertexIndexBuffer.numItems, this.ctx.UNSIGNED_SHORT, 0);
+			}
+		};
+
+		GLRenderer.prototype.destroy = function(){
+
+			var gl = this.ctx;
+
+			for(i = 0; i < this.layers.length; i++)
+				this.layers[i].cleanupGL(gl);
+			for(i in this.symbols)
+				this.symbols[i].cleanupGL(this.ctx);
+
+			// Restore GL Defaults
+			gl.disable(gl.BLEND);
+			gl.useProgram(null);
+			gl.blendFunc(gl.ONE, gl.ZERO);
+			gl.clearColor(0, 0, 0, 0);
+
+			// Unbind all buffers
+			gl.bindTexture(gl.TEXTURE_2D, null);
+			gl.bindBuffer(gl.ARRAY_BUFFER, null);
+			gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+			gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+			// Clear viewport
+			gl.viewport(0, 0, gl.viewportWidth, gl.viewportheight);
+			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+			// Clear errors
+			while(gl.getError());
+
+		};
+
+		GLRenderer.prototype.setup = function(){
+
+			var i, gl = this.ctx;
+
+			// 2D Shader
+			this.shaderProgram = GLHelper.createProgram(gl, 
+					GLHelper.createShader(gl, gl.VERTEX_SHADER, VertexShader2Dtxt),
+					GLHelper.createShader(gl, gl.FRAGMENT_SHADER, FragmentShader2Dtxt));
+
+			this.shaderProgram.vertexPositionAttribute = gl.getAttribLocation(this.shaderProgram, "aVertexPosition");
+	        gl.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute);
+
+	        this.shaderProgram.pMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uPMatrix");
+	        this.shaderProgram.mvMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uMVMatrix");
+	        this.shaderProgram.vColorUniform = gl.getUniformLocation(this.shaderProgram, "vColor");
+
+	        this.shaderProgram.pointsizeUniform = gl.getUniformLocation(this.shaderProgram, "pointsize");
+	        this.shaderProgram.innerRadiusUniform = gl.getUniformLocation(this.shaderProgram, "innerRadius");
+	        this.shaderProgram.roundPointsUniform = gl.getUniformLocation(this.shaderProgram, "roundPoints");
+	        this.shaderProgram.startAngleUniform = gl.getUniformLocation(this.shaderProgram, "startAngle");
+	        this.shaderProgram.sweepUniform = gl.getUniformLocation(this.shaderProgram, "sweep");
+	        this.shaderProgram.arcEnabledUniform = gl.getUniformLocation(this.shaderProgram, "arcEnabled");
+	        this.shaderProgram.invertedUniform = gl.getUniformLocation(this.shaderProgram, "inverted");
+
+	        // Texture Shader
+			this.texShaderProgram = GLHelper.createProgram(gl, 
+					GLHelper.createShader(gl, gl.VERTEX_SHADER, VertexShaderTextxt),
+					GLHelper.createShader(gl, gl.FRAGMENT_SHADER, FragmentShaderTextxt));
+
+	        this.texShaderProgram.vertexPositionAttribute = gl.getAttribLocation(this.texShaderProgram, "aVertexPosition");
+	        gl.enableVertexAttribArray(this.texShaderProgram.vertexPositionAttribute);
+
+	        this.texShaderProgram.textureCoordAttribute = gl.getAttribLocation(this.texShaderProgram, "aTextureCoord");
+	        gl.enableVertexAttribArray(this.texShaderProgram.textureCoordAttribute);
+
+	        this.texShaderProgram.pMatrixUniform = gl.getUniformLocation(this.texShaderProgram, "uPMatrix");
+	        this.texShaderProgram.mvMatrixUniform = gl.getUniformLocation(this.texShaderProgram, "uMVMatrix");
+	        this.texShaderProgram.samplerUniform = gl.getUniformLocation(this.texShaderProgram, "uSampler");
+
+	        // Board Polygon
+			this.boardVertexBuffer = gl.createBuffer();
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.boardVertexBuffer);
+			vertices = [
+			 this.boardWidth, this.boardHeight,  -1.0,
+			  -1.0, this.boardHeight,  -1.0,
+			  this.boardWidth,   -1.0,  -1.0,
+			  -1.0,   -1.0,  -1.0
+			];
+			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+			this.boardVertexBuffer.itemSize = 3;
+			this.boardVertexBuffer.numItems = 4;
+
+			// Layer Polygon
+			this.layerVertexPositionBuffer = gl.createBuffer();
+		    gl.bindBuffer(gl.ARRAY_BUFFER, this.layerVertexPositionBuffer);
+		    vertices = [
+		     -1.0, -1.0,  1.0,
+		      1.0, -1.0,  1.0,
+		      1.0,  1.0,  1.0,
+		     -1.0,  1.0,  1.0,
+		    ];
+		    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+		    this.layerVertexPositionBuffer.itemSize = 3;
+		    this.layerVertexPositionBuffer.numItems = 4;
+
+		    // Layer Texture Coordinates
+		    this.layerVertexTextureCoordBuffer = gl.createBuffer();
+		    gl.bindBuffer(gl.ARRAY_BUFFER, this.layerVertexTextureCoordBuffer);
+		    var textureCoords = [
+		      0.0, 0.0,
+		      1.0, 0.0,
+		      1.0, 1.0,
+		      0.0, 1.0,
+		    ];
+		    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
+		    this.layerVertexTextureCoordBuffer.itemSize = 2;
+		    this.layerVertexTextureCoordBuffer.numItems = 4;
+
+		    // Layer Vertex Indice Buffer
+		    this.layerVertexIndexBuffer = gl.createBuffer();
+		    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.layerVertexIndexBuffer);
+		    var cubeVertexIndices = [
+		        0, 1, 2,      0, 2, 3
+		    ];
+		    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
+		    this.layerVertexIndexBuffer.itemSize = 1;
+		    this.layerVertexIndexBuffer.numItems = 6;
+
+	        // Viewport setup
+			gl.viewportWidth = this.canvas.width;
+			gl.viewportHeight = this.canvas.height;
+
+			// GL Setup
+	        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+
+		    // Set-Up Frame Buffers
+			for(i = 0; i < this.layers.length; i++)
+				this.layers[i].setupFramebuffer(gl);
+
+			// Set-Up 3D Buffer Arrays
+			for(i = 0; i < this.layers.length; i++)
+				this.layers[i].init3DArrays(gl);
+			for(i in this.symbols)
+				this.symbols[i].init3DArrays(this.ctx);
+		}
+
+		GLRenderer.prototype.resize = function(){
+
+			this.ctx.viewportWidth = this.canvas.width;
+			this.ctx.viewportHeight = this.canvas.height;
+
+			for(var i = 0; i < this.layers.length; i++){
+				this.layers[i].resizeFrameBuffer(this.ctx);
+			}
+
+		}
+
+		GLRenderer.prototype.render = function(side, offsetX, offsetY, scaleFactor){
+			
+			var oMatrix, mvMatrix, i, r, g, b, l, color, gl = this.ctx;
+
+			oMatrix = glMatrix.mat4.create();
+			mvMatrix = glMatrix.mat4.create();
+
+			gl.scaleFactor = scaleFactor;
+			gl.oMatrix = oMatrix;
+			gl.mvMatrix = mvMatrix;
+			gl.side = side;
+
+			glMatrix.mat4.ortho(0, gl.viewportWidth, gl.viewportHeight, 0, -10, 10, oMatrix);
+
+			glMatrix.mat4.identity(mvMatrix);
+
+			if(side == Layer.TOP){
+				glMatrix.mat4.scale(mvMatrix, [1.0, -1.0, 1.0]);
+				glMatrix.mat4.translate(mvMatrix, [0.0, -gl.viewportHeight, 0.0]);
+			}
+
+			glMatrix.mat4.scale(mvMatrix, [scaleFactor, scaleFactor, 1.0]);
+			glMatrix.mat4.translate(mvMatrix, [-offsetX, -offsetY, 0.0]);
+
+	        gl.disableVertexAttribArray(this.texShaderProgram.textureCoordAttribute);
+	        gl.disable(gl.BLEND);
+
+	        gl.useProgram(this.shaderProgram);
+
+			gl.uniformMatrix4fv(this.shaderProgram.pMatrixUniform, false, oMatrix);
+	        gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, mvMatrix);
+
+			if(side == Layer.TOP){
+
+				this.topSilk.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 0.0, g: 0.0, b: 0.0}, this.pins, null);
+				this.bottomSilk.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 1.0, g: 1.0, b: 1.0}, this.pins, null);
+
+				this.top.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 1.0, g: 1.0, b: 1.0}, this.pins, null);
+				this.solder.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 0.0, g: 0.0, b: 0.0}, this.pins, null);
+
+			} else {
+
+				this.topSilk.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 1.0, g: 1.0, b: 1.0}, this.pins, null);
+				this.bottomSilk.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 0.0, g: 0.0, b: 0.0}, this.pins, null);
+
+				this.top.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 0.0, g: 0.0, b: 0.0}, this.pins, null);
+				this.solder.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 1.0, g: 1.0, b: 1.0}, this.pins, null);
+
+			}
+
+			this.pins.renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: 1.0, g: 1.0, b: 1.0}, null);
+
+			for(l = 0; l < this.otherLayers.length; l++){
+
+				color = this.otherLayers[l].pcbv.getLayerColors()[l];
+
+				r = parseInt(color.substring(1, 3), 16) / 256;
+				g = parseInt(color.substring(3, 5), 16) / 256;
+				b = parseInt(color.substring(5, 7), 16) / 256;
+
+				if(this.otherLayers[l].name == "bottom")
+					this.otherLayers[l].renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: r, g: g, b: b}, this.pins, this.solder);
+				else if(this.otherLayers[l].name == "top")
+					this.otherLayers[l].renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: r, g: g, b: b}, this.pins, this.top);
+				else
+					this.otherLayers[l].renderGL(gl, this.shaderProgram, oMatrix, mvMatrix, {r: r, g: g, b: b}, this.pins, null);
+
+			}
+
+			gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+			// Draw Board
+			gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
+			gl.clearColor(0.79, 0.79, 0.79, 1.0);
+			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+			gl.uniform4f(this.shaderProgram.vColorUniform, 0.89, 0.89, 0.89, 1.0);
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.boardVertexBuffer);
+			gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, this.boardVertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
+			gl.drawArrays(gl.TRIANGLE_STRIP, 0, this.boardVertexBuffer.numItems);
+
+			// Setup texture program
+	        gl.useProgram(this.texShaderProgram);
+
+			glMatrix.mat4.ortho(-1, 1, -1, 1, -1, 1, oMatrix);
+			glMatrix.mat4.identity(mvMatrix);
+
+			gl.uniformMatrix4fv(this.texShaderProgram.pMatrixUniform, false, oMatrix);
+	        gl.uniformMatrix4fv(this.texShaderProgram.mvMatrixUniform, false, mvMatrix);
+
+	        gl.enableVertexAttribArray(this.texShaderProgram.textureCoordAttribute);
+
+	        gl.enable(gl.BLEND);
+
+	        // Draw Layers
+	        gl.bindBuffer(gl.ARRAY_BUFFER, this.layerVertexPositionBuffer);
+	        gl.vertexAttribPointer(this.texShaderProgram.vertexPositionAttribute, this.layerVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
+	        gl.bindBuffer(gl.ARRAY_BUFFER, this.layerVertexTextureCoordBuffer);
+	        gl.vertexAttribPointer(this.texShaderProgram.textureCoordAttribute, this.layerVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
+	        gl.activeTexture(gl.TEXTURE0);
+	        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.layerVertexIndexBuffer);
+
+	        if(side != Layer.TOP){
+
+	        	this._drawLayer(this.topSilk);
+	        	this._drawLayer(this.solder);
+	        	for(l = this.otherLayers.length - 1; l >= 0; l--)
+		        	this._drawLayer(this.otherLayers[l]);
+	        	this._drawLayer(this.top);
+	        	this._drawLayer(this.pins);
+	        	this._drawLayer(this.bottomSilk);
+
+	        } else {
+
+	        	this._drawLayer(this.bottomSilk);
+	        	this._drawLayer(this.top);
+	        	for(l = 0; l < this.otherLayers.length; l++)
+		        	this._drawLayer(this.otherLayers[l]);
+	        	this._drawLayer(this.solder);
+	        	this._drawLayer(this.pins);
+	        	this._drawLayer(this.topSilk);
+
+	        }
+
+
+		}
+
+		return GLRenderer;
+
+	}
+);

--- /dev/null
+++ b/src/js/circuit/PCB/Renderers/Renderer.js
@@ -1,1 +1,68 @@
+define(
+	[
+	 	"../Layer",
+	 	"Graphics/glMatrix",
+	 	"Util/Exception/NotImplementedException"
+	],
+	function(
+		Layer,
+		glMatrix,
+		NotImplementedException
+	){
 
+		var Renderer = function(ctx, canvas, symbols, layers, boardWidth, boardHeight){
+
+			if(!layers) return;
+
+			this.ctx = ctx;
+			this.canvas = canvas;
+
+			this.boardWidth = boardWidth;
+			this.boardHeight = boardHeight;
+
+			this.symbols = symbols;
+
+			this.layers = [];
+
+			this.topSilk = null;
+			this.bottomSkil = null;
+
+			this.top = null;
+			this.solder = null;
+			this.pins = null;
+
+			this.otherLayers = [];
+
+			for(var i = 0; i < layers.length; i++)
+				this.addLayer(layers[i]);
+
+		};
+
+		Renderer.prototype.addLayer = function(layer){
+
+			this.layers.push(layer);
+
+			if(layer.name == "silk"){
+				if(!this.topSilk) this.topSilk = layer;
+				else this.bottomSilk = layer;
+			}
+			else if(layer.name == "top_component")
+				this.top = layer;
+			else if(layer.name == "solder_component")
+				this.solder = layer;
+			else if(layer.name == "pins")
+				this.pins = layer;
+			else
+				this.otherLayers.push(layer);
+
+		};
+
+		Renderer.prototype.destroy = function(){throw new NotImplementedException("Renderer.destroy is virtual function");};
+		Renderer.prototype.setup = function(){throw new NotImplementedException("Renderer.setup is virtual function");};
+		Renderer.prototype.resize = function(){throw new NotImplementedException("Renderer.resize is virtual function");};
+		Renderer.prototype.render = function( side, offsetX, offsetY, scaleFactor){throw new NotImplementedException("Renderer.render is virtual function");};
+
+		return Renderer;
+
+	}
+);

--- /dev/null
+++ b/src/js/circuit/PCB/Renderers/TwoDRenderer.js
@@ -1,1 +1,107 @@
+define(
+	[
+	 	"./Renderer",
+	 	"../Layer",
+	 	"Util/Class"
+	],
+	function(
+		Renderer,
+		Layer,
+		Class
+	){
 
+		var TwoDRenderer = function(ctx, canvas, symbols, layers, boardWidth, boardHeight){
+
+			Renderer.call(this, ctx, canvas, symbols, layers, boardWidth, boardHeight);
+
+		};
+
+		Class.extend(Renderer, TwoDRenderer);
+
+		TwoDRenderer._renderLayer = function(ctx, bufferCtx, bufferCanvas, layer, color, pins, components){
+
+			if(layer && !layer.isEmpty()){
+				bufferCtx.clearRect(0, 0, bufferCanvas.width, bufferCanvas.height);
+				layer.render(bufferCtx, color);
+				ctx.drawImage(bufferCanvas, 0, 0);
+			}
+
+		};
+
+		TwoDRenderer.prototype.destroy = function(){};
+
+		TwoDRenderer.prototype.setup = function(){
+
+			this.bufferCanvas = document.createElement('canvas');
+			this.bufferCanvas.width = this.canvas.width;
+			this.bufferCanvas.height = this.canvas.height;
+	
+			this.bufferCtx = this.bufferCanvas.getContext('2d');
+
+		};
+
+		TwoDRenderer.prototype.resize = function(){
+
+			this.bufferCanvas.width = this.canvas.width;
+			this.bufferCanvas.height = this.canvas.height;
+
+		}
+
+		TwoDRenderer.prototype.render = function(side, offsetX, offsetY, scaleFactor){
+
+			// Fill canvas background Dark Grey
+			this.bufferCtx.fillStyle = '#CCCCCC';
+			this.bufferCtx.fillRect(0, 0, this.bufferCanvas.width, this.bufferCanvas.height);
+
+			// Save default transform
+			this.bufferCtx.save();
+		
+			// Flip canvas if solder side
+			if(side){
+				this.bufferCtx.scale(1, -1);
+				this.bufferCtx.translate(0, -this.bufferCanvas.height);
+			}
+		
+			// Scale and shift
+			this.bufferCtx.scale(scalef, scalef);
+			this.bufferCtx.translate(-offsetX, -offsetY);	
+
+			// Fill board space Grey
+			this.bufferCtx.fillStyle = '#E5E5E5';
+			this.bufferCtx.fillRect(0, 0, this.boardWidth, this.boardHeight);
+			this.ctx.globalAlpha = 1.0;
+			this.ctx.drawImage(this.bufferCanvas, 0, 0);
+
+			// Draw Layers
+			this.ctx.globalAlpha = 0.5;
+			if(side != Layer.TOP){
+
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.topSilk, '#FFFFFF', null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.solder, '#FFFFFF', null, null);
+	        	for(l = this.otherLayers.length - 1; l >= 0; l--)
+					TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.otherLayers[l], null, null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.top, '#000000', null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.pins, null, null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.bottomSilk, '#000000', null, null);
+
+	        } else {
+
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.bottomSilk, '#FFFFFF', null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.top, '#FFFFFF', null, null);
+	        	for(l = 0; l < this.otherLayers.length; l++)
+					TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.otherLayers[l], null, null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.solder, '#000000', null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.pins, null, null, null);
+	        	TwoDRenderer._renderLayer(this.ctx, this.bufferCtx, this.bufferCanvas, this.topSilk, '#000000', null, null);
+
+	        }
+		
+			// Restore default transform
+			this.bufferCtx.restore();
+
+		};
+
+		return TwoDRenderer;
+
+	}
+);

--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,6 +1,9 @@
 requirejs.config({
 
 	baseUrl: 'js',
+	paths: {
+		shaders: '../data/shaders',
+	}
 
 });
 
@@ -59,11 +62,11 @@
 
 		PCBLoadReq.onload = function(){
 
-			if(window.PCBViewer.pcb){
+			var pcb = window.PCBViewer.pcb;
 
-				window.PCBViewer.pcb.destroy();
-				window.PCBViewer.pcb = null;
-
+			if(pcb){
+				pcb.destroy();
+				pcb = null;
 			}
 
 			var mode = document.querySelector('input[name="mode"]:checked').value;
@@ -75,9 +78,11 @@
 			window.PCBViewer.canvas.width = window.innerWidth-20;
 			window.PCBViewer.canvas.height = window.innerHeight-50;
 
-			window.PCBViewer.pcb = new PCBV(window.PCBViewer.canvas, true, mode);
-			window.PCBViewer.pcb.parse_data(PCBLoadReq.response);
-			window.PCBViewer.pcb.render();
+			pcb = new PCBV(window.PCBViewer.canvas, true, mode);
+			window.PCBViewer.pcb = pcb;
+			pcb.parse(PCBLoadReq.response);
+			pcb.setup();
+			pcb.render();
 
 			status.innerHTML = window.PCBViewer.pcb.mode;