Tomek
Tomek

Reputation: 183

html canvas check that object is in angle

I have a circle, and a object. I want to draw a circle segment with specified spread, and next check that the object is in defined angle, if it is, angle color will be red, otherwise green. But my code does not work in some cases...

in this case it work: enter image description here

in this too: enter image description here

but here it isn't: enter image description here

I know that my angle detection code part is not perfect, but I have no idea what I can do.

This is my code:

html:

<html>
    <head></head>
    <body>
        <canvas id="c" width="800" height="480" style="background-color: #DDD"></canvas>
        <script src="script.js"></script>
    </body>
</html>

js:

window.addEventListener('mousemove', updateMousePos, false);
var canvas = document.getElementById("c");
var context = canvas.getContext("2d");

//mouse coordinates
var mx = 0, my = 0;

draw();

function draw()
{
    context.clearRect(0, 0, canvas.width, canvas.height);

    //object coordinates
    var ox = 350, oy = 260;

    context.beginPath();
    context.arc(ox,oy,5,0,2*Math.PI);
    context.fill();

    //circle
    var cx = 400, cy = 280;
    var r = 100;
    var segmentPoints = 20;
    var circlePoints = 40;
    var spread = Math.PI / 2;
    var mouseAngle = Math.atan2(my - cy, mx - cx); //get angle between circle center and mouse position

    context.beginPath();
    context.strokeStyle = "blue";
    context.moveTo(cx + r, cy);

    for(var i=0; i<circlePoints; i++)
    {
        var a = 2 * Math.PI / (circlePoints - 1) * i;
        var x = cx + Math.cos(a) * r;
        var y = cy + Math.sin(a) * r;

        context.lineTo(x, y);
    }

    context.lineTo(cx + r, cy);
    context.stroke();

    var objAngle = Math.atan2(oy - cy, ox - cx);

    var lowerBorder = mouseAngle - spread / 2;
    var biggerBorder = mouseAngle + spread / 2;

    /////////////////////////////////////////////ANGLES DETECTION PART
    if(objAngle >= lowerBorder && objAngle <= biggerBorder ||
       objAngle <= biggerBorder && objAngle >= lowerBorder)
    {
        context.strokeStyle = "red";
    }
    else
        context.strokeStyle = "green";

    context.lineWidth = 3;

    //angle center line
    context.beginPath();
    context.moveTo(cx, cy);
    context.lineTo(cx + Math.cos(mouseAngle) * r * 2, cy + Math.sin(mouseAngle) * r * 2);
    context.stroke();

    //draw spread arc
    context.beginPath();
    context.moveTo(cx, cy);

    for(var i=0; i<segmentPoints; i++)
    {
        var a = mouseAngle - spread / 2 + spread / (segmentPoints - 1) * i; 
        var x = cx + Math.cos(a) * r;
        var y = cy + Math.sin(a) * r;

        context.lineTo(x, y);
    }
    context.lineTo(cx, cy);
    context.stroke();

    //show degrees
    context.font = "20px Arial";
    context.fillText((lowerBorder * 180 / Math.PI).toFixed(2), Math.cos(lowerBorder) * r + cx, Math.sin(lowerBorder) * r + cy);
    context.fillText((biggerBorder * 180 / Math.PI).toFixed(2), Math.cos(biggerBorder) * r + cx, Math.sin(biggerBorder) * r + cy);
    context.fillText((mouseAngle * 180 / Math.PI).toFixed(2), Math.cos(mouseAngle) * r + cx, Math.sin(mouseAngle) * r + cy);

    //update
    setTimeout(function() { draw(); }, 10);
}

//getting mouse coordinates
function updateMousePos(evt) 
{
    var rect = document.getElementById("c").getBoundingClientRect();
    mx = evt.clientX - rect.left;
    my = evt.clientY - rect.top;
}

Upvotes: 2

Views: 299

Answers (1)

Mr. Reddy
Mr. Reddy

Reputation: 1114

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				position: absolute;
				margin: auto;
				left: 0;
				right: 0;
				border: solid 1px white;
				border-radius: 10px;
			}
		</style>
	</head>
	
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
			
			// Rotation here is being measured in Radians
			// Given two 2D vectors A & B, the angle between them can be drawn from this formula
			// A dot B = length(a) * length(b) * cos(angle)
			// if the vectors are normalized (the length is 1) the formula becomes
			// A dot B = cos(angle)
			// angle = acos(a.x * b.x + a.y * b.y)
			// So here you are concerned with the direction of the two vectors
			// One will be the vector facing outward from the middle of your arc segment
			// The other will be a directional vector from the point you want to do collision with to the center
			// of the circle
			
			var canvasWidth = 180;
			var canvasHeight = 160;
			var canvas = null;
			var ctx = null;
			var bounds = {top: 0.0, left: 0.0};
			
			var circle = {
				x: (canvasWidth * 0.5)|0,
				y: (canvasHeight * 0.5)|0,
				radius: 50.0,
				rotation: 0.0, // In Radians
				arcSize: 1.0
			};
			
			var point = {
				x: 0.0,
				y: 0.0
			};
			
			window.onmousemove = function(e) {
				point.x = e.clientX - bounds.left;
				point.y = e.clientY - bounds.top;
			}
			
			// runs after the page has loaded
			window.onload = function() {
				canvas = document.getElementById("canvas");
				canvas.width = canvasWidth;
				canvas.height = canvasHeight;
				bounds = canvas.getBoundingClientRect();
				ctx = canvas.getContext("2d");
				loop();
			}
			
			function loop() {
				// Update Circle Rotation
				circle.rotation = circle.rotation + 0.025;
				if (circle.rotation > 2*Math.PI) { 
					circle.rotation = 0.0;
				}
				
				// Vector A (Point Pos -> Circle Pos)
				var aX = circle.x - point.x;
				var aY = circle.y - point.y;
				var aLength = Math.sqrt(aX * aX + aY * aY);
				
				// Vector B (The direction the middle of the arc is facing away from the circle)
				var bX = Math.sin(circle.rotation);
				var bY =-Math.cos(circle.rotation); // -1 is facing upward, not +1
				var bLength = 1.0;
				
				// Normalize vector A
				aX = aX / aLength;
				aY = aY / aLength;
				
				// Are we inside the arc segment?
				var isInsideRadius = aLength < circle.radius;
				var isInsideAngle = Math.abs(Math.acos(aX * bX + aY * bY)) < circle.arcSize * 0.5;
				var isInsideArc = isInsideRadius && isInsideAngle;
				
				// Clear the screen
				ctx.fillStyle = "gray";
				ctx.fillRect(0,0,canvasWidth,canvasHeight);
				
				// Draw the arc
				ctx.strokeStyle = isInsideArc ? "green" : "black";
				ctx.beginPath();
				ctx.moveTo(circle.x,circle.y);
				ctx.arc(
					circle.x,
					circle.y,
					circle.radius,
					circle.rotation - circle.arcSize * 0.5 + Math.PI * 0.5,
					circle.rotation + circle.arcSize * 0.5 + Math.PI * 0.5,
					false
				);
				ctx.lineTo(circle.x,circle.y);
				ctx.stroke();
				
				// Draw the point
				ctx.strokeStyle = "black";
				ctx.fillStyle = "darkred";
				ctx.beginPath();
				ctx.arc(
					point.x,
					point.y,
					5.0,
					0.0,
					2*Math.PI,
					false
				);
				ctx.fill();
				ctx.stroke();
				
				// This is better to use then setTimeout()
				// It automatically syncs the loop to 60 fps for you
				requestAnimationFrame(loop);
			}
		
		</script>
	</body>
</html>

Upvotes: 2

Related Questions