Unwanted half circle when trying to draw quarter circles - javascript

I'm trying to create the Fibonacci spiral with the canvas element in HTML5.
This is my drawing code, this function gets called for every number in the Fibonacci sequence. Side is the square's side length, or the current Fibonacci number. And start is the bottom right corner of each square.
function drawSpiral(side, start) {
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(start.x, start.y - side);
ctx.lineTo(start.x - side, start.y - side);
ctx.lineTo(start.x - side, start.y);
ctx.lineTo(start.x, start.y);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
switch (direction) {
case 0:
ctx.arc(start.x, start.y - side, side, Math.PI, 1.5 * Math.PI, true);
break;
case 1:
ctx.arc(
start.x - side,
start.y - side,
side,
1.5 * Math.PI,
2 * Math.PI,
true
);
break;
case 2:
ctx.arc(start.x - side, start.y, side, 2 * Math.PI, Math.PI / 2, true);
break;
case 3:
ctx.arc(start.x, start.y, side, Math.PI / 2, Math.PI, true);
break;
}
ctx.stroke();
ctx.closePath();
}
Direction is a number, 0 to 4, that represents the direction of the next square, counterclockwise - 0 = right, 1 = up, ...
It draws the squares and arcs just fine, but it's also drawing half circles everywhere, and I can't figure out why.
Screenshot of the issue

The last param is "counterclockwise",
ctx.arc(x, y, radius, startAngle, endAngle, true);
will do the same as
ctx.arc(x, y, rad, endAngle, startAngle);
// note how start and end have been swapped
that is to trace an arc from endAngle to startAngle, so in your case, it will draw the 3/4 of a circle:
const ctx = document.querySelector("canvas").getContext("2d");
const startAngle = Math.PI; // 9 o'clock
const endAngle = Math.PI * 1.5; // 12 o'clock
ctx.arc(50, 50, 50, startAngle, endAngle, true);
ctx.strokeStyle = "green";
ctx.stroke();
ctx.beginPath();
ctx.arc(160, 50, 50, endAngle, startAngle);
ctx.strokeStyle = "blue";
ctx.stroke();
<canvas></canvas>
If you want to trace only the 1/4 of the circle, then don't set this param.

Related

How do you make the eyes move and follow the cursor? I honestly dont know what to do from here

Am I meant to draw the eyes in an alternative way for it to follow the cursor? Please help :) I am completely lost from here and have tried online solutions but they all require css in which my code doesn't. I want to run all of this purely from javascript, any tips?
function drawEyes() {
const c = document.getElementById("canvasEyes")
const ctx = c.getContext('2d');
//left eye
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, false);
ctx.stroke();
//iris
ctx.beginPath();
ctx.arc(75, 75, 30, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
//pupil
ctx.beginPath();
ctx.arc(75, 75, 15, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
//right eye
ctx.beginPath();
ctx.arc(225, 75, 50, 0, Math.PI * 2, false);
ctx.stroke();
//iris
ctx.beginPath();
ctx.arc(225, 75, 30, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "black";
ctx.fill();
//pupil
ctx.beginPath();
ctx.arc(225, 75, 15, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
Basic 2D eyes that follow mouse
The eyes follow mouse by scaling the mouse coordinates to the range of motion that the iris & pupil have within the radius of the eye.
The lookat position is relative to the top left of the canvas and assumes that the eyes are at the center of the canvas.
The scaled lookat position is then set relative to the canvas center (center of both eyes)
To prevent the iris & pupil from being drawn outside the eye use the canvas clip function to clip the iris & pupil if outside the circles of the eye.
More details
It is possible to add more details
Consider adding shading, highlights, eyelids, blink, etc.. to give the animation more depth and life, for instance...
Spheres
Eyes are spheres, you can use ellipses to draw the iris & pupil, Flattening the ellipses of the iris & pupil as they get near the edge, also rotate the ellipse in the direction of the mouse. This will make the eyes look rounder in the 3rd dimention.
Example
Basic 2D eyes. See comments for details
const ctx = canvas.getContext("2d");
// Object to hold mouse coords
const lookat = {x: 150, y: 75};
// details need to make eye look at mouse coords
const eye = {
radius: 50,
iris: 30,
// limits of movement
limMin: -0.1,
limMax: 1.1,
};
// add mouse move listener to whole page
addEventListener("mousemove",e => {
// make mouse coords relative to the canvas ignoring scroll in this case
const bounds = canvas.getBoundingClientRect();
lookat.x = e.pageX - bounds.left;// - scrollX;
lookat.y = e.pageY - bounds.top;// - scrollY;
ctx.clearRect(0, 0, 300, 150);
drawEyes(lookat);
});
drawEyes(lookat);
function drawEyes(lookat) {
var {x,y} = lookat;
// normalise lookat range from 0 to 1 across and down canvas
x /= canvas.width;
y /= canvas.height;
// limit eye movement to -0.1 to 1.1 or what ever you prefer
x = x < eye.limMin ? eye.limMin : x > eye.limMax ? eye.limMax : x;
y = y < eye.limMin ? eye.limMin : y > eye.limMax ? eye.limMax : y;
// move lookat so that 0.5 is center
x -= 0.5;
y -= 0.5;
// get range of movement of iris
const range = (eye.radius - eye.iris) * 2;
// scale the lookats to the range of movement
x *= range;
y *= range;
// draw outer eyes left, right
ctx.beginPath();
ctx.arc(75, 75, eye.radius, 0, Math.PI * 2, false);
ctx.moveTo(225 + eye.radius, 75);
ctx.arc(225, 75, eye.radius, 0, Math.PI * 2, false);
ctx.stroke();
// use eyes to create a clip so iris does not draw outside the eye.
// first save canvas state so clip can be turned off at end
ctx.save();
// turn on clip which will use the two circles currently the active path
ctx.clip();
// draw iris & pupil are offset by x,y within the clip
//iris left, right
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(75 + x, 75 + y, eye.iris, 0, Math.PI * 2, false);
ctx.moveTo(225 + x + eye.iris, 75 + y);
ctx.arc(225 + x, 75 + y, eye.iris, 0, Math.PI * 2, false);
ctx.fill();
//pupil left, right
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(75 + x, 75 + y, 15, 0, Math.PI * 2, false);
ctx.moveTo(225 + x + 15, 75 + y);
ctx.arc(225 + x, 75 + y, 15, 0, Math.PI * 2, false);
ctx.fill();
// turn the clip off by restoring canvas state
ctx.restore();
}
<canvas id="canvas" width="300" height="150"></canvas>

Clipping lines within ellipse on javascript canvas [duplicate]

This question already has an answer here:
All html canvas shapes are given the color of the last object added
(1 answer)
Closed 2 years ago.
I am drawing a filled ellipse (using arc()) on the javascript canvas:
ctx.arc(centerX, centerY, dotDiameter / 2, 0, 2 * Math.PI, false);
ctx.fillStyle = color1;
ctx.fill();
I then want to draw a line, that is clipped to this ellipse, in a different color.
ctx.clip(); // Clip to the ellipse
ctx.strokeStyle = color2;
ctx.moveTo(centerX - dotRadius, centerY);
ctx.lineTo(centerX + dotRadius, centerY);
ctx.stroke();
However, the ellipse is also stroked with color2.
How can I clip lines to my ellipse, but not have the ellipse stroked? Is there a way to remove the ellipse (aka arc) from the canvas after I call clip()?
Thanks!
(FYI, this is an oversimplification of my code. The lines that stroked are more complicated than a single horizontal line and the lines do need to be clipped.)
We can calculate a point on the perimeter of an ellipsis with some basic trigonometry...
ctx.moveTo(x, y);
x += radiusX * Math.cos(lineangle)
y += radiusY * Math.sin(lineangle)
ctx.lineTo(x, y);
See code snippet below, I'm drawing an ellipse and a line from center to edge.
ctx = document.getElementById('c').getContext('2d');
ctx.lineWidth = 2;
function draw(x, y, radiusX, radiusY, lineangle, color1, color2) {
ctx.beginPath()
ctx.ellipse(x, y, radiusX, radiusY, 0, 0, 2 * Math.PI, false);
ctx.fillStyle = color1;
ctx.fill();
ctx.beginPath()
ctx.strokeStyle = color2;
ctx.moveTo(x, y);
x += radiusX * Math.cos(lineangle)
y += radiusY * Math.sin(lineangle)
ctx.lineTo(x, y);
ctx.stroke();
}
angle = 0
function loop() {
ctx.clearRect(0,0, 500, 500);
draw(80, 80, 70, 50, angle, "red", "lime")
draw(240, 60, 80, 30, angle*0.7, "black", "cyan")
draw(360, 80, 30, 70, angle*2, "white", "black")
angle += 0.05
}
setInterval(loop, 100)
<canvas height="160" width="500" id="c">

Rotate an ellipse using arc method Javascript

Using context arc method i need to rotate an arc on the canvas.
ctx.save();
ctx.beginPath();
ctx.translate(cx-rx, cy-ry);
ctx.scale(rx, ry);
ctx.rotate(angle * Math.PI / 180); // angle = 45 will move away from the cordinates
ctx.arc(1, 1, 1, 0, 2 * Math.PI, false);
ctx.restore();
ctx.stroke();
ctx.restore();

HTML5 canvas : counterclockwise arc with angle > 2 PI

I try to figure why angle > 2 PI does not give the same result when drawing an arc clockwise and counterclockwise.
Look at this code snippet, on the first line I draw "clockwise" 3 red arcs with a start angle of 0 and an end angle of PI, 2*PI and 3*PI.
Then I draw "counterclockwise" 3 blue arcs with same parameters.
The 3rd result bewilders me... Can anyone explain this to me ?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// CLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 50, 40, 0, Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 50, 40, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// CLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 50, 40, 0, 3 * Math.PI, false);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
// COUNTERCLOCKWISE, angle = PI
ctx.beginPath();
ctx.arc(50, 150, 40, 0, Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 2 PI
ctx.beginPath();
ctx.arc(150, 150, 40, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 0, 3 * Math.PI, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>
According to specs:
If anticlockwise is false and endAngle-startAngle is equal to or greater than 2π, or, if anticlockwise is true and startAngle-endAngle is equal to or greater than 2π, then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.
Otherwise, the points at startAngle and endAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, are the start and end points respectively, and the arc is the path along the circumference of this ellipse from the start point to the end point, going anti-clockwise if anticlockwise is true, and clockwise otherwise. Since the points are on the ellipse, as opposed to being simply angles from zero, the arc can never cover an angle greater than 2π radians.
Put in maybe clearer pseudo-code:
if(
(anticlockwise === false && (endAngle - startAngle) >= 2π) ||
(anticlockwise === true && (startAngle - endAngle) >= 2π)
) {
arc_circumference = 2π;
}
else {
startAngle = startAngle % 2π;
endAngle = endAngle % 2π;
}
In your case, startAngle = 0, endAngle = 3π, anticlowkwise = true, if we run the above algorithm, we end up in the else case (0 - 3π < 2π) and endAngle is now (3π % 2π = 1π).
We could achieve the same output without the anticlockwise flag by swapping startAngle and endAngle:
var ctx = canvas.getContext("2d");
// COUNTERCLOCKWISE, angle = -3 PI (from OP)
ctx.beginPath();
ctx.arc(50, 150, 40, 0, 3 * Math.PI, true);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
// CLOCKWISE, angle = -3 PI
ctx.beginPath();
ctx.arc(50, 50, 40, 3 * Math.PI, 0);
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
<canvas id="canvas"></canvas>
When you go COUNTERCLOCKWISE, after 0 comes 2PI. You should try this instead:
// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 2 * Math.PI, 0, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
UPDATE:
After OP's comment I've added an animated demo:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
let delta = 0;
function Draw(){
requestAnimationFrame(Draw)
delta+= .01;
ctx.clearRect(0,0,c.width,c.height)
// CLOCKWISE: animating the end point
ctx.beginPath();
ctx.arc(50, 50, 40, 0, delta, false);
ctx.closePath();
ctx.stroke();
// CONTERCLOCKWISE, animating the start point
ctx.beginPath();
ctx.arc(150, 50, 40, 0,-delta, true);
ctx.closePath();
ctx.stroke();
}
Draw()
<canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/>

HTML canvas: drawing separated circles

I wanted to draw some circles on the canvas with the following layout: A circle right in the centre of the canvas and four circles at the mid points of the four edges of the canvas. I've put up my code here jsfiddle. The circles are rendered, however there are multiple filled paths between them too, resulting in an amalgamated mess.
I'm drawing all the circles by using
ctx.arc(points[i].x, points[i].y, radius, 0, 2 * Math.PI, true);
How do I remove them ?
EDIT: working jsfiddle
Put closePath after each circular-arc so that the circles are not connected:
ctx.arc(points[i].x, points[i].y, radius, 0, 2 * Math.PI, true);
ctx.closePath();
You are probably wanting to fill after each arc.
Something like this should work
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, true);
ctx.fill();
for(var i = 0; i < points.length; i++){
var _ = points[i];
ctx.beginPath();
ctx.arc(_.x, _.y, 25, 0, 2 * Math.PI, true);
ctx.fill();
}

Categories

Resources