I have the following function in C++ that is supposed to find the area of a triangle using Heron's formula. I haven't made any mistake in the math but it doesn't produce the right result! I am looking at it for more than 3 hours and I can't find any mistake. What am I missing?
float TriangleArea(float x0, float y0, float x1, float y1, float x2, float y2)
{
float area_triangle;
float a, b, c, s;
a=std::sqrt((x0-x1)*(x0-x1)-(y0-y1)*(y0-y1));
b=std::sqrt((x1-x2)*(x1-x2)-(y1-y2)*(y1-y2));
c=std::sqrt((x0-x2)*(x0-x2)-(y0-y2)*(y0-y2));
s=(a+b+c)/2;
area_triangle=std::sqrt((s*(s-a)*(s-b)*(s-c)));
return area_triangle;
}
I haven't made any mistake in the math but it doesn't produce the right result!
If it's not producing the right result, then I think there's a very high chance you made a mistake in the math.
a=std::sqrt((x0-x1)*(x0-x1)-(y0-y1)*(y0-y1));
That - looks suspicious. I'm assuming you're trying to find the distance between (x0, y0) and (x1, y1). If that's the case, you should be adding the two quantities, not subtracting.
I'm not familiar with Heron's formula, but you can use this simpler formula:
area = std::abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) / 2;
Edit: I forgot to mention the abs function for the simplified formula, which was pointed out by Antonio.
I think you have a sign error. The values a, b, and c are the triangle sides, right?
a=std::sqrt((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1));
^
An alternative formula (with demonstration) can be found here
area = fabs((x0 - x2) * (y1 - y0) - (x0 - x1) * (y2 - y0) ) / 2;
(As a side note, if the formula has to be used to test points collinearity, the absolute value can be removed... But then you might need it anyway, as working with floating points you cannot test equality but against some epsilon value)
Related
I have a 2D Coordinate system where The Origin Starts at the top Left
(Y Is higher as I move downward)
I am Given Two Points in Space, Lets Say Point A, and Point B.
How can I determine that next Point on the line From Point A to Point B?
For example, I have Point A(10, 10) and Point B (1,1)
I know the point I'm looking for is (9,9).
But how do I do this mathematically?
For say a more complicated Set of points
A(731, 911) and B(200, 1298)
I'm trying to move my mouse, one pixel at a time from its current location to a new one.
This doesn't work, but honestly I'm stumped where to begin.
int rise = x2 - 460; //(460 is Point A x)
int run = y2 - 360;//(360 is Point A Y)
float slope = rise / run;
int newx = x1 + ((slope / slope) * 1); //x1 Is my current mouse POS x
int newy = y1 + (slope * -1);//y1 is my current mouse Pos y
It almost works but seems inverted, and wrong.
You already have the slope, so to get the next point on the line (there are infinitely many), you have to choose a step value or just arbitrarily pick one of the points.
Given A(y1, x1), your goal in finding a new point, B(y2, x2) is that it must satisfy the equation: (y2 - y1) / (x2 - x1) = slope.
To simplify, (x2 - x1) * slope = y2 - y1
You already have x1, slope, y1, and you can choose any arbitrary x2, so when you plug all those into the equation, you can simplify it further to:
y2 = (x2 - x1) * slope + y1
To illustrate this with your other points (A(731, 911) and C(200, 1298)) and say you want to find a new point B, we can proceed as follows:
Find the slope first:
float slope = (1298 - 911) / (200 - 731); // -0.728813559322
Choose x and solve for y:
x1 = 731, slope = -0.728813559322, y1 = 911
Choose x2 = 500 and solving for y2, we get:
float y2 = (500 - 731) * -0.728813559322 + 911; // 1079.355932203382
So your new point is:
B(500, 1079.355932203382)
You can verify this new point still has the same slope to point C
With A = (x1,y1) and B = (x2,y2) the line is (expressed in two same equations):
(1) y = (x-x1)*(y2-y1)/(x2-x1) + y1
(2) x = (y-y1)*(x2-x1)/(y2-y1) + x1
To find next point, put x1+1 (or x1-1 you know) in equation (1) and find y and also put y1+1 or y1-1 in equation (2) and find x.
You can decide which one is better choice. Take care of vertical or horizontal lines, where one of the equations won't work.
NOTE: do not cast floating point result to int. Do round instead.
I'm running into a problem where my square of X is always becoming infinite leading to the resulting distance also being infinite, however I can't see anything wrong with my own maths:
// Claculate distance
xSqr = (x1 - x2) * (x1 - x2);
ySqr = (y1 - y2) * (y1 - y2);
zSqr = (z1 - z2) * (z1 - z2);
double mySqr = xSqr + ySqr + zSqr;
double myDistance = sqrt(mySqr);
When I run my program I get user input for each of the co-ordinates and then display the distance after I have run the calulation.
If your inputs are single-precision float, then you should be fine if you force double-precision arithmetic:
xSqr = double(x1 - x2) * (x1 - x2);
// ^^^^^^
If the inputs are already double-precision, and you don't have a larger floating-point type available, then you'll need to rearrange the Euclidean distance calculation to avoid overflow:
r = sqrt(x^2 + y^2 + z^2)
= abs(x) * sqrt(1 + (y/x)^2 + (z/x)^2)
where x is the largest of the three coordinate distances.
In code, that might look something like:
double d[] = {abs(x1-x2), abs(y1-y2), abs(z1-z2)};
if (d[0] < d[1]) swap(d[0],d[1]);
if (d[0] < d[2]) swap(d[0],d[2]);
double distance = d[0] * sqrt(1.0 + d[1]/d[0] + d[2]/d[0]);
or alternatively, use hypot, which uses similar techniques to avoid overflow:
double distance = hypot(hypot(x1-x2,y1-y2),z1-z2);
although this may not be available in pre-2011 C++ libraries.
Try this:
long double myDistance=sqrt(pow(x1-x2,2.0)+pow(y1-y2,2.0)+pow(z1-z2,2.0));
I figured out what was going on, I had copied and pasted the code for setting x1, y1 and z1 and forgot to change it to x2 y2 and z2, It's always the silliest of things with me :P thanks for the help anyway guys
I want to find out the clockwise angle between 2 vectors(2D, 3D).
The clasic way with the dot product gives me the inner angle(0-180 degrees) and I need to use some if statements to determine if the result is the angle I need or its complement.
Do you know a direct way of computing clockwise angle?
2D case
Just like the dot product is proportional to the cosine of the angle, the determinant is proprortional to its sine. So you can compute the angle like this:
dot = x1*x2 + y1*y2 # dot product between [x1, y1] and [x2, y2]
det = x1*y2 - y1*x2 # determinant
angle = atan2(det, dot) # atan2(y, x) or atan2(sin, cos)
The orientation of this angle matches that of the coordinate system. In a left-handed coordinate system, i.e. x pointing right and y down as is common for computer graphics, this will mean you get a positive sign for clockwise angles. If the orientation of the coordinate system is mathematical with y up, you get counter-clockwise angles as is the convention in mathematics. Changing the order of the inputs will change the sign, so if you are unhappy with the signs just swap the inputs.
3D case
In 3D, two arbitrarily placed vectors define their own axis of rotation, perpendicular to both. That axis of rotation does not come with a fixed orientation, which means that you cannot uniquely fix the direction of the angle of rotation either. One common convention is to let angles be always positive, and to orient the axis in such a way that it fits a positive angle. In this case, the dot product of the normalized vectors is enough to compute angles.
dot = x1*x2 + y1*y2 + z1*z2 #between [x1, y1, z1] and [x2, y2, z2]
lenSq1 = x1*x1 + y1*y1 + z1*z1
lenSq2 = x2*x2 + y2*y2 + z2*z2
angle = acos(dot/sqrt(lenSq1 * lenSq2))
Edit: Note that some comments and alternate answers advise against the use of acos for numeric reasons, in particular if the angles to be measured are small.
Plane embedded in 3D
One special case is the case where your vectors are not placed arbitrarily, but lie within a plane with a known normal vector n. Then the axis of rotation will be in direction n as well, and the orientation of n will fix an orientation for that axis. In this case, you can adapt the 2D computation above, including n into the determinant to make its size 3×3.
dot = x1*x2 + y1*y2 + z1*z2
det = x1*y2*zn + x2*yn*z1 + xn*y1*z2 - z1*y2*xn - z2*yn*x1 - zn*y1*x2
angle = atan2(det, dot)
One condition for this to work is that the normal vector n has unit length. If not, you'll have to normalize it.
As triple product
This determinant could also be expressed as the triple product, as #Excrubulent pointed out in a suggested edit.
det = n · (v1 × v2)
This might be easier to implement in some APIs, and gives a different perspective on what's going on here: The cross product is proportional to the sine of the angle, and will lie perpendicular to the plane, hence be a multiple of n. The dot product will therefore basically measure the length of that vector, but with the correct sign attached to it.
To compute angle you just need to call atan2(v1.s_cross(v2), v1.dot(v2)) for 2D case.
Where s_cross is scalar analogue of cross production (signed area of parallelogram).
For 2D case that would be wedge production.
For 3D case you need to define clockwise rotation because from one side of plane clockwise is one direction, from other side of plane is another direction =)
Edit: this is counter clockwise angle, clockwise angle is just opposite
This answer is the same as MvG's, but explains it differently (it's the result of my efforts in trying to understand why MvG's solution works). I'm posting it on the off chance that others find it helpful.
The anti-clockwise angle theta from x to y, with respect to the viewpoint of their given normal n (||n|| = 1), is given by
atan2( dot(n, cross(x,y)), dot(x,y) )
(1) = atan2( ||x|| ||y|| sin(theta), ||x|| ||y|| cos(theta) )
(2) = atan2( sin(theta), cos(theta) )
(3) = anti-clockwise angle between x axis and the vector (cos(theta), sin(theta))
(4) = theta
where ||x|| denotes the magnitude of x.
Step (1) follows by noting that
cross(x,y) = ||x|| ||y|| sin(theta) n,
and so
dot(n, cross(x,y))
= dot(n, ||x|| ||y|| sin(theta) n)
= ||x|| ||y|| sin(theta) dot(n, n)
which equals
||x|| ||y|| sin(theta)
if ||n|| = 1.
Step (2) follows from the definition of atan2, noting that atan2(cy, cx) = atan2(y,x), where c is a scalar. Step (3) follows from the definition of atan2. Step (4) follows from the geometric definitions of cos and sin.
Since one of the simplest and most elegant solutions is hidden in one the comments, I think it might be useful to post it as a separate answer.
acos can cause inaccuracies for very small angles, so atan2 is usually preferred. For the 3D case:
dot = x1 * x2 + y1 * y2 + z1 * z2
cross_x = (y1 * z2 – z1 * y2)
cross_y = (z1 * x2 – x1 * z2)
cross_z = (x1 * y2 – y1 * x2)
det = sqrt(cross_x * cross_x + cross_y * cross_y + cross_z * cross_z)
angle = atan2(det, dot)
Scalar (dot) product of two vectors lets you get the cosinus of the angle between them.
To get the 'direction' of the angle, you should also calculate the cross product, it will let you check (via z coordinate) is angle is clockwise or not (i.e. should you extract it from 360 degrees or not).
For a 2D method, you could use the law of
cosines and the "direction" method.
To calculate the angle of segment P3:P1
sweeping clockwise to segment P3:P2.
P1 P2
P3
double d = direction(x3, y3, x2, y2, x1, y1);
// c
int d1d3 = distanceSqEucl(x1, y1, x3, y3);
// b
int d2d3 = distanceSqEucl(x2, y2, x3, y3);
// a
int d1d2 = distanceSqEucl(x1, y1, x2, y2);
//cosine A = (b^2 + c^2 - a^2)/2bc
double cosA = (d1d3 + d2d3 - d1d2)
/ (2 * Math.sqrt(d1d3 * d2d3));
double angleA = Math.acos(cosA);
if (d > 0) {
angleA = 2.*Math.PI - angleA;
}
This has the same number of transcendental
operations as suggestions above and only one
more or so floating point operation.
the methods it uses are:
public int distanceSqEucl(int x1, int y1,
int x2, int y2) {
int diffX = x1 - x2;
int diffY = y1 - y2;
return (diffX * diffX + diffY * diffY);
}
public int direction(int x1, int y1, int x2, int y2,
int x3, int y3) {
int d = ((x2 - x1)*(y3 - y1)) - ((y2 - y1)*(x3 - x1));
return d;
}
If by "direct way" you mean avoiding the if statement, then I don't think there is a really general solution.
However, if your specific problem would allow loosing some precision in angle discretization and you are ok with loosing some time in type conversions, you can map the [-pi,pi) allowed range of phi angle onto the allowed range of some signed integer type. Then you would get the complementarity for free. However, I didn't really use this trick in practice. Most likely, the expense of float-to-integer and integer-to-float conversions would outweigh any benefit of the directness. It's better to set your priorities on writing autovectorizable or parallelizable code when this angle computation is done a lot.
Also, if your problem details are such that there is a definite more likely outcome for the angle direction, then you can use compilers' builtin functions to supply this information to the compiler, so it can optimize the branching more efficiently. E.g., in case of gcc, that's __builtin_expect function. It's somewhat more handy to use when you wrap it into such likely and unlikely macros (like in linux kernel):
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
For 2D case atan2 can easily calculate angle between (1, 0) vector (X-axis) and one of your vectors.
Formula is:
Atan2(y, x)
So you can easily calculate difference of two angles relatively X-axis
angle = -(atan2(y2, x2) - atan2(y1, x1))
Why is it not used as default solution? atan2 is not efficient enough. Solution from the top answer is better. Tests on C# showed that this method has 19.6% less performance (100 000 000 iterations). It's not critical but unpleasant.
So, another info that can be useful:
The smallest angle between outer and inner in degrees:
abs(angle * 180 / PI)
Full angle in degrees:
angle = angle * 180 / PI
angle = angle > 0 ? angle : 360 - angle
or
angle = angle * 180 / PI
if (angle < 0) angle = 360 - angle;
A formula for clockwise angle,2D case, between 2 vectors, xa,ya and xb,yb.
Angle(vec.a-vec,b)=
pi()/2*((1+sign(ya))*
(1-sign(xa^2))-(1+sign(yb))*
(1-sign(xb^2))) +pi()/4*
((2+sign(ya))*sign(xa)-(2+sign(yb))*
sign(xb)) +sign(xa*ya)*
atan((abs(ya)-abs(xa))/(abs(ya)+abs(xa)))-sign(xb*yb)*
atan((abs(yb)-abs(xb))/(abs(yb)+abs(xb)))
just copy & paste this.
angle = (acos((v1.x * v2.x + v1.y * v2.y)/((sqrt(v1.x*v1.x + v1.y*v1.y) * sqrt(v2.x*v2.x + v2.y*v2.y))))/pi*180);
you're welcome ;-)
I am working in c++ application. And its new to me. Here I write a function which gain two coordinates of a line. I have to process these coordinates and find another point which in that same line.
I will gain A(x1,y1) and B(x2,y2) coordinates.
need to find
C(x3,y3) coordinates.
Therefore I calculate the Slope of given line.
Double slope = (x1-x2)/(y1-y2);
And I know the distance of 3rd point from A point.
Double dis = sqrt(pow(x2-x1) + pow(y2-y1)) * 1.35 ;
I want to find new coordinates x3 ,y3 using Slope and dis.
Can anyone help me to solve this please.
To calculate the x3 I can use mathematical part,
x3 = slope * y3 -------------------1
dis = sqrt(pow(x3-x1) + pow(y3-y1)) ------------2
using these 2 equations which generating in run time , I want to calculate x3 and y3.
Too much math.
x3 = (x1 - x2) * 1.35 + x2
y3 = (y1 - y2) * 1.35 + y2
Unless you are working with a "1.5d" graph y=y(x) you should never use formulas based on y=m*x+q because that doesn't work for vertical lines (and works poorly for near-vertical lines).
In your case the best approach is to use the parametric equation for a line
x = x1 + t * dx
y = y1 + t * dy
where dx = x2 - x1 and dy = y2 - y1 are proportional to the components of the direction unit vector oriented from P1 to P2 and are used instead of m and q to define the line (avoiding any problem with vertical or almost vertical lines).
If you need a point on a specific distance then you just need to find the actual unit vector components with
double dx = x2 - x1;
double dy = y2 - y1;
double dist = sqrt(dx*dx + dy*dy);
dx /= dist;
dy /= dist;
and then the coordinates of the point you need are
double x3 = x1 + prescribed_distance * dx;
double y3 = y1 + prescribed_distance * dy;
or using -prescribed_distance instead depending on which side you want the point: toward P2 or away from it?
If however the prescribed distance is proportional to the current distance between the two points the normalization is not needed and the result can be the simpler:
double x3 = x1 + (x2 - x1) * k;
double y3 = y1 + (y2 - y1) * k;
where k is the ratio between the prescribed distance and the distance between the two points (once again with positive or negative sign depending on which side you are interested in).
By using parametric equations x=x(t), y=y(t) instead of explicit equations y=y(x) in addition to not having artificial singularity problems that depend on the coordinate system you also get formulas that are trivial to extend in higher dimensions. For example for a 3d line you just basically need to add z coordinate to the above formulas in the very same way x and y are used...
If you substitute the first equation "y3 = slope * x3" into the 2nd equation "dis = sqrt(pow(x3-x1) + pow(y3-y1))", and square both sides, you get a quadratic which you can solve using the quadratic formula.
After substitution you get:
dis^2 = (x3-x1)^2 + (slope*x3 - y1)^2
Square both sides:
(slope^2+1)*x3^2 + (-2*slope*y1-2*x1) + 2*y1^2 = dis^2
Solve for x3 using the quadratic formula:
x3 = (2*slope*y1+2*x1) +/- sqrt((2*slope*y1+2*x1)^b - 4*(slope^2+1)*(2*y1^2-dis^2))/(2*(slope^2+1))
Substitute x3 into the first equation to get y3:
y3 = slope * x3
I'm computing the ordinate y of a point on a line at a given abscissa x. The line is defined by its two end points coordinates (x0,y0)(x1,y1). End points coordinates are floats and the computation must be done in float precision for use in GPU.
The maths, and thus the naive implementation, are trivial.
Let t = (x - x0)/(x1 - x0), then y = (1 - t) * y0 + t * y1 = y0 + t * (y1 - y0).
The problem is when x1 - x0 is small. The result will introduce cancellation error. When combined with the one of x - x0, in the division I expect a significant error in t.
The question is if there exist another way to determine y with a better accuracy ?
i.e. should I compute (x - x0)*(y1 - y0) first, and divide by (x1 - x0) after ?
The difference y1 - y0 will always be big.
To a large degree, your underlying problem is fundamental. When (x1-x0) is small, it means there are only a few bits in the mantissa of x1 and x0 which differ. And by extension, there are only a limted number of floats between x0 and x1. E.g. if only the lower 4 bits of the mantissa differ, there are at most 14 values between them.
In your best algorithm, the t term represents these lower bits. And to continue or example, if x0 and x1 differ by 4 bits, then t can take on only 16 values either. The calculation of these possible values is fairly robust. Whether you're calculating 3E0/14E0 or 3E-12/14E-12, the result is going to be close to the mathematical value of 3/14.
Your formula has the additional advantage of having y0 <= y <= y1, since 0 <= t <= 1
(I'm assuming that you know enough about float representations, and therefore "(x1-x0) is small" really means "small, relative to the values of x1 and x0 themselves". A difference of 1E-1 is small when x0=1E3 but large if x0=1E-6 )
You may have a look at Qt's "QLine" (if I remember it right) sources; they have implemented an intersection determination algorithm taken from one the "Graphics Gems" books (the reference must be in the code comments, the book was on EDonkey a couple of years ago), which, in turn, has some guarantees on applicability for a given screen resolution when calculations are performed with given bit-width (they use fixed-point arithmetics if I'm not wrong).
If you have the possibility to do it, you can introduce two cases in your computation, depending on abs(x1-x0) < abs(y1-y0). In the vertical case abs(x1-x0) < abs(y1-y0), compute x from y instead of y from x.
EDIT. Another possibility would be to obtain the result bit by bit using a variant of dichotomic search. This will be slower, but may improve the result in extreme cases.
// Input is X
xmin = min(x0,x1);
xmax = max(x0,x1);
ymin = min(y0,y1);
ymax = max(y0,y1);
for (int i=0;i<20;i++) // get 20 bits in result
{
xmid = (xmin+xmax)*0.5;
ymid = (ymin+ymax)*0.5;
if ( x < xmid ) { xmax = xmid; ymax = ymid; } // first half
else { xmin = xmid; ymin = ymid; } // second half
}
// Output is some value in [ymin,ymax]
Y = ymin;
I have implemented a benchmark program to compare the effect of the different expression.
I computed y using double precision and then compute y using single precision with different expressions.
Here are the expression tested:
inline double getYDbl( double x, double x0, double y0, double x1, double y1 )
{
double const t = (x - x0)/(x1 - x0);
return y0 + t*(y1 - y0);
}
inline float getYFlt1( float x, float x0, float y0, float x1, float y1 )
{
double const t = (x - x0)/(x1 - x0);
return y0 + t*(y1 - y0);
}
inline float getYFlt2( float x, float x0, float y0, float x1, float y1 )
{
double const t = (x - x0)*(y1 - y0);
return y0 + t/(x1 - x0);
}
inline float getYFlt3( float x, float x0, float y0, float x1, float y1 )
{
double const t = (y1 - y0)/(x1 - x0);
return y0 + t*(x - x0);
}
inline float getYFlt4( float x, float x0, float y0, float x1, float y1 )
{
double const t = (x1 - x0)/(y1 - y0);
return y0 + (x - x0)/t;
}
I computed the average and stdDev of the difference between the double precision result and single precision result.
The result is that there is none on the average over 1000 and 10K random value sets. I used icc compiler with and without optimization as well as g++.
Note that I had to use the isnan() function to filter out bogus values. I suspect these result from underflow in the difference or division.
I don't know if the compilers rearrange the expression.
Anyway, the conclusion from this test is that the above rearrangements of the expression have no effect on the computation precision. The error remains the same (on average).
Check if the distance between x0 and x1 is small, i.e. fabs(x1 - x0) < eps. Then the line is parallell to the y axis of the coordinate system, i.e. you can't calculuate the y values of that line depending on x. You have infinite many y values and therefore you have to treat this case differently.
If your source data is already a float then you already have fundamental inaccuracy.
To explain further, imagine if you were doing this graphically. You have a 2D sheet of graph paper, and 2 point marked.
Case 1: Those points are very accurate, and have been marked with a very sharp pencil. Its easy to draw the line joining them, and easy to then get y given x (or vice versa).
Case 2: These point have been marked with a big fat felt tip pen, like a bingo marker. Clearly the line you draw will be less accurate. Do you go through the centre of the spots? The top edge? The bottom edge? Top of one, bottom of the other? Clearly there are many different options. If the two dots are close to each other then the variation will be even greater.
Floats have a certain level of inaccuracy inherent in them, due to the way they represent numbers, ergo they correspond more to case 2 than case 1 (which one could suggest is the equivalent of using an arbitrary precision librray). No algorithm in the world can compensate for that. Imprecise data in, Imprecise data out
How about computing something like:
t = sign * power2 ( sqrt (abs(x - x0))/ sqrt (abs(x1 - x0)))
The idea is to use a mathematical equivalent formula in which low (x1-x0) has less effect.
(not sure if the one I wrote matches this criteria)
As MSalters said, the problem is already in the original data.
Interpolation / extrapolation requires the slope, which already has low accuracy in the given conditions (worst for very short line segments far away from the origin).
Choice of algorithm canot regain this accuracy loss. My gut feeling is that the different evaluation order will not change things, as the error is introduced by the subtractions, not the devision.
Idea:
If you have more accurate data when the lines are generated, you can change the representation from ((x0, y0), (x1, y1)) to (x0,y0, angle, length). You could store angle or slope, slope has a pole, but angle requires trig functions... ugly.
Of course that won't work if you need the end point frequently, and you have so many lines that you can't store additional data, I have no idea. But maybe there is another representation that works well for your needs.
doubles have enough resolution in most situations, but that would double the working set too.