Files
2025-02-26 14:49:25 +07:00

630 lines
20 KiB
JavaScript

/**
* Definition of shape
*/
ovm.shape.angle = function() {
var handleSize = 5;
var angles = [];
this.curr_angle = null;
this.isCurrentDrawingAngle = false;
this.initAngle = function() {
this.curr_angle = new ovm.angle();
};
this.angleStarted = function() {
return this.curr_angle!=null;
};
this.drawAngle = function(canvasCtx) {
canvasCtx.save();
this.draw(canvasCtx,this.curr_angle);
canvasCtx.restore();
};
this.draw = function(canvasCtx,graphic) {
canvasCtx.strokeStyle=canvasCtx.fillStyle='orange';
canvasCtx.lineWidth='2';
canvasCtx.beginPath();
canvasCtx.moveTo(graphic.xA,graphic.yA);
canvasCtx.lineTo(graphic.x0,graphic.y0);
if(graphic.xB!=undefined && graphic.yB!=undefined) {
canvasCtx.lineTo(graphic.xB,graphic.yB);
}
canvasCtx.stroke();
canvasCtx.closePath();
if(graphic.arcAngle1!=undefined && graphic.arcAngle2!=undefined) {
canvasCtx.save();
canvasCtx.beginPath();
canvasCtx.moveTo(graphic.x0,graphic.y0);
canvasCtx.arc(graphic.x0,graphic.y0,graphic.arcRadius,graphic.arcAngle1,graphic.arcAngle2,graphic.counterClockwise);
canvasCtx.closePath();
canvasCtx.stroke();
canvasCtx.restore();
}
// Handles
canvasCtx.strokeStyle=canvasCtx.fillStyle=graphic.active ? 'red' : 'white';
canvasCtx.beginPath();
canvasCtx.fillRect(graphic.xA-handleSize,graphic.yA-handleSize,handleSize*2,handleSize*2);
canvasCtx.fillRect(graphic.x0-handleSize,graphic.y0-handleSize,handleSize*2,handleSize*2);
canvasCtx.fillRect(graphic.xB-handleSize,graphic.yB-handleSize,handleSize*2,handleSize*2);
canvasCtx.closePath();
// Text
if(graphic.angle!=undefined) {
canvasCtx.beginPath();
canvasCtx.fillStyle = !graphic.txtActive ? "maroon" : "blue";
canvasCtx.globalAlpha = 0.7;
canvasCtx.font = "14px Arial";
var text = canvasCtx.measureText(graphic.angle);
canvasCtx.fillRect(graphic.textX,graphic.textY,Math.ceil(text.width)+5,20);
canvasCtx.globalAlpha = 0.9;
canvasCtx.fillStyle = "white";
//canvasCtx.fillText(graphic.angle,graphic.textX+2,graphic.textY+15);
if(state.hflip && !state.vflip && !state.rotate!=0 && !this.isCurrentDrawingAngle){
canvasCtx.save();
canvasCtx.translate(drawCanvas.width,0);
canvasCtx.scale(-1,1);
canvasCtx.fillText(graphic.angle,(drawCanvas.width - graphic.textX)-60,graphic.textY+14);
canvasCtx.restore();
}
if(state.vflip && !state.hflip && !state.rotate!=0 && !this.isCurrentDrawingAngle){
canvasCtx.save();
canvasCtx.translate(0,drawCanvas.height);
canvasCtx.scale(1,-1);
canvasCtx.fillText(graphic.angle, graphic.textX,(drawCanvas.height -graphic.textY));
canvasCtx.restore();
}
if(!state.vflip && state.hflip && (state.rotate===90 || state.rotate===180 || state.rotate===270) && !this.isCurrentDrawingAngle){
if(state.rotate===90 || state.rotate===180 || state.rotate===270) {
canvasCtx.save();
canvasCtx.translate(0,drawCanvas.height);
canvasCtx.scale(1,-1);
canvasCtx.fillText(graphic.angle,graphic.textX,(drawCanvas.height -graphic.textY));
canvasCtx.restore();
}
}
if(state.vflip && !state.hflip && (state.rotate===90 || state.rotate===180 || state.rotate===270) && !this.isCurrentDrawingAngle){
if(state.rotate===90 || state.rotate===180 || state.rotate===270) {
canvasCtx.save();
canvasCtx.translate(drawCanvas.width,0);
canvasCtx.scale(-1,1);
canvasCtx.fillText(graphic.angle, (drawCanvas.width - graphic.textX)-60, graphic.textY+14);
canvasCtx.restore();
}
}
if(state.rotate!=0 && !state.vflip && !state.hflip && !this.isCurrentDrawingAngle) {
if(state.rotate===180) {
canvasCtx.save();
canvasCtx.translate(drawCanvas.width/2,drawCanvas.height/2);
canvasCtx.rotate(Math.PI);
canvasCtx.translate(-drawCanvas.width/2,-drawCanvas.height/2);
canvasCtx.fillText(graphic.angle, (drawCanvas.width -graphic.textX)-60,(drawCanvas.height -graphic.textY));
canvasCtx.restore();
}
else {
canvasCtx.fillText(graphic.angle,graphic.textX+2,graphic.textY+14);
}
}
if((state.rotate===0 || state.rotate===90 || state.rotate===180 || state.rotate===270) && state.vflip && state.hflip && !this.isCurrentDrawingAngle) {
if(state.rotate===0) {
canvasCtx.save();
canvasCtx.translate(drawCanvas.width,drawCanvas.height);
canvasCtx.scale(-1,-1);
canvasCtx.fillText(graphic.angle, (drawCanvas.width -graphic.textX)-60,(drawCanvas.height -graphic.textY));
canvasCtx.restore();
}
else {
canvasCtx.fillText(graphic.angle,graphic.textX+2,graphic.textY+14);
}
}
//if((!state.rotate===0 || !state.rotate===90 || !state.rotate===180 || !state.rotate===270) && state.hflip && state.vflip) {
//console.log("if(state.hflip && state.vflip) called state.rotate="+state.rotate);
//canvasCtx.save();
//canvasCtx.translate(drawCanvas.width,drawCanvas.height);
//canvasCtx.scale(-1,-1);
//canvasCtx.fillText(graphic.angle, (drawCanvas.width -graphic.textX)-60,(drawCanvas.height -graphic.textY));
//canvasCtx.restore();
//}
if(!state.hflip && !state.vflip && !state.rotate!=0) {
canvasCtx.fillText(graphic.angle,graphic.textX+2,graphic.textY+14);
}
if(this.isCurrentDrawingAngle) {
canvasCtx.fillText(graphic.angle,graphic.textX+2,graphic.textY+14);
}
canvasCtx.closePath();
// Reference Lines
if(graphic.textX!=(graphic.xB+15)) {
canvasCtx.save();
canvasCtx.beginPath();
canvasCtx.globalAlpha = 0.5;
canvasCtx.strokeStyle = "yellow";
canvasCtx.setLineDash([10,7]);
var closestPt = this.getClosestAnchor([{x:graphic.textX,y:graphic.textY},{x:graphic.textX+Math.ceil(text.width+5),y:graphic.textY},{x:graphic.textX,y:graphic.textY+20},{x:graphic.textX+Math.ceil(text.width+5),y:graphic.textY+20}],{x:graphic.refX,y:graphic.refY});
canvasCtx.moveTo(closestPt.x, closestPt.y);
canvasCtx.lineTo(graphic.refX,graphic.refY);
canvasCtx.stroke();
canvasCtx.closePath();
canvasCtx.restore();
}
}
};
this.createNewAngle = function() {
this.curr_angle.generateTrueGraphic();
angles.push(this.curr_angle);
this.curr_angle = null;
};
this.drawData = function(ctx) {
ctx.save();
for(var i=0;i<angles.length;i++) {
var angle_i = angles[i];
this.draw(ctx,this.viewPortGraphic(angle_i));
}
ctx.restore();
};
this.getActiveAngle = function(canvasCtx,mouseX,mouseY) {
for(var i=0;i<angles.length;i++) {
var curr = angles[i];
if(curr.isActiveShape(canvasCtx,mouseX,mouseY)) {
return curr;
}
}
return null;
};
this.setOAorOB = function(canvasCtx,x1,y1,x2,y2) {
this.curr_angle.setLine(x1,y1,x2,y2);
this.isCurrentDrawingAngle = true;
this.drawAngle(canvasCtx);
this.isCurrentDrawingAngle = false;
};
this.getActiveAngleText = function(canvasCtx,mouseX,mouseY) {
for(var i=0;i<angles.length;i++) {
var curr = angles[i];
if(curr.isTextSelection(canvasCtx,mouseX,mouseY)) {
return curr;
}
}
return null;
};
this.viewPortGraphic = function(shape) {
return {
xA: shape.xA*state.scale+state.translationX,
yA: shape.yA*state.scale+state.translationY,
x0: shape.x0*state.scale+state.translationX,
y0: shape.y0*state.scale+state.translationY,
xB: shape.xB*state.scale+state.translationX,
yB: shape.yB*state.scale+state.translationY,
angle: shape.angle,
active: shape.active,
arcAngle1:shape.arcAngle1,
arcAngle2: shape.arcAngle2,
arcRadius: shape.arcRadius,
counterClockwise: shape.counterClockwise,
textX: shape.textX*state.scale+state.translationX,
textY: shape.textY*state.scale+state.translationY,
txtActive: shape.txtActive,
refX: shape.refX*state.scale+state.translationX,
refY: shape.refY*state.scale+state.translationY,
};
};
this.removeShape = function(shape) {
angles.splice(angles.indexOf(shape), 1);
};
this.clearAll = function() {
angles = [];
};
this.getClosestAnchor = function(points,refPt) {
var dist1 = Math.round(Math.sqrt(Math.pow(points[0].x-refPt.x,2) + Math.pow(points[0].y-refPt.y,2)));
var dist2 = Math.round(Math.sqrt(Math.pow(points[1].x-refPt.x,2) + Math.pow(points[1].y-refPt.y,2)));
var dist3 = Math.round(Math.sqrt(Math.pow(points[2].x-refPt.x,2) + Math.pow(points[2].y-refPt.y,2)));
var dist4 = Math.round(Math.sqrt(Math.pow(points[3].x-refPt.x,2) + Math.pow(points[3].y-refPt.y,2)));
var min = Math.min(dist1,dist2,dist3,dist4);
switch(min) {
case dist1:
return points[0];
case dist2:
return points[1];
case dist3:
return points[2];
case dist4:
return points[3];
default:
return points[0];
}
};
};
/**
* @namespace angle
*/
ovm.angle = function() {
// Variables
var handleSize = 5;
this.xA = undefined;
this.yA = undefined;
this.x0 = undefined;
this.y0 = undefined;
this.xB = undefined;
this.yB = undefined;
this.angle = undefined;
this.OAvalid = false;
this.OBvalid = false;
this.active = false;
this.arcAngle1 = undefined;
this.arcAngle2 = undefined;
this.arcRadius = 30;
this.counterClockwise = true;
this.lineColinear = false;
this.textX = undefined;
this.textY = undefined;
this.txtActive = false;
this.refX = undefined;
this.refY = undefined;
// Functions
this.lineOAValid = function() {
return this.OAvalid;
};
this.lineOBValid = function() {
return this.OBvalid;
};
this.setLine = function(x1,y1,x2,y2) {
if(!this.OAvalid) {
this.setOA(x1,y1,x2,y2,false);
} else {
this.setEnd(x2,y2);
}
};
this.setOA = function(xa,ya,x,y,oavalid) {
this.xA = xa;
this.yA = ya;
this.x0 = x;
this.y0 = y;
this.OAvalid = oavalid;
};
this.setIntersectionPt = function(x,y) {
if(this.OAvalid) {
this.xB = x;
this.yB = y;
this.OBvalid = true;
return true;
} else if(this.xA!=undefined && this.yA!=undefined) {
this.x0 = x;
this.y0 = y;
this.OAvalid = true;
return false;
}
};
this.setEnd = function(x,y) {
this.xB = x;
this.yB = y;
this.textX = x+15;
this.textY = y+15;
this.validate();
};
this.generateTrueGraphic = function() {
var tmpDrawCanvas = document.getElementById("canvasLayer2");
if(state.hflip){
this.xA = tmpDrawCanvas.width - this.xA;
this.x0 = tmpDrawCanvas.width - this.x0;
this.xB = tmpDrawCanvas.width - this.xB;
this.textX = this.xB+15;
this.validate();
}
if(state.vflip){
this.yA = tmpDrawCanvas.height - this.yA;
this.y0 = tmpDrawCanvas.height - this.y0;
this.yB = tmpDrawCanvas.height - this.yB;
this.textY = this.yB+15;
this.validate();
}
if(state.rotate!=0) {
var tempXA,tempYA,tempX0,tempY0,tempXB,tempYB ;
var widthHeightDifference = tmpDrawCanvas.width - tmpDrawCanvas.height;
widthHeightDifference = widthHeightDifference/2;
if(state.rotate===90) {
tempXA = this.yA + widthHeightDifference;
tempYA = (tmpDrawCanvas.width - widthHeightDifference) - this.xA;
tempX0 = this.y0 + widthHeightDifference;
tempY0 = (tmpDrawCanvas.width - widthHeightDifference) - this.x0;
tempXB = this.yB + widthHeightDifference;
tempYB = (tmpDrawCanvas.width - widthHeightDifference) - this.xB;
this.xA = tempXA;
this.yA = tempYA;
this.x0 = tempX0;
this.y0 = tempY0;
this.xB = tempXB;
this.yB = tempYB;
this.textX = this.xB+15;
this.textY = this.yB+15;
this.validate();
}
else if(state.rotate===180) {
tempXA = tmpDrawCanvas.width - this.xA;
tempYA = tmpDrawCanvas.height - this.yA;
tempX0 = tmpDrawCanvas.width - this.x0;
tempY0 = tmpDrawCanvas.height - this.y0;
tempXB = tmpDrawCanvas.width - this.xB;
tempYB = tmpDrawCanvas.height - this.yB;
this.xA = tempXA;
this.yA = tempYA;
this.x0 = tempX0;
this.y0 = tempY0;
this.xB = tempXB;
this.yB = tempYB;
this.textX = this.xB+15;
this.textY = this.yB+15;
this.validate();
} else {
tempXA = (tmpDrawCanvas.width - widthHeightDifference) - this.yA;
tempYA = this.xA - widthHeightDifference;
tempX0 = (tmpDrawCanvas.width - widthHeightDifference) - this.y0;
tempY0 = this.x0 - widthHeightDifference;
tempXB = (tmpDrawCanvas.width - widthHeightDifference) - this.yB;
tempYB = this.xB - widthHeightDifference;
this.xA = tempXA;
this.yA = tempYA;
this.x0 = tempX0;
this.y0 = tempY0;
this.xB = tempXB;
this.yB = tempYB;
this.textX = this.xB+15;
this.textY = this.yB+15;
this.validate();
}
}
this.xA = ((this.xA-state.translationX)/state.scale);
this.yA = ((this.yA-state.translationY)/state.scale);
this.x0 = ((this.x0-state.translationX)/state.scale);
this.y0 = ((this.y0-state.translationY)/state.scale);
this.xB = ((this.xB-state.translationX)/state.scale);
this.yB = ((this.yB-state.translationY)/state.scale);
this.textX = ((this.textX-state.translationX)/state.scale);
this.textY = ((this.textY-state.translationY)/state.scale);
this.fixReferencePoints();
};
this.calculateAngle = function() {
if(!this.lineColinear) {
this.calculateArcAngles({x:this.xA,y:this.yA},{x:this.x0,y:this.y0},{x:this.xB,y:this.yB});
this.counterClockwise = (this.arcAngle1>this.arcAngle2);
}
var tangent = this.getRadians({x:this.xA,y:this.yA},{x:this.x0,y:this.y0},{x:this.xB,y:this.yB});
var deg = tangent * (180/Math.PI);
this.angle = deg % 360.0;
if (Math.abs(this.angle) > 180.0) {
this.counterClockwise = (this.arcAngle2>this.arcAngle1);
this.angle -= Math.sign(this.angle) * 360.0;
}
this.angle = Math.abs(this.angle).toFixed(2) + " deg";
};
this.validate = function() {
this.OAvalid = (this.xA!=undefined && this.yA!=undefined && this.x0!=undefined && this.y0!=undefined && this.x0!=this.xA && this.y0!=this.yA);
this.OBvalid = (this.x0!=undefined && this.y0!=undefined && this.xB!=undefined && this.yB!=undefined && this.x0!=this.xB && this.y0!=this.yB);
this.arcAngle1 = this.arcAngle2 = undefined;
this.lineColinear = false;
this.counterClockwise = false;
this.angle = 0.0;
if(this.OAvalid && this.OBvalid) {
this.lineColinear = this.isColinear({x:this.x0,y:this.yo}, {x:this.xA,y:this.yA}, {x:this.x0,y:this.y0}, {x:this.xB,y:this.yB});
this.calculateAngle();
}
};
this.isColinear = function(ptA,ptB,ptC,ptD) {
if(this.isParallel(ptA, ptB, ptC, ptD)) {
if (((ptA.y - ptC.y) * (ptD.x - ptC.x) - (ptA.x - ptC.x)
* (ptD.y - ptC.y)) == 0) {
return true;
}
}
return false;
};
this.isParallel = function(ptA,ptB,ptC,ptD) {
if (((ptB.x - ptA.x) * (ptD.y - ptC.y) - (ptB.y - ptA.y)
* (ptD.x - ptC.x)) == 0) {
return true;
}
return false;
};
this.calculateArcAngles = function(pt1,pt2,pt3) {
this.arcAngle1 = Math.atan2(pt3.y - pt2.y, pt3.x - pt2.x);
this.arcAngle2 = Math.atan2(pt1.y - pt2.y, pt1.x - pt2.x);
};
this.getRadians2 = function(ptA,ptB) {
return Math.atan2(ptA.y-ptB.y,ptB.x-ptA.x);
};
this.getRadians = function(ptA,ptB,ptC) {
return this.getRadians2(ptB,ptC)-this.getRadians2(ptB,ptA);
};
this.getType = function() {
return "angle";
};
this.detectHandle = function(mouseX,mouseY,target) {
var x = (mouseX-state.translationX)/state.scale;
var y = (mouseY-state.translationY)/state.scale;
if(x>=this.xA-handleSize && x<=this.xA+handleSize && y>=this.yA-handleSize && y<=this.yA+handleSize) {
target.style.cursor = "pointer";
return 0;
} else if(x>=this.x0-handleSize && x<=this.x0+handleSize && y>=this.y0-handleSize && y<=this.y0+handleSize) {
target.style.cursor = "pointer";
return 1;
} else if(x>=this.xB-handleSize && x<=this.xB+handleSize && y>=this.yB-handleSize && y<=this.yB+handleSize) {
target.style.cursor = "pointer";
return 2;
} else {
return -1;
}
};
this.detectLine = function(start,end,mouse) {
var a = Math.round(Math.sqrt(Math.pow((end.x+handleSize)-(start.x+handleSize),2) + Math.pow((end.y+handleSize)-(start.y+handleSize),2)));
var b = Math.round(Math.sqrt(Math.pow(mouse.x-(start.x+handleSize),2) + Math.pow(mouse.y-(start.y+handleSize),2)));
var c = Math.round(Math.sqrt(Math.pow((end.x+handleSize)-mouse.x,2) + Math.pow((end.y+handleSize)-mouse.y,2)));
return (a==b+c);
};
this.isActiveShape = function(canvasCtx,mouseX,mouseY) {
var x = (mouseX-state.translationX)/state.scale;
var y = (mouseY-state.translationY)/state.scale;
if(this.detectLine({x:this.xA,y:this.yA},{x:this.x0,y:this.y0},{x:x,y:y}) || this.detectLine({x:this.x0,y:this.y0},{x:this.xB,y:this.yB},{x:x,y:y})) {
this.active = true;
} else {
this.active = false;
}
return this.active || this.isTextSelection(canvasCtx,mouseX,mouseY);
};
this.moveShape = function(deltaX,deltaY) {
if(this.active) {
this.x0+=deltaX;
this.y0+=deltaY;
this.xA+=deltaX;
this.yA+=deltaY;
this.xB+=deltaX;
this.yB+=deltaY;
}
this.textX+=deltaX;
this.textY+=deltaY;
this.fixReferencePoints();
};
this.resizeShape = function(deltaX,deltaY,handleIndex) {
switch(handleIndex) {
case 0:
this.xA+=deltaX;
this.yA+=deltaY;
break;
case 1:
this.x0+=deltaX;
this.y0+=deltaY;
break;
case 2:
this.xB+=deltaX;
this.yB+=deltaY;
this.textX = this.xB+15;
this.textY = this.yB+15;
break;
}
this.validate();
this.fixReferencePoints();
};
this.isTextSelection = function(canvasCtx,mouseX,mouseY) {
var x = Math.ceil((mouseX-state.translationX)/state.scale);
var y = Math.floor((mouseY-state.translationY)/state.scale);
var textWid = Math.ceil(canvasCtx.measureText(this.angle).width)+5;
this.txtActive = (x>=this.textX && x<=this.textX+(textWid/state.scale)+5 && y>=this.textY && y<=(this.textY+(15/state.scale)));
return this.txtActive;
};
this.distanceToLine = function(line,point) {
return Math.round(Math.sqrt(Math.pow(point.x-line.x1,2) + Math.pow(point.y-line.y1,2))) + Math.round(Math.sqrt(Math.pow(line.x2-point.x,2) + Math.pow(line.y2-point.y,2)));
};
this.distanceToPoint = function(point1,point2) {
return Math.round(Math.sqrt(Math.pow(point2.x-point1.x,2) + Math.pow(point2.y-point1.y,2)));
};
this.fixReferencePoints = function() {
var dist1 = this.distanceToLine({x1:this.xA,y1:this.yA,x2:this.x0,y2:this.y0}, {x:this.textX,y:this.textY});
var dist2 = this.distanceToLine({x1:this.xB,y1:this.yB,x2:this.x0,y2:this.y0}, {x:this.textX,y:this.textY});
if(dist1<dist2) {
this.findClosestPointForLine({x:this.x0,y:this.y0},{x:this.xA,y:this.yA},{x:this.textX,y:this.textY});
if(!this.isPtWithinAngle({x:this.x0,y:this.y0},{x:this.xA,y:this.yA},{x:this.refX,y:this.refY})) {
this.selectNearestAnchor({x:this.x0,y:this.y0},{x:this.xA,y:this.yA},{x:this.textX,y:this.textY});
}
} else {
this.findClosestPointForLine({x:this.x0,y:this.y0},{x:this.xB,y:this.yB},{x:this.textX,y:this.textY});
if(!this.isPtWithinAngle({x:this.x0,y:this.y0},{x:this.xB,y:this.yB},{x:this.refX,y:this.refY})) {
this.selectNearestAnchor({x:this.x0,y:this.y0},{x:this.xB,y:this.yB},{x:this.textX,y:this.textY});
}
}
};
this.findClosestPointForLine = function(A,B,P) {
var a_to_p = [P.x - A.x , P.y - A.y]; // Vector A to P
var a_to_b = [B.x - A.x , B.y - A.y]; // Vector A to B
// Find squared magnitude of a_to_b
var atb2 = Math.pow(a_to_b[0],2) + Math.pow(a_to_b[1],2);
// Find dot product of a_to_p and a_to_b
var atp_dot_atb = a_to_p[0] * a_to_b[0] + a_to_p[1] * a_to_b[1];
// Normalized distance from a to closest point
var t = atp_dot_atb / atb2;
// Add the distance to A, moving towards B
this.refX = A.x + a_to_b[0] * t;
this.refY = A.y + a_to_b[1] * t;
};
this.isPtWithinAngle = function(A,B,P) {
if(A.x<=P.x && B.x>=P.x || A.x>=P.x && B.x<=P.x) {
if(A.y<=P.y && B.y>=P.y || A.y>=P.y && B.y<=P.y) {
return true;
}
}
return false;
};
this.selectNearestAnchor = function(ptA,ptB,ptC) {
var d1 = this.distanceToPoint(ptA, ptC);
var d2 = this.distanceToPoint(ptB, ptC);
if(d1<d2) {
this.refX = ptA.x;
this.refY = ptA.y;
} else {
this.refX = ptB.x;
this.refY = ptB.y;
}
}
};