I am working on a visualizer to show the area on a graph that is currently being checked for points from a starting points POV, and within a limited FOV. I am trying to show this with canvas arc, however, instead of filling in the arc area, it is filling in the outermost sliver of the arc.
I have tried drawing lines using the rad angle, and those are successful, it is only the arc that does not appear to work correctly. I would expect that it would complete the arc within the two angles, instead of seemingly subtracting the area of the triangle within the arc.
Relevant section:
let startingLocation = {x:0,y:0};
let angle = 18.43494882292201;
let fovAngle = 30;
let r = 100;
//Convert start and end angles to radians
let theta = angle * (Math.PI / 180);
let theta2 = (angle + fovAngle) * (Math.PI / 180);
//Draw start to end arc area
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(startingLocation.x, startingLocation.y, r, theta, theta2, false);
ctx.fill();
ctx.closePath();
Full snippet
const canvas = document.getElementById('canvas');
let ctx = canvas.getContext("2d");
const width = document.getElementById('canvas').width;
const height = document.getElementById('canvas').height;
//Starting variables
let fovAngle = 30;
let data = [{
x: 60,
y: 20
}];
let startingLocation = {
x: 0,
y: 0
};
//Get angle in radians
let angle = Math.atan2(data[0].y - startingLocation.y, data[0].x - startingLocation.x);
//Convert to degrees
angle = (angle * 180) / Math.PI;
if (angle < 0) {
angle = angle + 360;
}
drawCanvas(angle);
function drawCanvas(angle) {
//Clear the canvas
ctx.clearRect(0, 0, width, height);
ctx.textAlign = 'start';
ctx.textBaseline = 'alphabetic';
//Draw the points
var counter = 1;
for (const point of data) {
//Draw the point on the canvas (with an offset of two so the point is in the center of the square)
ctx.fillStyle = 'blue';
ctx.fillRect(point.x - 2, point.y - 2, 5, 5);
//Label the point on the canvas (offset to center number, and to have the number above the square instead of on top of it)
ctx.fillStyle = 'black';
ctx.fillText(counter, point.x - 2, point.y - 5);
counter++;
}
//Draw Starting Location
ctx.fillStyle = 'red';
ctx.fillRect(startingLocation.x - 2, startingLocation.y - 2, 5, 5);
ctx.fillStyle = 'black';
ctx.fillText("Start", startingLocation.x - 10, startingLocation.y - 5);
if (angle != null) {
//let r=Math.max(width,height)*2;
let r = 100;
//Convert start and end angles to radians
let theta = angle * (Math.PI / 180);
let theta2 = (angle + fovAngle) * (Math.PI / 180);
//Draw start to end arc area
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.globalAlpha = 0.3;
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(startingLocation.x, startingLocation.y, r, theta, theta2, false);
ctx.fill();
ctx.globalAlpha = 1;
ctx.closePath();
//Draw start angle line
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.lineTo(startingLocation.x + r * Math.cos(theta), startingLocation.y + r * Math.sin(theta));
ctx.stroke();
ctx.closePath();
//Draw end angle line
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.lineTo(startingLocation.x + r * Math.cos(theta2), startingLocation.y + r * Math.sin(theta2));
ctx.stroke();
ctx.closePath();
console.log(theta, theta2);
}
}
#canvas {
width: 200px;
height: 200px;
border: 1px solid black;
margin: 10px 20px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML Canvas Arc</title>
</head>
<body>
<canvas id="canvas" width=200 height=200></canvas>
</body>
</html>
That's because you do call beginPath()
after you moveTo
the start point, which will remove the moveTo subpath from the current path.
In the absence of a subpath, calling arc()
will automatically moveTo
the start point of the arc.1
Simply call beginPath
before you call moveTo
:
const canvas = document.getElementById('canvas');
let ctx = canvas.getContext("2d");
const width = document.getElementById('canvas').width;
const height = document.getElementById('canvas').height;
//Starting variables
let fovAngle = 30;
let data = [{
x: 60,
y: 20
}];
let startingLocation = {
x: 0,
y: 0
};
//Get angle in radians
let angle = Math.atan2(data[0].y - startingLocation.y, data[0].x - startingLocation.x);
//Convert to degrees
angle = (angle * 180) / Math.PI;
if (angle < 0) {
angle = angle + 360;
}
drawCanvas(angle);
function drawCanvas(angle) {
//Clear the canvas
ctx.clearRect(0, 0, width, height);
ctx.textAlign = 'start';
ctx.textBaseline = 'alphabetic';
//Draw the points
var counter = 1;
for (const point of data) {
//Draw the point on the canvas (with an offset of two so the point is in the center of the square)
ctx.fillStyle = 'blue';
ctx.fillRect(point.x - 2, point.y - 2, 5, 5);
//Label the point on the canvas (offset to center number, and to have the number above the square instead of on top of it)
ctx.fillStyle = 'black';
ctx.fillText(counter, point.x - 2, point.y - 5);
counter++;
}
//Draw Starting Location
ctx.fillStyle = 'red';
ctx.fillRect(startingLocation.x - 2, startingLocation.y - 2, 5, 5);
ctx.fillStyle = 'black';
ctx.fillText("Start", startingLocation.x - 10, startingLocation.y - 5);
if (angle != null) {
//let r=Math.max(width,height)*2;
let r = 100;
//Convert start and end angles to radians
let theta = angle * (Math.PI / 180);
let theta2 = (angle + fovAngle) * (Math.PI / 180);
//Draw start to end arc area
ctx.beginPath(); // Call beginPath before moveTo
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.globalAlpha = 0.3;
ctx.fillStyle = 'green';
ctx.arc(startingLocation.x, startingLocation.y, r, theta, theta2, false);
ctx.fill();
ctx.globalAlpha = 1;
ctx.closePath(); // useless
//Draw start angle line
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.lineTo(startingLocation.x + r * Math.cos(theta), startingLocation.y + r * Math.sin(theta));
ctx.stroke();
ctx.closePath(); // useless
//Draw end angle line
ctx.strokeStyle = 'green';
ctx.beginPath();
ctx.moveTo(startingLocation.x, startingLocation.y);
ctx.lineTo(startingLocation.x + r * Math.cos(theta2), startingLocation.y + r * Math.sin(theta2));
ctx.stroke();
ctx.closePath(); // useless
console.log(theta, theta2);
}
}
#canvas {
width: 200px;
height: 200px;
border: 1px solid black;
margin: 10px 20px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML Canvas Arc</title>
</head>
<body>
<canvas id="canvas" width=200 height=200></canvas>
</body>
</html>
Also note that all your closePath
calls are useless here. This method is basically a lineTo(startX, startY)
but is in no way akin to beginPath()
.
1. Technically it's the inverse: if there is a subpath, a lineTo
from the last point to the start point is drawn, but it boils down to the same.