I'm trying to learn some basic vector math, but I can't seem to get this method for rotating a point to work. The magnitude of the rotated vectors is scaling up and I don't know what's up with the angle.
Here's the relevant function. I'm working in javascript/HTML canvas.
function rotate(point, center, angle) {
var theta = (Math.PI / 180) * angle,
cX = center.pos.x,
cY = center.pos.y,
pX = point.pos.x,
pY = point.pos.y,
pCos = Math.cos(theta),
pSin = Math.sin(theta),
x = pX - cX,
y = pY - cY;
x = (x * pCos - y * pSin) + cX;
y = (x * pSin + y * pCos) + cY;
return {x: Math.floor(x), y: Math.floor(y)};
}
Here's a jsbin of the weird result
The function is almost right but you are just using the modified x value to calculate y
function rotate(point, center, angle) {
var theta = (Math.PI / 180) * angle,
cX = center.pos.x,
cY = center.pos.y,
pX = point.pos.x,
pY = point.pos.y,
pCos = Math.cos(theta),
pSin = Math.sin(theta),
x = pX - cX,
y = pY - cY;
/* You had
x = (x * pCos - y * pSin) + cX; // you change x on this line
y = (x * pSin + y * pCos) + cY; /// then used the modified x to get y
*/
// this will fix the problem
var xx = (x * pCos - y * pSin) + cX;
var yy = (x * pSin + y * pCos) + cY;
return {x: Math.floor(xx), y: Math.floor(yy)};
}
Related
I realize this is a simple Trigonometry question, but my high school is failing me right now.
Given an angle, that I have converted into radians to get the first point. How do I figure the next two points of the triangle to draw on the canvas, so as to make a small triangle always point outwards to the circle. So lets say Ive drawn a circle of a given radius already. Now I want a function to plot a triangle that sits on the edge of the circle inside of it, that points outwards no matter the angle. (follows the edge, so to speak)
function drawPointerTriangle(ctx, angle){
var radians = angle * (Math.PI/180)
var startX = this.radius + this.radius/1.34 * Math.cos(radians)
var startY = this.radius - this.radius/1.34 * Math.sin(radians)
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
}
Example
You don't say what type of triangle you want to draw so I suppose that it is an equilateral triangle.
Take a look at this image (credit here)
I will call 3 points p1, p2, p3 from top right to bottom right, counterclockwise.
You can easily calculate the coordinate of three points of the triangle in the coordinate system with the origin is coincident with the triangle's centroid.
Given a point belongs to the edge of the circle and the point p1 that we just calculated, we can calculate parameters of the translation from our main coordinate system to the triangle's coordinate system. Then, we just have to translate the coordinate of two other points back to our main coordinate system. That is (x1,y1) and (x2,y2).
You can take a look at the demo below that is based on your code.
const w = 300;
const h = 300;
function calculateTrianglePoints(angle, width) {
let r = width / Math.sqrt(3);
let firstPoint = [
r * Math.cos(angle),
r * Math.sin(angle),
]
let secondPoint = [
r * Math.cos(angle + 2 * Math.PI / 3),
r * Math.sin(angle + 2 * Math.PI / 3),
]
let thirdPoint = [
r * Math.cos(angle + 4 * Math.PI / 3),
r * Math.sin(angle + 4 * Math.PI / 3),
]
return [firstPoint, secondPoint, thirdPoint]
}
const radius = 100
const triangleWidth = 20;
function drawPointerTriangle(ctx, angle) {
var radians = angle * (Math.PI / 180)
var startX = radius * Math.cos(radians)
var startY = radius * Math.sin(radians)
var [pt0, pt1, pt2] = calculateTrianglePoints(radians, triangleWidth);
var delta = [
startX - pt0[0],
startY - pt0[1],
]
pt1[0] = pt1[0] + delta[0]
pt1[1] = pt1[1] + delta[1]
pt2[0] = pt2[0] + delta[0]
pt2[1] = pt2[1] + delta[1]
ctx.beginPath();
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
[x1, y1] = pt1;
[x2, y2] = pt2;
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.fillStyle = '#FF0000';
ctx.fill();
}
function drawCircle(ctx, radius) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = '#000';
ctx.fill();
}
function clear(ctx) {
ctx.fillStyle = '#fff';
ctx.fillRect(-w / 2, -h / 2, w, h);
}
function normalizeAngle(pointCoordinate, angle) {
const [x, y] = pointCoordinate;
if (x > 0 && y > 0) return angle;
else if (x > 0 && y < 0) return 360 + angle;
else if (x < 0 && y < 0) return 180 - angle;
else if (x < 0 && y > 0) return 180 - angle;
}
function getAngleFromPoint(point) {
const [x, y] = point;
if (x == 0 && y == 0) return 0;
else if (x == 0) return 90 * (y > 0 ? 1 : -1);
else if (y == 0) return 180 * (x >= 0 ? 0: 1);
const radians = Math.asin(y / Math.sqrt(
x ** 2 + y ** 2
))
return normalizeAngle(point, radians / (Math.PI / 180))
}
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.querySelector('canvas');
const angleText = document.querySelector('.angle');
const ctx = canvas.getContext('2d');
ctx.translate(w / 2, h / 2);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, 0);
canvas.addEventListener('mousemove', _.throttle(function(ev) {
let mouseCoordinate = [
ev.clientX - w / 2,
ev.clientY - h / 2
]
let degAngle = getAngleFromPoint(mouseCoordinate)
clear(ctx);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, degAngle)
angleText.innerText = Math.floor((360 - degAngle)*100)/100;
}, 15))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<canvas width=300 height=300></canvas>
<div class="angle">0</div>
reduce the radius, change the angle and call again cos/sin:
function drawPointerTriangle(ctx, angle)
{
var radians = angle * (Math.PI/180);
var radius = this.radius/1.34;
var startX = this.center.x + radius * Math.cos(radians);
var startY = this.center.y + radius * Math.sin(radians);
ctx.moveTo(startX, startY);
radius *= 0.9;
radians += 0.1;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
radians -= 0.2;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(startX, startY);
}
the resulting triangle's size is proportional to the size of the circle.
in case you need an equilateral, fixed size triangle, use this:
//get h by pythagoras
h = sqrt( a^2 - (a/2)^2 );)
//get phi using arcustangens:
phi = atan( a/2, radius-h );
//reduced radius h by pythagoras:
radius = sqrt( (radius-h)^2 + (a/2)^2 );
radians += phi;
...
radians -= 2*phi;
...
I have program where I click three times, each click creating a point on canvas. I then calculate angle between those three points like this:
function find_angle(A, B, C) {
var AB = Math.sqrt(Math.pow(B.x - A.x, 2) + Math.pow(B.y - A.y, 2));
var BC = Math.sqrt(Math.pow(B.x - C.x, 2) + Math.pow(B.y - C.y, 2));
var AC = Math.sqrt(Math.pow(C.x - A.x, 2) + Math.pow(C.y - A.y, 2));
return Math.acos((BC * BC + AB * AB - AC * AC) / (2 * BC * AB));
}
In example picture above, the calculated angle is 93°. I need to move point 3 by -3° so the points make exactly 90°. I have this function for it:
var angleToCorrect = alpha * (Math.PI / 180) - 90 * (Math.PI / 180);
correct_angle(point2, point3, angleToCorrect)
...
function correct_angle(p2, p3, angle) {
var x = p2.x - p3.x;
var y = p2.y - p3.y;
var r = Math.sqrt(x * x + y * y); //circle radius. origin of the circle is point 2
return {
X: p2.x + (Math.cos(angle) * r),
Y: p2.y + (Math.sin(angle) * r)
};
}
Now, this function should return new x and y for point 3 with corrected angle to 90°. Yet the coordinates don't agree with what I expect. Can someone point out what I'm doing wrong?
To calculate the new position it isn't enough to provide just two of the points since the angle is measured between the three.
So inside this function you have to figure out what the current angle of the vector between point 1 and point 2 is. Javascript offers a nifty built-in function for this Math.atan2()
Now that we know the angle (in radians) we need to add the new angle to it. This makes sure we can place point 3 correctly.
function correct_angle(p1, p2, p3, angle)
{
var currentAngle=Math.atan2(p1.y-p2.y, p1.x-p2.x);
currentAngle+=angle;
var x = p2.x - p3.x;
var y = p2.y - p3.y;
var r = Math.sqrt(x * x + y * y);
return {
X: p2.x + (Math.cos(currentAngle) * r),
Y: p2.y + (Math.sin(currentAngle) * r)
};
}
The angle parameter of the function should be the target angle in radians (90 or 1.5707963267949 in your case)
Here's an interactive example:
Point = function(x, y) {
this.x = x;
this.y = y;
}
var pointA = new Point(162, 39);
var pointB = new Point(105, 161);
var pointC = new Point(211, 242);
var context = document.getElementById("canvas").getContext("2d");
function correct() {
var newPoint = correct_angle(pointA, pointB, pointC, 1.5707963267949);
pointC.x = newPoint.X;
pointC.y = newPoint.Y;
draw();
}
function correct_angle(p1, p2, p3, angle) {
var currentAngle = Math.atan2(p1.y - p2.y, p1.x - p2.x);
currentAngle += angle;
var x = p2.x - p3.x;
var y = p2.y - p3.y;
var r = Math.sqrt(x * x + y * y);
return {
X: p2.x + (Math.cos(currentAngle) * r),
Y: p2.y + (Math.sin(currentAngle) * r)
};
}
function draw() {
context.clearRect(0, 0, 400, 300);
context.fillStyle = "red";
context.beginPath();
context.arc(pointA.x, pointA.y, 10, 0, 2 * Math.PI);
context.fill();
context.beginPath();
context.arc(pointB.x, pointB.y, 10, 0, 2 * Math.PI);
context.fill();
context.beginPath();
context.arc(pointC.x, pointC.y, 10, 0, 2 * Math.PI);
context.fill();
}
draw();
<canvas id="canvas" width="400" height="300" style="background-color:#dddddd;"></canvas>
<button onclick="correct()" style="float:left">
correct me
</button>
I am trying to find the X, Y points on a circle where 0 degrees starts at the top of the circle and moves clockwise. Typically, to find the x, y coordinates on a circle with a known radius and angle you could simply use the formula x = r(cos(degrees°)), y = r(sin(degrees°)). The circle would look like this and the degrees would expand counterclockwise from 0°.
However, I am using a circle where the 0° starts at the top and the degrees expand as one moves clockwise around the circle. Supposing that var r = 60; and var degrees = 130; what formula could I use (or javascript methods) to determine the X, Y values. Note: I can assume an origin point of 0, 60 because r = 60. Thanks.
As full circle has 2 radiants so you could calculate point coordinates for your circle with following formula:
x = radius * Math.sin(Math.PI * 2 * angle / 360);
y = radius * Math.cos(Math.PI * 2 * angle / 360);
var radius = 60;
var angle = 140;
var x = radius * Math.sin(Math.PI * 2 * angle / 360);
var y = radius * Math.cos(Math.PI * 2 * angle / 360);
console.log('Points coors are x='+
Math.round(x * 100) / 100 +', y=' +
Math.round(y * 100) / 100)
The trick is to convert your problem into the problem you know how to solve. You can do this by subtracting 90 degrees from your angle and negating y, i.e. x=r cos(theta-90) and y = -r sin(theta-90). In JavaScript:
function circleXY(r, theta) {
// Convert angle to radians
theta = (theta-90) * Math.PI/180;
return {x: r*Math.cos(theta),
y: -r*Math.sin(theta)}
}
for (var theta=0; theta<=360; theta += 30) {
var answer = circleXY(60, theta);
console.log('(x, y) = ' + '(' + answer.x + ', ' + answer.y + ') for theta=' + theta);
}
produces the following result:
(x, y) = (3.67394039744206e-15, 60) for theta=0
(x, y) = (30.000000000000007, 51.96152422706631) for theta=30
(x, y) = (51.96152422706632, 29.999999999999996) for theta=60
(x, y) = (60, 0) for theta=90
(x, y) = (51.96152422706632, -29.999999999999996) for theta=120
(x, y) = (30.000000000000007, -51.96152422706631) for theta=150
(x, y) = (3.67394039744206e-15, -60) for theta=180
(x, y) = (-29.999999999999986, -51.96152422706632) for theta=210
(x, y) = (-51.96152422706632, -29.999999999999996) for theta=240
(x, y) = (-60, -7.34788079488412e-15) for theta=270
(x, y) = (-51.96152422706631, 30.000000000000007) for theta=300
(x, y) = (-30.00000000000003, 51.961524227066306) for theta=330
(x, y) = (-1.1021821192326178e-14, 60) for theta=360
Should be
x = Math.cos(Math.PI * 2 * angle/360);
and
y = Math.sin(Math.PI * 2 * angle/360);
I am trying to create a graph using parabola equation (y=x*x). But I am bit confused to calculate the value for control point. How should I calculate the control point value.
My JavaScript function:
function drawParabola()
{
ctx.beginPath();
for(i=-2;i<=2;i++)
{
//formual y= x * x;
y = i * i;
x = i;
if (i == -2) {
ctx.moveTo((5 + x) * 30, Math.abs((-5 + y)) * 30);
}
else {
//ctx.lineTo((5 + x) * 30, Math.abs((-5 + y)) * 30);
context.quadraticCurveTo(**?**, **?**, (5 + x) * 30, Math.abs((-5 + y)) * 30);
}
ctx.strokeStyle = 'orange';
ctx.stroke();
}
}
The control point for a quadratic curve is the intersection point of the tangents.
context.beginPath();
context.strokeStyle = 'orange';
for(i=-2;i<=2;i++) {
// Current point
x1 = i;
y1 = x1 * x1;
y1p = 2 * x1; // derivitive
// Previous point
x0 = i - 1;
y0 = x0 * x0;
y0p = 2 * x0; // derivitive
// Find intersection of tangents
// line0: y - y0 = y0p * (x - x0)
// line1: y - y1 = y1p * (x - x1)
//
// line0: y = y0p * x - y0p * x0 + y0
// line1: y = y1p * x - y1p * x1 + y1
//
// y0p * x - y0p * x0 + y0 = y1p * x - y1p * x1 + y1
// y0p * x - y1p * x = y0p * x0 - y0 - y1p * x1 + y1
// x = (y0p * x0 - y0 - y1p * x1 + y1) / (y0p - y1p)
// Intersection point of tangents
xi = (y0p * x0 - y0 - y1p * x1 + y1) / (y0p - y1p);
yi = y0p * xi - y0p * x0 + y0;
// Rescale for rendering
cx = (5 + x1) * 30;
cy = (5 + y1) * 30;
cix = (5 + xi) * 30;
ciy = (5 + yi) * 30;
if (i == -2) {
context.moveTo(cx, cy);
}
else {
//context.lineTo(cx, cy);
context.quadraticCurveTo(cix, ciy, cx, cy);
}
}
context.stroke();
What I have:
A lot of bubbles. But to make it more simple, let's say I have two. When they meet each other they collide and change the direction.
var xVelocityBubble1 = Math.random();
var yVelocityBubble1 = Math.random();
var xVelocityBubble2 = Math.random();
var yVelocityBubble2 = Math.random();
moveBubbles = function() {
xbubble1 += xVelocityBubble1;
ybubble1 += yVelocityBubble1;
xbubble2 -= xVelocityBubble2;
xbubble2 -= yVelocityBubble2;
if (Math.sqrt(Math.pow(xbubble1 - xbubble2, 2) + Math.pow(ybubble1 - ybubble2, 2)) < radius * 2) {
xVelocityBubble1 *= -1;
yVelocityBubble1 *= -1;
xVelocityBubble2 *= -1;
yVelocityBubble2 *= -1;
}
}
What I want:
I do not want the circles to simply change the direction, because that looks strange and boring. So I want to calculate the angle where the circle meet, and from that I need to calculate how much momentum they exchange and how that affects each circle.
My problem:
I really do not know how to calculate the angle and the momentum! Any hints?
To get the angle between those two bubbles if they collide do as follows:
get the direction vector in which one of those bubbles were moving
direction = {x: Math.abs(xVelocityBubble1), y: Math.abs(yVelocityBubble1)};
Then normalize that vector (divide it's x and y components by it's length)
After doing that you'll have the cosine of the angle as the x component and the sine as the y, just use any of them in Math.acos or Math.asin and you'll have the angle in which they collided.
This code shows collision of asteroids:
for (var i = 0; i < asteroidsLength; i++) {
var tmpAsteroid = asteroids[i];
for (var j = i + 1; j < asteroidsLength; j++) {
var tmpAsteroidB = asteroids[j];
var dX = tmpAsteroidB.x - tmpAsteroid.x;
var dY = tmpAsteroidB.y - tmpAsteroid.y;
var distance = Math.sqrt((dX * dX) + (dY * dY));
if (distance < tmpAsteroid.radius + tmpAsteroidB.radius) {
var angle = Math.atan2(dY, dX);
var sine = Math.sin(angle);
var cosine = Math.cos(angle);
// Rotate asteroid position
var x = 0;
var y = 0;
// Rotate asteroidB position
var xB = dX * cosine + dY * sine;
var yB = dY * cosine - dX * sine;
// Rotate asteroid velocity
var vX = tmpAsteroid.vX * cosine + tmpAsteroid.vY * sine;
var vY = tmpAsteroid.vY * cosine - tmpAsteroid.vX * sine;
// Rotate asteroidB velocity
var vXb = tmpAsteroidB.vX * cosine + tmpAsteroidB.vY * sine;
var vYb = tmpAsteroidB.vY * cosine - tmpAsteroidB.vX * sine;
// Conserve momentum
var vTotal = vX - vXb;
vX = ((tmpAsteroid.mass - tmpAsteroidB.mass) * vX + 2 * tmpAsteroidB.mass * vXb) / (tmpAsteroid.mass + tmpAsteroidB.mass);
vXb = vTotal + vX;
// Move asteroids apart
xB = x + (tmpAsteroid.radius + tmpAsteroidB.radius);
// Rotate asteroid positions back
tmpAsteroid.x = tmpAsteroid.x + (x * cosine - y * sine);
tmpAsteroid.y = tmpAsteroid.y + (y * cosine + x * sine);
tmpAsteroidB.x = tmpAsteroid.x + (xB * cosine - yB * sine);
tmpAsteroidB.y = tmpAsteroid.y + (yB * cosine + xB * sine);
// Rotate asteroid velocities back
tmpAsteroid.vX = vX * cosine - vY * sine;
tmpAsteroid.vY = vY * cosine + vX * sine;
tmpAsteroidB.vX = vXb * cosine - vYb * sine;
tmpAsteroidB.vY = vYb * cosine + vXb * sine;
};
};