I want to know how to get an angle of a line A-B from horizontal axis X. Other questions in SO do that only between two lines. I'm aware I can always draw second line A-C and calculate but I'm wondering if there's a faster method.
EDIT: I'm very sure I'm not doing a premature optimization.
You can use atan for that.
angle = atan((By-Ay)/(Bx-Ax))
private double Angulo(int x1, int y1, int x2, int y2)
{
double degrees;
// Avoid divide by zero run values.
if (x2 - x1 == 0)
{
if (y2 > y1)
degrees = 90;
else
degrees = 270;
}
else
{
// Calculate angle from offset.
double riseoverrun = (double)(y2 - y1) / (double)(x2 - x1);
double radians = Math.Atan(riseoverrun);
degrees = radians * ((double)180 / Math.PI);
// Handle quadrant specific transformations.
if ((x2 - x1) < 0 || (y2 - y1) < 0)
degrees += 180;
if ((x2 - x1) > 0 && (y2 - y1) < 0)
degrees -= 180;
if (degrees < 0)
degrees += 360;
}
return degrees;
}
If you need all four quadrants, Atan2 is more suitable than Atan.
public static int GetAngleBetweenPoints(PointF pt1, PointF pt2)
{
float dx = pt2.X - pt1.X;
float dy = pt2.Y - pt1.Y;
int deg = Convert.ToInt32(Math.Atan2(dy, dx) * (180 / Math.PI));
if (deg < 0) { deg += 360; }
return deg;
}
If
The angle is small,
you can live with small inaccuracies, and
You can use the angle in radians and not degrees,
then there is a fast solution: Under these conditions, you can assume that tan(a) = a = atan(a), and hence just omit the atan() call.
You could also use arccosine, if your line is in the form [r_x,r_y], where r_x is the change in x and r_y is the change in y.
angle = arccos( r_x/( r_x*r_x + r_y*r_y ) )
It's slightly more opaque, but it's basically the dot product law:
angle = arccos (r . v)
Where r and v are both unit vectors (vectors of length 1). In our case, v is the vector [1,0], and r is
[r_x,r_y] / (r_x^2+r_y^2)
in order to make it a unit vector.
The x-axis is actually a line with equation
y = 0
so you could use the solution you have already.
Related
This question already has answers here:
Algorithm to fill triangle
(3 answers)
Closed 5 years ago.
I have a triangle with 3 vertices,namely: (x1,y1,z1); (x2,y2,z2) and (x3,y3,z3).
I am using Bresenhams 3D Line algorithm,currently for generating 3D Points between two vertices of the triangle,depending on the resolution size(resSize).
void Bresenham3Algo(float x0, float y0, float z0, float x1, float y1, float z1)
{
float dx = Math.Abs(x1 - x0);
float sx = x0 < x1 ? resSize : -resSize;
float dy = Math.Abs(y1 - y0);
float sy = y0 < y1 ? resSize : -resSize;
float dz = Math.Abs(z1 - z0);
float sz = z0 < z1 ? resSize : -resSize;
float dm = Math.Max(dx, Math.Max(dy, dz)), i = dm;
x1 = y1 = z1 = dm / 2;
for (; ; )
{
Console.WriteLine(x0,y0,z0); //Printing points here
if (i <= 0) break;
x1 -= dx; if (x1 < 0) { x1 += dm; x0 += sx; }
y1 -= dy; if (y1 < 0) { y1 += dm; y0 += sy; }
z1 -= dz; if (z1 < 0) { z1 += dm; z0 += sz; }
i -= resSize;
}
}
So, As of now,I am calling the above function three times to generate 3D Sampling points on the boundary of the three Triangular edges.
Bresenham3Algo(x1,y1,z1,x2,y2,z2);
Bresenham3Algo(x2,y2,z2,x3,y3,z3);
Bresenham3Algo(x3,y3,z3,x1,y1,z1);
I am finding it difficult to find the internal sampling points lying inside the triangle.
For example,If I have the vertices (0,0,0); (5,0,0) and (3,3,0), With the help of the above function, I find 3D Points on the three triangular edges i.e.
(0,0,0),(1,0,0),(2,0,0),(3,0,0),(4,0,0),(5,0,0) -> first Edge
(3,3,0),(4,1,0),(4,2,0),(5,0,0) ->Second Edge
(0,0,0),(1,1,0),(2,2,0),(3,3,0) -> Third Edge
Now,I need to find the internal 3D Sampling points,lying inside the triangle i.e. (2,1,0) , (3,1,0), (3,2,0)
I would be glad,if someone can help me with this algo.
Thanks in Advance!
Assuming you aren't constrained to a regular grid, you do the following:
Rotate the triangle onto the x-y plane.
Draw and fill the triangle using your favourite algorithm in 2D (e.g. Bresenham).
Add z-values (all zero) to the points drawn
Rotate back to the original orientation.
I need to be able to check whether the angle between three points (A, B and C) which make up part of a shape is reflex (> PI radians), as in the diagram below (sorry for poor paint skills!):
My points should always be anti-clockwise, and I always want to measure the angle on the inside of the shape.
I am currently doing this using the following code:
//triangle[] is an array of the three points I am testing, corresponding
// to [A, B, C] on the diagram above
//Vectors from B to A and C
PointF toA = PointFVectorTools.difference(triangle[0], triangle[1]);
PointF toC = PointFVectorTools.difference(triangle[2], triangle[1]);
double angle = Math.Atan2(toB.Y, toB.X) - Math.Atan2(toA.Y, toA.X);
//Put angle in range 0 to 2 PI
if (angle < 0) angle += 2 * Math.PI;
return angle > Math.PI;
This has worked in all the cases I have tried up until now, but with these co-ords it does not work:
(Where B=(2,3) )
The angle I get back is ~-0.5, whereas I would expect ~+0.5. Any ideas why this is wrong?
UPDATE
I've attempted to implement Nico's solution, and while I understand it in theory I'm getting a real headache trying to implement it. Here is the code so far:
//Vector A -> B
float dx = triangle[1].X - triangle[0].X;
float dy = triangle[1].Y - triangle[0].Y;
//Left normal = (y, -x)
PointF leftDir = new PointF(dy, -dx);
//Vector B -> C
dx = triangle[2].X - triangle[1].X;
dy = triangle[2].Y - triangle[1].Y;
//Dot product of B->C and Left normal
float dot = dx * leftDir.X + dy * leftDir.Y;
return dot < 0;
In the following, I assume that the x-axis points to the right and the y-axis points upwards. If this is not the case in your scenario, you might need to switch some signs.
If you have the line segment (x1, y1) - (x2, y2) and points are sorted counter-clockwise, you know that the shape is left of the line segment. The orthogonal direction vector that points to the line segment's left is:
leftDir = (y1 - y2, x2 - x1)
Together with the line segment, this direction defines a half space. If the following angle is convex, the third point must lie in this half space. If that's not the case, the angle is concave (which you apparently call reflex):
You can determine if the point lies in the same half space with the dot product:
isConcave = dot(p3 - p2, leftDir) < 0
In code:
float dx = x3 - x2;
float dy = y3 - y2;
float dot = dx * leftDir.x + dy * leftDir.y
return dot < 0;
I'm not sure how toB in your code is defined, and also I'm not familar with PointF.
Anyway you should use the cosine rule c^2 = a^2 + b^2 - 2ab cos(C) (where a,b,c are the lengths of the sides of the triangle, and C is the angle subtending c):
public bool IsReflex(... triangle)
{
var a = GetVectorLength(triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y);
var b = GetVectorLength(triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y);
var c = GetVectorLength(triangle[2].x, triangle[2].y, triangle[0].x, triangle[0].y);
var cosC = (c*c - a*a - b*b) / (2*a*b);
var C = Math.Acos(cosC); // this returns a value between 0 and pi
return Math.Abs(C) > (Math.PI/2);
}
private double GetVectorLength(double x0, double y0, double x1, double y1)
{
// using Pythagoras
var sideX = x0 - x1;
var sideY = y0 - y1;
return Math.Sqrt(sideX*sideX + sideY*sideY);
}
I have a line (DrawLine-Event) with the points (a,b) (c,d) and now I want to calculate the angle of it but I don't know how.
I tried it like this:
double atan = ((d - b) / c - a)) * Math.PI / 180;
double solution = Math.Atan(atan);
int angle = Convert.ToInt32(Math.Round(solution * 180 / Math.PI));
You can use Vector.AngleBetween Method to get the angle
private Double angleBetweenExample()
{
Vector vector1 = new Vector(20, 30);
Vector vector2 = new Vector(45, 70);
Double angleBetween;
// angleBetween is approximately equal to 0.9548
angleBetween = Vector.AngleBetween(vector1, vector2);
return angleBetween;
}
I'm looking for a bit of math help. I have a game were a 2D heightmap is generated and then stretched over a sphere using a length/direction formula. Now I need to know how to calculate the height between 2 points on my heightmap.
What I know:
The array that holds the heightmap
The angle in radians to my object
how many points there are on the heightmap
My problem look somewhat like so:
image
more images
The red and blue lines are the 2 heightmap points, and the light blue is where I'd like to calculate the height at.
Here's my current code to do it, but it doesn't work to well.
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double offset = MathHelper.TwoPi / (heightmap.Length - 1);
//total brainfart attempt
double lowerAngle = offset * angle;
double upperAngle = offset * angle + offset;
//find heights
double height1 = heightmap[(int)lowerAngle];
double height2 = heightmap[(int)upperAngle];
//find offset angle
double u = angle - lowerAngle / (upperAngle - lowerAngle);
//return the height
return height1 + (height1 - height2) * u;
}
from my vegetation code, this seems to work okay, but is to rough to use for units and such, as they jump up/down as they move, due to it using only 1 heightmap point.
double[] hMap = planet.getHeightMap();
double i = hMap.Length / (Math.PI * 2);
this.height = hMap[(int)(angle * i)];
EDIT: example at end based on additional question info
Sounds to me like a linear interpolation - if you look at it from a 2d point of view, you've got two points:
(x1, y1) = point one on heightmap
(x2, y2) = point two on heightmap
and one point somewhere between (x1,x2) at an unknown height:
pu = (xu, yu)
A generic formula for LERP is:
pu = p0 + (p1 - p0) * u
where:
p0 = first value
p1 = second value
u = % where your unknown point lies between (p0,p1)
Here, we'll say p0 == y2 and p1 == y1. Now we need to determine "how far" the unknown point is between x1 and x2 - if you know the angles to the two heightmap points, this is easy:
u = ang(xu) - ang(x1) / (ang(x2) - ang(x1))
Alternatively, you could project your angle out to Max(y1,y2) and get the "unknown x pos" that way, then calculate the above.
So, let's try a contrived example:
p1 = point one in map = (1,2) therefore ang(p1) ~ 57 degrees
p2 = point two in map = (2,4) therefore ang(p2) ~ 114 degrees
note that here, the "x axis" is along the surface of the sphere, and the "y-axis" is the distance away from the center.
pu = object location = py #angle 100 degrees ~ 1.74 radians
px = (1.74 rad - 1 rad ) / (2 rad - 1 rad) = 0.74 / 1.0 = 0.74 => 74%
py = y0 + (y1 - y0) * u
= 2 + (4 - 2) * 0.74
= 2.96
Hopefully I didn't drop or misplace a sign there somewhere... :)
Ok, your example code - I've tweaked it a bit, here's what I've come up with:
First, let's define some helpers of my own:
public static class MathHelper
{
public const double TwoPi = Math.PI * 2.0;
public static double DegToRad(double deg)
{
return (TwoPi / 360.0) * deg;
}
public static double RadToDeg(double rad)
{
return (360.0 / TwoPi) * rad;
}
// given an upper/lower bounds, "clamp" the value into that
// range, wrapping over to lower if higher than upper, and
// vice versa
public static int WrapClamp(int value, int lower, int upper)
{
return value > upper ? value - upper - 1
: value < lower ? upper - value - 1
: value;
}
}
Our Test setup:
void Main()
{
var random = new Random();
// "sea level"
var baseDiameter = 10;
// very chaotic heightmap
heightmap = Enumerable
.Range(0, 360)
.Select(_ => random.NextDouble() * baseDiameter)
.ToArray();
// let's walk by half degrees, since that's roughly how many points we have
for(double i=0;i<360;i+=0.5)
{
var angleInDegrees = i;
var angleInRads = MathHelper.DegToRad(i);
Console.WriteLine("Height at angle {0}°({1} rad):{2} (using getheight:{3})",
angleInDegrees,
angleInRads,
heightmap[(int)angleInDegrees],
getheight(angleInRads));
}
}
double[] heightmap;
And our "getheight" method:
// assume: input angle is in radians
public double getheight(double angle)
{
//find out angle between 2 heightmap point
double dTheta = MathHelper.TwoPi / (heightmap.Length);
// our "offset" will be how many dThetas we are
double offset = angle / dTheta;
// Figure out two reference points in heightmap
// THESE MAY BE THE SAME POINT, if angle ends up
// landing on a heightmap index!
int lowerAngle = (int)offset;
int upperAngle = (int)Math.Round(
offset,
0,
MidpointRounding.AwayFromZero);
// find closest heightmap points to angle, wrapping
// around if we go under 0 or over max
int closestPointIndex = MathHelper.WrapClamp(
lowerAngle,
0,
heightmap.Length-1);
int nextPointIndex = MathHelper.WrapClamp(
upperAngle,
0,
heightmap.Length-1);
//find heights
double height1 = heightmap[closestPointIndex];
double height2 = heightmap[nextPointIndex];
// percent is (distance from angle to closest angle) / (angle "step" per heightmap point)
double percent = (angle - (closestPointIndex * dTheta)) / dTheta;
// find lerp height = firstvalue + (diff between values) * percent
double lerp = Math.Abs(height1 + (height2 - height1) * percent);
// Show what we're doing
Console.WriteLine("Delta ang:{0:f3}, Offset={1:f3} => compare indices:[{2}, {3}]",
dTheta,
offset,
closestPointIndex,
nextPointIndex);
Console.WriteLine("Lerping {0:p} between heights {1:f4} and {2:f4} - lerped height:{3:f4}",
percent,
height1,
height2,
lerp);
return lerp;
}
I am rotating points around a center point in 2D space. The points are the center point, the old mouse position, and the new mouse position. My rotation function works fine, and I can calculate the angle perfectly. But I want to calculate a negative angle if the user is moving their mouse in a direction which should be interpreted as counter-clockwise.
For example, moving the mouse toward the right (positive x-axis) should rotate clockwise if you are above (less than) the y value of the center point, but it should rotate counter-clockwise if you are actually below (greater than) the y value of the center point.
Here's what I have:
PointF centerPoint;
PointF oldPoint;
PointF newPoint;
double Xc = centerPoint.X;
double Yc = centerPoint.Y;
double Xb = oldPoint.X;
double Yb = oldPoint.Y;
double Xa = newPoint.X;
double Ya = newPoint.Y;
double c2 = (Math.Pow(Xb - Xa, 2) + Math.Pow(Yb - Ya, 2));
double a2 = (Math.Pow(Xb - Xc, 2) + Math.Pow(Yb - Yc, 2));
double b2 = (Math.Pow(Xa - Xc, 2) + Math.Pow(Ya - Yc, 2));
double a = Math.Sqrt(a2);
double b = Math.Sqrt(b2);
double val = (a2 + b2 - c2) / (2 * a * b);
double angle = Math.Acos(val);
So I need a way to make angle negative when it needs to be, so the points rotate clockwise or counter-clockwise to follow the mouse position.
Try this, but I'm not sure:
double v1x = Xb - Xc;
double v1y = Yb - Yc;
double v2x = Xa - Xc;
double v2y = Ya - Yc;
double angle = Math.Atan2(v1x, v1y) - Math.Atan2(v2x, v2y);
private double AngleFrom3PointsInDegrees(double x1, double y1, double x2, double y2, double x3, double y3)
{
double a = x2 - x1;
double b = y2 - y1;
double c = x3 - x2;
double d = y3 - y2;
double atanA = Math.Atan2(a, b);
double atanB = Math.Atan2(c, d);
return (atanA - atanB) * (-180 / Math.PI);
// if Second line is counterclockwise from 1st line angle is
// positive, else negative
}
It seems like all you need to do is
angle = angle > Math.PI ? angle - 2*Math.PI : angle;
at the end of your code. That will give you a clockwise rotation to the right of the line defined by centerPoint and oldPoint, and counter-clockwise to the left of it, regardless of orientation.
Given vectors (x1,y1) and (x2,y2), I would suggest computing the cross product and dot product, and then using Atan2() on them. That will work in all cases where both vectors are non-zero and vector lengths are "reasonable".