jui.define("util.math", [ "util.base" ], function(_) { // 2x1 or 3x1 or ?x1 형태의 매트릭스 연산 function matrix(a, b) { var m = []; for(var i = 0, len = a.length; i < len; i++) { var sum = 0; for(var j = 0, len2 = a[i].length; j < len2; j++) { sum += a[i][j] * b[j]; } m.push(sum); } return m; } // 2x2 or 3x3 or ?x? 형태의 매트릭스 연산 function deepMatrix(a, b) { var m = [], nm = []; for(var i = 0, len = b.length; i < len; i++) { m[i] = []; nm[i] = []; } for(var i = 0, len = b.length; i < len; i++) { for(var j = 0, len2 = b[i].length; j < len2; j++) { m[j].push(b[i][j]); } } for(var i = 0, len = m.length; i < len; i++) { var mm = matrix(a, m[i]); for(var j = 0, len2 = mm.length; j < len2; j++) { nm[j].push(mm[j]); } } return nm; } function matrix3d(a, b) { var m = new Float32Array(4); m[0] = a[0][0] * b[0] + a[0][1] * b[1] + a[0][2] * b[2] + a[0][3] * b[3]; m[1] = a[1][0] * b[0] + a[1][1] * b[1] + a[1][2] * b[2] + a[1][3] * b[3]; m[2] = a[2][0] * b[0] + a[2][1] * b[1] + a[2][2] * b[2] + a[2][3] * b[3]; m[3] = a[3][0] * b[0] + a[3][1] * b[1] + a[3][2] * b[2] + a[3][3] * b[3]; return m; } function deepMatrix3d(a, b) { var nm = [ new Float32Array(4), new Float32Array(4), new Float32Array(4), new Float32Array(4) ]; var m = [ new Float32Array([b[0][0],b[1][0],b[2][0],b[3][0]]), new Float32Array([b[0][1],b[1][1],b[2][1],b[3][1]]), new Float32Array([b[0][2],b[1][2],b[2][2],b[3][2]]), new Float32Array([b[0][3],b[1][3],b[2][3],b[3][3]]) ]; nm[0][0] = a[0][0] * m[0][0] + a[0][1] * m[0][1] + a[0][2] * m[0][2] + a[0][3] * m[0][3]; nm[1][0] = a[1][0] * m[0][0] + a[1][1] * m[0][1] + a[1][2] * m[0][2] + a[1][3] * m[0][3]; nm[2][0] = a[2][0] * m[0][0] + a[2][1] * m[0][1] + a[2][2] * m[0][2] + a[2][3] * m[0][3]; nm[3][0] = a[3][0] * m[0][0] + a[3][1] * m[0][1] + a[3][2] * m[0][2] + a[3][3] * m[0][3]; nm[0][1] = a[0][0] * m[1][0] + a[0][1] * m[1][1] + a[0][2] * m[1][2] + a[0][3] * m[1][3]; nm[1][1] = a[1][0] * m[1][0] + a[1][1] * m[1][1] + a[1][2] * m[1][2] + a[1][3] * m[1][3]; nm[2][1] = a[2][0] * m[1][0] + a[2][1] * m[1][1] + a[2][2] * m[1][2] + a[2][3] * m[1][3]; nm[3][1] = a[3][0] * m[1][0] + a[3][1] * m[1][1] + a[3][2] * m[1][2] + a[3][3] * m[1][3]; nm[0][2] = a[0][0] * m[2][0] + a[0][1] * m[2][1] + a[0][2] * m[2][2] + a[0][3] * m[2][3]; nm[1][2] = a[1][0] * m[2][0] + a[1][1] * m[2][1] + a[1][2] * m[2][2] + a[1][3] * m[2][3]; nm[2][2] = a[2][0] * m[2][0] + a[2][1] * m[2][1] + a[2][2] * m[2][2] + a[2][3] * m[2][3]; nm[3][2] = a[3][0] * m[2][0] + a[3][1] * m[2][1] + a[3][2] * m[2][2] + a[3][3] * m[2][3]; nm[0][3] = a[0][0] * m[3][0] + a[0][1] * m[3][1] + a[0][2] * m[3][2] + a[0][3] * m[3][3]; nm[1][3] = a[1][0] * m[3][0] + a[1][1] * m[3][1] + a[1][2] * m[3][2] + a[1][3] * m[3][3]; nm[2][3] = a[2][0] * m[3][0] + a[2][1] * m[3][1] + a[2][2] * m[3][2] + a[2][3] * m[3][3]; nm[3][3] = a[3][0] * m[3][0] + a[3][1] * m[3][1] + a[3][2] * m[3][2] + a[3][3] * m[3][3]; return nm; } function inverseMatrix3d(me) { var te = [ new Float32Array(4), new Float32Array(4), new Float32Array(4), new Float32Array(4) ]; var n11 = me[0][0], n12 = me[0][1], n13 = me[0][2], n14 = me[0][3]; var n21 = me[1][0], n22 = me[1][1], n23 = me[1][2], n24 = me[1][3]; var n31 = me[2][0], n32 = me[2][1], n33 = me[2][2], n34 = me[2][3]; var n41 = me[3][0], n42 = me[3][1], n43 = me[3][2], n44 = me[3][3]; te[0][0] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; te[0][1] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; te[0][2] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; te[0][3] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; te[1][0] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; te[1][1] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; te[1][2] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; te[1][3] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; te[2][0] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; te[2][1] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; te[2][2] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; te[2][3] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; te[3][0] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; te[3][1] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; te[3][2] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; te[3][4] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; var det = 1 / (n11 * te[0][0] + n21 * te[0][1] + n31 * te[0][2] + n41 * te[0][3]); if(det === 0) { te = [ new Float32Array([ 1, 0, 0, 0 ]), new Float32Array([ 0, 1, 0, 0 ]), new Float32Array([ 0, 0, 1, 0 ]), new Float32Array([ 0, 0, 0, 1 ]) ]; } else { te[0][0] *= det; te[0][1] *= det; te[0][2] *= det; te[0][3] *= det; te[1][0] *= det; te[1][1] *= det; te[1][2] *= det; te[1][3] *= det; te[2][0] *= det; te[2][1] *= det; te[2][2] *= det; te[2][3] *= det; te[3][0] *= det; te[3][1] *= det; te[3][2] *= det; te[3][4] *= det; } return te; } /** * @class util.math * * Math Utility * * @singleton */ var self = { /** * @method rotate * * 2d rotate * * @param {Number} x * @param {Number} y * @param {Number} radian roate 할 radian * @return {Object} * @return {Number} return.x 변환된 x * @return {Number} return.y 변환된 y * */ rotate : function(x, y, radian) { return { x : x * Math.cos(radian) - y * Math.sin(radian), y : x * Math.sin(radian) + y * Math.cos(radian) } }, resize : function(maxWidth, maxHeight, objectWidth, objectHeight) { var ratio = objectHeight / objectWidth; if (objectWidth >= maxWidth && ratio <= 1) { objectWidth = maxWidth; objectHeight = maxHeight * ratio; } else if (objectHeight >= maxHeight) { objectHeight = maxHeight; objectWidth = maxWidth / ratio; } return { width : objectWidth, height : objectHeight}; }, /** * @method radian * * convert degree to radian * * @param {Number} degree * @return {Number} radian */ radian : function(degree) { return degree * Math.PI / 180; }, /** * @method degree * * convert radian to degree * * @param {Number} radian * @return {Number} degree */ degree : function(radian) { return radian * 180 / Math.PI; }, angle : function(x1, y1, x2, y2) { var dx = x2 - x1, dy = y2 - y1; return Math.atan2(dy, dx); }, /** * @method interpolateNumber * * a, b 의 중간값 계산을 위한 callback 함수 만들기 * * @param {Number} a first value * @param {Number} b second value * @return {Function} */ interpolateNumber : function(a, b) { var dist = (b - a); return function(t) { return a + dist * t; } }, // 중간값 round 해서 계산하기 interpolateRound : function(a, b) { var dist = (b - a); return function(t) { return Math.round(a + dist * t); } }, getFixed : function (a, b) { var aArr = (a+"").split("."); var aLen = (aArr.length < 2) ? 0 : aArr[1].length; var bArr = (b+"").split("."); var bLen = (bArr.length < 2) ? 0 : bArr[1].length; return (aLen > bLen) ? aLen : bLen; }, fixed : function (fixed) { var fixedNumber = this.getFixed(fixed, 0); var pow = Math.pow(10, fixedNumber); var func = function (value) { return Math.round(value * pow) / pow; }; func.plus = function (a, b) { return Math.round((a * pow) + (b * pow)) / pow; }; func.minus = function (a, b) { return Math.round((a * pow) - (b * pow)) / pow; }; func.multi = function (a, b) { return Math.round((a * pow) * (b * pow)) / (pow*pow); }; func.div = function (a, b) { var result = (a * pow) / (b * pow); var pow2 = Math.pow(10, this.getFixed(result, 0)); return Math.round(result*pow2) / pow2; }; func.remain = function (a, b) { return Math.round((a * pow) % (b * pow)) / pow; }; return func; }, round: function (num, fixed) { var fixedNumber = Math.pow(10, fixed); return Math.round(num * fixedNumber) / fixedNumber; }, plus : function (a, b) { var pow = Math.pow(10, this.getFixed(a, b)); return Math.round((a * pow) + (b * pow)) / pow; }, minus : function (a, b) { var pow = Math.pow(10, this.getFixed(a, b)); return Math.round((a * pow) - (b * pow)) / pow; }, multi : function (a, b) { var pow = Math.pow(10, this.getFixed(a, b)); return Math.round((a * pow) * (b * pow)) / (pow*pow); }, div : function (a, b) { var pow = Math.pow(10, this.getFixed(a, b)); var result = (a * pow) / (b * pow); var pow2 = Math.pow(10, this.getFixed(result, 0)); return Math.round(result*pow2) / pow2; }, remain : function (a, b) { var pow = Math.pow(10, this.getFixed(a, b)); return Math.round((a * pow) % (b * pow)) / pow; }, /** * 특정 구간의 값을 자동으로 계산 * * @param {Object} min * @param {Object} max * @param {Object} ticks * @param {Object} isNice */ nice : function(min, max, ticks, isNice) { isNice = isNice || false; if (min > max) { var _max = min; var _min = max; } else { var _min = min; var _max = max; } var _ticks = ticks; var _tickSpacing = 0; var _range = []; var _niceMin; var _niceMax; function niceNum(range, round) { var exponent = Math.floor(Math.log(range) / Math.LN10); var fraction = range / Math.pow(10, exponent); var nickFraction; if (round) { if (fraction < 1.5) niceFraction = 1; else if (fraction < 3) niceFraction = 2; else if (fraction < 7) niceFraction = 5; else niceFraction = 10; } else { if (fraction <= 1) niceFraction = 1; else if (fraction <= 2) niceFraction = 2; else if (fraction <= 5) niceFraction = 5; else niceFraction = 10; //console.log(niceFraction) } return niceFraction * Math.pow(10, exponent); } function caculate() { _range = (isNice) ? niceNum(_max - _min, false) : _max - _min; _tickSpacing = (isNice) ? niceNum(_range / _ticks, true) : _range / _ticks; _niceMin = (isNice) ? Math.floor(_min / _tickSpacing) * _tickSpacing : _min; _niceMax = (isNice) ? Math.floor(_max / _tickSpacing) * _tickSpacing : _max; } caculate(); return { min : _niceMin, max : _niceMax, range : _range, spacing : _tickSpacing } }, matrix: function(a, b) { if(_.typeCheck("array", b[0])) { return deepMatrix(a, b); } return matrix(a, b); }, matrix3d: function(a, b) { if(b[0] instanceof Array || b[0] instanceof Float32Array) { return deepMatrix3d(a, b); } return matrix3d(a, b); }, inverseMatrix3d: function(a) { return inverseMatrix3d(a); }, scaleValue: function(value, minValue, maxValue, minScale, maxScale) { // 최소/최대 값이 같을 경우 처리 minValue = (minValue == maxValue) ? 0 : minValue; var range = maxScale - minScale, tg = range * getPer(); function getPer() { var range = maxValue - minValue, tg = value - minValue, per = tg / range; return per; } return tg + minScale; } } return self; }, null, true);