I'm doing a graph theory project, and I need to show the edge weight above each of the edges.
currently I'm using this method:
var x1; //starting point
var x2; //ending point
function setup() {
createCanvas(640, 480);
x1 = createVector(random(0, width/2), random(0, height/2)); //random position to the upper left
x2 = createVector(random(width/2, width), random(height/2, height)); //random position to the lower right
}
function draw() {
background(200);
stroke(0);
line(x1.x, x1.y, x2.x, x2.y); //draw a line beetween the points
d = dist(x1.x, x1.y, x2.x, x2.y);
var angle = atan2(x1.y - x2.y, x1.x - x2.x); // gets the angle of the line
textAlign(CENTER);
text("X1", x1.x + 5, x1.y + 5); // just to show where is the begining
text("X2", x2.x - 5, x2.y - 5); // just to show there is the end
fill(0);
signalx = x1.x > x2.x ? -1 : 1; // if the start is to the right of the end
signaly = x1.y > x2.y ? -1 : 1; // if the start is below the end
// I think i need to use the angle here
text(42, (x1.x + (d / 2) * signalx), (x1.y + (d / 2) * signaly));
}
the problem is that the result, well, is not as expected:
The idea is that the text I'm showing (42, the edge weight) is a little bit above the middle of the line, what is currently not happening.
I know that I have to take the angle of the line into consideration, but not sure where.
Thanks for any help, and if there's any need of more information let me know.
What you want to do is use linear interpolation. First, find the equation of the line in slope-intercept form, so you can solve for y (when you know x). (I'm just going to rename x1 to p1 and x2 to p2 for clarity.)
(math)
// with points (x1, y1) and (x2, y2)
y - y1 = m*(x - x1) // point-slope form (just a step)
y - y1 = m*x - m*x1
y = m*x - m*x1 + y1 // slope-intercept
Then, since x is the midpoint of the line, x equals the average of the two endpoints. And then calculate y, based on the above equation:
(code)
float m = (p2.y - p1.y) / (p2.x - p1.x);
int x = (x2 + x1) / 2;
int y = m*x - m*p1.x + p1.y;
Related
self.hitBall = function(ball, x, y) {
var angle = Math.atan2((x - ball.centerX), (y - ball.centerY));
ball.velocityY = (Math.sin(angle) * 10);
ball.velocityX = (Math.cos(angle) * 10);
};
So the function takes in the ball, which has a centerX variable and a centerY variabe. The x and y passed into the function is the x and y is the point the ball was hit. I want to make the ball travel in the direction it was hit from.
Not really sure why my code isn't working.. it's behaving very strangely and I'm not that good with trigonometry so I'm not really quite sure why it isn't working.
Two problems with your code:
Math.atan2() takes the arguments in (y, x) order. Most languages (Java, JavaScript, C, etc.) do this (except Microsoft Excel and some others, which use (x, y) order).
When you say "[make] the ball travel in the angle it was hit from", you want to subtract the hit point from the ball point. In other words, the vector is (ball.centerX - hitX, ball.centerY - hitY).
Thus, the solutions:
Solution 1:
var angle = Math.atan2((ball.centerY - y), (ball.centerX - x));
Solution 2 - do vector math without angles (equivalent calculation):
var dx = ball.centerX - x;
var dy = ball.centerY - y;
var norm = Math.sqrt(dx * dx + dy * dy);
ball.velocityX = (dx / norm) * 10;
ball.velocityY = (dy / norm) * 10;
I am trying to create a function that will return the 3 points coordinates of arrow head (isoscele triangle) that I want to draw at the end of a line.
The challenge is in the orientation (angle) of the line that can vary between 0 and 360 degree in the quadrant.
I have the following values:
//start coordinates of the line
var x0 = 100;
var y0 = 100;
//end coordinates of the line
var x1 = 200;
var y1 = 200;
//height of the triangle
var h = 10;
//width of the base of the triangle
var w = 30 ;
This is my function until now that returns the two point coordinates of the base of the triangle:
var drawHead = function(x0, y0, x1, y1, h, w){
var L = Math.sqrt(Math.pow((x0 - x1),2)+Math.pow((y0 - y1),2));
//first base point coordinates
var base_x0 = x1 + (w/2) * (y1 - y0) / L;
var base_y0 = y1 + (w/2) * (x0 - x1) / L;
//second base point coordinates
var base_x1 = x1 - (w/2) * (y1 - y0) / L;
var base_y1 = y1 - (w/2) * (x0 - x1) / L;
//now I have to find the last point coordinates ie the top of the arrow head
}
How can I determine the coordinates of the top of the triangle considering the angle of the line?
The head of the arrow will lie along the same line as the body of the arrow. Therefore, the slope of the line segment between (x1, y1) and (head_x, head_y) will be the same as the slope of the line segment between(x0, y0) and (x1, y1). Let's say that dx = head_x - x1 and dy = head_y - y1 and slope = (y1 - y0) / (x1 - x0). Therefore, dy / dx = slope. We also know that dx^2 + dy^2 = h^2. We can solve for dx in terms of slope and h. Then, dy = dx * slope. Once you have dx and dy, you can just add those to x1 and y1 to get the head point. Some pseudocode:
if x1 == x0: #avoid division by 0
dx = 0
dy = h
if y1 < y0:
dy = -h #make sure arrow head points the right way
else:
dy = h
else:
if x1 < x0: #make sure arrow head points the right way
h = -h
slope = (y1 - y0) / (x1 - x0)
dx = h / sqrt(1 + slope^2)
dy = dx * slope
head_x = x1 + dx
head_y = y1 + dy
I see it like this:
A=(x0,y0) , B=(x1,y1) are the known line endpoints
dir=B-A; dir/=|dir|; is unit vector of direction of line (|| is vector size)
dir.x=B.x-A.x;
dir.y=B.y-A.y;
dir/=sqrt((dir.x*dir.x)+(dir.y*dir.y));
so you can use it and its 90 degree rotation as as a basis vectors. Let q be the 90 degrees rotated vector, in 2D it is easy to obtain:
q.x=+dir.y
q.y=-dir.x
so now you can compute your wanted points:
C=B-(h*dir)+(w*q/2.0);
D=B-(h*dir)-(w*q/2.0);
it is just translation by h and w/2 along basis vectors
I'm trying to calculate an angle based on triangle sides, preferably with sin.
The first 2 are helper functions getDistance and getPointsDifference
I have these functions:
var getDistance = function(p1, p2){
var dx = p1.x - p2.x, dy = p1.y - p2.y;
return Math.sqrt(dx*dx + dy*dy);
}
var getPointsDifference = function(p1, p2){
return {
x: -1 * (p1.x - p2.x),
y: (p1.y - p2.y)
}
}
and finaly:
var getMenuChoice = function(cx,cy, x, y){
var distance = getDistance({x:cx,y:cy}, {x:x,y:y});
if (distance <= 100) {
console.log(1)
} else {
console.log(2)
}
var diff = getPointsDifference({x:cx,y:cy}, {x:x,y:y});
var a = Math.sin(diff.y/distance)
console.log("asdf:", a)
}
Could someone please show me what am I doing wrong? I would like to calculate the result in degrees.
update
I detect a lick on the screen which gives me a x,y, and then I subtract those x,y from cx and cy which are the center of the screen
This is called the angle (or direction) of the vector from (or to, depends on what you need) point of click to the center of the screen. There is no need in calculation of the distance and arcsin of the angle (instead of yours sin) - you can just use Math.atan2(dy, dx);.
dy is change in y (y2 - y1) and dx is change in x (x2 - x1) between those two points. You can use a regular Math.atan(dy / dx), but then you must be sure that you are not dividing by zero and have to take into account the signs of dy and dx to have answer in the correct quadrant. Math.atan2 will do it all for you. And the picture below is just a reminder.
And yes, the answer will be in radians, as it was mentioned in comments. Conversion is simple degrees = radians * (180 / Math.PI);
I have a line [from (x1, y1) to (x2, y2)] on the canvas that acts like a gun. I want the bullet to travel in the direction of the line (gun). Let the bullet also be a line. I know that from x1, y1 and x2, y2 I can find the slope of the line m and the y-intercept b. I'm also aware that the equation of a line is y = mx + b. I want the bullet to travel along the equation y = mx + b.
I do not want my bullet to look like a long line that starts from the end of my gun all the way to the boundary of the canvas. I want it to be a small line redrawn multiple times along the equation y = mx + b.
Can someone please guide me on how to draw my bullet's movement? Thanks in advance!
You can use a simple interpolation formula where you animate it by adjusting the factor f.
The formula is (shown only for x):
x = x1 + (x2 - x1) * f
An example on how to implement -
AN ONLINE DEMO
/// add click callback for canvas (id = demo)
demo.onclick = function(e) {
/// get mouse coordinate
var rect = demo.getBoundingClientRect(),
/// gun at center bottom
x1 = demo.width * 0.5,
y1 = demo.height,
/// target is where we click on canvas
x2 = e.clientX - rect.left,
y2 = e.clientY - rect.top,
/// factor [0, 1] is where we are at the line
f = 0,
/// our bullet
x, y;
loop();
}
Then we provide the following code for the loop
function loop() {
/// clear previous bullet (for demo)
ctx.clearRect(x - 2, y - 2, 6, 6);
/// HERE we calculate the position on the line
x = x1 + (x2 - x1) * f;
y = y1 + (y2 - y1) * f;
/// draw some bullet
ctx.fillRect(x, y, 3, 3);
/// increment f until it's 1
if (f < 1) {
f += 0.05;
requestAnimationFrame(loop);
} else {
ctx.clearRect(x - 2, y - 2, 6, 6);
}
}
To draw a "longer" bullet that follows the line you can either store an older value of the x/y pair and draw a line between that and current, or less optimal, calculate the position separately or even calculate the angle and use a fixed length.
Also worth to be aware of: the longer the line is the faster the bullet goes. You can calculate a delta value for f based on length (not shown in demo) to get around this.
I have 4 points 1,2,3,4 that closes a rectangle.
The points are in a array in this following way: x1 y1 x2 y2 x3 y3 x4 y4
The problem I have is that the rectangle can be rotated in a angle.
How can I calculate the original points (gray outline), and the angle?
I'm trying to reproduce this effect in javascript+css3-transform, so I need to first know the straight dimensions and then rotate with the css.
I just know if the rectangle is straight by comparing points e.g. y1==y2
if(x1==x4 && x2==x3 && y1==y2 && y4==y3){
rectangle.style.top = y1;
rectangle.style.left = x1;
rectangle.style.width = x2-x1;
rectangle.style.height = y4-y1;
rectangle.style.transform = "rotate(?deg)";
}
You can use any coordinate pair on the same side to calculate the rotation angle. Note that mathematic angles normally assume 0 as long the +ve X axis and increase by rotating anti–clockwise (so along the +ve Y axis is 90°, -ve X axis is 180° and so on).
Also, javascript trigonometry functions return values in radians that must be converted to degrees before being used in a CSS transform.
If the shape is not rotated more than 90°, then life is fairly simple and you can use the tanget ratio of a right angle triangle:
tan(angle) = length of opposite side / length of adjacent side
For the OP, the best corners to use are 1 and 4 so that rotation is kept in the first quadrant and clockwise (per the draft CSS3 spec). In javascript terms:
var rotationRadians = Math.atan((x1 - x4) / (y1 - y4));
To convert to degrees:
var RAD2DEG = 180 / Math.PI;
var rotationDegrees = rotationRadians * RAD2DEG;
If the rotation is more than 90°, you will need to adjust the angle. e.g. where the angle is greater than 90° but less than 180°, you'll get a -ve result from the above and need to add 180°:
rotationDegrees += 180;
Also, if you are using page dimentions, y coordinates increase going down the page, which is the opposite of the normal mathetmatic sense so you need to reverse the sense of y1 - y4 in the above.
Edit
Based on the orientation of points in the OP, the following is a general function to return the center and clockwise rotation of the rectangle in degrees. That's all you should need, though you can rotate the corners to be "level" yourself if you wish. You can apply trigonometric functions to calculate new corners or just do some averages (similar to Ian's answer).
/** General case solution for a rectangle
*
* Given coordinages of [x1, y1, x2, y2, x3, y3, x4, y4]
* where the corners are:
* top left : x1, y1
* top right : x2, y2
* bottom right: x3, y3
* bottom left : x4, y4
*
* The centre is the average top left and bottom right coords:
* center: (x1 + x3) / 2 and (y1 + y3) / 2
*
* Clockwise rotation: Math.atan((x1 - x4)/(y1 - y4)) with
* adjustment for the quadrant the angle is in.
*
* Note that if using page coordinates, y is +ve down the page which
* is the reverse of the mathematic sense so y page coordinages
* should be multiplied by -1 before being given to the function.
* (e.g. a page y of 400 should be -400).
*
* #see https://stackoverflow.com/a/13003782/938822
*/
function getRotation(coords) {
// Get center as average of top left and bottom right
var center = [(coords[0] + coords[4]) / 2,
(coords[1] + coords[5]) / 2];
// Get differences top left minus bottom left
var diffs = [coords[0] - coords[6], coords[1] - coords[7]];
// Get rotation in degrees
var rotation = Math.atan(diffs[0]/diffs[1]) * 180 / Math.PI;
// Adjust for 2nd & 3rd quadrants, i.e. diff y is -ve.
if (diffs[1] < 0) {
rotation += 180;
// Adjust for 4th quadrant
// i.e. diff x is -ve, diff y is +ve
} else if (diffs[0] < 0) {
rotation += 360;
}
// return array of [[centerX, centerY], rotation];
return [center, rotation];
}
The center of the rectangle is right between two opposite corners:
cx = (x1 + x3) / 2
cy = (y1 + y3) / 2
The size of the rectangle is the distance between two points:
w = sqrt(pow(x2-x1, 2) + pow(y2-y1, 2))
h = sqrt(pow(x3-x2, 2) + pow(y3-y2, 2))
The corners of the gray rectangle can be calculated from the center and the size, for example the top left corner:
x = cx - w / 2
y = cy - h / 2
The angle is the arctangent of a side of the square:
a = arctan2(y4 - y1, x4 - x1)
(I'm not sure exactly which angle it returns, or what angle you expect for that matter, so you get to test a bit.)
This is how you get the angle between the vertical pink line and the black line starting at the pink line intersection:
var deg = 90 - Math.arctan((x2-x1) / (y2-y1));
The dimensions can be calculated with the help of the Pythagoras theorem:
var width = Math.sqrt((x2-x1)^2 / (y2-y1)^2));
var height = Math.sqrt((x1-x4)^2) / (y4-y1)^2));
The positional coordinates (left and top) are the averages of x1 and x3 and y1 and y3 respectively.
var left = Math.floor((x1 + x3) / 2);
var top = Math.floor((y1 + y3) / 2);
You want to use the negative-margin trick.
var marginLeft = -Math.ceil(width / 2);
var marginTop = -Math.ceil(height / 2);