define(["lib/jquery", "lib/underscore", "common/util/binding_list"], function($, _, BindingList) { "use strict"; var douglasPeuker = function(pts, tolerance) { var segment = [pts[0], pts[pts.length-1]]; var ret; //get point at max distance from segment var maxDist = 0; var furthestPoint = -1; for (var p = 0; p<pts.length; p++){ if (pts[p] !== segment[0] && pts[p] !== segment[1]){ var dist = distancePointToSegment(pts[p], segment); if (dist > maxDist){ maxDist = dist; furthestPoint = p; } } } if (furthestPoint !== -1 && maxDist > tolerance){ //point distance exceeds threshold, recurse var t1 = douglasPeuker(pts.slice(0, furthestPoint+1), tolerance); var t2 = douglasPeuker(pts.slice(furthestPoint+1, pts.length), tolerance); ret = []; if (t1.length === 1){ ret.push(t1[0]); } else { // add points 0 ... end-1 for(var i1 = 0; i1<t1.length-1; i1++){ ret.push(t1[i1]); } } for(var i2 = 0; i2<t2.length; i2++){ ret.push(t2[i2]); } return ret; } else { //return the segment ret = []; ret.push(segment[0]); ret.push(segment[1]); return ret; } }; var distancePointToSegment = function(point, segment){ var k = (segment[1][1] - segment[0][1])/(segment[1][0] - segment[0][0]); var m = (segment[0][1])-(k*segment[0][0]); return Math.abs(k*point[0] - point[1] + m)/Math.sqrt(Math.pow(k,2)+1); }; /** * @privconstructor * @class A contour found by a {@link Lens.ContourSearch} * * @property {String} id The ID of this contour. Read-only. * @property {Array} points Array of x,y-coordinates of points in contour. Read-only. * @property {Object} center Center of mass. Read-only. * @property {Number} center.x Center of mass in X dimension. * @property {Number} center.y Center of mass in Y dimension. * @property {Number} center.z The Z dimension value for the center of mass. (as distance from model) * @property {Object} moments Image moments of the contour. (see: http://en.wikipedia.org/wiki/Image_moment) Read-only. * @property {Array} contourChildren All the child contours of this one. * @property {Number} contourClassification Gesture classification. * * @name Contour */ var Contour = function(id, points, cm, moments_, contourChildren, contourClassification){ /** @alias Contour.prototype */ var self = {}; var moveSubscribers = BindingList(); var endSubscribers = BindingList(); var center = (cm === undefined) ? {} : cm; var moments = moments_; var classification = contourClassification; var children = []; if (contourChildren!==undefined) { children = contourChildren.map(function(child) { return Contour(child.id, child.points, child.cm, child.moments, child.children, child.classification); }); } Object.freeze(center); Object.freeze(points); Object.freeze(moments); Object.freeze(children); /** * Updates the points in the contour * @param {Array} newPoints The new points in the contour. * @param {Object} newCm The new center of mass object with x,y,[z] coordinates * @param {Object} newMoments The new images moments of this contour. * @param {Array} newChildren The new children for this contour. * @param {Number} newClassification The new gesture classification. * @private */ self._update = function(newPoints, newCm, newMoments, newChildren, newClassification){ points = newPoints; center = (newCm === undefined) ? {} : newCm; moments = newMoments; classification = newClassification; var children = []; if (newChildren!==undefined) { children = newChildren.map(function(child) { return Contour(child.id, child.points, child.cm, child.moments, child.children, child.classification); }); } Object.freeze(center); Object.freeze(points); Object.freeze(moments); Object.freeze(children); moveSubscribers.callAll(self); }; /** * Marks this contours as having disappeared. * @private */ self._end = function (){ endSubscribers.callAll(self); }; /** * Registers a function to be called whenever this contour moves. * @param {Function} fn The function to call. Gets this contour as an * argument. * @return {Binding} A Binding that allows this handler to be * cleared */ self.moved = function(fn){ moveSubscribers.add(fn); }; /** * Registers a function to be called whenever this contour disappears from screen. * @param {Function} fn The function to call. Gets this contour as an * argument. * @return {Binding} A Binding that allows this handler to be * cleared */ self.disappeared = function(fn){ endSubscribers.add(fn); }; /** * Returns the points for the contour, simplified by the * Ramer–Douglas–Peucker algorithm: * http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm * * @param {Number} [epsilon] Tolerance factor. The lower this is, the * more points will be dicarded. Defaults to * 5. */ self.simplifiedPoints = function(epsilon) { return douglasPeuker(points, (epsilon || 5)); }; self.id = id; // define getters Object.defineProperty(self, "points", {get: function() { return points; }}); Object.defineProperty(self, "center", {get: function() { return center; }}); Object.defineProperty(self, "moments", {get: function() { return moments; }}); Object.defineProperty(self, "classification", {get: function() { return classification; }}); Object.defineProperty(self, "children", {get: function() { return children; }}); Object.freeze(self); return self; }; return Contour; });