Merge branch 'thermal' into dev
Merge branch 'thermal' into dev
All thermals rendering correctly

--- a/src/data/pcb/SoftRockLiteIIRX.pcb
+++ b/src/data/pcb/SoftRockLiteIIRX.pcb
@@ -805,7 +805,7 @@
 Via[108000 85500 4200 2000 0 2600 "" "thermal(5S)"]
 Via[4000 20500 4200 2000 0 2600 "" "thermal(5S)"]
 Via[65500 67000 4200 2000 0 2600 "" ""]
-Via[52000 62500 4700 2000 0 2600 "" "thermal(0-2,5S)"]
+Via[52000 62500 4700 2000 0 2600 "" "thermal(0S,1S,2S,5S)"]
 
 Element["" "HC49U_1" "X1" "rect_100" 22000 9000 -11000 6500 0 100 ""]
 (

--- a/src/data/pcb/ThermalTest.pcb
+++ b/src/data/pcb/ThermalTest.pcb
@@ -6,7 +6,7 @@
 PCB["" 50000 50000]
 
 Grid[1000.0 0 0 0]
-Cursor[0 0 0.000000]
+Cursor[4000 43000 0.000000]
 PolyArea[3100.006200]
 Thermal[0.500000]
 DRC[1000 1000 1000 1000 1500 1000]
@@ -792,7 +792,7 @@
 Via[25000 14000 3600 2000 0 2000 "" "thermal(0S)"]
 Via[32000 14000 3600 2000 0 2000 "" "thermal(0X)"]
 Via[39000 14000 3600 2000 0 2000 "" "thermal(0t)"]
-Via[11000 36000 3600 2000 0 2000 "" "thermal(0+,5)"]
+Via[11000 36000 3600 2000 0 2000 "" "thermal(0-2,5)"]
 Via[18000 36000 3600 2000 0 2000 "" "thermal(0X,5+)"]
 Via[25000 36000 3600 2000 0 2000 "" "thermal(5S)"]
 Via[32000 36000 3600 2000 0 2000 "" "thermal(0+,5X)"]
@@ -811,9 +811,17 @@
 )
 Layer(2 "ground")
 (
+	Polygon("clearpoly")
+	(
+		[3000 44000] [15000 44000] [15000 29000] [3000 29000] 
+	)
 )
 Layer(3 "signal2")
 (
+	Polygon("clearpoly")
+	(
+		[7000 40000] [15000 40000] [15000 29000] [7000 29000] 
+	)
 )
 Layer(4 "signal3")
 (

--- a/src/data/shaders/2D.fs
+++ b/src/data/shaders/2D.fs
@@ -7,10 +7,12 @@
 uniform float innerRadius;
 uniform float startAngle;
 uniform float sweep;
+uniform float shaveInside;
 
 uniform bool arcEnabled;
 uniform bool roundPoints;
 uniform bool inverted;
+uniform bool shaveFF;
 
 void main(void) {
 	if(roundPoints){
@@ -20,9 +22,22 @@
     		discard;
 
 		if(arcEnabled){
+
+			float sx = gl_PointCoord.x - 0.5;
+			float sy = gl_PointCoord.y - 0.5;
+			if(shaveFF){
+				float sx2 = (sx + sy)/1.41421356237;
+				float sy2 = (sy - sx)/1.41421356237;
+				sx = sx2;
+				sy = sy2;
+			}
+			if(abs(sx) < shaveInside || abs(sy) < shaveInside)
+				discard;
+
 			float y_dif = gl_PointCoord.y - 0.5;
 			if(inverted)
 				y_dif = y_dif * -1.0;
+
 			float ang = atan(y_dif, gl_PointCoord.x - 0.5);
 			if(startAngle + sweep > M_PI){
 				if(ang < startAngle && ang > startAngle + sweep - M_PI * 2.0)

--- a/src/js/circuit/PCB/ElementArc.js
+++ b/src/js/circuit/PCB/ElementArc.js
@@ -66,7 +66,7 @@
 				gl.uniform1f(shaderProgram.invertedUniform, false);
 			gl.uniform1f(shaderProgram.startAngleUniform, (this.start / 180.0 * Math.PI) - Math.PI);
 			gl.uniform1f(shaderProgram.sweepUniform, (this.sweep / 180.0 * Math.PI));
-		
+
 			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
 			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
 	

--- a/src/js/circuit/PCB/Layer.js
+++ b/src/js/circuit/PCB/Layer.js
@@ -48,14 +48,24 @@
 						elements.parts[i].clear(ctx);
 				if(pins)
 					for(i = 0; i < pins.parts.length; i++)
-						pins.parts[i].clear(ctx);
+						pins.parts[i].clear(ctx, this.number);
 				ctx.globalCompositeOperation = "source-over";
+
 			}
 
 			// Render non clearing polygons
 			for(i = 0; i < this.polygons.length; i++)
 				if(!this.polygons[i].isClear())
 					this.polygons[i].render(ctx, color);
+
+			// Clear centers
+			ctx.globalCompositeOperation = "destination-out";
+			ctx.strokeStyle = 'rgba(0,0,0,1)';
+			ctx.fillStyle = 'rgba(0,0,0,1)';
+			if(pins)
+				for(i = 0; i < pins.parts.length; i++)
+					pins.parts[i].clearInner(ctx);
+			ctx.globalCompositeOperation = "source-over";
 
 		}
 
@@ -77,12 +87,12 @@
 				gl.uniform4f(shaderProgram.vColorUniform, 0.0, 0.0, 0.0, 0.0);
 				for(i = 0; i < this.parts.length; i++)
 					this.parts[i].clearGL(gl, shaderProgram);
-				if(pins)
-					for(i = 0; i < pins.parts.length; i++)
-						pins.parts[i].clearGL(gl, shaderProgram);
 				if(elements)
 					for(i = 0; i < elements.parts.length; i++)
 						elements.parts[i].clearGL(gl, shaderProgram);
+				if(pins)
+					for(i = 0; i < pins.parts.length; i++)
+						pins.parts[i].clearGL(gl, shaderProgram, this.number);
 			}
 
 			// render non clear polygons
@@ -90,6 +100,12 @@
 			for(i = 0; i < this.polygons.length; i++)
 				if(!this.polygons[i].isClear())
 					this.polygons[i].renderGL(gl, shaderProgram);
+
+			// Clear centers
+			gl.uniform4f(shaderProgram.vColorUniform, 0.0, 0.0, 0.0, 0.0);
+			if(pins)
+				for(i = 0; i < pins.parts.length; i++)
+					pins.parts[i].clearInnerGL(gl, shaderProgram);
 
 		}
 

--- a/src/js/circuit/PCB/Pin.js
+++ b/src/js/circuit/PCB/Pin.js
@@ -1,137 +1,191 @@
-define(function() {
+define(
+	[
+	 	"./Thermal",
+	 	"./parseFlags"
+	],
+	function(
+		Thermal,
+		parseFlags
+	){
+	
+		// Pin [x y thickness clearance mask drillholedia name number flags]
+		var Pin = function(x, y, thick, clearance, mask, drill, name, num, flags) {
+	
+			this.x = x;
+			this.y = y;
+			this.thick = thick;
+			this.clearance = clearance;
+			this.mask = mask;
+			this.drill = drill;
+			this.name = name;
+			this.num = num;
+			this.flags = parseFlags(Pin._defaultFlags, flags);
 
-	// Pin [x y thickness clearance mask drillholedia name number flags]
-	var Pin = function(x, y, thick, clearance, mask, drill, name, num, flags) {
-
-		this.x = x;
-		this.y = y;
-		this.thick = thick;
-		this.clearance = clearance;
-		this.mask = mask;
-		this.drill = drill;
-		this.name = name;
-		this.num = num;
-		this.flags = {
+		};
+	
+		Pin._defaultFlags = {
 			square: false
 		};
+	
+		Pin.prototype._createCache = function(){
+	
+			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);
+	
+		}
+	
+		Pin.prototype.render = function(ctx, color) {
+	
+			if(!this._cache) this._createCache();
+	
+			if(color == '#FFFFFF')
+				return;
+	
+			ctx.beginPath();
+			if(this.flags.square)
+				ctx.rect(this._cache.rx, this._cache.ry, this.thick, this.thick);
+			else
+				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._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();
+	
+		};
 
-		var i, split = flags.split(',');
-		for(i = 0; i < split.length; i++)
-			this.flags[split[i]] = true;
+		Pin.prototype.clear = function(ctx, layerNumber){
+	
+			if(!this._cache) this._createCache();
 
-	};
+			var thrm, i;
 
-	Pin.prototype._createCache = function(){
+			thrm = this.flags.thermal;
+			if(thrm){
+				thrm = Thermal.findThermal(thrm, layerNumber);
+				if(thrm)
+					thrm.clear(ctx, this.pointBuffer, this.clearance, this.thick, this.drill);
+			}
 
-		this._cache = {};
+			if(!thrm){
+				ctx.beginPath();
+				ctx.arc(this._cache.x, this._cache.y, (this.clearance + this.thick) / 2.0, 0, Math.PI * 2, true);
+				ctx.closePath();
+				ctx.fill();
+			}
 
-		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;
+		};
+
+		Pin.prototype.clearInner = function(ctx){
+
+			if(!this._cache) this._createCache();
+
+			ctx.beginPath();
+			ctx.arc(this._cache.x, this._cache.y, this.drill / 2.0, 0, Math.PI * 2, true);
+			ctx.closePath();
+			ctx.fill();
+
+		};
+	
+		Pin.prototype.renderGL = function(gl, shaderProgram){
+			
+			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);
+	
+			gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+	
+			gl.uniform4f(shaderProgram.vColorUniform, 0.35, 0.35, 0.35, 1.0);
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.thick * gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+	
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+			gl.uniform4f(shaderProgram.vColorUniform, 0.74, 0.74, 0.74, 1.0);
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.drill * gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+	
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+	
 		}
-		this._cache.rx = this._cache.x - (this.thick / 2);
-		this._cache.ry = this._cache.y - (this.thick / 2);
+	
+		Pin.prototype.clearGL = function(gl, shaderProgram, layerNumber){
+	
+			if(!this._cache) this._createCache();
+
+			var thrm = this.flags.thermal;
+			if(thrm){
+				thrm = Thermal.findThermal(thrm, layerNumber);
+				if(thrm)
+					thrm.clearGL(gl, shaderProgram, this._cache.x, this._cache.y, this.pointBuffer, this.clearance, this.thick, this.drill);
+			}
+
+			if(!thrm){
+				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);
+		
+				gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+				gl.uniform1f(shaderProgram.pointsizeUniform, (this.thick + this.clearance) * gl.scaleFactor);
+				gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+		
+				gl.uniform1f(shaderProgram.roundPointsUniform, false);
+			}
+	
+		};
+	
+		Pin.prototype.cleanupGL = function(gl){
+	
+			if(this.pointBuffer){
+				gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+				gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
+				gl.deleteBuffer(this.pointBuffer);
+				this.pointBuffer = null;
+			}
+	
+		}
+
+		Pin.prototype.clearInnerGL = function(gl, shaderProgram){
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
+
+			gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.id * gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+		};
+
+		Pin.prototype.setup3DArrayBuffer = function(gl, x, y){
+	
+			var vBuffer;
+			vBuffer = gl.createBuffer();
+			gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
+			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([this.x + this.parent.mx,this.y + this.parent.my,0.0]), gl.STATIC_DRAW);
+			vBuffer.itemSize = 3;
+			vBuffer.numItems = 1;
+			this.pointBuffer = vBuffer;
+	
+		}
+	
+		return Pin;
 
 	}
-
-	Pin.prototype.render = function(ctx, color) {
-
-		if(!this._cache) this._createCache();
-
-		if(color == '#FFFFFF')
-			return;
-
-		ctx.beginPath();
-		if(this.flags.square)
-			ctx.rect(this._cache.rx, this._cache.ry, this.thick, this.thick);
-		else
-			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._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();
-
-	};
-
-	Pin.prototype.clear = function(ctx){
-
-		if(!this._cache) this._createCache();
-
-		ctx.beginPath();
-		ctx.arc(this._cache.x, this._cache.y, (this.clearance + this.thick) / 2.0, 0, Math.PI * 2, true);
-		ctx.closePath();
-		ctx.fill();
-
-	};
-
-	Pin.prototype.renderGL = function(gl, shaderProgram){
-		
-		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);
-
-		gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
-
-		gl.uniform4f(shaderProgram.vColorUniform, 0.35, 0.35, 0.35, 1.0);
-		gl.uniform1f(shaderProgram.pointsizeUniform, this.thick * gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
-
-		gl.uniform1f(shaderProgram.roundPointsUniform, true);
-		gl.uniform4f(shaderProgram.vColorUniform, 0.74, 0.74, 0.74, 1.0);
-		gl.uniform1f(shaderProgram.pointsizeUniform, this.drill * gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
-
-		gl.uniform1f(shaderProgram.roundPointsUniform, false);
-
-	}
-
-	Pin.prototype.clearGL = function(gl, shaderProgram){
-
-		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);
-
-		gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
-		gl.uniform1f(shaderProgram.pointsizeUniform, (this.thick + this.clearance) * gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
-
-		gl.uniform1f(shaderProgram.roundPointsUniform, false);
-
-	};
-
-	Pin.prototype.cleanupGL = function(gl){
-
-		if(this.pointBuffer){
-			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
-			gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
-			gl.deleteBuffer(this.pointBuffer);
-			this.pointBuffer = null;
-		}
-
-	}
-
-	Pin.prototype.setup3DArrayBuffer = function(gl, x, y){
-
-		var vBuffer;
-		vBuffer = gl.createBuffer();
-		gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
-		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([this.x + this.parent.mx,this.y + this.parent.my,0.0]), gl.STATIC_DRAW);
-		vBuffer.itemSize = 3;
-		vBuffer.numItems = 1;
-		this.pointBuffer = vBuffer;
-
-	}
-
-	return Pin;
-
-});
+);

--- a/src/js/circuit/PCB/Renderers/GLRenderer.js
+++ b/src/js/circuit/PCB/Renderers/GLRenderer.js
@@ -90,6 +90,8 @@
 	        this.shaderProgram.sweepUniform = gl.getUniformLocation(this.shaderProgram, "sweep");
 	        this.shaderProgram.arcEnabledUniform = gl.getUniformLocation(this.shaderProgram, "arcEnabled");
 	        this.shaderProgram.invertedUniform = gl.getUniformLocation(this.shaderProgram, "inverted");
+	        this.shaderProgram.shaveInsideUniform = gl.getUniformLocation(this.shaderProgram, "shaveInside");
+	        this.shaderProgram.shaveFFUniform = gl.getUniformLocation(this.shaderProgram, "shaveFF");
 
 	        // Texture Shader
 			this.texShaderProgram = GLHelper.createProgram(gl, 

--- /dev/null
+++ b/src/js/circuit/PCB/Thermal.js
@@ -1,1 +1,231 @@
-
+define(
+	function(){
+
+		var Thermal = function(layerNumber, type){
+
+			this.layerNumber = layerNumber;
+			this.type = type;
+
+		};
+
+		Thermal.findThermal = function(list, layerNumber){
+			for(var i = 0; i < list.length; i++)
+				if(list[i].onLayer(layerNumber))
+					return list[i];
+			return null;
+		};
+
+		Thermal.prototype.onLayer = function(layerNumber){
+			return this.layerNumber == layerNumber - 1;
+		};
+
+		Thermal.prototype.clear = function(ctx, x, y, clearance, outerDiameter, innerDiameter){
+			switch(this.type){
+				case 'S':
+					break;
+				case 't':
+					var radius = ((clearance / 2) + outerDiameter) / 2;
+					var clearanceAngle = clearance / radius / 2;
+
+					ctx.lineCap = 'round';
+					ctx.lineWidth = clearance / 2;
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -clearanceAngle, -Math.PI * 0.5 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.5 - clearanceAngle, -Math.PI * 1.0 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.0 - clearanceAngle, -Math.PI * 1.5 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.5 - clearanceAngle, -Math.PI * 2.0 + clearanceAngle, true);
+					ctx.stroke();
+					break;
+				case 'X':
+					var radius = ((clearance / 2) + outerDiameter) / 2;
+					var clearanceAngle = clearance / radius / 2;
+
+					ctx.lineCap = 'round';
+					ctx.lineWidth = clearance / 2;
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.25 - clearanceAngle, -Math.PI * 0.75 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.75 - clearanceAngle, -Math.PI * 1.25 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.25 - clearanceAngle, -Math.PI * 1.75 + clearanceAngle, true);
+					ctx.stroke();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.75 - clearanceAngle, -Math.PI * 2.25 + clearanceAngle, true);
+					ctx.stroke();
+					break;
+				case '+':
+					var radius = (clearance + outerDiameter) / 2;
+					var clearanceAngle = clearance / radius / 4;
+					var clearanceAngle2 = clearance / outerDiameter / 2;
+					ctx.beginPath();
+					ctx.arc(x, y, radius, - clearanceAngle, -Math.PI * 0.5 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 0.5 + clearanceAngle2, - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.5 - clearanceAngle, -Math.PI * 1.0 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 1.0 + clearanceAngle2, -Math.PI * 0.5 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.0 - clearanceAngle, -Math.PI * 1.5 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 1.5 + clearanceAngle2, -Math.PI * 1.0 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.5 - clearanceAngle, -Math.PI * 2.0 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 2.0 + clearanceAngle2, -Math.PI * 1.5 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					break;
+				case 'O':
+					var radius = (clearance + outerDiameter) / 2;
+					var clearanceAngle = clearance / radius / 4;
+					var clearanceAngle2 = clearance / outerDiameter / 2;
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.25 - clearanceAngle, -Math.PI * 0.75 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 0.75 + clearanceAngle2, -Math.PI * 0.25 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 0.75 - clearanceAngle, -Math.PI * 1.25 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 1.25 + clearanceAngle2, -Math.PI * 0.75 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.25 - clearanceAngle, -Math.PI * 1.75 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 1.75 + clearanceAngle2, -Math.PI * 1.25 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					ctx.beginPath();
+					ctx.arc(x, y, radius, -Math.PI * 1.75 - clearanceAngle, -Math.PI * 2.25 + clearanceAngle, true);
+					ctx.arc(x, y, outerDiameter / 2, -Math.PI * 2.25 + clearanceAngle2, -Math.PI * 1.75 - clearanceAngle2, false);
+					ctx.closePath();
+					ctx.fill();
+					break;
+				default:
+					ctx.beginPath();
+					ctx.arc(x, y, (clearance + outerDiameter) / 2, 0, Math.PI * 2, true);
+					ctx.closePath();
+					ctx.fill();
+					break;
+			}
+
+		};
+
+		Thermal._clearArc = function(gl, shaderProgram, pointBuffer, ang1, ang2, shave, outerDiameter, innerDiameter, clearance){
+
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
+			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
+
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+			gl.uniform1f(shaderProgram.arcEnabledUniform, true);
+			gl.uniform1f(shaderProgram.innerRadiusUniform, innerDiameter / outerDiameter / 2);
+			gl.uniform1f(shaderProgram.pointsizeUniform, outerDiameter * gl.scaleFactor);
+			gl.uniform1f(shaderProgram.startAngleUniform, ang1 + ang2);
+			gl.uniform1f(shaderProgram.sweepUniform, Math.PI / 2 - ang2 * 2);
+			if(shave)
+				gl.uniform1f(shaderProgram.shaveInsideUniform, clearance / outerDiameter / 4);
+
+			gl.drawArrays(gl.POINTS, 0, pointBuffer.numItems);
+
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+			gl.uniform1f(shaderProgram.arcEnabledUniform, false);
+			gl.uniform1f(shaderProgram.shaveInsideUniform, 0.0);
+
+		};
+
+		Thermal.prototype._buildCache = function(gl, x, y, angleOffset, angle, radius){
+
+			var pointBuffer, points, angle2;
+
+			angle2 = Math.PI * 0.5 + angle + angleOffset;
+			angle += angleOffset;
+			points = [
+			 x + radius * Math.cos(angle), y + radius * Math.sin(angle), 0,
+			 x - radius * Math.cos(angle), y + radius * Math.sin(angle), 0,
+			 x + radius * Math.cos(angle), y - radius * Math.sin(angle), 0,
+			 x - radius * Math.cos(angle), y - radius * Math.sin(angle), 0,
+			 x + radius * Math.cos(angle2), y + radius * Math.sin(angle2), 0,
+			 x - radius * Math.cos(angle2), y + radius * Math.sin(angle2), 0,
+			 x + radius * Math.cos(angle2), y - radius * Math.sin(angle2), 0,
+			 x - radius * Math.cos(angle2), y - radius * Math.sin(angle2), 0,
+		    ];
+
+			pointBuffer = gl.createBuffer();
+			gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
+			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(points), gl.STATIC_DRAW);
+			pointBuffer.itemSize = 3;
+			pointBuffer.numItems = 8;
+			this.pointBuffer = pointBuffer;
+
+		};
+
+		Thermal.prototype._clearPoints = function(gl, shaderProgram, clearance){
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+			gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+			gl.uniform1f(shaderProgram.pointsizeUniform, (clearance / 2) * gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+
+		};
+
+		Thermal.prototype.clearGL = function(gl, shaderProgram, x, y, pointBuffer, clearance, outerDiameter, innerDiameter){
+			switch(this.type){
+				case 'S':
+					break;
+				case 't':
+					var clearanceAngle = clearance / ((clearance / 2 + outerDiameter) / 2) / 2;
+					if(!this.pointBuffer) this._buildCache(gl, x, y, 0, clearanceAngle, ((clearance / 2) + outerDiameter) / 2);
+					this._clearPoints(gl, shaderProgram, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, 0            , clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.5, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer,-Math.PI * 0.5, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer,-Math.PI     , clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					break;
+				case 'X':
+					var clearanceAngle = clearance / ((clearance / 2 + outerDiameter) / 2) / 2;
+					if(!this.pointBuffer) this._buildCache(gl, x, y, Math.PI * 0.25, clearanceAngle, ((clearance / 2) + outerDiameter) / 2);
+					this._clearPoints(gl, shaderProgram, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.25, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer,-Math.PI * 0.25, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.75, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer,-Math.PI * 0.75, clearanceAngle, false, outerDiameter + clearance, outerDiameter, clearance);
+					break;
+				case '+':
+					var clearanceAngle = clearance / ((clearance + outerDiameter) / 2) / 4;
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, 0            , clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.5, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI      , clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 1.5, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					break;
+				case 'O':
+					var clearanceAngle = clearance / ((clearance + outerDiameter) / 2) / 4;
+					gl.uniform1f(shaderProgram.shaveFFUniform, true);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.25, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 0.75, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 1.25, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					Thermal._clearArc(gl, shaderProgram, pointBuffer, Math.PI * 1.75, clearanceAngle, true, outerDiameter + clearance, outerDiameter, clearance);
+					gl.uniform1f(shaderProgram.shaveFFUniform, false);
+					break;
+				default:
+					break;
+			}
+		};
+
+		return Thermal;
+
+	}
+);

--- a/src/js/circuit/PCB/Via.js
+++ b/src/js/circuit/PCB/Via.js
@@ -1,100 +1,154 @@
-define(function() {
-	var Via = function(x, y, od, clearance, u2, id, u3, u4) {
+define(
+	[
+	 	"./Thermal",
+	 	"./parseFlags"
+	],
+	function(
+		Thermal,
+		parseFlags
+	){
 
-		this.x = x;
-		this.y = y;
-		this.od = od;
-		this.id = id;
-		this.clearance = clearance;
+		var Via = function(x, y, od, clearance, u2, id, u3, flags) {
 
-	};
+			this.x = x;
+			this.y = y;
+			this.od = od;
+			this.id = id;
+			this.clearance = clearance;
+			this.flags = parseFlags(Via._defaultFlags, flags);
 
-	Via.prototype.render = function(ctx) {
+		};
 
-		ctx.beginPath();
-		ctx.arc(this.x, this.y, this.od / 2, 0, Math.PI * 2, true);
-		ctx.closePath();
-		ctx.fillStyle = '#7F7F7F'; // TODO: set colors to global option
-		ctx.fill();
+		Via._defaultFlags = {
+		};
 
-		ctx.beginPath();
-		ctx.arc(this.x, this.y, this.id / 2, 0, Math.PI * 2, true);
-		ctx.closePath();
-		ctx.fillStyle = '#E5E5E5'; // TODO: set colors to global option
-		ctx.fill();
+		Via.prototype.render = function(ctx) {
+	
+			ctx.beginPath();
+			ctx.arc(this.x, this.y, this.od / 2, 0, Math.PI * 2, true);
+			ctx.closePath();
+			ctx.fillStyle = '#7F7F7F'; // TODO: set colors to global option
+			ctx.fill();
+	
+			ctx.beginPath();
+			ctx.arc(this.x, this.y, this.id / 2, 0, Math.PI * 2, true);
+			ctx.closePath();
+			ctx.fillStyle = '#E5E5E5'; // TODO: set colors to global option
+			ctx.fill();
+	
+		};
+	
+		Via.prototype.clear = function(ctx, layerNumber){
 
-	};
+			var thrm = this.flags.thermal;
+			if(thrm){
+				thrm = Thermal.findThermal(thrm, layerNumber);
+				if(thrm)
+					thrm.clear(ctx, this.x, this.y, this.clearance, this.od, this.id);
+			}
 
-	Via.prototype.clear = function(ctx){
+			if(!thrm){
+				ctx.beginPath();
+				ctx.arc(this.x, this.y, (this.clearance + this.od) / 2.0, 0, Math.PI * 2, true);
+				ctx.closePath();
+				ctx.fill();
+			}
 
-		ctx.beginPath();
-		ctx.arc(this.x, this.y, (this.clearance + this.od) / 2.0, 0, Math.PI * 2, true);
-		ctx.closePath();
-		ctx.fill();
+		};
 
-	};
+		Via.prototype.clearInner = function(ctx){
 
-	Via.prototype.renderGL = function(gl, shaderProgram){
+			ctx.beginPath();
+			ctx.arc(this.x, this.y, this.id / 2.0, 0, Math.PI * 2, true);
+			ctx.closePath();
+			ctx.fill();
 
-		gl.uniform1f(shaderProgram.roundPointsUniform, true);
+		};
 
-		gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
-		gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
+		Via.prototype.renderGL = function(gl, shaderProgram){
+	
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+	
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
 
-		gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+			gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+	
+			gl.uniform4f(shaderProgram.vColorUniform, 0.59, 0.59, 0.59, 1.0);
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.od*gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+	
+			gl.uniform4f(shaderProgram.vColorUniform, 0.74, 0.74, 0.74, 1.0);
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.id*gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+	
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+	
+		}
+	
+		Via.prototype.clearGL = function(gl, shaderProgram, layerNumber){
+	
+			var thrm = this.flags.thermal;
+			if(thrm){
+				thrm = Thermal.findThermal(thrm, layerNumber);
+				if(thrm)
+					thrm.clearGL(gl, shaderProgram, this.x, this.y, this.pointBuffer, this.clearance, this.od, this.id);
+			}
 
-		gl.uniform4f(shaderProgram.vColorUniform, 0.59, 0.59, 0.59, 1.0);
-		gl.uniform1f(shaderProgram.pointsizeUniform, this.od*gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+			if(!thrm){
+				gl.uniform1f(shaderProgram.roundPointsUniform, true);
 
-		gl.uniform4f(shaderProgram.vColorUniform, 0.74, 0.74, 0.74, 1.0);
-		gl.uniform1f(shaderProgram.pointsizeUniform, this.id*gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+				gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+				gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
 
-		gl.uniform1f(shaderProgram.roundPointsUniform, false);
+				gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+
+				gl.uniform1f(shaderProgram.pointsizeUniform, (this.od + this.clearance) * gl.scaleFactor);
+				gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+
+				gl.uniform1f(shaderProgram.roundPointsUniform, false);
+			}
+	
+		};
+	
+		Via.prototype.cleanupGL = function(gl){
+	
+			if(this.pointBuffer){
+				gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+				gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
+				gl.deleteBuffer(this.pointBuffer);
+				this.pointBuffer = null;
+			}
+	
+		}
+
+		Via.prototype.clearInnerGL = function(gl, shaderProgram){
+			gl.uniform1f(shaderProgram.roundPointsUniform, true);
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
+			gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
+
+			gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
+
+			gl.uniform1f(shaderProgram.pointsizeUniform, this.id * gl.scaleFactor);
+			gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
+
+			gl.uniform1f(shaderProgram.roundPointsUniform, false);
+		};
+
+		Via.prototype.setup3DArrayBuffer = function(gl){
+	
+			var vBuffer;
+			vBuffer = gl.createBuffer();
+			gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
+			gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([this.x,this.y,0.0]), gl.STATIC_DRAW);
+			vBuffer.itemSize = 3;
+			vBuffer.numItems = 1;
+			this.pointBuffer = vBuffer;
+	
+		}
+	
+		return Via;
 
 	}
-
-	Via.prototype.clearGL = function(gl, shaderProgram){
-
-		gl.uniform1f(shaderProgram.roundPointsUniform, true);
-
-		gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
-		gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.pointBuffer.itemSize, gl.FLOAT, false, 0, 0);
-
-		gl.uniform1f(shaderProgram.innerRadiusUniform, 0.0);
-
-		gl.uniform1f(shaderProgram.pointsizeUniform, (this.od + this.clearance) * gl.scaleFactor);
-		gl.drawArrays(gl.POINTS, 0, this.pointBuffer.numItems);
-
-
-		gl.uniform1f(shaderProgram.roundPointsUniform, false);
-
-	};
-
-	Via.prototype.cleanupGL = function(gl){
-
-		if(this.pointBuffer){
-			gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer);
-			gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
-			gl.deleteBuffer(this.pointBuffer);
-			this.pointBuffer = null;
-		}
-
-	}
-
-	Via.prototype.setup3DArrayBuffer = function(gl){
-
-		var vBuffer;
-		vBuffer = gl.createBuffer();
-		gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
-		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([this.x,this.y,0.0]), gl.STATIC_DRAW);
-		vBuffer.itemSize = 3;
-		vBuffer.numItems = 1;
-		this.pointBuffer = vBuffer;
-
-	}
-
-	return Via;
-
-});
+);

--- /dev/null
+++ b/src/js/circuit/PCB/parseFlags.js
@@ -1,1 +1,72 @@
+define(
+	[
+	 	"./Thermal"
+	],
+	function(
+		Thermal
+	){
+	
+		var parseFlags = function(defaultFlags, flags){
+	
+			if(!flags) return {};
+	
+			var parsedFlags, split, parts, flag, attr, thrm, i, j, k;
+	
+			split = flags.split("\(.*?\)|(,)");
+	
+			parsedFlags = {};
+	
+			for(attr in defaultFlags)
+	            if(defaultFlags.hasOwnProperty(attr))
+	            	parsedFlags[attr] = defaultFlags[attr];
 
+			for(i = 0; i < split.length; i++){
+	
+				flag = split[i];
+	
+				if(flag.indexOf("thermal") != -1){
+
+					thrm = [];
+	
+					flag = flag.split('(')[1].split(')')[0].split(',');
+					for(j = 0; j < flag.length; j++){
+						// check for #-#
+						// for each add layer
+						//else layer #
+						parts = flag[j].split('-');
+						if(parts.length == 2){
+							parts = flag[j][flag[j].length-1];
+							if(isNaN(parseInt(parts))){
+								parts = flag[j].substring(0, flag[j].length - 1).split('-');
+								attr = parts;
+							} else {
+								parts = flag[j].split('-');
+								attr = 'O';
+							}
+							
+							for(k = parseInt(parts[0]); k <= parseInt(parts[1]); k++){
+								thrm.push(new Thermal(k, attr));
+							}
+						} else {
+							parts = flag[j][flag[j].length-1];
+							if(isNaN(parseInt(parts)))
+								thrm.push(new Thermal(parseInt(flag[j].substring(0, flag[j].length - 1)), parts));
+							else
+								thrm.push(new Thermal(parseInt(flag[j].substring(0, flag[j].length)), 'O'));
+						}
+					}
+					parsedFlags.thermal = thrm;
+				}
+				else
+					parsedFlags[flag] = true;
+	
+			}
+	
+			return parsedFlags;
+	
+		};
+	
+		return parseFlags;
+
+	}
+);