From 389dcc27d2a4ec8eaf58db1163b01b816caafc57 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Fri, 29 May 2015 13:51:18 +0200 Subject: [PATCH] added progress bar --- library/cal.js | 2329 +++++++++++++++++++++++++++++ settings/user_settings.json | 2 +- slice_test.html | 5 +- src/slicer.js | 45 +- src/slicerworker.js | 16 +- webworker/worker.js | 20 +- webworker_benchmark_resutaten.txt | 19 + webworker_test.html | 10 +- 8 files changed, 2419 insertions(+), 27 deletions(-) create mode 100644 library/cal.js create mode 100644 webworker_benchmark_resutaten.txt diff --git a/library/cal.js b/library/cal.js new file mode 100644 index 0000000..dbe3a06 --- /dev/null +++ b/library/cal.js @@ -0,0 +1,2329 @@ +//canvas cheat sheet +//http://cheatsheetworld.com/programming/html5-canvas-cheat-sheet/ + +//TODO +//Global Composite Operation +//Linear Gradient +//Radial Gradient +//Bezier Curve +//Circle +//Touch support + +"use strict"; +var CAL = { + name: "Canvas Abstraction Layer", + version: "1.0", + author: "Casper Lamboo", + contact: "casperlamboo@gmail.com" +}; + +CAL.Math = { + clamb: function (value, min, max) { + return (value > min) ? ((value < max) ? value : max) : min; + }, + randomInt: function (min, max) { + return Math.round(CAL.Math.random(min, max)); + }, + random: function (min, max) { + min = min || 0; + max = max === undefined ? 1 : max; + + return Math.random()*(max - min) + min; + }, + sign: function (value) { + return (value > 0) ? 1 : ((value < 0) ? -1 : 0); + }, + lineCollision: function (v1, v2, v3, v4) { + //bron: http://mathworld.wolfram.com/Line-LineIntersection.html + var intersection = new CAL.Vector( + ((v1.x*v2.y-v1.y*v2.x)*(v3.x-v4.x)-(v1.x-v2.x)*(v3.x*v4.y-v3.y*v4.x)) / ((v1.x-v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x-v4.x)), + ((v1.x*v2.y-v1.y*v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x*v4.y-v3.y*v4.x)) / ((v1.x-v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x-v4.x)) + ); + + var line1 = v1.subtract(v2).length(); + var line2 = v3.subtract(v4).length(); + + var a = line1 >= v1.subtract(intersection).length(); + var b = line1 >= v2.subtract(intersection).length(); + var c = line2 >= v3.subtract(intersection).length(); + var d = line2 >= v4.subtract(intersection).length(); + + return (a && b && c && d) ? intersection : false; + } +}; + +CAL.Easings = { + bounceEaseOut: function (dt, b, c, d) { + if ((dt /= d) < (1 / 2.75)) { + return c * (7.5625 * dt * dt) + b; + } + else if (dt < (2 / 2.75)) { + return c * (7.5625 * (dt -= (1.5 / 2.75)) * dt + 0.75) + b; + } + else if (dt < (2.5 / 2.75)) { + return c * (7.5625 * (dt -= (2.25 / 2.75)) * dt + 0.9375) + b; + } + else { + return c * (7.5625 * (dt -= (2.625 / 2.75)) * dt + 0.984375) + b; + } + }, + easeIn: function (dt, b, c, d) { + return c * (dt /= d) * dt + b; + }, + easeOut: function (dt, b, c, d) { + return -c * (dt /= d) * (dt - 2) + b; + }, + easeInOut: function (dt, b, c, d) { + if ((dt /= d / 2) < 1) { + return c / 2 * dt * dt + b; + } + return -c / 2 * ((--dt) * (dt - 2) - 1) + b; + }, + strongEaseIn: function (dt, b, c, d) { + return c * (dt /= d) * dt * dt * dt * dt + b; + }, + strongEaseOut: function (dt, b, c, d) { + return c * (( dt = dt / d - 1) * dt * dt * dt * dt + 1) + b; + }, + strongEaseInOut: function (dt, b, c, d) { + if ((dt /= d / 2) < 1) { + return c / 2 * dt * dt * dt * dt * dt + b; + } + return c / 2 * ((dt -= 2) * dt * dt * dt * dt + 2) + b; + }, + linear: function (dt, b, c, d) { + return c * dt / d + b; + } +}; + + +//this doesn't work, everything is an instance of Object in JavaScript +/*Object.prototype.clone = function () { + var object = {}; + for (var i in this) { + var element = this[i]; + + object[i] = element.clone ? element.clone() : element; + } + return object; +}; +Object.prototype.foreach = function (callback) { + for (var i in this) { + var element = this[i]; + + callback(element, i); + } +};*/ + + +Array.prototype.foreach = function (callback, scope) { + for (var i = 0; i < this.length; i ++) { + var element = this[i]; + + if ((scope !== undefined) ? callback.call(scope, element, i) : callback(element, i)) { + break; + } + } +}; +Array.prototype.foreachReverse = function (callback, scope) { + for (var i = this.length-1; i >= 0; i --) { + var element = this[i]; + + if ((scope !== undefined) ? callback.call(scope, element, i) : callback(element, i)) { + break; + } + } +}; +Array.prototype.max = function () { + var max = -Infinity; + this.foreach(function (element) { + if (element > max) { + max = element; + } + }); + return max; +}; +Array.prototype.min = function () { + var min = Infinity; + this.foreach(function (element) { + if (element < min) { + min = element; + } + }); + return min; +}; +Array.prototype.clone = function () { + var array = []; + this.foreach(function (element) { + array.push((element.clone !== undefined) ? element.clone() : element); + }); + return array; +}; +Array.prototype.remove = function () { + for (var i = 0; i < arguments.length; i ++) { + var element = arguments[i]; + var index = this.indexOf(element); + if (index !== -1) { + this.splice(index, 1); + } + } +}; + +var requestAnimFrame = (function () { + return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function (callback) { + setTimeout(callback, 1000/60); + }; +})(); + +CAL.Vector = function (x, y) { + this.x = x || 0; + this.y = y || 0; +}; +CAL.Vector.prototype.add = function (vector) { + var x = this.x + vector.x; + var y = this.y + vector.y; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.subtract = function (vector) { + var x = this.x - vector.x; + var y = this.y - vector.y; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.scale = function (scalar) { + var x = this.x * scalar; + var y = this.y * scalar; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.rotate = function (angle) { + var cos = Math.cos(angle); + var sin = Math.sin(angle); + + var x = cos*this.x - sin*this.y; + var y = sin*this.x + cos*this.y; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.multiply = function (vector) { + var x = this.x * vector.x; + var y = this.y * vector.y; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.length = function () { + return Math.sqrt(this.x*this.x + this.y*this.y); +}; +CAL.Vector.prototype.normal = function () { + return new CAL.Vector(this.y, -this.x); +}; +CAL.Vector.prototype.normalize = function () { + var length = this.length(); + + var x = this.x/length; + var y = this.y/length; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.angle = function () { + return Math.atan2(this.y, this.x); +}; +CAL.Vector.prototype.dot = function (vector) { + return this.x * vector.x + this.y * vector.y; +}; +CAL.Vector.prototype.cross = function (vector) { + return this.x * vector.y - this.y * vector.x; +}; +CAL.Vector.prototype.round = function () { + var x = Math.round(this.x); + var y = Math.round(this.y); + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.applyMatrix = function (matrix) { + var m = matrix.matrix; + + var x = m[0]*this.x + m[1]*this.y + m[2]; + var y = m[3]*this.x + m[4]*this.y + m[5]; + + return new CAL.Vector(x, y); +}; +CAL.Vector.prototype.clone = function () { + return new CAL.Vector(this.x, this.y); +}; +CAL.Vector.prototype.draw = function (context, x, y) { + var end = new CAL.Vector(this.x + x, this.y + y); + var arrowOrigin = new CAL.Vector(x, y).add(this.subtract(this.normalize().scale(10))); + var left = this.normal().normalize().scale(10).add(arrowOrigin); + var right = this.normal().normalize().scale(-10).add(arrowOrigin); + + context.beginPath(); + context.moveTo(x, y); + context.lineTo(end.x, end.y); + context.moveTo(left.x, left.y); + context.lineTo(end.x, end.y); + context.lineTo(right.x, right.y); + + context.stroke(); +}; + +CAL.Matrix = function (options) { + options = options || {}; + + if (options.matrix !== undefined && options.sx !== undefined && options.sy !== undefined && options.rotation !== undefined && options.x !== undefined && options.y !== undefined) { + this.matrix = options.matrix; + this._sx = options.sx; + this._sy = options.sy; + this._rotation = options.rotation; + this._x = options.x; + this._y = options.y; + } + else if (options instanceof Array || options.matrix) { + this.matrix = options.matrix || options; + + this._sx = CAL.Math.sign(this.matrix[0])*Math.sqrt(Math.pow(this.matrix[0], 2) + Math.pow(this.matrix[1], 2)); + this._sy = CAL.Math.sign(this.matrix[4])*Math.sqrt(Math.pow(this.matrix[3], 2) + Math.pow(this.matrix[4], 2)); + this._rotation = Math.atan2(-this.matrix[1], this.matrix[0]); + this._x = this.matrix[2]; + this._y = this.matrix[5]; + //source: http://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix + //BUG doesn't convert right when both sx and sy aren't 1 + } + else { + this._sx = options.sx !== undefined ? options.sx : 1; + this._sy = options.sy !== undefined ? options.sy : 1; + this._x = options.x || 0; + this._y = options.y || 0; + + if (options.rotation === undefined) { + this._rotation = 0; + + this.matrix = [ + this._sx, 0, this._x, + 0, this._sy, this._y + ]; + } + else { + this._rotation = options.rotation; + this.updateMatrix(); + } + } +}; +CAL.Matrix.prototype = { + get sx () { + return this._sx; + }, + set sx (sx) { + this._sx = sx; + this.updateMatrix(); + }, + get sy () { + return this._sy; + }, + set sy (sy) { + this._sy = sy; + this.updateMatrix(); + }, + get rotation () { + return this._rotation; + }, + set rotation (rotation) { + this._rotation = rotation; + this.updateMatrix(); + }, + get x () { + return this._x; + }, + set x (x) { + this._x = this.matrix[2] = x; + }, + get y () { + return this._y; + }, + set y (y) { + this._y = this.matrix[5] = y; + } +}; +CAL.Matrix.prototype.updateMatrix = function () { + this.matrix = [ + this._sx * Math.cos(this._rotation), this._sy * -Math.sin(this._rotation), this._x, + this._sx * Math.sin(this._rotation), this._sy * Math.cos(this._rotation), this._y + ]; +}; +CAL.Matrix.prototype.multiplyMatrix = function (matrix) { + var a = this.matrix; + var b = matrix.matrix; + var translation = new CAL.Vector(b[2], b[5]).applyMatrix(this); + + return new CAL.Matrix([ + a[0]*b[0] + a[3]*b[1], a[1]*b[0] + a[4]*b[1], translation.x, + a[0]*b[3] + a[3]*b[4], a[1]*b[3] + a[4]*b[4], translation.y, + ]); +}; +CAL.Matrix.prototype.inverse = function () { + var m = this.matrix; + return new CAL.Matrix([ + m[4], -m[1], -m[2], + -m[3], m[0], -m[5] + ]); +}; +CAL.Matrix.prototype.translate = function (x, y) { + this.x += x; + this.y += y; + + this.matrix[2] = this.x; + this.matrix[5] = this.y; + + return this; +}; +CAL.Matrix.prototype.setMatrix = function (matrix) { + this.matrix = matrix.matrix.clone(); + this._x = matrix._x; + this._y = matrix._y; + this._sx = matrix._sx; + this._sy = matrix._sy; + this._rotation = matrix._rotation; +}; +CAL.Matrix.prototype.setContext = function (context) { + var m = this.matrix; + context.transform(m[0], m[3], m[1], m[4], m[2], m[5]); +}; +CAL.Matrix.prototype.rotateAroundAbsolute = function (angle, center) { + if (angle !== 0) { + var center = center + .subtract(new CAL.Vector(this.x, this.y)) + .rotate(-this.rotation) + .multiply(new CAL.Vector(1/this.sx, 1/this.sy)); + + this.rotateAroundRelative(angle, center); + } +}; +CAL.Matrix.prototype.rotateAroundRelative = function (angle, center) { + if (angle !== 0) { + var before = center.applyMatrix(this); + + this.rotation += angle; + var after = center.applyMatrix(this); + + var offset = before.subtract(after); + + this._x += offset.x; + this._y += offset.y; + this.updateMatrix(); + } +}; +CAL.Matrix.prototype.clone = function () { + return new CAL.Matrix({ + matrix: this.matrix.clone(), + sx: this._sx, + sy: this._sy, + rotation: this._rotation, + x: this._x, + y: this._y + }); +}; + +CAL.Draw = function (centerX, centerY, numberWidth, numberHeight, options) { + CAL.Matrix.call(this, options); + + this.visible = options.visible !== undefined ? options.visible : true; + this.active = options.active || false; + this.depth = options.depth || 0; + + this.alpha = (typeof options.alpha === "number") ? options.alpha : 1; + + this.centerX = centerX || 0; + this.centerY = centerY || 0; + this.index = 0; + + this.numberWidth = numberWidth || 1; + this.numberHeight = numberHeight || 1; + this.length = this.numberWidth*this.numberHeight; +}; +CAL.Draw.prototype = Object.create(CAL.Matrix.prototype); +CAL.Draw.prototype.draw = function (context, matrix) { + context.save(); + (matrix || this).setContext(context); + context.globalAlpha = this.alpha; + this.drawSimple(context, this.index, 0, 0); + context.restore(); +}; +CAL.Draw.prototype.drawSimple = function (context, number, x, y) { + var sx = (number % this.numberWidth)*this.width; + var sy = Math.floor(number/this.numberWidth)*this.height; + + context.drawImage(this.image, sx, sy, this.width, this.height, x-this.centerX, y-this.centerY, this.width, this.height); +}; +CAL.Draw.prototype.drawAlpha = function (context, number, x, y, alpha) { + context.globalAlpha = alpha; + this.drawSimple(context, number, x, y); + context.globalAlpha = 1; +}; +CAL.Draw.prototype.drawAngle = function (context, number, x, y, angle) { + context.save(); + context.translate(x, y); + context.rotate(angle); + this.drawSimple(context, number, 0, 0); + context.restore(); +}; +CAL.Draw.prototype.drawScale = function (context, number, x, y, width, height) { + var sx = (number % this.numberWidth)*this.width; + var sy = Math.floor(number/this.numberWidth)*this.height; + + context.drawImage(this.image, sx, sy, this.width, this.height, x-this.centerX, y-this.centerY, width, height); +}; +CAL.Draw.prototype.drawContain = function (context, number, x, y, width, height) { + if (width/height > this.width/this.height) { + x = x + (width-height/this.height*this.width)/2; + width = height/this.height*this.width; + this.drawScale(context, number, x, y, width, height); + } + else { + y = y + (height-width/this.width*this.height)/2; + height = width/this.width*this.height; + this.drawScale(context, number, x, y, width, height); + } +}; + +CAL.Surface = function (options) { + options = options || {}; + CAL.Draw.call(this, options.centerX, options.centerY, options.numberWidth, options.numberHeight, options); + + this.clearColor = options.clearColor || false; + + this.setCanvas(options.canvas || document.createElement("canvas")); + + this.setSize(options.width, options.height); +}; +CAL.Surface.prototype = Object.create(CAL.Draw.prototype); +CAL.Surface.prototype.setSize = function (width, height) { + this.image.width = width || this.image.width; + this.image.height = height || this.image.height; + + this.width = this.image.width/this.numberWidth; + this.height = this.image.height/this.numberHeight; +}; +CAL.Surface.prototype.setCanvas = function (canvas) { + this.image = canvas; + this.context = canvas.getContext("2d"); +}; +CAL.Surface.prototype.clear = function () { + if (this.clearColor) { + this.clearColor.setColor(this.context); + this.context.fillRect(0, 0, this.image.width, this.image.height); + } + else { + this.context.clearRect(0, 0, this.image.width, this.image.height); + } +}; +CAL.Surface.prototype.getImageData = function (x, y, width, height) { + var x = x || 0; + var y = y || 0; + var width = width || this.image.width; + var height = height || this.image.height; + + return this.context.getImageData(x, y, width, height); +}; +CAL.Surface.prototype.getDataURL = function () { + return this.image.toDataURL(); +}; +CAL.Surface.prototype.blur = (function () { + //source: http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html + //author: Mario Klingemann + + var mul_table = [512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259]; + var shg_table = [9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 ]; + + return function (radius, x, y, width, height) { + x = x || 0; + y = y || 0; + width = this.image.width || 0; + height = this.image.height || 0; + var imageData = this.getImageData(x, y, width, height); + + var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, + r_out_sum, g_out_sum, b_out_sum, a_out_sum, + r_in_sum, g_in_sum, b_in_sum, a_in_sum, + pr, pg, pb, pa, rbs; + + var div = radius + radius + 1; + var w4 = width << 2; + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; + var pixels = imageData.data; + + var stackStart = {r: 0, g: 0, b: 0, next: null}; + var stack = stackStart; + for (i = 1; i < div; i ++) { + stack = stack.next = {r: 0, g: 0, b: 0, next: null}; + if (i == radiusPlus1) { + var stackEnd = stack; + } + } + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + + yw = yi = 0; + + var mul_sum = mul_table[radius]; + var shg_sum = shg_table[radius]; + + for (y = 0; y < height; y ++) { + r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; + + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi+1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi+2]); + a_out_sum = radiusPlus1 * (pa = pixels[yi+3]); + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i ++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + for (i = 1; i < radiusPlus1; i ++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = (pg = pixels[p+1])) * rbs; + b_sum += (stack.b = (pb = pixels[p+2])) * rbs; + a_sum += (stack.a = (pa = pixels[p+3])) * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack = stack.next; + } + + stackIn = stackStart; + stackOut = stackEnd; + for (x = 0; x < width; x ++) { + pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum; + if (pa === 0) { + pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0; + } + else { + pa = 255 / pa; + pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; + pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa; + pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa; + } + + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2; + + r_in_sum += (stackIn.r = pixels[p]); + g_in_sum += (stackIn.g = pixels[p+1]); + b_in_sum += (stackIn.b = pixels[p+2]); + a_in_sum += (stackIn.a = pixels[p+3]); + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + + stackIn = stackIn.next; + + r_out_sum += (pr = stackOut.r); + g_out_sum += (pg = stackOut.g); + b_out_sum += (pb = stackOut.b); + a_out_sum += (pa = stackOut.a); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += 4; + } + yw += width; + } + + + for (x = 0; x < width; x ++) { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + + yi = x << 2; + r_out_sum = radiusPlus1 * (pr = pixels[yi]); + g_out_sum = radiusPlus1 * (pg = pixels[yi+1]); + b_out_sum = radiusPlus1 * (pb = pixels[yi+2]); + a_out_sum = radiusPlus1 * (pa = pixels[yi+3]); + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for (i = 0; i < radiusPlus1; i ++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + yp = width; + + for (i = 1; i <= radius; i ++) { + yi = (yp + x) << 2; + + r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i); + g_sum += (stack.g = (pg = pixels[yi+1])) * rbs; + b_sum += (stack.b = (pb = pixels[yi+2])) * rbs; + a_sum += (stack.a = (pa = pixels[yi+3])) * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack = stack.next; + + if (i < heightMinus1) { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (y = 0; y < height; y ++) { + p = yi << 2; + pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum; + if (pa > 0) { + pa = 255 / pa; + pixels[p] = ((r_sum * mul_sum) >> shg_sum) * pa; + pixels[p+1] = ((g_sum * mul_sum) >> shg_sum) * pa; + pixels[p+2] = ((b_sum * mul_sum) >> shg_sum) * pa; + } + else { + pixels[p] = pixels[p+1] = pixels[p+2] = 0; + } + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2; + + r_sum += (r_in_sum += (stackIn.r = pixels[p])); + g_sum += (g_in_sum += (stackIn.g = pixels[p+1])); + b_sum += (b_in_sum += (stackIn.b = pixels[p+2])); + a_sum += (a_in_sum += (stackIn.a = pixels[p+3])); + + stackIn = stackIn.next; + + r_out_sum += (pr = stackOut.r); + g_out_sum += (pg = stackOut.g); + b_out_sum += (pb = stackOut.b); + a_out_sum += (pa = stackOut.a); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += width; + } + } + return imageData; + }; +})(); + +CAL.Draw.prototype.drawBlur = (function () { + var surface = new CAL.Surface(); + + return function (context, number, x, y, radius) { + if (radius > 0) { + surface.setSize(this.width + 2*radius, this.height + 2*radius); + this.drawSimple(surface.context, number, this.centerX+radius, this.centerY+radius); + var imageData = surface.blur(radius); + + context.putImageData(imageData, x-this.centerX - radius, y-this.centerY - radius); + + /*surface.setSize(this.width, this.height); + this.drawSimple(surface.context, number, this.centerX, this.centerY); + var imageData = surface.blur(radius); + + context.putImageData(imageData, x-this.centerX, y-this.centerY);*/ + } + else { + this.drawSimple(context, number, x, y); + } + }; +})(); + +CAL.Group = function (options) { + options = options || {}; + CAL.Surface.call(this, options); + + this.active = true; + this.visible = true; + + this.objects = []; + this.useCanvas = options.useCanvas || false; + + this.clearCanvas = true; + this.drawCanvas = true; + + this.mouse = { + x: null, + y: null, + startX: null, + startY: null, + deltaX: null, + deltaY: null, + down: false, + moved: false + }; +}; +CAL.Group.prototype = Object.create(CAL.Surface.prototype); +CAL.Group.prototype.updateEvents = function () { + var scope = this; + this.image.onmousedown = function (event) { + if (scope.useCanvas) { + scope.mouse.x = scope.mouse.startX = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX); + scope.mouse.y = scope.mouse.startY = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY); + scope.mouse.deltaX = 0; + scope.mouse.deltaY = 0; + scope.mouse.down = true; + scope.mouse.moved = false; + + scope.mouseDown(scope.mouse, this); + } + }; + this.image.onmouseup = function (event) { + if (scope.useCanvas) { + scope.mouse.x = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX); + scope.mouse.y = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY); + scope.mouse.down = false; + + scope.mouseUp(scope.mouse, this); + + scope.mouse.startX = null; + scope.mouse.startY = null; + scope.mouse.deltaX = null; + scope.mouse.deltaY = null; + scope.mouse.moved = false; + } + }; + this.image.onmousemove = function (event) { + if (scope.useCanvas) { + scope.mouse.x = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX); + scope.mouse.y = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY); + if (scope.mouse.down) { + scope.mouse.moved = true; + scope.mouse.deltaX = scope.mouse.x - scope.mouse.startX; + scope.mouse.deltaY = scope.mouse.y - scope.mouse.startY; + } + } + }; + //this.image.ontouchstart = function (event) {scope.touchStart(event);}; + //this.image.ontouchmove = function (event) {scope.touchMove(event);}; + //this.image.ontouchend = function (event) {scope.touchEnd(event);}; +}; +CAL.Group.prototype.setCanvas = function (canvas) { + /*this.image.onmousedown = null; + this.image.onmouseup = null; + this.image.onmousemove = null; + this.image.ontouchstart = null; + this.image.ontouchmove = null; + this.image.ontouchend = null;*/ + + this.image = canvas; + this.context = canvas.getContext("2d"); + + this.drawCanvas = true; + this.updateEvents(); +}; +CAL.Group.prototype.add = function () { + for (var i = 0; i < arguments.length; i ++) { + var object = arguments[i]; + if (this.objects.indexOf(object) === -1) { + this.objects.push(object); + if (object.init) { + object.init(this); + } + } + } + this.sort(); + this.drawCanvas = true; +}; +CAL.Group.prototype.remove = function () { + for (var i = 0; i < arguments.length; i ++) { + var object = arguments[i]; + this.objects.remove(object); + if (object.active && object.remove !== undefined) { + object.remove(this); + } + } + this.drawCanvas = true; +}; +CAL.Group.prototype.sort = function () { + this.objects.sort(function (a, b) { + return (a.depth || 0) - (b.depth || 0); + }); +}; +CAL.Group.prototype.keyDown = function (keyCode) { + for (var i = this.objects.length-1; i >= 0; i --) { + var object = this.objects[i]; + if (object.active && object.keyDown !== undefined) { + if (object.keyDown(keyCode, this)) { + break; + } + } + } +}; +CAL.Group.prototype.keyUp = function (keyCode) { + for (var i = this.objects.length-1; i >= 0; i --) { + var object = this.objects[i]; + if (object.active && object.keyUp !== undefined) { + if (object.keyUp(keyCode, this)) { + break; + } + } + } +}; +CAL.Group.prototype.mouseDown = function (mouse) { + for (var i = this.objects.length-1; i >= 0; i --) { + var object = this.objects[i]; + if (object.useCanvas !== true && object.active && object.mouseDown !== undefined) { + if (object.mouseDown(mouse, this)) { + break; + } + } + } +}; +CAL.Group.prototype.mouseUp = function (mouse) { + for (var i = this.objects.length-1; i >= 0; i --) { + var object = this.objects[i]; + if (object.useCanvas !== true && object.active && object.mouseUp !== undefined) { + if (object.mouseUp(mouse, this)) { + break; + } + } + } +}; +CAL.Group.prototype.step = function (deltaTime) { + for (var i = 0; i < this.objects.length; i ++) { + var object = this.objects[i]; + if (object.active && object.step !== undefined) { + object.step(deltaTime, this); + } + } + + if (this.clearCanvas && this.useCanvas) { + this.clear(); + } + + if (this.drawCanvas && this.useCanvas) { + this.draw(); + } + + this.clearCanvas = false; + this.drawCanvas = false; +}; +CAL.Group.prototype.draw = function (context, matrix) { + context = this.useCanvas ? this.context : context; + matrix = this.useCanvas ? this : matrix; + + for (var i = 0; i < this.objects.length; i ++) { + var object = this.objects[i]; + if (object.useCanvas !== true && object.visible && object.draw !== undefined) { + if (object instanceof CAL.Matrix) { + object.draw(context, matrix.multiplyMatrix(object)); + } + else { + object.draw(context, matrix); + } + } + } +}; + +CAL.Scene = function () { + CAL.Group.call(this, {useCanvas: true}); + + this.lastTime = new Date().getTime(); + + this.keysDown = []; + this.focus = true; + + var scope = this; + window.onkeydown = function (event) { + if (!scope.keysDown[event.keyCode]) { + scope.keysDown[event.keyCode] = true; + scope.keyDown(event.keyCode); + } + }; + window.onkeyup = function (event) { + scope.keysDown[event.keyCode] = false; + + scope.keyUp(event.keyCode); + }; + window.onblur = function (event) { + this.focus = false; + }; + window.onfocus = function (event) { + scope.lastTime = new Date().getTime(); + this.focus = true; + }; +}; +CAL.Scene.prototype = Object.create(CAL.Group.prototype); +CAL.Scene.prototype.cycle = function () { + if (this.focus) { + var currentTime = new Date().getTime(); + var deltaTime = currentTime-this.lastTime; + this.lastTime = currentTime; + + this.step(deltaTime); + } +}; +CAL.Scene = new CAL.Scene(); + +CAL.Image = function (source, centerX, centerY, numberWidth, numberHeight, options) { + options = options || {}; + CAL.Draw.call(this, centerX, centerY, numberWidth, numberHeight, options); + + this.image = new Image(); + this.source = source; +}; +CAL.Image.prototype = Object.create(CAL.Draw.prototype); +CAL.Image.prototype.load = function (callback) { + var scope = this; + this.image.onload = function () { + scope.loaded = true; + + scope.width = scope.image.width/scope.numberWidth; + scope.height = scope.image.height/scope.numberHeight; + + if (callback !== undefined) { + callback(); + } + }; + this.image.src = this.source; +}; + +CAL.ImageLoader = function () { + this.images = []; + + for (var i = 0; i < arguments.length; i ++) { + var image = arguments[i]; + this.images.push(image); + } +}; +CAL.ImageLoader.prototype.add = function () { + for (var i = 0; i < arguments.length; i ++) { + var image = arguments[i]; + if (this.images.indexOf(image) === -1) { + this.images.push(image); + } + } +}; +CAL.ImageLoader.prototype.remove = function () { + for (var i = 0; i < arguments.length; i ++) { + var image = arguments[i]; + this.images.remove(image); + } +}; +CAL.ImageLoader.prototype.load = function (callback) { + var imagesToLoad = this.images.length; + for (var i = 0; i < this.images.length; i ++) { + var image = this.images[i]; + image.load(function () { + imagesToLoad --; + if (imagesToLoad === 0 && callback !== undefined) { + callback(); + } + }, this); + }; +}; + +CAL.Color = function () { + if (typeof arguments[0] === "number" && typeof arguments[1] === "number" && typeof arguments[2] === "number") { + this.r = arguments[0]; + this.g = arguments[1]; + this.b = arguments[2]; + this.a = typeof arguments[3] === "number" ? arguments[3] : 1; + } + else if (typeof arguments[0] === "number") { + var hex = Math.floor(arguments[0]); + + this.r = hex >> 16 & 255; + this.g = hex >> 8 & 255; + this.b = hex & 255; + this.a = 1; + } + else { + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 1; + } +}; +CAL.Color.prototype.setStroke = function (context) { + context.strokeStyle = "rgba("+this.r+", "+this.g+", "+this.b+", "+this.a+")"; +}; +CAL.Color.prototype.setFill = function (context) { + context.fillStyle = "rgba("+this.r+", "+this.g+", "+this.b+", "+this.a+")"; +}; +CAL.Color.prototype.setColor = function (context) { + this.setStroke(context); + this.setFill(context); +}; + +CAL.Tween = function (object, attributes, duration, options) { + options = options || {}; + this.visible = false; + this.active = true; + this.depth = -10000; + + this.object = object; + this.attributes = attributes; + this.timer = 0; + this.duration = duration; + this.easing = options.easing || CAL.Easings.linear; + this.callback = options.callback; + + this.begin = {}; + for (var i in attributes) { + this.begin[i] = this.object[i]; + } + + this.change = {}; + for (var i in attributes) { + this.change[i] = attributes[i] - this.begin[i]; + } + + this.drawCanvas = options.drawCanvas !== undefined ? options.drawCanvas : true; + this.clearCanvas = options.clearCanvas !== undefined ? options.clearCanvas : false; +}; +CAL.Tween.prototype.start = function () { + this.t = 0; + this.active = true; +}; +CAL.Tween.prototype.stop = function () { + this.t = 0; + this.active = false; +}; +CAL.Tween.prototype.pause = function () { + this.active = false; +}; +CAL.Tween.prototype.resume = function () { + this.active = true; +}; +CAL.Tween.prototype.step = function (deltaTime, group) { + this.timer += deltaTime; + + if (this.timer < this.duration) { + + for (var i in this.attributes) { + var dt = this.timer; + var d = this.duration; + var b = this.begin[i]; + var c = this.change[i]; + + this.object[i] = this.easing(dt, b, c, d); + } + } + else { + for (var i in this.attributes) { + this.object[i] = this.attributes[i]; + } + if (this.callback !== undefined) { + this.callback(); + } + group.remove(this); + } + + if (this.clearCanvas) { + group.clearCanvas = true; + } + if (this.drawCanvas) { + group.drawCanvas = true; + } +}; + +CAL.TimeLine = function (options) { + options = options || {}; + + this.visible = false; + this.active = true; + this.depth = -10000; + + this.moments = []; + this.autoRemove = (options.autoRemove !== undefined) ? options.autoremove : true; + this.loop = (options.loop !== undefined) ? options.loop : false; + this.t = 0; + + CAL.Scene.add(this); +}; +CAL.TimeLine.prototype = { + addMoment: function (time, callback) { + this.moments.push({ + time: time, + callback: callback + }); + }, + removeMoment: function (remove) { + for (var i = 0; i < this.moments.length; i ++) { + var moment = this.moments[i]; + + if (moment === remove || moment.time === remove || moment.callback === remove) { + this.moments.remove(moment); + } + } + }, + start: function () { + this.t = 0; + this.active = true; + }, + stop: function () { + this.t = 0; + this.active = false; + }, + pause: function () { + this.active = false; + }, + resume: function () { + this.active = true; + }, + step: function (dt) { + var newTime = this.t + dt; + var remove = true; + + for (var i = 0; i < this.moments.length; i ++) { + var moment = this.moments[i]; + if (moment.time >= this.t) { + if (moment.time < newTime) { + moment.callback(); + } + else { + remove = false; + } + } + } + + if (remove && this.loop) { + this.t = 0; + } + else if (remove && this.autoRemove) { + CAL.Scene.remove(this); + } + this.t = newTime; + } +}; + +CAL.Shape = function (options) { + options = options || {}; + CAL.Matrix.call(this, options); + + this.visible = options.visible !== undefined ? options.visible : true; + this.active = false; + this.depth = options.depth || 0; + this.lines = []; + + this.closePath = options.closePath !== undefined ? options.closePath : true; + this.lineColor = options.lineColor !== undefined ? options.lineColor : new CAL.Color(); + this.shapeColor = options.shapeColor !== undefined ? options.shapeColor : new CAL.Color(); + this.lineWidth = options.lineWidth || 1; + this.lineJoin = options.lineJoin || "miter"; + this.lineCap = options.lineCap || "square"; + + this.points = options.points || []; +}; +CAL.Shape.prototype = Object.create(CAL.Matrix.prototype); +CAL.Shape.prototype.addPoint = function () { + for (var i = 0; i < arguments.length; i ++) { + var point = arguments[i]; + this.points.push(point); + } + //this.update(); +}; +CAL.Shape.prototype.hit = function (x, y) { + + for (var i = 0; i < this.points.length; i ++) { + if (new CAL.Vector(x, y).subtract(this.points[i].applyMatrix(this)).dot(this.getNormal(i)) > 0) { + return false; + } + } + + return true; +}; +CAL.Shape.prototype.setContext = function (context, matrix) { + var matrix = matrix || this; + context.beginPath(); + for (var i = 0; i < this.points.length; i ++) { + var point = this.points[i].applyMatrix(matrix); + context.lineTo(point.x, point.y); + } + if (this.closePath) { + context.closePath(); + } +}; +CAL.Shape.prototype.getBoundingBox = function () { + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + + for (var i = 0; i < this.points.length; i ++) { + var point = this.points[i]; + minX = point.x < minX ? point.x : minX; + minY = point.y < minY ? point.y : minY; + maxX = point.x > maxX ? point.x : maxX; + maxY = point.y > maxY ? point.y : maxY; + }; + + return {x: minX, y: minY, width: maxX-minX, height: maxY-minY}; +}; +CAL.Shape.prototype.getNormal = function (i) { + var pointA = this.points[(i+1)%this.points.length].applyMatrix(this); + var pointB = this.points[i].applyMatrix(this); + return pointA.subtract(pointB).normal().normalize(); +}; +CAL.Shape.prototype.clip = function (context, matrix) { + this.setContext(context, matrix); + context.clip(); +}; +CAL.Shape.prototype.fill = function (context, matrix) { + this.setContext(context, matrix); + + this.shapeColor.setFill(context); + + context.fill(); +}; +CAL.Shape.prototype.stroke = function (context, matrix) { + this.setContext(context, matrix); + + context.lineColor = this.lineColor; + context.lineWidth = this.lineWidth; + context.lineJoin = this.lineJoin; + context.lineCap = this.lineCap; + + this.lineColor.setStroke(context); + + context.stroke(); +}; +CAL.Shape.prototype.draw = function (context, matrix) { + this.setContext(context, matrix); + + if (this.shapeColor) { + this.shapeColor.setFill(context); + context.fill(); + } + + if (this.lineColor) { + context.lineColor = this.lineColor; + context.lineWidth = this.lineWidth; + context.lineJoin = this.lineJoin; + context.lineCap = this.lineCap; + this.lineColor.setStroke(context); + context.stroke(); + } +}; + +CAL.BezierPoint = function (position, controlPointA, controlPointB) { + this.position = position || new CAL.Vector(0, 0); + this.controlPointA = controlPointA || new CAL.Vector(0, 0); + this.controlPointB = controlPointB || new CAL.Vector(0, 0); +} +CAL.BezierPoint.prototype.applyMatrix = function (matrix) { + var position = this.position.applyMatrix(matrix); + var controlPointA = this.controlPointA.applyMatrix(matrix); + var controlPointB = this.controlPointB.applyMatrix(matrix); + + return new CAL.BezierPoint(position, controlPointA, controlPointB); +}; +CAL.BezierPoint.prototype.draw = function (context) { + var leftHandle = this.controlPointA.add(this.position); + var rightHandle = this.controlPointB.add(this.position); + + context.strokeStyle = "#09F"; + + context.beginPath(); + context.moveTo(leftHandle.x, leftHandle.y); + context.lineTo(this.position.x, this.position.y); + context.lineTo(rightHandle.x, rightHandle.y); + context.stroke(); + + context.beginPath(); + context.arc(this.position.x, this.position.y, 10, 0, Math.PI*2, true); + context.stroke(); + + context.beginPath(); + context.arc(leftHandle.x, leftHandle.y, 5, 0, Math.PI*2, true); + context.stroke(); + + context.beginPath(); + context.arc(rightHandle.x, rightHandle.y, 5, 0, Math.PI*2, true); + context.stroke(); +}; +/*CAL.Shape = function (options) { + options = options || {}; + CAL.Matrix.call(this, options); + + this.visible = options.visible !== undefined ? options.visible : true; + this.active = false; + this.depth = options.depth || 0; + + this.closePath = options.closePath !== undefined ? options.closePath : true; + this.lineColor = options.lineColor !== undefined ? options.lineColor : new CAL.Color(); + this.shapeColor = options.shapeColor !== undefined ? options.shapeColor : new CAL.Color(); + this.lineWidth = options.lineWidth || 1; + this.lineJoin = options.lineJoin || "miter"; + this.lineCap = options.lineCap || "square"; + + this.points = options.points || []; + this.precision = options.precision || 3; + + this.lines = []; + this.length = 0; + this.closed = false; +} +CAL.Shape.prototype.init = function (group) { + for (var i = 0; i < this.points.length; i ++) { + var point = this.points[i]; + if (point instanceof CAL.Vector) { + point = new CAL.BezierPoint(point); + } + this.points[i] = point; + } + + this.update(); +}; +CAL.Shape.prototype.addPoint = function () { + for (var i = 0; i < arguments.length; i ++) { + var point = arguments[i]; + + if (point instanceof CAL.Vector) { + point = new CAL.BezierPoint(point); + } + if (point instanceof CAL.BezierPoint) { + this.points.push(point); + } + } + + this.update(); +}; +CAL.Shape.prototype.collisionBox = function (vec1, vec2) { + for (var i = 0; i < this.lines.length; i ++) { + var point = this.lines[i]; + + if (point.x > vec1.x && point.y > vec1.y && point.x < vec2.x && point.y < vec2.y) { + return true; + } + } + return false; +}; +CAL.Shape.prototype.getNormal = function (i) { + var pointA = this.lines[(i+1)%this.lines.length].applyMatrix(this); + var pointB = this.lines[i].applyMatrix(this); + return pointA.subtract(pointB).normal().normalize(); +}; +CAL.Shape.prototype.collisionPoint = function (x, y) { + for (var i = 0; i < this.lines.length; i ++) { + if (new CAL.Vector(x, y).subtract(this.lines[i].applyMatrix(this)).dot(this.getNormal(i)) > 0) { + return false; + } + } + + return true; +}; +CAL.Shape.prototype.removePoint = function () { + for (var i = 0; i < arguments.length; i ++) { + + var argument = arguments[i]; + + if (typeof argument === "number") { + var index = argument; + } + else if (argument instanceof CAL.BezierPoint || argument instanceof CAL.Vector) { + var index = this.getPointIndex(argument); + } + + if (index !== -1) { + this.points.splice(index, 1); + } + } + + this.update(); +}; + +CAL.Shape.prototype.getPointIndex = function (point) { + if (point instanceof CAL.BezierPoint) { + return this.points.indexOf(argument); + } + else if (point instanceof CAL.Vector) { + for (i = 0; i < this.points.length; i ++) { + var length = this.points[i].position.subtract(point).length(); + + if (length === 0) { + return i; + } + } + return -1; + } +}; + +CAL.Shape.prototype.numberPoints = function () { + return this.points.length; +}; + +CAL.Shape.prototype.insertPoint = function (point, index) { + if (point instanceof CAL.Vector) { + point = new CAL.BezierPoint(point); + } + if (point instanceof CAL.BezierPoint) { + this.points.splice(index, 0, point); + + this.update(); + } +}; +CAL.Shape.prototype.setClosed = function (closed) { + this.closed = closed; + this.update(); +}; + +CAL.Shape.prototype.setPrecision = function (precision) { + this.precision = precision; + this.update(); +}; + +CAL.Shape.prototype.makeSmooth = function (strength, start, end) { + start = start || 0; + end = end || this.points.length; + + if (this.closed === false) { + start = Math.max(start, 1); + end = Math.min(end, this.points.length-1); + } + else { + start = Math.max(start, 0); + end = Math.min(end, this.points.length); + } + + for (var i = start; i < end; i ++) { + var p0 = this.points[i-1]; + var p1 = this.points[i]; + var p2 = this.points[i+1]; + + if (typeof(p0) === "undefined") { + p0 = this.points[this.points.length-1]; + } + else if (typeof(p2) === "undefined") { + p2 = this.points[0]; + } + + var a = p0.position.subtract(p1.position); + var b = p2.position.subtract(p1.position); + + var length = (a.length()+b.length())/4; + + var direction = a.normalize().add(b.scale(-1).normalize()).normalize(); + + p1.controlPointA = direction.scale(length*strength); + p1.controlPointB = direction.scale(-length*strength); + } + + this.update(); +}; + +CAL.Shape.prototype.calculateBezierPoints = function (p0, p1) { + var array = []; + + if (p0.controlPointB.length() === 0 && p1.controlPointA.length() === 0) { + var angle = p1.position.subtract(p0.position).angle(); + + array.push({ + x: p0.position.x, + y: p0.position.y, + angle: angle + }, { + x: p1.position.x, + y: p1.position.y, + angle: angle + }); + } + else { + var point = { + x: p1.position.x, + y: p1.position.y, + angle: p1.controlPointB.direction() + } + + array = array.concat( + recursiveBezier( + p0.position, + p0.position.add(p0.controlPointB), + p1.position.add(p1.controlPointA), + p1.position, + this.precision + ), + [point] + ); + } + + return array; +}; + +CAL.Shape.prototype.update = function () { + if (this.points.length >= 2) { + if (this.points[0].controlPointB.length() !== 0) { + var point = { + x: this.points[0].position.x, + y: this.points[0].position.y, + angle: this.points[0].controlPointB.direction() + }; + this.lines = [point]; + } + + for (var i = 0; i < this.points.length-1; i ++) { + var p0 = this.points[i]; + var p1 = this.points[i+1]; + + this.lines = this.lines.concat(this.calculateBezierPoints(p0, p1)); + } + + if (this.closed === true) { + + var p0 = this.points[this.points.length-1]; + var p1 = this.points[0]; + + this.lines = this.lines.concat(this.calculateBezierPoints(p0, p1)); + } + + var pathLength = 0; + this.lines[0].position = 0; + + for (var i = 0; i < this.lines.length-1; i ++) { + + var p0 = this.lines[i]; + var p1 = this.lines[i+1]; + var line = new CAL.Vector(p1.x, p1.y).subtract(p0); + pathLength += line.length(); + + p1.position = pathLength; + } + + this.length = pathLength; + } +}; + +CAL.Shape.prototype.getPosition = function (t) { + var t = Math.max(Math.min(t, this.length), 0); + + for (var i = 1; true; i ++) { + var p0 = this.lines[i-1]; + var p1 = this.lines[i]; + + if (p1.position >= t) { + t = (t-p0.position) / (p1.position-p0.position); + + var position = new CAL.Vector(p0.x, p0.y).scale(1-t).add(new CAL.Vector(p1.x, p1.y).scale(t)); + var angle = p0.angle + ((((p1.angle-p0.angle)%360)+540)%360-180)*t; + + return { + x: position.x, + y: position.y, + angle: angle + }; + } + } +}; + +CAL.Shape.prototype.setContextPart = function (context, begin, end) { + var beginPos = this.getPosition(begin); + var endPos = this.getPosition(end); + + context.beginPath(); + context.moveTo(beginPos.x, beginPos.y); + + for (var i = 0; i < this.lines.length; i ++) { + var line = this.lines[i]; + + if (line.position > begin) { + + context.lineTo(line.x, line.y); + if (line.position > end) { + break; + } + } + } + context.lineTo(endPos.x, endPos.y); +}; + +CAL.Shape.prototype.setContext = function (context) { + this.setContextPart(context, 0, this.length); +}; + +CAL.Shape.prototype.setClippingPart = function (context, begin, end) { + context.save(); + this.setContextPart(begin, end); + context.clip(); +}; + +CAL.Shape.prototype.setClipping = function (context) { + this.setClippingPart(context, 0, this.length); +}; + +CAL.Shape.prototype.drawPart = function (context, begin, end, color, width, cap) { + this.setContextPart(context, begin, end); + + context.strokeStyle = color || "black"; + context.lineWidth = width || 1; + context.lineCap = cap || 'butt'; + context.stroke(); +}; +CAL.Shape.prototype.setContext = function (context, matrix) { + var matrix = matrix || this; + context.beginPath(); + for (var i = 0; i < this.lines.length; i ++) { + var point = this.lines[i]//.applyMatrix(matrix); + context.lineTo(point.x, point.y); + } + if (this.closePath) { + context.closePath(); + } +}; + +CAL.Shape.prototype.fill = function (context, matrix) { + this.setContext(context, matrix); + + this.shapeColor.setFill(context); + + context.fill(); +}; +CAL.Shape.prototype.stroke = function (context, matrix) { + this.setContext(context, matrix); + context.lineColor = this.lineColor; + context.lineWidth = this.lineWidth; + context.lineJoin = this.lineJoin; + context.lineCap = this.lineCap; + + this.lineColor.setStroke(context); + + context.stroke(); +}; + +CAL.Shape.prototype.draw = function (context, matrix) { + if (this.shapeColor) { + this.shapeColor.setFill(context); + context.fill(); + } + + if (this.lineColor) { + this.stroke(context, matrix); + } + this.debugDraw(context); +}; +CAL.Shape.prototype.debugDraw = function (context) { + context.beginPath(); + context.lineWidth = 1; + + for (var i = 0; i < this.points.length; i ++) { + var point = this.points[i]; + + point.draw(context); + } + + context.beginPath(); + + for (var i = 0; i < this.lines.length; i ++) { + var point = this.lines[i]; + + context.lineTo(point.x, point.y); + context.arc(point.x, point.y, 2, 0, Math.PI*2, true); + context.lineTo(point.x, point.y); + } + + context.strokeStyle = "black"; + context.stroke(); +}; + +CAL.Shape.prototype.getSize = function () { + var width = 0; + var height = 0; + + for (var i = 0; i < this.lines.length; i ++) { + var line = this.lines[i]; + + width = Math.max(width, Math.ceil(line.x)); + height = Math.max(width, Math.ceil(line.y)); + } + + return { + width: width, + height: height + }; +}; + + +CAL.BezierPoint = function (position, controlPointA, controlPointB) { + this.position = position || new CAL.Vector(0, 0); + this.controlPointA = controlPointA || new CAL.Vector(0, 0); + this.controlPointB = controlPointB || new CAL.Vector(0, 0); +} +CAL.BezierPoint.prototype.applyMatrix = function (matrix) { + var position = this.position.applyMatrix(matrix); + var controlPointA = this.controlPointA.applyMatrix(matrix); + var controlPointB = this.controlPointB.applyMatrix(matrix); + + return new CAL.BezierPoint(position, controlPointA, controlPointB); +}; +CAL.BezierPoint.prototype.draw = function (context) { + var leftHandle = this.controlPointA.add(this.position); + var rightHandle = this.controlPointB.add(this.position); + + context.strokeStyle = "#09F"; + + context.beginPath(); + context.moveTo(leftHandle.x, leftHandle.y); + context.lineTo(this.position.x, this.position.y); + context.lineTo(rightHandle.x, rightHandle.y); + context.stroke(); + + context.beginPath(); + context.arc(this.position.x, this.position.y, 10, 0, Math.PI*2, true); + context.stroke(); + + context.beginPath(); + context.arc(leftHandle.x, leftHandle.y, 5, 0, Math.PI*2, true); + context.stroke(); + + context.beginPath(); + context.arc(rightHandle.x, rightHandle.y, 5, 0, Math.PI*2, true); + context.stroke(); +}; + +function recursiveBezier (p1, p2, p3, p4, precision) { + //source: http://antigrain.com/research/adaptive_bezier/ + //source: http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Derivative + + var p12 = p1.add(p2).scale(0.5); + var p23 = p2.add(p3).scale(0.5); + var p34 = p3.add(p4).scale(0.5); + var p123 = p12.add(p23).scale(0.5); + var p234 = p23.add(p34).scale(0.5); + var p1234 = p123.add(p234).scale(0.5); + + var d = p4.subtract(p1); + + var d2 = Math.abs((p2.x - p4.x) * d.y - (p2.y - p4.y) * d.x); + var d3 = Math.abs((p3.x - p4.x) * d.y - (p3.y - p4.y) * d.x); + + //if (Math.abs(p1.x + p3.x - p2.x - p2.x) + Math.abs(p1.y + p3.y - p2.y - p2.y) + Math.abs(p2.x + p4.x - p3.x - p3.x) + Math.abs(p2.y + p4.y - p3.y - p3.y) <= precision) { + if (Math.pow((d2 + d3), 2) < precision * d.dot(d)) { + + //var t = 0.5; + //var derivative = (p2.subtract(p1)).scale(Math.pow(3*(1-t), 2)).add((p3.subtract(p2)).scale(6*(1-t)*t)).add((p4.subtract(p3)).scale(Math.pow(3*t, 2))); + var derivative = (p2.subtract(p1)).scale(2.25).add((p3.subtract(p2)).scale(1.5)).add((p4.subtract(p3)).scale(2.25)); + + var point = { + x: p1234.x, + y: p1234.y, + angle: derivative.direction() + } + + return [point]; + } + else { + + return [].concat( + recursiveBezier(p1, p12, p123, p1234, precision), + recursiveBezier(p1234, p234, p34, p4, precision) + ); + } +}*/ + +CAL.Text = function (options) { + options = options || {}; + CAL.Matrix.call(this, options); + + this.visible = options.visible !== undefined ? options.visible : true; + this.active = options.active !== undefined ? options.active : true; + this.depth = options.depth || 0; + this.text = options.text || ""; + this.style = options.style || "normal"; + this.variant = options.variant || "normal"; + this.weight = options.weight || "normal"; + this.size = options.size || 12; + this.font = options.font || "Arial"; + + this.textAlign = options.textAlign || "left"; + + this.color = options.color || new CAL.Color(); + this.alpha = typeof options.alpha === "number" ? options.alpha : 1; +}; +CAL.Text.prototype = Object.create(CAL.Matrix.prototype); +CAL.Text.prototype.drawText = function (context, text, x, y) { + context.font = [this.style, this.variant, this.weight, this.size+"px", this.font].join(" "); + context.textAlign = this.textAlign; + this.color.setColor(context); + context.fillText(text, x, y); +}; +CAL.Text.prototype.drawTextAlpha = function (context, text, x, y, apha) { + context.font = [this.style, this.variant, this.weight, this.size+"px", this.font].join(" "); + context.globalAlpha = apha; + this.color.setColor(context); + context.fillText(text, x, y); + context.globalAlpha = 1; +}; +CAL.Text.prototype.draw = function (context, matrix) { + context.save(); + matrix.setContext(context); + context.globalAlpha = this.alpha; + this.drawText(context, this.text, 0, 0); + context.restore(); +}; +CAL.Text.prototype.clone = function () { + return new CAL.text({ + style : this.style, + variant : this.variant, + weight : this.weight, + size : this.size, + font : this.font, + color : this.color.clone() + }); +}; + +CAL.KeyListener = function (options) { + options = options || {}; + + this.visible = false; + this.active = options.active !== undefined ? option.active : true; + this.depth = -10000; + + this.actions = options.actions || {}; + + CAL.Scene.add(this); +}; +CAL.KeyListener.prototype.add = function (key, callback) { + this.actions[key] = callback; +}; +CAL.KeyListener.prototype.keyDown = function (key) { + if (this.actions[key]) { + this.actions[key](); + } +}; + +CAL.Physics = function () { + CAL.Scene.add(this); + this.depth = -1000; + this.active = true; + this.visible = false; + + this.objects = []; + this.manifolds = []; + this.forces = []; +} +CAL.Physics.prototype.keyDown = function () { + //DEBUG REASONS + //EASIER TO DEBUG ON KEYDOWN + + CAL.Scene.clear(); + + this.test(120); + + polygon.debugDraw(CAL.Scene.context); + box.debugDraw(CAL.Scene.context); + +}; +CAL.Physics.prototype.add = function () { + for (var i = 0; i < arguments.length; i ++) { + var object = arguments[i]; + + if (object instanceof CAL.PhysicsObject && this.objects.indexOf(object) === -1) { + for (var j = 0; j < this.objects.length; j ++) { + this.manifolds.push(new CAL.Manifold(object, this.objects[j])); + this.manifolds.push(new CAL.Manifold(this.objects[j], object)); + } + + this.objects.push(object); + } + else if (object instanceof CAL.Force && this.forces.indexOf(object) === -1) { + this.forces.push(object); + } + } +}; +CAL.Physics.prototype.test = function (dt) { + //var dt = 120; + + this.objects.foreach(function (object) { + object.step(dt); + }, this); + + this.forces.foreach(function (force) { + force.step(dt); + }, this); + + this.manifolds.foreach(function (manifold) { + manifold.step(dt); + }, this); + +}; + +CAL.Manifold = function (a, b) { + this.a = a; + this.b = b; + + this.normal; +}; +CAL.Manifold.prototype.checkCollision = function (dt) { + var collisionData = {collision: false}; + + this.b.shape.points.foreach(function (pointA, i) { + var pointA = pointA.applyMatrix(this.b.shape); + var collision = true; + var bestDistance = -Infinity; + var bestNormal; + + this.a.shape.points.foreach(function (pointB, j) { + var pointB = pointB.applyMatrix(this.a.shape); + var normal = this.a.shape.getNormal(j); + var distance = normal.dot(pointA.subtract(pointB)); + + if (distance > 0) { + collision = false; + return true; + } + else if (distance > bestDistance) { + bestDistance = distance; + bestNormal = normal; + } + }, this); + + if (collision) { + collisionData = {collision: true, penetrationDepth: -bestDistance, normal: bestNormal, impactPoint: pointA}; + + return true; + } + + }, this); + + return collisionData; + + /*this.a.shape.points.foreach(function (point, i) { + var normal = this.a.shape.getNormal(i); + var support = this.b.getSupport(normal.scale(-1)); + + var point = point.applyMatrix(this.a.shape); + var distance = normal.dot(support.subtract(point)); + + //if (distance > 0) { + // console.log("test"); + // bestDistance = 1; + // return true; + //} + if (distance > bestDistance || bestDistance === false) { + + //var velocity = this.b.velocity.add(this.b.getPointVelocity(support)); + //if (velocity.dot(normal) < 0 || velocity.length === 0) { + + bestDistance = distance; + bestNormal = normal; + bestImpactPoint = support; + //bestVelocity = velocity; + //} + } + }, this); + + var i = 2; + var normal = this.a.shape.getNormal(i); + normal.scale(100).draw(CAL.Scene.context, 200, 200); + var support = this.b.getSupport(normal.scale(-1)); + + var point = this.a.shape.points[i].applyMatrix(this.a.shape); + var distance = normal.dot(support.subtract(point)); + + console.log(distance); + + CAL.Scene.context.beginPath(); + CAL.Scene.context.arc(support.x, support.y, 10, 0, Math.PI*2); + CAL.Scene.context.stroke(); + + return {collision: bestDistance < 0, penetrationDepth: bestDistance, normal: bestNormal, impactPoint: bestImpactPoint};*/ +}; +CAL.Manifold.prototype.positionalCorrection = function (collisionData) { + var percent = 0.2; // usually 20% to 80% + var slop = 0.1; // usually 0.01 to 0.1 + + var correction = collisionData.normal.scale(Math.max(collisionData.penetrationDepth - slop, 0) / (1/this.a.mass + 1/this.b.mass) * percent); + + var aPos = correction.scale(1/this.a.mass); + this.a.shape._x += aPos.x; + this.a.shape._y += aPos.y; + this.a.shape.updateMatrix(); + + var bPos = correction.scale(1/this.b.mass); + this.b.shape._x -= bPos.x; + this.b.shape._y -= bPos.y; + this.b.shape.updateMatrix(); +}; +CAL.Manifold.prototype.resolveCollision = function (collisionData) { + var restVelocity = this.b.velocity.subtract(this.a.velocity); + + var velAlongNormal = restVelocity.dot(collisionData.normal); + if (velAlongNormal > 0) { + return; + } + var restitution = Math.min(this.a.restitution, this.b.restitution); + + var force = -(1 + restitution) * velAlongNormal / (1/this.a.mass + 1/this.b.mass); + + var impulse = collisionData.normal.scale(force); + + this.a.addForce(impulse.scale(-1), collisionData.impactPoint); + this.b.addForce(impulse, collisionData.impactPoint); +}; +CAL.Manifold.prototype.step = function (dt) { + var collisionData = this.checkCollision(); + + if (collisionData.collision) { + this.resolveCollision(collisionData); + this.positionalCorrection(collisionData); + } +}; + +CAL.PhysicsObject = function (options) { + options = options || {}; + + this.shape = options.shape || new CAL.Shape(); + this.velocity = options.velocity || new CAL.Vector(); + this.angularVelocity = options.angularVelocity !== undefined ? options.angularVelocity : 0; + this.restitution = options.restitution !== undefined ? options.restitution : 0.5; + this.density = options.density || 1; + this.updateMass(); +}; +CAL.PhysicsObject.prototype.calculateBoundingBox = function (matrix) { + matrix = matrix || this.shape; + + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + this.shape.points.foreach(function (point) { + var point = point.applyMatrix(matrix); + + minX = (point.x < minX) ? point.x : minX; + minY = (point.y < minY) ? point.y : minY; + maxX = (point.x > maxX) ? point.x : maxX; + maxY = (point.y > maxY) ? point.y : maxY; + }, this); + + return {minX: minX, minY: minY, maxX: maxX, maxY: maxY}; +}; +CAL.PhysicsObject.prototype.addForce = function (impulse, contactVector) { + this.velocity = this.velocity.add(impulse.scale(1.0/this.mass)); + + /*if (contactVector !== undefined) { + this.angularVelocity += contactVector.subtract(this.centerOfMass.applyMatrix(this.shape)).cross(impulse)*(1.0/this.inertia); + //this.angularVelocity = contactVector.subtract(this.centerOfMass.applyMatrix(this.shape)).cross(impulse)*(1.0/this.inertia); + }*/ +}; +CAL.PhysicsObject.prototype.pointCollision = function (point) { + var point = point + .subtract(new CAL.Vector(this.shape.x, this.shape.y)) + .rotate(-this.shape.rotation) + .multiply(new CAL.Vector(1/this.shape.sx, 1/this.shape.sy)) + .subtract(new CAL.Vector(this.collisionMask.offsetX, this.collisionMask.offsetY)); + + if (point.x < 0) return false; + if (point.y < 0) return false; + if (point.x > this.collisionMask.width) return false; + if (point.y > this.collisionMask.height) return false; + + var i = Math.round(point.y)*this.collisionMask.width + Math.round(point.x); + return this.collisionMask.data[i]; +}; +CAL.PhysicsObject.prototype.setDensity = function (density) { + this.density = density; + this.mass = this.area*density; + this.inertia = this.mass*this.area; +}; +CAL.PhysicsObject.prototype.updateMass = function () { + var boundingBox = this.calculateBoundingBox(new CAL.Matrix()); + var surface = new CAL.Surface({x: boundingBox.maxX - boundingBox.minX, y: boundingBox.maxY - boundingBox.minY}); + this.shape.clip(surface.context, new CAL.Matrix({x: -boundingBox.minX, y: -boundingBox.minY})); + surface.context.fillStyle = "black"; + surface.context.fillRect(0, 0, surface.width, surface.height); + + var imageData = surface.getImageData(); + + var point = new CAL.Vector(); + var area = 0; + this.collisionMask = { + data: [], + width: imageData.width, + height: imageData.height, + offsetX: boundingBox.minX, + offsetY: boundingBox.minY + }; + for (var i = 0; i < imageData.data.length; i += 4) { + var alpha = imageData.data[i + 3]; + if (alpha > 0) { + point = point.add(new CAL.Vector(i/4 % imageData.width, Math.floor(i/4/imageData.width))); + area ++; + this.collisionMask.data.push(true); + } + else { + this.collisionMask.data.push(false); + } + } + this.area = area/**this.sx*this.sy*/; + this.mass = area*this.density; + this.inertia = this.mass*area; + this.centerOfMass = point.scale(1/area).add(new CAL.Vector(boundingBox.minX, boundingBox.minY)); +}; +CAL.PhysicsObject.prototype.getSupport = function (n) { + var bestProjection = -Infinity; + var bestPoint; + + for (var i = 0; i < this.shape.points.length; i ++) { + var point = this.shape.points[i].applyMatrix(this.shape); + var projection = point.dot(n); + + if (projection > bestProjection) { + bestPoint = point; + bestProjection = projection; + } + } + + return bestPoint; +}; +CAL.PhysicsObject.prototype.getPointVelocity = function (point) { + + var relativePoint = point.subtract(this.centerOfMass.applyMatrix(this.shape)); + var rotatedPoint = relativePoint.rotate(this.angularVelocity); + + return rotatedPoint.subtract(relativePoint); +}; +CAL.PhysicsObject.prototype.findCollisionFace = function (point) { + +}; +CAL.PhysicsObject.prototype.step = function (dt) { + //this.rotateAroundRelative(this.torque*dt, this.centerOfMass); + + //this.angularVelocity += torque * (1.0 / this.inertia) * dt + + /*if (this.velocity.length < 0.0001) { + this.velocity = new CAL.Vector(); + } + if (Math.abs(this.angularVelocity) < 0.00001) { + this.angularVelocity = 0; + }*/ + + this.shape._x += this.velocity.x*dt; + this.shape._y += this.velocity.y*dt; + this.shape.updateMatrix(); + + //this.shape.rotateAroundRelative(this.angularVelocity*dt, this.centerOfMass); +}; + +CAL.PhysicsObject.prototype.debugDraw = function (context) { + context.lineWidth = 1; + context.strokeStyle = "black"; + + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + context.beginPath(); + this.shape.points.foreach(function (point) { + var point = point.applyMatrix(this.shape); + + context.lineTo(point.x, point.y); + context.arc(point.x, point.y, 3, 0, 2*Math.PI*2); + context.lineTo(point.x, point.y); + + minX = (point.x < minX) ? point.x : minX + minY = (point.y < minY) ? point.y : minY + maxX = (point.x > maxX) ? point.x : maxX + maxY = (point.y > maxY) ? point.y : maxY + }, this); + context.closePath(); + context.stroke(); + + this.shape.points.foreach(function (point, i) { + var point = point.applyMatrix(this.shape); + var text = new CAL.Text(); + text.drawText(context, i, point.x + 10, point.y - 10); + //this.getPointVelocity(point).scale(1000).draw(context, point.x, point.y); + }, this); + + + context.beginPath(); + context.moveTo(minX, minY); + context.lineTo(minX, maxY); + context.lineTo(maxX, maxY); + context.lineTo(maxX, minY); + context.closePath(); + + var centerOfMass = this.centerOfMass.applyMatrix(this.shape); + context.moveTo(centerOfMass.x-5, centerOfMass.y-5); + context.lineTo(centerOfMass.x+5, centerOfMass.y+5); + context.moveTo(centerOfMass.x+5, centerOfMass.y-5); + context.lineTo(centerOfMass.x-5, centerOfMass.y+5); + context.strokeStyle = "red"; + context.stroke(); + + var center = this.centerOfMass.applyMatrix(this.shape); + this.velocity.scale(1000).draw(context, center.x, center.y); +}; + +CAL.Force = function (options) { + + this.objects = options.objects || []; + this.velocity = options.velocity || new CAL.Vector(); +} +CAL.Force.prototype.add = function () { + for (var i = 0; i < arguments.length; i ++) { + var argument = arguments[i]; + + this.objects.push(argument); + } +}; +CAL.Force.prototype.step = function (dt) { + + for (var i = 0; i < this.objects.length; i ++) { + var object = this.objects[i]; + + object.velocity = object.velocity.add(this.velocity.scale(dt)); + } +}; diff --git a/settings/user_settings.json b/settings/user_settings.json index 1b71721..a2f81d7 100644 --- a/settings/user_settings.json +++ b/settings/user_settings.json @@ -3,7 +3,7 @@ "printer.bottomFlowRate": 2.0, "printer.bottomLayerSpeed": 35.0, "printer.bottomThickness": 0.4, - "printer.fillSize": 10.0, + "printer.fillSize": 5.0, "printer.firstLayerSlow": true, "printer.layerHeight": 0.2, "printer.retraction.amount": 3.0, diff --git a/slice_test.html b/slice_test.html index 618988f..b02b71a 100644 --- a/slice_test.html +++ b/slice_test.html @@ -72,14 +72,11 @@ function init () { mesh.position.x = 100; mesh.position.z = 100; - //scene.add(mesh); + scene.add(mesh); mesh.updateMatrix(); var slicer = new D3D.Slicer().setMesh(mesh.geometry, mesh.matrix); - var mesh = new THREE.Mesh(slicer.geometry, material); - scene.add(mesh); - //var canvas = document.getElementById("canvas"); //var context = canvas.getContext("2d"); diff --git a/src/slicer.js b/src/slicer.js index 2b43f42..3809e28 100644 --- a/src/slicer.js +++ b/src/slicer.js @@ -14,14 +14,21 @@ D3D.Slicer = function () { "use strict"; + + this.progress = { + totalLayers: 0, + sliceLayer: 0, + dataLayer: 0, + gcodeLayer: 0 + }; }; D3D.Slicer.prototype.setMesh = function (geometry, matrix) { "use strict"; //convert buffergeometry to geometry; - //if (geometry instanceof THREE.BufferGeometry) { - // geometry = new THREE.Geometry().fromBufferGeometry(geometry); - //} + if (geometry instanceof THREE.BufferGeometry) { + geometry = new THREE.Geometry().fromBufferGeometry(geometry); + } //remove duplicate vertices; /* @@ -51,6 +58,11 @@ D3D.Slicer.prototype.setMesh = function (geometry, matrix) { return this; }; +D3D.Slicer.prototype.updateProgress = function () { + if (this.onProgress !== undefined) { + this.onProgress(this.progress); + } +}; D3D.Slicer.prototype.createLines = function () { "use strict"; @@ -70,8 +82,7 @@ D3D.Slicer.prototype.createLines = function () { self.lines.push({ line: new THREE.Line3(self.geometry.vertices[a], self.geometry.vertices[b]), connects: [], - normals: [], - ignore: 0 + normals: [] }); } @@ -168,12 +179,12 @@ D3D.Slicer.prototype.slice = function (layerHeight, height) { var normal = a.sub(b).normal().normalize(); var faceNormal = faceNormals[Math.floor(j/2)]; - if (normal.dot(faceNormal) > 0) { + //if (normal.dot(faceNormal) > 0) { break; - } - else { - index = -1; - } + //} + //else { + // index = -1; + //} } else { index = -1; @@ -237,6 +248,9 @@ D3D.Slicer.prototype.slice = function (layerHeight, height) { else { break; } + + this.progress.sliceLayer = layer; + this.updateProgress(); } return slices; @@ -351,6 +365,9 @@ D3D.Slicer.prototype.slicesToData = function (slices, printer) { }); } } + + this.progress.dataLayer = layer; + this.updateProgress(); } return data; @@ -481,6 +498,9 @@ D3D.Slicer.prototype.dataToGcode = function (data, printer) { gcode = gcode.concat(sliceToGcode(layerPart.insets)); gcode = gcode.concat(sliceToGcode(layerPart.fill)); } + + this.progress.gcodeLayer = layer; + this.updateProgress(); } gcode = gcode.concat(printer.getEndCode()); @@ -523,6 +543,11 @@ D3D.Slicer.prototype.getGcode = function (printer) { var layerHeight = printer.config["printer.layerHeight"]; var dimensionsZ = printer.config["printer.dimensions.z"]; + this.progress.totalLayers = Math.floor(this.geometry.boundingBox.max.y / layerHeight); + this.progress.sliceLayer = 0; + this.progress.dataLayer = 0; + this.progress.gcodeLayer = 0; + var start = new Date().getTime(); var slices = this.slice(layerHeight, dimensionsZ); var end = new Date().getTime(); diff --git a/src/slicerworker.js b/src/slicerworker.js index 1ac0b0a..c233a68 100644 --- a/src/slicerworker.js +++ b/src/slicerworker.js @@ -3,9 +3,21 @@ D3D.SlicerWorker = function () { this.worker = new Worker('webworker/worker.js'); + var progressBar = document.getElementById("progress-bar"); + var scope = this; this.worker.addEventListener('message', function (event) { - console.log(event); - gcode = event.data; + + switch (event.data["cmd"]) { + case "PROGRESS": + var progress = event.data["progress"]; + var procent = (progress.sliceLayer + progress.dataLayer + progress.gcodeLayer) / progress.totalLayers / 3; + progressBar.style.width = procent * 100 + "%"; + break; + + case "GCODE": + gcode = event.data["gcode"]; + break; + } }, false); } D3D.SlicerWorker.prototype.setSettings = function (USER_SETTINGS, PRINTER_SETTINGS) { diff --git a/webworker/worker.js b/webworker/worker.js index af28510..5818184 100644 --- a/webworker/worker.js +++ b/webworker/worker.js @@ -7,6 +7,16 @@ importScripts("../src/slicer.js"); var printer = new D3D.Printer(); var slicer = new D3D.Slicer(); +slicer.onProgress = function (progress) { + "use strict"; + + self.postMessage({ + "cmd": "PROGRESS", + "progress": progress + }); + + //console.log(progress); +}; self.addEventListener("message", function (event) { "use strict"; @@ -29,17 +39,15 @@ self.addEventListener("message", function (event) { case "SLICE": var gcode = slicer.getGcode(printer); - self.postMessage(gcode); + self.postMessage({ + "cmd": "GCODE", + "gcode": gcode + }); break; case "CLOSE": self.close(); break; - default: - - //console.log(event); - - break; } }); \ No newline at end of file diff --git a/webworker_benchmark_resutaten.txt b/webworker_benchmark_resutaten.txt new file mode 100644 index 0000000..bc3bdda --- /dev/null +++ b/webworker_benchmark_resutaten.txt @@ -0,0 +1,19 @@ +----------- RESULTATEN TORUS KNOT -------------- + +Buffer Geometry x 0.53 ops/sec ±134.70% (8 runs sampled) + +Buffer Geometry Transferrable Object x 63.04 ops/sec ±2.19% (67 runs sampled) + +Geometry JSON x 0.50 ops/sec ±6.55% (6 runs sampled) + +Fastest is "Buffer Geometry Transferrable Object" + +----------- RESULTATEN KUBUS -------------- + +Buffer Geometry x 5,360 ops/sec ±19.42% (85 runs sampled) + +Buffer Geometry Transferrable Object x 1,535 ops/sec ±10.17% (35 runs sampled) + +Geometry JSON x 3,831 ops/sec ±2.52% (82 runs sampled) + +Fastest is "Buffer Geometry" \ No newline at end of file diff --git a/webworker_test.html b/webworker_test.html index fa3b32b..9c08303 100644 --- a/webworker_test.html +++ b/webworker_test.html @@ -12,36 +12,38 @@ Doedel Drie Dee || Webworker Test +