I have an ellipse, defined by Center Point, radiusX and radiusY, and I have a Point. I want to find the point on the ellipse that is closest to the given point. In the illustration below, that would be S1.
Now I already have code, but there is a logical error somewhere in it, and I seem to be unable to find it. I broke the problem down to the following code example:
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <math.h>
using namespace std;
void dostuff();
int main()
{
dostuff();
return 0;
}
typedef std::vector<cv::Point> vectorOfCvPoints;
void dostuff()
{
const double ellipseCenterX = 250;
const double ellipseCenterY = 250;
const double ellipseRadiusX = 150;
const double ellipseRadiusY = 100;
vectorOfCvPoints datapoints;
for (int i = 0; i < 360; i+=5)
{
double angle = i / 180.0 * CV_PI;
double x = ellipseRadiusX * cos(angle);
double y = ellipseRadiusY * sin(angle);
x *= 1.4;
y *= 1.4;
x += ellipseCenterX;
y += ellipseCenterY;
datapoints.push_back(cv::Point(x,y));
}
cv::Mat drawing = cv::Mat::zeros( 500, 500, CV_8UC1 );
for (int i = 0; i < datapoints.size(); i++)
{
const cv::Point & curPoint = datapoints[i];
const double curPointX = curPoint.x;
const double curPointY = curPoint.y * -1; //transform from image coordinates to geometric coordinates
double angleToEllipseCenter = atan2(curPointY - ellipseCenterY * -1, curPointX - ellipseCenterX); //ellipseCenterY * -1 for transformation to geometric coords (from image coords)
double nearestEllipseX = ellipseCenterX + ellipseRadiusX * cos(angleToEllipseCenter);
double nearestEllipseY = ellipseCenterY * -1 + ellipseRadiusY * sin(angleToEllipseCenter); //ellipseCenterY * -1 for transformation to geometric coords (from image coords)
cv::Point center(ellipseCenterX, ellipseCenterY);
cv::Size axes(ellipseRadiusX, ellipseRadiusY);
cv::ellipse(drawing, center, axes, 0, 0, 360, cv::Scalar(255));
cv::line(drawing, curPoint, cv::Point(nearestEllipseX,nearestEllipseY*-1), cv::Scalar(180));
}
cv::namedWindow( "ellipse", CV_WINDOW_AUTOSIZE );
cv::imshow( "ellipse", drawing );
cv::waitKey(0);
}
It produces the following image:
You can see that it actually finds "near" points on the ellipse, but it are not the "nearest" points. What I intentionally want is this: (excuse my poor drawing)
would you extent the lines in the last image, they would cross the center of the ellipse, but this is not the case for the lines in the previous image.
I hope you get the picture. Can anyone tell me what I am doing wrong?
Consider a bounding circle around the given point (c, d), which passes through the nearest point on the ellipse. From the diagram it is clear that the closest point is such that a line drawn from it to the given point must be perpendicular to the shared tangent of the ellipse and circle. Any other points would be outside the circle and so must be further away from the given point.
So the point you are looking for is not the intersection between the line and the ellipse, but the point (x, y) in the diagram.
Gradient of tangent:
Gradient of line:
Condition for perpedicular lines - product of gradients = -1:
When rearranged and substituted into the equation of your ellipse...
...this will give two nasty quartic (4th-degree polynomial) equations in terms of either x or y. AFAIK there are no general analytical (exact algebraic) methods to solve them. You could try an iterative method - look up the Newton-Raphson iterative root-finding algorithm.
Take a look at this very good paper on the subject:
http://www.spaceroots.org/documents/distance/distance-to-ellipse.pdf
Sorry for the incomplete answer - I totally blame the laws of mathematics and nature...
EDIT: oops, i seem to have a and b the wrong way round in the diagram xD
There is a relatively simple numerical method with better convergence than Newtons Method. I have a blog post about why it works http://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/
This implementation works without any trig functions:
def solve(semi_major, semi_minor, p):
px = abs(p[0])
py = abs(p[1])
tx = 0.707
ty = 0.707
a = semi_major
b = semi_minor
for x in range(0, 3):
x = a * tx
y = b * ty
ex = (a*a - b*b) * tx**3 / a
ey = (b*b - a*a) * ty**3 / b
rx = x - ex
ry = y - ey
qx = px - ex
qy = py - ey
r = math.hypot(ry, rx)
q = math.hypot(qy, qx)
tx = min(1, max(0, (qx * r / q + ex) / a))
ty = min(1, max(0, (qy * r / q + ey) / b))
t = math.hypot(ty, tx)
tx /= t
ty /= t
return (math.copysign(a * tx, p[0]), math.copysign(b * ty, p[1]))
Credit to Adrian Stephens for the Trig-Free Optimization.
Here is the code translated to C# implemented from this paper to solve for the ellipse:
http://www.geometrictools.com/Documentation/DistancePointEllipseEllipsoid.pdf
Note that this code is untested - if you find any errors let me know.
//Pseudocode for robustly computing the closest ellipse point and distance to a query point. It
//is required that e0 >= e1 > 0, y0 >= 0, and y1 >= 0.
//e0,e1 = ellipse dimension 0 and 1, where 0 is greater and both are positive.
//y0,y1 = initial point on ellipse axis (center of ellipse is 0,0)
//x0,x1 = intersection point
double GetRoot ( double r0 , double z0 , double z1 , double g )
{
double n0 = r0*z0;
double s0 = z1 - 1;
double s1 = ( g < 0 ? 0 : Math.Sqrt(n0*n0+z1*z1) - 1 ) ;
double s = 0;
for ( int i = 0; i < maxIter; ++i ){
s = ( s0 + s1 ) / 2 ;
if ( s == s0 || s == s1 ) {break; }
double ratio0 = n0 /( s + r0 );
double ratio1 = z1 /( s + 1 );
g = ratio0*ratio0 + ratio1*ratio1 - 1 ;
if (g > 0) {s0 = s;} else if (g < 0) {s1 = s ;} else {break ;}
}
return s;
}
double DistancePointEllipse( double e0 , double e1 , double y0 , double y1 , out double x0 , out double x1)
{
double distance;
if ( y1 > 0){
if ( y0 > 0){
double z0 = y0 / e0;
double z1 = y1 / e1;
double g = z0*z0+z1*z1 - 1;
if ( g != 0){
double r0 = (e0/e1)*(e0/e1);
double sbar = GetRoot(r0 , z0 , z1 , g);
x0 = r0 * y0 /( sbar + r0 );
x1 = y1 /( sbar + 1 );
distance = Math.Sqrt( (x0-y0)*(x0-y0) + (x1-y1)*(x1-y1) );
}else{
x0 = y0;
x1 = y1;
distance = 0;
}
}
else // y0 == 0
x0 = 0 ; x1 = e1 ; distance = Math.Abs( y1 - e1 );
}else{ // y1 == 0
double numer0 = e0*y0 , denom0 = e0*e0 - e1*e1;
if ( numer0 < denom0 ){
double xde0 = numer0/denom0;
x0 = e0*xde0 ; x1 = e1*Math.Sqrt(1 - xde0*xde0 );
distance = Math.Sqrt( (x0-y0)*(x0-y0) + x1*x1 );
}else{
x0 = e0;
x1 = 0;
distance = Math.Abs( y0 - e0 );
}
}
return distance;
}
The following python code implements the equations described at "Distance from a Point to an Ellipse" and uses newton's method to find the roots and from that the closest point on the ellipse to the point.
Unfortunately, as can be seen from the example, it seems to only be accurate outside the ellipse. Within the ellipse weird things happen.
from math import sin, cos, atan2, pi, fabs
def ellipe_tan_dot(rx, ry, px, py, theta):
'''Dot product of the equation of the line formed by the point
with another point on the ellipse's boundary and the tangent of the ellipse
at that point on the boundary.
'''
return ((rx ** 2 - ry ** 2) * cos(theta) * sin(theta) -
px * rx * sin(theta) + py * ry * cos(theta))
def ellipe_tan_dot_derivative(rx, ry, px, py, theta):
'''The derivative of ellipe_tan_dot.
'''
return ((rx ** 2 - ry ** 2) * (cos(theta) ** 2 - sin(theta) ** 2) -
px * rx * cos(theta) - py * ry * sin(theta))
def estimate_distance(x, y, rx, ry, x0=0, y0=0, angle=0, error=1e-5):
'''Given a point (x, y), and an ellipse with major - minor axis (rx, ry),
its center at (x0, y0), and with a counter clockwise rotation of
`angle` degrees, will return the distance between the ellipse and the
closest point on the ellipses boundary.
'''
x -= x0
y -= y0
if angle:
# rotate the points onto an ellipse whose rx, and ry lay on the x, y
# axis
angle = -pi / 180. * angle
x, y = x * cos(angle) - y * sin(angle), x * sin(angle) + y * cos(angle)
theta = atan2(rx * y, ry * x)
while fabs(ellipe_tan_dot(rx, ry, x, y, theta)) > error:
theta -= ellipe_tan_dot(
rx, ry, x, y, theta) / \
ellipe_tan_dot_derivative(rx, ry, x, y, theta)
px, py = rx * cos(theta), ry * sin(theta)
return ((x - px) ** 2 + (y - py) ** 2) ** .5
Here's an example:
rx, ry = 12, 35 # major, minor ellipse axis
x0 = y0 = 50 # center point of the ellipse
angle = 45 # ellipse's rotation counter clockwise
sx, sy = s = 100, 100 # size of the canvas background
dist = np.zeros(s)
for x in range(sx):
for y in range(sy):
dist[x, y] = estimate_distance(x, y, rx, ry, x0, y0, angle)
plt.imshow(dist.T, extent=(0, sx, 0, sy), origin="lower")
plt.colorbar()
ax = plt.gca()
ellipse = Ellipse(xy=(x0, y0), width=2 * rx, height=2 * ry, angle=angle,
edgecolor='r', fc='None', linestyle='dashed')
ax.add_patch(ellipse)
plt.show()
Which generates an ellipse and the distance from the boundary of the ellipse as a heat map. As can be seen, at the boundary the distance is zero (deep blue).
Given an ellipse E in parametric form and a point P
the square of the distance between P and E(t) is
The minimum must satisfy
Using the trigonometric identities
and substituting
yields the following quartic equation:
Here's an example C function that solves the quartic directly and computes sin(t) and cos(t) for the nearest point on the ellipse:
void nearest(double a, double b, double x, double y, double *ecos_ret, double *esin_ret) {
double ax = fabs(a*x);
double by = fabs(b*y);
double r = b*b - a*a;
double c, d;
int switched = 0;
if (ax <= by) {
if (by == 0) {
if (r >= 0) { *ecos_ret = 1; *esin_ret = 0; }
else { *ecos_ret = 0; *esin_ret = 1; }
return;
}
c = (ax - r) / by;
d = (ax + r) / by;
} else {
c = (by + r) / ax;
d = (by - r) / ax;
switched = 1;
}
double cc = c*c;
double D0 = 12*(c*d + 1); // *-4
double D1 = 54*(d*d - cc); // *4
double D = D1*D1 + D0*D0*D0; // *16
double St;
if (D < 0) {
double t = sqrt(-D0); // *2
double phi = acos(D1 / (t*t*t));
St = 2*t*cos((1.0/3)*phi); // *2
} else {
double Q = cbrt(D1 + sqrt(D)); // *2
St = Q - D0 / Q; // *2
}
double p = 3*cc; // *-2
double SS = (1.0/3)*(p + St); // *4
double S = sqrt(SS); // *2
double q = 2*cc*c + 4*d; // *2
double l = sqrt(p - SS + q / S) - S - c; // *2
double ll = l*l; // *4
double ll4 = ll + 4; // *4
double esin = (4*l) / ll4;
double ecos = (4 - ll) / ll4;
if (switched) {
double t = esin;
esin = ecos;
ecos = t;
}
*ecos_ret = copysign(ecos, a*x);
*esin_ret = copysign(esin, b*y);
}
Try it online!
You just need to calculate the intersection of the line [P1,P0] to your elipse which is S1.
If the line equeation is:
and the elipse equesion is:
than the values of S1 will be:
Now you just need to calculate the distance between S1 to P1 , the formula (for A,B points) is:
I've solved the distance issue via focal points.
For every point on the ellipse
r1 + r2 = 2*a0
where
r1 - Euclidean distance from the given point to focal point 1
r2 - Euclidean distance from the given point to focal point 2
a0 - semimajor axis length
I can also calculate the r1 and r2 for any given point which gives me another ellipse that this point lies on that is concentric to the given ellipse. So the distance is
d = Abs((r1 + r2) / 2 - a0)
As propposed by user3235832
you shall solve quartic equation to find the normal to the ellipse (https://www.mathpages.com/home/kmath505/kmath505.htm). With good initial value only few iterations are needed (I use it myself). As an initial value I use S1 from your picture.
The fastest method I guess is
http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf
Which has been mentioned also by Matt but as he found out the method doesn't work very well inside of ellipse.
The problem is the theta initialization.
I proposed an stable initialization:
Find the intersection of ellipse and horizontal line passing the point.
Find the other intersection using vertical line.
Choose the one that is closer the point.
Calculate the initial angle based on that point.
I got good results with no issue inside and outside:
As you can see in the following image it just iterated about 3 times to reach 1e-8. Close to axis it is 1 iteration.
The C++ code is here:
double initialAngle(double a, double b, double x, double y) {
auto abs_x = fabs(x);
auto abs_y = fabs(y);
bool isOutside = false;
if (abs_x > a || abs_y > b) isOutside = true;
double xd, yd;
if (!isOutside) {
xd = sqrt((1.0 - y * y / (b * b)) * (a * a));
if (abs_x > xd)
isOutside = true;
else {
yd = sqrt((1.0 - x * x / (a * a)) * (b * b));
if (abs_y > yd)
isOutside = true;
}
}
double t;
if (isOutside)
t = atan2(a * y, b * x); //The point is outside of ellipse
else {
//The point is inside
if (xd < yd) {
if (x < 0) xd = -xd;
t = atan2(y, xd);
}
else {
if (y < 0) yd = -yd;
t = atan2(yd, x);
}
}
return t;
}
double distanceToElipse(double a, double b, double x, double y, int maxIter = 10, double maxError = 1e-5) {
//std::cout <<"p="<< x << "," << y << std::endl;
auto a2mb2 = a * a - b * b;
double t = initialAngle(a, b, x, y);
auto ct = cos(t);
auto st = sin(t);
int i;
double err;
for (i = 0; i < maxIter; i++) {
auto f = a2mb2 * ct * st - x * a * st + y * b * ct;
auto fp = a2mb2 * (ct * ct - st * st) - x * a * ct - y * b * st;
auto t2 = t - f / fp;
err = fabs(t2 - t);
//std::cout << i + 1 << " " << err << std::endl;
t = t2;
ct = cos(t);
st = sin(t);
if (err < maxError) break;
}
auto dx = a * ct - x;
auto dy = b * st - y;
//std::cout << a * ct << "," << b * st << std::endl;
return sqrt(dx * dx + dy * dy);
}
Related
I implemented the improved Perlin noise algorithm. The code as provided for 3D noise works correctly.
I adjusted the algorithm to make a 2D version in what seemed the obvious way. It almost works, but produces artefacts as the images below show.
Here is the correct 3D version:
unsigned inc (unsigned number)
{
return (number + 1) & 255;
}
double fade (double t)
{
// Fade function as defined by Ken Perlin.
// This eases coordinate values
// so that they will "ease" towards integral values.
// This ends up smoothing the final output.
// 6t^5 - 15t^4 + 10t^3
return t * t * t * (t * (t * 6 - 15) + 10);
}
double lerp (double a, double b, double x)
{
return a + x * (b - a);
}
double grad (unsigned hash, double x, double y, double z)
{
// Take the hashed value and take the first 4 bits of it
// (15 == 0b1111)
unsigned h = hash & 15;
// If the most significant bit (MSB) of the hash is 0
// then set u = x. Otherwise y.
double u = h < 8 /* 0b1000 */ ? x : y;
double v;
if (h < 4 /* 0b0100 */)
// If the first and second significant bits
// are 0, set v = y
v = y;
else if (h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)
// If the first and second significant bits
// are 1, set v = x
v = x;
else
// If the first and second significant bits are not
// equal (0/1, 1/0) set v = z
v = z;
// Use the last 2 bits to decide if u and v are positive
// or negative. Then return their addition.
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
double
ImprovedNoise :: noise (double x, double y, double z)
{
// Calculate the "unit cube" that the point asked will be located in
// The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
// plus 1. Next we calculate the location (from 0.0 to 1.0) in that
// cube. We also fade the location to smooth the result.
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
double xf = x - (int) x;
double yf = y - (int) y;
double zf = z - (int) z;
double u = fade (xf);
double v = fade (yf);
double w = fade (zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
auto & p = permutation;
aaa = p[p[p[ xi ] + yi ] + zi ];
aba = p[p[p[ xi ] + inc(yi)] + zi ];
aab = p[p[p[ xi ] + yi ] + inc(zi)];
abb = p[p[p[ xi ] + inc(yi)] + inc(zi)];
baa = p[p[p[inc(xi)] + yi ] + zi ];
bba = p[p[p[inc(xi)] + inc(yi)] + zi ];
bab = p[p[p[inc(xi)] + yi ] + inc(zi)];
bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];
double x1, x2, y1, y2;
// The gradient function calculates the dot product between a
// pseudorandom gradient vector and the vector from the input
// coordinate to the 8 surrounding points in its unit cube.
// This is all then lerped together as a sort of weighted average
// based on the faded (u,v,w) values we made earlier.
x1 = lerp (
grad (aaa, xf , yf , zf),
grad (baa, xf-1, yf , zf),
u);
x2 = lerp (
grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp (x1, x2, v);
x1 = lerp (
grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp (
grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
auto result = (lerp (y1, y2, w) + 1) / 2;
assert (0 <= result);
assert (result <= 1);
assert (false == std :: isnan (result));
return result;
}
I generate a 2D image by fixing z=0. This a frequency of 10 so x,y are in [0..10]:
My 2D version:
double grad (unsigned hash, double x, double y)
{
double u = (hash & 1) ? x : y;
double v = (hash & 2) ? x : y;
return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
}
double
ImprovedNoise :: noise (double x, double y)
{
int xi = (int)x & 255;
int yi = (int)y & 255;
double xf = x - (int) x;
double yf = y - (int) y;
double u = fade (xf);
double v = fade (yf);
int aaa, aba,baa, bba;
auto & p = permutation;
aaa = p[p[ xi ] + yi ];
aba = p[p[ xi ] + inc(yi)];
baa = p[p[inc(xi)] + yi ];
bba = p[p[inc(xi)] + inc(yi)];
double x1, x2;
// The gradient function calculates the dot product between a
// pseudorandom gradient vector and the vector from the input
// coordinate to the 8 surrounding points in its unit cube.
// This is all then lerped together as a sort of weighted average
// based on the faded (u,v,w) values we made earlier.
x1 = lerp (
grad (aaa, xf , yf),
grad (baa, xf-1, yf),
u);
x2 = lerp (
grad (aba, xf , yf-1),
grad (bba, xf-1, yf-1),
u);
double result = (lerp (x1, x2, v) + 1) / 2;
assert (0 <= result);
assert (result <= 1);
assert (false == std :: isnan (result));
return result;
}
Here is the image it generates.
It's generated using this method:
int size=400;
int freq=10;
create_widget (size, size, [&] (int x, int y)
{
return noise (x*freq / float (size), y*freq / float (size));
});
What's causing those horizontal and vertical lines? I thought it might be an integer boundary issue, but that would predict freq artefacts across the whole image, so I guess it's something else.
Can you see what the mistake is?
There's probably a mistake in grad (the precedence of + is higher than ?:), which causes abrupt change of the (anyway incorrect) result on specific xf/yf/hash values.
return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
( )
After advice from krlzlx I have posted it as a new question.
From here:
3D Line Segment and Plane Intersection
I have a problem with this algorithm, I have implemented it like so:
template <class T>
class AnyCollision {
public:
std::pair<bool, T> operator()(Point3d &ray, Point3d &rayOrigin, Point3d &normal, Point3d &coord) const {
// get d value
float d = (normal.x * coord.x) + (normal.y * coord.y) + (normal.z * coord.z);
if (((normal.x * ray.x) + (normal.y * ray.y) + (normal.z * ray.z)) == 0) {
return std::make_pair(false, T());
}
// Compute the X value for the directed line ray intersecting the plane
float a = (d - ((normal.x * rayOrigin.x) + (normal.y * rayOrigin.y) + (normal.z * rayOrigin.z)) / ((normal.x * ray.x) + (normal.y * ray.y) + (normal.z * ray.z)));
// output contact point
float rayMagnitude = (sqrt(pow(ray.x, 2) + pow(ray.y, 2) + pow(ray.z, 2)));
Point3d rayNormalised((ray.x / rayMagnitude), (ray.y / rayMagnitude), (ray.z / rayMagnitude));
Point3d contact((rayOrigin.x + (rayNormalised.x * a)), (rayOrigin.y + (rayNormalised.y * a)), (rayOrigin.z + (rayNormalised.z * a))); //Make sure the ray vector is normalized
return std::make_pair(true, contact);
};
Point3d is defined as:
class Point3d {
public:
double x;
double y;
double z;
/**
* constructor
*
* 0 all elements
*/
Point3d() {
x = 0.0;
y = 0.0;
z = 0.0;
}
I am forced to use this structure, because in the larger system my component runs in it is defined like this and it cannot be changed.
My code compiles fine, but testing I get incorrect values for the point. The ratio of x, y, z is correct but the magnitude is wrong.
For example if:
rayOrigin.x = 0;
rayOrigin.y = 0;
rayOrigin.z = 0;
ray.x = 3;
ray.y = -5;
ray.z = 12;
normal.x = -3;
normal.y = 12;
normal.z = 0;
coord.x = 7;
coord.y = -5;
coord.z = 10;
I expect the point to be:
(0.63, 1.26, 1.89)
However, it is:
(3.52, -5.87, 14.09)
A magnitude of 5.09 too big.
And I also tested:
rayOrigin.x = 0;
rayOrigin.y = 0;
rayOrigin.z = 0;
ray.x = 2;
ray.y = 3;
ray.z = 3;
normal.x = 4;
normal.y = 1;
normal.z = 0;
p0.x = 2;
p0.y = 1;
p0.z = 5;
I expect the point to be:
(1.64, 2.45, 2.45)
However, it is:
(3.83761, 5.75642, 5.75642)
A magnitude of 2.34 too big?
Pseudocode (does not require vector normalization):
Diff = PlaneBaseCoordinate - RayOrigin
d = Normal.dot.Diff
e = Normal.dot.RayVector
if (e)
IntersectionPoint = RayOrigin + RayVector * d / e
otherwise
ray belongs to the plane or is parallel
Quick check:
Ray (0,0,0) (2,2,2) //to catch possible scale issues
Plane (0,1,0) (0,3,0) //plane y=1
Diff = (0,1,0)
d = 3
e = 6
IntersectionPoint = (0,0,0) + (2,2,2) * 3 / 6 = (1, 1, 1)
I have to draw an arrow. I have a head point and a tail point now i need to draw a triangular arrow cap. A triangle whose length is of size 5.How can i find coordinates of the end points of triangle. One thing is we have angle of 45.so if we can rotate the vector by 45 to obtain it.
` int x1=arrowStart.X;
int y1=arrowStart.Y;
int x2=arrowend.X;
int y2=arrowend.Y;
PointF arrowPoint=arrowend;
double arrowlength=sqrt(pow(x1-x2,2)+pow(y1-y2,2));
int ArrowMultiplier=1;
double arrowangle=atan2(y1-y2,x1-x2);
double pointx,pointy;
if(x1>x2)
{
pointx=x1 - (cos(arrowangle) * (arrowlength-3 * ArrowMultiplier ));
}
else
{
pointx = cos(arrowangle) * (arrowlength-3 * ArrowMultiplier ) + x1;
}
if (y1 > y2)
{
pointy = y1 - (sin(arrowangle) * (arrowlength -3 * ArrowMultiplier));
}
else
{
pointy = (sin(arrowangle) * (arrowlength-3 * ArrowMultiplier )) + y1;
}
PointF arrowPointBack(pointx,pointy);
double angleB = atan2((3 * ArrowMultiplier), (arrowlength - (3 * ArrowMultiplier)));
double angleC = (3.14) * (90 - (arrowangle * (180 /3.14)) - (angleB * (180 / 3.14))) / 180;
double secondaryLength = (3 * ArrowMultiplier)/sin(angleB);
if (x1 > x2)
{
pointx = x1 - (sin(angleC) * secondaryLength);
}
else
{
pointx = (sin(angleC) * secondaryLength) + x1;
}
if (y1 > y2)
{
pointy = y1 - (cos(angleC) * secondaryLength);
}
else
{
pointy = (cos(angleC) * secondaryLength) + y1;
}
PointF arrowPointLeft((float)pointx, (float)pointy);
angleC = arrowangle - angleB;
if (x1 > x2)
{
pointx = x1 - (cos(angleC) * secondaryLength);
}
else
{
pointx = (cos(angleC) * secondaryLength) +x1;
}
if (y1 > y2)
{
pointy =y1 - (sin(angleC) * secondaryLength);
}
else
{
pointy = (sin(angleC) * secondaryLength) + y1;
}
PointF arrowPointRight((float)pointx,(float)pointy);
PointF arrowPoints[4];
arrowPoints[0] = arrowPoint;
arrowPoints[1] = arrowPointLeft;
//arrowPoints[2] = arrowPointBack;
arrowPoints[2] = arrowPointRight;
arrowPoints[3] = arrowPoint;
`
Right, I suppose I should break it down for you:
First, you need to calculate the angle that the arrow sits at. This can be achieved with the inverse tangent function:
atan(diff_y, diff_x)
where diff_y and diff_x are the difference between the x and y values of your two end-points.
You can then add the desired angle of the arrow-head to this angle and use sin and cos to calculate the x and y values of the first of the extra points of the arrow-head.
new_x = head_x - 5 * cos (angle + pi/4)
new_y = head_y + 5 * sin (angle + pi/4)
for the other point, you do the same, but with a subtraction of the difference in angle.
new_x = head_x - 5 * cos (angle - pi/4)
new_y = head_y + 5 * sin (angle - pi/4)
You then have all the points you need.
I did this for fun (sue me, I was bored) and came up with this:
#include <math.h>
#include <iostream>
const double arrow_head_length = 3;
const double PI = 3.14159265;
const double arrow_head_angle = PI/6;
//returns the angle between two points, with coordinate1 describing the centre of the circle, with the angle progressing clockwise
double angle_between_points( std::pair<double,double> coordinate1, std::pair<double,double> coordinate2)
{
return atan2(coordinate2.second - coordinate1.second, coordinate1.first - coordinate2.first);
}
//calculate the position of a new point [displacement] away from an original point at an angle of [angle]
std::pair<double,double> displacement_angle_offset(std::pair<double,double> coordinate_base, double displacement, double angle)
{
return std::make_pair
(
coordinate_base.first - displacement * cos(angle),
coordinate_base.second + displacement * sin(angle)
);
}
int main()
{
std::pair<double,double> arrow_tail( 0, 0);
std::pair<double,double> arrow_head( 15,-15);
//find the angle of the arrow
double angle = angle_between_points(arrow_head, arrow_tail);
//calculate the new positions
std::pair<double,double> head_point_1 = displacement_angle_offset(arrow_head, arrow_head_length, angle + arrow_head_angle);
std::pair<double,double> head_point_2 = displacement_angle_offset(arrow_head, arrow_head_length, angle - arrow_head_angle);
//output the points in order: tail->head->point1->point2->head so if you follow them it draws the arrow
std::cout << arrow_tail.first << ',' << arrow_tail.second << '\n'
<< arrow_head.first << ',' << arrow_head.second << '\n'
<< head_point_1.first << ',' << head_point_1.second << '\n'
<< head_point_2.first << ',' << head_point_2.second << '\n'
<< arrow_head.first << ',' << arrow_head.second << std::endl;
}
The output can be saved as a .csv and loaded into excel for example, where you can use it to draw a connected scatter-graph that will form the shape of the arrow.
If this is homework, then before you do anything with it, make sure you know exactly how it works. That includes knowing the answers to questions like:
when calculating the angle, why does the code do point2_y-point1_y but point1_x-point2_x?
what direction is angle 0?
why does the angle increase going clockwise and not anti-clockwise?
why are there 5 outputs when only 4 points are needed?
what is the significance of PI/6 in the code? It isn't == 45 degrees. Why would this angle be better?
Also note that this question and answer will now pop up in a google search.
Working example: http://ideone.com/D4IwOy
You can paste the output into any graphing tool (such as this one) or save as a .csv and open in excel/spreadsheet of choice and plot a scatter graph to see the arrow coordinates. Note that it (annoyingly) doesn't keep the x and y scales equal so will stretch arrows like this one:
3,7
24,15
21.0381,15.4768
22.1061,12.6734
24,15
I have been testing collision between two circles using the method:
Circle A = (x1,y1) Circle b = (x2,y2)
Radius A Radius b
x1 - x2 = x' * x'
y1 - y2 = y' * y'
x' + y' = distance
square root of distance - Radius A + Radius B
and if the resulting answer is a negative number it is intersecting.
I have used this method in a test but it doesn't seem to be very accurate at all.
bool circle::intersects(circle & test)
{
Vector temp;
temp.setX(centre.getX() - test.centre.getX());
temp.setY(centre.getY() - test.centre.getY());
float distance;
float temp2;
float xt;
xt = temp.getX();
temp2 = xt * xt;
temp.setX(temp2);
xt = temp.getY();
temp2 = xt * xt;
temp.setY(temp2);
xt = temp.getX() + temp.getY();
distance = sqrt(xt);
xt = radius + test.radius;
if( distance - xt < test.radius)
{
return true;
}
else return false;
}
This is the function using this method maybe I'm wrong here. I just wondered what other methods I could use. I know separating axis theorem is better , but I wouldn't know where to start.
if( distance - xt < test.radius)
{
return true;
}
distance - xt will evaluate to the blue line, the distance between the two disks. It also meets the condition of being less than the test radius, but there is no collision going on.
The solution:
if(distance <= (radius + test.radius) )
return true;
Where distance is the distance from the centres.
Given: xt = radius + test.radius;
The correct test is: if( distance < xt)
Here is an attempt to re-write the body for you: (no compiler, so may be errors)
bool circle::intersects(circle & test)
{
float x = this->centre.getX() - test.centre.getX()
float y = this->centre.getY() - test.centre.getY()
float distance = sqrt(x*x+y*y);
return distance < (this->radius + test.radius);
}
Based on Richard solution but comparing the squared distance. This reduce the computation errors and the computation time.
bool circle::intersects(circle & test)
{
float x = this->centre.getX() - test.centre.getX()
float y = this->centre.getY() - test.centre.getY()
float distance2 = x * x + y * y;
float intersect_distance2 = (this->radius + test.radius) * (this->radius + test.radius);
return distance <= intersect_distance2;
}
Use Pythagoras theorem to compute the distance between the centres
That is a straight line
If they have collided then that distance is shorter that the sum of the two radiuses
I would like to transform the coordinates of a window from cartesian to an elliptical coordinate system. The transformation is:
x= e*cosh(eta)*cos(phi)
y= e*sinh(eta)*sin(phi)
and inverse:
eta=Re(acosh(x/e+i*y/e))
phi=Im(acosh(x/e+i*y/e))
(see p.14 of http://itp.tugraz.at/~schnizer/AnalyticalMethods/AnMe6%267.pdf)
So far I implemented the following:
cv::Size2f _size(33,70);
float e = 0.0f;
float eta0 = 0.0f;
if(_size.height > _size.width)
{
e = sqrt(_size.height*_size.height - _size.width*_size.width);
std::complex<double> z0(0,_size.height/2/e);
eta0 = std::acosh(z0).real();
} else
{
e = sqrt(_size.width*_size.width - _size.height*_size.height);
std::complex<double> z0(_size.width/2/e,0);
eta0 = std::acosh(z0).real();
}
for(int dx = ceil(-_size.width/2); dx < floor(_size.width/2); dx++ )
{
for(int dy = ceil(-_size.height/2); dy < floor(_size.height/2); dy++ )
{
float eta;
float phi;
if((dx == 0) && (dy == 0))
{
eta = 0.0f;
phi = 0.0f;
}else
{
std::complex<double> z(dx/e,dy/e);
std::complex<double> k = std::acosh(z);
eta = (k.real()) / eta0;
phi = fmod(k.imag() + 2.0f*CV_PI, 2.0f*CV_PI);
}
int x = 200 + dx;
int y = 200 + dy;
}
}
The problem is, that the elliptical coordinates don't scale. eta is always greater 1, but it should scale between 0 < eta < inf. Eta should be <1 for points inside the ellipse, specified by _size and >1 for points outside.
Thanks for any ideas.
I am not sure but check that your double (multiplication) and division produces a double i.e. the divide by constants is either /(double)2 or /2.0
hope that help