/** @file This file contains the functions to adjust an existing polygon. */
/**
* Creates the adjusting event
* @constructor
* @param {string} dom_attach - The html element where the polygon lives
* @param {array} x - The x coordinates for the polygon points
* @param {array} y - The y coordinates for the polygon points
* @param {string} obj_name - The name of the adjusted_polygon
* @param {function} ExitFunction - the_function to execute once adjusting is done
* @param {float} scale - Scaling factor for polygon points
*/
function AdjustEvent(dom_attach,x,y,obj_name,ExitFunction,scale) {
/****************** Private variables ************************/
// ID of DOM element to attach to:
this.dom_attach = dom_attach;
this.scale_button_pressed = false;
// Polygon:
this.x = x;
this.y = y;
// Object name:
this.obj_name = obj_name;
// Function to call when event is finished:
this.ExitFunction = ExitFunction;
// Scaling factor for polygon points:
this.scale = scale;
// Boolean indicating whether a control point has been edited:
this.editedControlPoints = false;
// Boolean indicating whether a control point is being edited:
this.isEditingControlPoint = false;
// Boolean indicating whether a scaling point is being edited:
this.isEditingScalingPoint = false;
// Boolean indicating whether the center of mass of the polygon is being
// adjusted:
this.isMovingCenterOfMass = false;
// Index into which control point has been selected:
this.selectedControlPoint;
// Index into which scaling point has been selected:
this.selectedScalingPoint;
// Location of center of mass:
this.center_x;
this.center_y;
// Element ids of drawn control points:
this.control_ids = null;
this.scalepoints_ids = null;
// Element id of drawn center point:
this.center_id = null;
// ID of drawn polygon:
this.polygon_id;
/****************** Public functions ************************/
/** This function starts the adjusting event: */
this.StartEvent = function() {
console.log('LabelMe: Starting adjust event...');
// Draw polygon:
this.polygon_id = this.DrawPolygon(this.dom_attach,this.x,this.y,this.obj_name,this.scale);
select_anno.polygon_id = this.polygon_id;
FillPolygon(this.polygon_id);
// Show control points:
this.ShowControlPoints();
// Show center of mass:
this.ShowCenterOfMass();
// Set mousedown action to stop adjust event when user clicks on canvas:
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousedown({obj: this},function(e) {
return e.data.obj.StopAdjustEvent();
});
$(window).keydown({obj: this}, function (e){
if (!e.data.obj.scale_button_pressed && e.keyCode == 17 && !e.data.obj.isEditingControlPoint){
e.data.obj.RemoveScalingPoints();
e.data.obj.RemoveControlPoints();
e.data.obj.RemoveCenterOfMass();
e.data.obj.ShowScalingPoints();
e.data.obj.scale_button_pressed = true;
}
});
$(window).keyup({obj: this}, function (e){
if (e.keyCode == 17 && !e.data.obj.isEditingControlPoint){
e.data.obj.scale_button_pressed = false;
e.data.obj.RemoveScalingPoints();
e.data.obj.RemoveControlPoints();
e.data.obj.RemoveCenterOfMass();
e.data.obj.ShowControlPoints();
e.data.obj.ShowCenterOfMass();
}
});
};
/** This function stops the adjusting event and calls the ExitFunction: */
this.StopAdjustEvent = function() {
// Remove polygon:
$('#'+this.polygon_id).remove();
// Remove key press action
$(window).unbind("keydown");
$(window).unbind("keyup");
// Remove control points and center of mass point:
this.RemoveControlPoints();
this.RemoveCenterOfMass();
this.RemoveScalingPoints();
console.log('LabelMe: Stopped adjust event.');
// Call exit function:
this.ExitFunction(this.x,this.y,this.editedControlPoints);
};
/** This function shows the scaling points for a polygon */
this.ShowScalingPoints = function (){
if(!this.scalepoints_ids) this.scalepoints_ids = new Array();
for (var i = 0; i < this.x.length; i++){
this.scalepoints_ids.push(DrawPoint(this.dom_attach,this.x[i],this.y[i],'r="5" fill="#0000ff" stroke="#ffffff" stroke-width="2.5"',this.scale));
}
for (var i = 0; i < this.scalepoints_ids.length; i++) $('#'+this.scalepoints_ids[i]).mousedown({obj: this,point: i},function(e) {
return e.data.obj.StartMoveScalingPoint(e.data.point);
});
}
/** This function removes the displayed scaling points for a polygon */
this.RemoveScalingPoints = function (){
if(this.scalepoints_ids) {
for(var i = 0; i < this.scalepoints_ids.length; i++) $('#'+this.scalepoints_ids[i]).remove();
this.scalepoints_ids = null;
}
}
/** This function shows the control points for a polygon */
this.ShowControlPoints = function() {
if(!this.control_ids) this.control_ids = new Array();
for(var i = 0; i < this.x.length; i++) {
// Draw control point:
this.control_ids.push(DrawPoint(this.dom_attach,this.x[i],this.y[i],'r="5" fill="#00ff00" stroke="#ffffff" stroke-width="2.5"',this.scale));
// Set action:
$('#'+this.control_ids[i]).mousedown({obj: this,point: i},function(e) {
return e.data.obj.StartMoveControlPoint(e.data.point);
});
}
};
/** This function removes the displayed control points for a polygon */
this.RemoveControlPoints = function() {
if(this.control_ids) {
for(var i = 0; i < this.control_ids.length; i++) $('#'+this.control_ids[i]).remove();
this.control_ids = null;
}
};
/** This function shows the middle grab point for a polygon. */
this.ShowCenterOfMass = function() {
var MarkerSize = 8;
if(this.x.length==1) MarkerSize = 6;
// Get center point for polygon:
this.CenterOfMass(this.x,this.y);
// Draw center point:
this.center_id = DrawPoint(this.dom_attach,this.center_x,this.center_y,'r="' + MarkerSize + '" fill="red" stroke="#ffffff" stroke-width="' + MarkerSize/2 + '"',this.scale);
// Set action:
$('#'+this.center_id).mousedown({obj: this},function(e) {
return e.data.obj.StartMoveCenterOfMass();
});
};
/** This function removes the middle grab point for a polygon */
this.RemoveCenterOfMass = function() {
if(this.center_id) {
$('#'+this.center_id).remove();
this.center_id = null;
}
};
/** This function is called when one scaling point is clicked
* It prepares the polygon for scaling.
* @param {int} i - the index of the scaling point being modified
*/
this.StartMoveScalingPoint = function(i) {
if(!this.isEditingScalingPoint) {
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousemove({obj: this},function(e) {
return e.data.obj.MoveScalingPoint(e.originalEvent);
});
$('#body').mouseup({obj: this},function(e) {
return e.data.obj.StopMoveScalingPoint(e.originalEvent);
});
this.selectedScalingPoint = i;
this.isEditingScalingPoint = true;
this.editedControlPoints = true;
}
};
/** This function is called when one scaling point is being moved
* It computes the position of the scaling point in relation to the polygon's center of mass
* and resizes the polygon accordingly
* @param {event} event - Indicates a point is being moved and the index of such point
*/
this.MoveScalingPoint = function(event) {
var x = GetEventPosX(event);
var y = GetEventPosY(event);
if(this.isEditingScalingPoint && this.scale_button_pressed) {
var origx, origy, pointx, pointy, prx, pry;
pointx = this.x[this.selectedScalingPoint];
pointy = this.y[this.selectedScalingPoint];
this.CenterOfMass(this.x,this.y);
var sx = pointx - this.center_x;
var sy = pointy - this.center_y;
if (sx < 0) origx = Math.max.apply(Math, this.x);
else origx = Math.min.apply(Math, this.x);
if (sy < 0) origy = Math.max.apply(Math, this.y);
else origy = Math.min.apply(Math, this.y);
prx = (x-origx)/(pointx-origx);
pry = (y-origy)/(pointy-origy);
pry = prx;
if (prx <= 0 || pry <= 0 ) return;
for (var i = 0; i < this.x.length; i++){
// Set point:
var dx = (this.x[i] - origx)*prx;
var dy = (this.y[i] - origy)*pry;
x = origx + dx;
y = origy + dy;
this.x[i] = Math.max(Math.min((x/this.scale),main_media.width_orig),1);
this.y[i] = Math.max(Math.min((y/this.scale),main_media.height_orig),1);
}
// Remove polygon and redraw:
$('#'+this.polygon_id).parent().remove();
$('#'+this.polygon_id).remove();
this.polygon_id = this.DrawPolygon(this.dom_attach,this.x,this.y,this.obj_name,this.scale);
select_anno.polygon_id = this.polygon_id;
// Adjust control points:
this.RemoveScalingPoints();
this.ShowScalingPoints();
}
};
/** This function is called when one scaling point stops being moved
* It updates the xml with the new coordinates of the polygon.
* @param {event} event - Indicates a point is being moved and the index of such point
*/
this.StopMoveScalingPoint = function(event) {
console.log('Moving scaling point');
if(this.isEditingScalingPoint) {
this.MoveScalingPoint(event);
FillPolygon(this.polygon_id);
this.isEditingScalingPoint = false;
select_anno.pts_x = this.x;
select_anno.pts_y = this.y;
if (video_mode) main_media.UpdateObjectPosition(select_anno);
// Set action:
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousedown({obj: this},function(e) {
return e.data.obj.StopAdjustEvent();
});
}
};
/** This function is called when one control point is clicked
* @param {int} i - the index of the control point being modified
*/
this.StartMoveControlPoint = function(i) {
if(!this.isEditingControlPoint) {
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousemove({obj: this},function(e) {
return e.data.obj.MoveControlPoint(e.originalEvent);
});
$('#body').mouseup({obj: this},function(e) {
return e.data.obj.StopMoveControlPoint(e.originalEvent);
});
this.RemoveCenterOfMass();
this.selectedControlPoint = i;
this.isEditingControlPoint = true;
this.editedControlPoints = true;
}
};
/** This function is called when one control point is being moved
* @param {event} event - Indicates a point is being moved and the index of such point
*/
this.MoveControlPoint = function(event) {
if(this.isEditingControlPoint) {
var x = GetEventPosX(event);
var y = GetEventPosY(event);
// Set point:
this.x[this.selectedControlPoint] = Math.max(Math.min(Math.round(x/this.scale),main_media.width_orig),1);
this.y[this.selectedControlPoint] = Math.max(Math.min(Math.round(y/this.scale),main_media.height_orig),1);
this.originalx = this.x;
this.originaly = this.y;
// Remove polygon and redraw:
$('#'+this.polygon_id).parent().remove();
$('#'+this.polygon_id).remove();
this.polygon_id = this.DrawPolygon(this.dom_attach,this.x,this.y,this.obj_name,this.scale);
select_anno.polygon_id = this.polygon_id;
// Adjust control points:
this.RemoveControlPoints();
this.ShowControlPoints();
}
};
/** This function is called when one control point stops being moved
* It updates the xml with the new coordinates of the polygon.
* @param {event} event - Indicates a point is being moved and the index of such point
*/
this.StopMoveControlPoint = function(event) {
console.log('Moving control point');
if(this.isEditingControlPoint) {
this.MoveControlPoint(event);
FillPolygon(this.polygon_id);
this.ShowCenterOfMass();
this.isEditingControlPoint = false;
select_anno.pts_x = this.x;
select_anno.pts_y = this.y;
if (video_mode) main_media.UpdateObjectPosition(select_anno);
// Set action:
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousedown({obj: this},function(e) {
return e.data.obj.StopAdjustEvent();
});
}
};
/** This function is called when the middle grab point is clicked
* It prepares the polygon for moving.
*/
this.StartMoveCenterOfMass = function() {
if(!this.isMovingCenterOfMass) {
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousemove({obj: this},function(e) {
return e.data.obj.MoveCenterOfMass(e.originalEvent);
});
$('#body').mouseup({obj: this},function(e) {
return e.data.obj.StopMoveCenterOfMass(e.originalEvent);
});
this.RemoveControlPoints();
this.isMovingCenterOfMass = true;
this.editedControlPoints = true;
}
};
/** This function is called when the middle grab point is being moved
* @param {event} event - Indicates the middle grab point is moving
* It modifies the control points to be consistent with the polygon shift
*/
this.MoveCenterOfMass = function(event) {
if(this.isMovingCenterOfMass) {
var x = GetEventPosX(event);
var y = GetEventPosY(event);
// Get displacement:
var dx = Math.round(x/this.scale)-this.center_x;
var dy = Math.round(y/this.scale)-this.center_y;
// Adjust dx,dy to make sure we don't go outside of the image:
for(var i = 0; i < this.x.length; i++) {
dx = Math.max(this.x[i]+dx,1)-this.x[i];
dy = Math.max(this.y[i]+dy,1)-this.y[i];
dx = Math.min(this.x[i]+dx,main_media.width_orig)-this.x[i];
dy = Math.min(this.y[i]+dy,main_media.height_orig)-this.y[i];
}
// Adjust polygon and center point:
for(var i = 0; i < this.x.length; i++) {
this.x[i] = Math.round(this.x[i]+dx);
this.y[i] = Math.round(this.y[i]+dy);
}
this.center_x = Math.round(this.scale*(dx+this.center_x));
this.center_y = Math.round(this.scale*(dy+this.center_y));
// Remove polygon and redraw:
$('#'+this.polygon_id).parent().remove();
$('#'+this.polygon_id).remove();
this.polygon_id = this.DrawPolygon(this.dom_attach,this.x,this.y,this.obj_name,this.scale);
select_anno.polygon_id = this.polygon_id;
// Redraw center of mass:
this.RemoveCenterOfMass();
this.ShowCenterOfMass();
}
};
/** This function is called when the middle grab point stops being moved
* It updates the xml with the new coordinates of the polygon.
* @param {event} event - Indicates the middle grab point is being moved and the index of such point
*/
this.StopMoveCenterOfMass = function(event) {
if(this.isMovingCenterOfMass) {
// Move to final position:
this.MoveCenterOfMass(event);
// Refresh control points:
this.RemoveControlPoints();
this.RemoveCenterOfMass();
this.ShowControlPoints();
this.ShowCenterOfMass();
FillPolygon(this.polygon_id);
this.isMovingCenterOfMass = false;
select_anno.pts_x = this.x;
select_anno.pts_y = this.y;
if (video_mode) main_media.UpdateObjectPosition(select_anno);
// Set action:
$('#'+this.dom_attach).unbind();
$('#'+this.dom_attach).mousedown({obj: this},function(e) {
return e.data.obj.StopAdjustEvent();
});
}
};
/*************** Helper functions ****************/
/** Compute center of mass for a polygon given array of points (x,y):
*/
this.CenterOfMass = function(x,y) {
var N = x.length;
// Center of mass for a single point:
if(N==1) {
this.center_x = x[0];
this.center_y = y[0];
return;
}
// The center of mass is the average polygon edge midpoint weighted by
// edge length:
this.center_x = 0; this.center_y = 0;
var perimeter = 0;
for(var i = 1; i <= N; i++) {
var length = Math.round(Math.sqrt(Math.pow(x[i-1]-x[i%N], 2) + Math.pow(y[i-1]-y[i%N], 2)));
this.center_x += length*Math.round((x[i-1] + x[i%N])/2);
this.center_y += length*Math.round((y[i-1] + y[i%N])/2);
perimeter += length;
}
this.center_x /= perimeter;
this.center_y /= perimeter;
};
this.DrawPolygon = function(dom_id,x,y,obj_name,scale) {
if(x.length==1) return DrawFlag(dom_id,x[0],y[0],obj_name,scale);
var attr = 'fill="none" stroke="' + HashObjectColor(obj_name) + '" stroke-width="4"';
return DrawPolygon(dom_id,x,y,obj_name,attr,scale);
};
}