As you can see in the attached GIF, the white dot moves in circular path.
How to make it move along the trajectory of the hexagon. in other words, make the dot follow the hexagon path?
I am rotating the white dot using the following code:
const angle = Math.PI / 6 * delta; // delta is the current time to increment the angle.
const radius = 300;
const x = center.x + Math.cos(angle) * radius;
const y = center.y + Math.sin(angle) * radius;
thank you
There are various ways of doing this. If you want to keep a constant angular rotation speed around the centre of the hexagon, then you could modify the radius of the moving object according to the angle of rotation.
First choose the radius corresponding to the vertices of the hexagon. In this example, I'm using a radius of 90px. The inner radius (of the inscribed circle tangential to the sides of the hexagon) is sqrt(0.75) times this value:
r_outer = 90.0
r_inner = r_outer * Math.sqrt(0.75)
If an angle of zero corresponds to the middle of one side of the hexagon, then by simple geometry the radius along this side will equal r_inner / Math.cos(theta) as theta varies from −30° to +30°. You can use a floating-point modulus operator to confine the angle to the range from 0 to 60°. To keep the angle within ±30° for the radius calculations, just add 30° to the angle before calculating the modulus, and subtract it afterwards.
So the complete calculation looks something like this:
r0 = 90.0 (Outer radius)
r1 = r0 * sqrt(0.75) (Inner radius)
d30 = 30 * pi/180 (30 degrees in radians)
d60 = 60 * pi/180 (60 degrees in radians)
r = r1 / cos((theta + d30) % d60 - d30)
x = x_center + sin(theta) * r
y = y_center - cos(theta) * r
Here's a working example:
let theta=0.0;
let r0 = 90.0; // Outer radius
let r1 = r0 * Math.sqrt(0.75); // Inner radius
let d30 = 30 * Math.PI/180; // 30 degrees in radians
let d60 = 60 * Math.PI/180; // 60 degrees in radians
function move_dot() {
theta += 0.025;
let r = r1 / Math.cos((theta + d30) % d60 - d30);
let x = Math.sin(theta) * r;
let y = -Math.cos(theta) * r;
document.getElementById('dot').style.cx=x.toFixed(2)+'px';
document.getElementById('dot').style.cy=y.toFixed(2)+'px';
}
document.body.onload=function(){setInterval(move_dot,30);}
#hex { fill:none; stroke:#aaf; stroke-width:1; }
#dot { fill:#000; stroke:none; cx:90px; cy:0px; }
<svg width="200" height="200" viewBox="-100 -100 200 200">
<path id="hex" d="M45-77.94 90 0 45 77.94-45 77.94-90 0-45-77.94Z"/>
<circle id="dot" cx="90" cy="0" r="5"/>
</svg>
Related
I am not so good in Mathematics. I have a requirement to draw an ellipse using 5 coordinates where user will click on 5 different position in a canvas and getting that clicked coordinate 1 ellipse will drawn. To draw ellipse in canvas I have the method
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle [, anticlockwise]);
Where I need the center position and 2radius of the ellipse. I only have 5coordinates of the perimeter of the ellipse. I get a matrics formula to calculate the ellipse.
ax2+bxy+cy2+dx+ey+f=0
I am unable to convert that equation to js. I am thankful to you if you please help me out to calculate the mazor and minor radius and center point of ellipse from 5 arbitary points
Having 5 points, you can find general formula of conic section (here ellipse) expanding determinant of this matrix (substitute xi,yi with your point coordinates):
(picture taken here)
Simple example to begin with
Using my answer for inverse problem
//calc implicit ellipse equation
//semiaxes rx, ry; rotated at fi radians; centered at (cx,cy)
//note: implicit form Ax^2+Bxy+Cy^2+Dx+Ey+F=0 (not 2B,2D,2E)
// in Pascal notation Sqr is squared
B := Sin(2 * Fi) * (ry * ry - rx * rx);
A := Sqr(ry * Cos(fi)) + Sqr(rx * Sin(fi));
C := Sqr(rx * Cos(fi)) + Sqr(ry * Sin(fi));
D := -B * cy - 2 * A * cx;
E := -2 * C * cy - B * cx;
F := C * cy * cy + A * cx * cx + B * cx * cy - rx * rx * ry * ry;
we can see that
Fi = 0.5 * atan2(B, A-C)
then
ry^2+rx^2 = A + C
ry^2-rx^2 = B / Sin(2*Fi)
so
ry = Sqrt((A + C + B / Sin(2*Fi))/2)
rx = Sqrt((A + C - B / Sin(2*Fi))/2)
(except for Fi=0 case, where we can extract semiaxes from A and C directly)
and then find cx, cy from D/E equation system
Also look at Wiki formulas for the same problem
I have created a blob with small points. I want my blob to show noise on its surface according to mouseX and mouseY. I want it to show high noise in the quadrant in which the mouse lies. I want it to be wavy. Below is my code.
var ctx = document.querySelector("canvas").getContext("2d");
var cx = 200;
var cy = 200;
var radius = 50;
var amp = 2;
var mouseX = 0;
var mouseY = 0;
document.querySelector("canvas").addEventListener("mousemove", function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob() {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
for (var a = 0; a < 360; a ++) {
var angle = a * Math.PI/180;
var x = cx + radius * Math.cos(angle) + Math.random() * amp;
var y = cy + radius * Math.sin(angle) + Math.random() * amp;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
requestAnimationFrame(drawTheBlob);
}
drawTheBlob();
<canvas width="400" height="400"></canvas>
Adds a sin wave on the circle. Use ctx.arc to draw the flat part of the circle for speed as drawing many circles with lines will be slow. See code for comments on what is done.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.lineWidth = 3;
ctx.lineJoin = "round";
var cx = 100;
var cy = 100;
var radius = 50;
var mouseX = 0;
var mouseY = 0;
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
document.querySelector("canvas").addEventListener("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
function drawTheBlob(time) { // time is passed from the requestAnimationFrame call
var amp = 0; // amplitude of noise
var wavePos = ((time / 1000) * Math.PI) * noiseWaveMoveSpeed;
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 400, 400);
ctx.beginPath();
ctx.strokeStyle = "#fff";
ctx.fillStyle = "red";
// draw arc for parts that have no noise as it is a log quicker
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
for (var a = 0; a < 1; a += 1 / steps) {
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
var angDist = Math.abs(angle - mouseDir); // find angular distance from mouse
// as a positive value, it does not mater
// what the sign is
if (angDist < quadWidth / 2) { // is angle distance within the range of effect
// normalise the distance (make it 0 to 1)
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0; // no noise
}
// amp will be zero if away from mouse direction and 0 to 1 the closer to
// mouse angle it gets.
// add a sin wave to the radius and scale it by amp
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp;
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
}
ctx.closePath(); // use close path to close the gap (only needed if you need to draw a line from the end to the start. It is not needed to match beginPath
ctx.fill();
ctx.stroke();
requestAnimationFrame(drawTheBlob);
}
requestAnimationFrame(drawTheBlob); // start this way so that you get the time argument
<canvas width="200" height="200"></canvas>
How it works.
Mouse direction
First we need the direction from the circle to the mouse. To do that we use the function Math.atan2 It takes the vector from the circle to the mouse and returns the direction in radians. The function is a little weird as it takes y first, then x.
var mouseDir = Math.atan2(mouseY - cy, mouseX - cx);
Draw arc to save CPU time
Now that we have the direction to the mouse we can draw the parts of the circle that has no noise using arc .
ctx.arc(cx, cy, radius, mouseDir + quadWidth / 2, mouseDir + Math.PI * 2 - quadWidth / 2);
The variable quadWidth is angular size of the noise bit so from the mouseDir we add half that angular width and draw the arc around to mouseDir plus 360deg take half the quadWidth.
Quick word on Radians
Almost all programming languages use radians to define angles, 360deg is equal to 2 * PI or 2 * 3.1415, which can be hard to get your head around, but there is good reason to use radians. For now just remember that a full circle in radians is 2 * Math.PI = 360deg, Math.PI = 180deg, Math.PI / 2 = 90deg, Math.PI / 4 = 45Deg and Math.PI / 180 = 1deg. You dont have to remember the digits just Math.PI is half a circle.
quadWidth from above is a constant defined as const quadWidth = Math.PI / 2; which is 90deg.
The for loop
The for loop only draws the (Math.PI / 2) 90deg section around the mouseDir, from 45 deg left to 45 right. or whatever you set quadWidth to.
for (var a = 0; a < 1; a += 1 / steps) {
I loop from 0 to 1 the number of steps that give a reasonably smooth curve. We can find what part of the noisy arc we are drawing by multiplying the value a *
quadWidth and adding that to the mouseDir - quadWidth / 2. This means that we start at mouseDir - 45deg and move clock wise to mouseDir + 45deg
var angle = (mouseDir - quadWidth / 2) + a * quadWidth;
Next i find how far that angle is from the mouseDir (could optimize it here a bit here but this way is a little more flexible, if you want to draw more noise on the other part of the arc)
var angDist = Math.abs(angle - mouseDir);
If that number is less than quadWidth / 2 convert the value to the range 0 to 1 where 0 is at the angle furthest from the mouse direction and 1 closest.
if (angDist < quadWidth / 2) {
amp = 1 - angDist / (quadWidth / 2);
} else {
amp = 0;
}
The sin wave
Now we calculate the radius of the circle at the current angle and add a sin wave to it. First the radius then the sin wave multiplied by the amp calculated in the last step. Where amp is zero none of the sin wave is added, where amp is 1 (in the direction of the mouse) the full sin wave is added.
var dist = radius + Math.sin(wavePos + noiseWaveFreq * angle) * noiseAmpMax * amp
The values wavePos, noiseWaveFreq, and noiseAmpMax control the animation of the sin wave. Play around with these values to get a feel of what they do, wavePos is calculated based on the time at the start of the function.
With dist we can calculate the x,y position for the next line of the circle
var x = cx + dist * Math.cos(angle);
var y = cy + dist * Math.sin(angle);
ctx.lineTo(x, y);
Experiment
I added some constants
const quadWidth = Math.PI / 2; // area of effect PI/2 is 90 degree
const steps = radius / quadWidth; // number steps around the circle matches 1 pixel per step,
const noiseAmpMax = 5; // in pixels
const noiseWaveMoveSpeed = 2; // speed of waves on circle in radians per second
const noiseWaveFreq = 16; // how many waves per 360 deg
To get a understanding what they do experiment and change the numbers to see what happens.
I am trying to make a train in js but i want add the velocity to the train.
but the train has an other axis relative to the canvas so how can i add the velocity relative to canvas
http://snapsoft.eu/example.png
If train system is rotated relative to stationary system by angle Theta, then you can transform velocity vector Vr (in rotated) to Vs (in stationary):
Vs.X = Vr.X * Cos(Theta) - Vr.Y * Sin(Theta)
Vs.Y = Vr.X * Sin(Theta) + Vr.Y * Cos(Theta)
If rotated system moves with velocity W, then add W also
Vs.X = W.X + Vr.X * Cos(Theta) - Vr.Y * Sin(Theta)
Vs.Y = W.Y + Vr.X * Sin(Theta) + Vr.Y * Cos(Theta)
In your example
Theta = -Pi/4
Vs.X = 0.5 * 0.707 + 0 * 0.707 = 0.3535
Vs.Y = - 0.5 * 0.707 + 0 * 0.707 = -0.3535
I don't understand a lot about the code below and I would like someone to break it down piece by piece.
$(function() {
var centerX = 200,
centerY = 200,
radius = 100,
width = 15,
angles = []
function draw() {
var ctx = document.getElementById("canvas").getContext("2d");
var angle;
//why 180
for (var i = 0; i < 180; i += 10) {
//is the "angle" increasing on every iteration? can you demostrate this please
angle = 2.1 + (i * Math.PI / 90);
angles.push(angle)
ctx.beginPath();
ctx.moveTo(
centerX + Math.sin(angle) * radius,
centerY + Math.cos(angle) * radius
);
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width)
);
ctx.closePath();
ctx.stroke();
}
}
draw()
console.log(angles)
var str = "";
angles.forEach(function(e, i) {
str += " " + i + " : " + e + "|| Math.sin = " + Math.sin(e) + "|| Math.sin * radius = " + Math.sin(e) * radius
$(".display").html(str)
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
<div class="display"></div>
like for this part angle = 2.1 + (i * Math.PI / 90); I see i is incrementing by 10 and the author is multiplying it by Math.PI / 90 which is equal to 0.0348888888888889. I know Math.PI is 180 degrees, but were not doing 180/90. We're increasing the number 2.1 by small amount. I can't put all the pieces together.
and in for(var i = 0; i < 180; i += 10){ why did the author choose 180. I know 180 degrees is a half a circle is that why they chose it?
And I always see in other code people use cos for the x coord and sin for the y coord. It doesn't look like the author uses it like the way i just described. could you elaborate.
Any help would be appreciated!
EDIT: I'm also wondering when we use for(var i = 0; i < 180; i += 10){ we get the dashes in a full circle and when i do i < 90 we get a half a circle but the half circle is not straight like against an x or y axis its on an angle. why is it not on the axis? why is it on an angle?
Let's start with SOH CAH TOA (link).
Given Right angled triangle (one side is 90 deg)..
Triangle has an angle.
Triangle has a side that's Opposite (O) to the angle.
Triangle has side that touches both, the angle and the right angle, called (A) Adjacent Side.
Triangle has a side that is called Hypotenuse (H). This is the side that also touches the Angle but doesn't make a right angle with Opposite side.
Now to find any side you need to know at minimum the angle and 1 another side.
Ex: You know angle, Q, is 40deg and Hypotenuse is 10. So what is Adjacent;
Looking at SOH CAH TOA. I See that I know H, and need to know A. CAH has both. So I choose CAH.
Cos Q = Adj/Hyp
Now if you put this Triangle inside circle. Then Hyp will become the radius, like so:
Cos Q = Adj/radius
Now to draw line going outward from a circle i need to know a starting point and the ending point of a line AND i need to make those points align with circle angles.
To get starting point i can just use circle's boundary.
So to get x,y for a point on circle boundary i solve this equation further..
Cos Q * radius = Adj
Cos Q * radius = x //adj is x
& for y...
SOH
Sin Q = Opp/Hyp
Sin Q = Opp/radius
Sin Q * radius = Opp
Sin Q * radius = y
So
x = Cos Q * radius
y = Sin Q * radius
or in js..
var x = Math.cos(angle) * radius;
var y = Math.sin(angle) * radius;
Now we have points that follow a circle's boundary. But for there to be line like we want, we need two points.
This code simply puts in a bigger radius, which gives bigger circle, which gives 2nd points what we needed.
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width));
Code Formatted to be clear:
var centerX = 200,
centerY = 200,
radius = 100,
width = 15,
angles = [],
ctx = document.getElementById("canvas").getContext("2d");
function draw() {
var angle;
for (var i = 0; i < 180; i += 10) {
angle = 2.1 + (i * Math.PI / 90);
ctx.beginPath();
ctx.moveTo(
centerX + Math.sin(angle) * radius,
centerY + Math.cos(angle) * radius);
ctx.lineTo(
centerX + Math.sin(angle) * (radius + width),
centerY + Math.cos(angle) * (radius + width));
ctx.closePath();
ctx.stroke();
}
}
draw();
The code you cite is a bit awkward.
To navigate around a circle, it would be more clear to increment i from 0 to 360 (as in 360 degrees).
for(var i=0; i<360; i++){
Instead, the coder increments from 0 to 180, but then they compensate for halving the 360 degrees by doubling the formula that converts degrees to radians.
// this is the usual conversion of degrees to radians
var radians = degrees * Math.PI / 180;
// This "compensating" conversion is used by the coder
// This doubling of the conversion compensates for halving "i" to 180
var radians = degrees * Math.PI / 90;
A clearer factoring of the iteration would look like this:
// the conversion factor to turn degrees into radians
var degreesToRadians = Math.PI / 180;
// navigate 360 degrees around a circle
for(var i=0; i<360; i++){
// use the standard degrees-to-radians conversion factor
var angle = i * degreesToRadians;
// This roughly rotates the angle so that it starts
// at the 12 o'clock position on the circle
// This is unnecessary if you're navigating completely
// around the circle anyway (then it doesn't matter where you start)
angle += 2.1;
....
}
And...
The code is intentionally drawing lines that radiate away from the circles circumference. The coder is not attempting to follow the circumference at all.
Im drawing a circle on html canvas, the circle is meant to indicate a "ship".
I have the current position (x, y) of the "Ship" object and randomly determine a degree (0 to 360) and an amount value.
I then want to alter the ship's current position by the degree and amount.
i.E. ship is currently at 100/100 (on a canvas).
I determine degree as 30 and amount as 50.
Now i would like to get the ships new location based on the assumption that 0 degree would indicate "straight up" and 180 degree would indicate straight down while 50 amount indicate a movement of 50 Pixels into the direction determined.
I know it has something to do with Radians, but unfortunally im unable to solve it further.
var ship = {
x: 100,
y: 100
}
var movement = {
degrees: 30,
amount: 50
}
Yes, all angles in JavaScript are in radians. In addition, the canvas context has 0° point to the right so you need to subtract 90° from all angles if you want 0° to be straight up:
var angle = (movement.degrees - 90) / 180 * Math.PI; // compensate angle -90°, conv. to rad
ship.x += movement.amount * Math.cos(angle); // move ship
ship.y += movement.amount * Math.sin(angle);
var movement = {
degrees: 30,
amount: 50
}
var ctx = document.querySelector("canvas").getContext("2d");
(function loop() {
ctx.clearRect(0, 0, 300, 150);
var ship = { // reset ship position for demo
x: 100,
y: 90
}
ctx.strokeRect(ship.x - 2, ship.y - 2, 4, 4);
ctx.fillText("From", ship.x + 5, ship.y);
var angle = (movement.degrees - 90) / 180 * Math.PI; // compensate angle -90°, conv. to rad
ship.x += movement.amount * Math.cos(angle); // move ship
ship.y += movement.amount * Math.sin(angle);
ctx.strokeRect(ship.x - 2, ship.y - 2, 4, 4);
ctx.fillText(movement.degrees + "°", ship.x + 5, ship.y);
movement.degrees++;
movement.degrees = movement.degrees % 360;
requestAnimationFrame(loop);
})();
<canvas></canvas>
You're right. You have to convert degrees to radians (1 degree = PI/180), then calculate appropriate sine and cosine.
var angle = degrees * Math.PI / 180;
var dx = Math.cos(angle) * amount;
var dy = Math.sin(angle) * amount;
x += dx;
y += dy;