I'm working on a pretty cool project where I'm collecting data about the movement of a cursor, but I've run into an issue where I think I could use some help. I am constantly reading in data about the x and y position of the cursor (along with other relevant data), and once the cursor exceeds a certain threshold in the y-dimension, I need to calculate the movement direction (angle). Let me illustrate with a figure I drew:
What tends to happen is that the cursor moves in a somewhat straight line, but then curves towards the end of the movement. I need to calculate theta, i.e., the angle of the blue vector with respect to the positive x-axis. The idea I came up with is to use the last 2 samples to largely determine what the movement direction is, otherwise if I use too many samples I would skew what the actual angle is. To give an extreme case let me follow up with another picture:
Here each dot represents a sample. Note that if I use BOTH dots, the real angle I want will be wrong (again, I need to find the direction the cursor was moving in last, which is the vector drawn at the end of the line). I dont expect this case to come up much, but was wondering if there would be a way to solve for it if it does.
Lastly, note that the these motions can occur in either the first or second quadrant, if that makes a difference.
I'd really appreciate any help here. I'm coding this in C++ but I think I could translate any answer. Thanks.
This should get you started http://jsfiddle.net/0ao9oa7a/
Get all of the recorded points
Filter out points that are close together (I use 5 pixels)
Find the angles of each consecutive pair of points (atan2)
Find the absolute differences between each consecutive pair of angles
Throw away all of the angles before the max difference
Average the remaining angles (average all point vectors then atan2 back into an angle)
Code
function process(points) {
if(points.length === 0) {
txt = "Not enough points\n" + txt;
return null;
}
// compress points, removing those that are too close together
var newPoints = [];
newPoints.push(points[0]);
for(var i = 1; i < points.length; i++) {
if(Math.sqrt(Math.pow(points[i].x - newPoints[newPoints.length - 1].x, 2) + Math.pow(points[i].y - newPoints[newPoints.length - 1].y, 2)) > 5) {
newPoints.push(points[i]);
}
}
points = newPoints;
if(points.length < 2) {
txt = "Not enough points\n" + txt;
return null;
}
// get all of the angles
var angles = [];
for(var i=0; i < points.length - 1; i++) {
var rad = Math.atan2(points[i + 1].y - points[i].y, points[i + 1].x - points[i].x);
angles[i] = rad;
txt += "x: " + (points[i].x|0) + " y: " + (points[i].y|0) + " x: " + (points[i+1].x|0) + " y: " + (points[i+1].y|0) + " [" + ((rad * 180 / Math.PI)|0) + "]" + "\n";
}
txt += "\n";
// get all of the diffs between angles
// save the index of the max diff
var absDiffs = [];
var maxDiff = -1;
var maxDiffAngleIndex = -1;
for(var i=0; i < points.length - 1; i++) {
var delta = Math.abs(angles[i] - angles[i + 1]);
if(delta >= maxDiff) {
maxDiff = delta;
maxDiffAngleIndex = i + 1;
}
}
if(maxDiffAngleIndex == -1) {
txt = "Angle: " + angles[0] + " : " + (angles[0] * 180 / Math.PI) + "\n" + txt;
return angles[0];
} else if(maxDiffAngleIndex == angles.length - 1) {
txt = "Angle: " + angles[angle.length - 1] + " : " + (angles[angles.length - 1] * 180 / Math.PI) + "\n" + txt;
return angles[angles.length - 1];
} else {
// find the average angle from the index to the end
var sumX = 0;
var sumY = 0;
for(var i = maxDiffAngleIndex; i < angles.length; i++) {
sumX += Math.cos(angles[i]);
sumY += Math.sin(angles[i]);
}
var avgX = sumX / (angles.length - maxDiffAngleIndex);
var avgY = sumY / (angles.length - maxDiffAngleIndex);
//
var avgAngle = Math.atan2(avgY, avgX);
txt = "Angle: " + avgAngle + " : " + (avgAngle * 180 / Math.PI) + "\n" + txt;
return avgAngle;
}
}
As I can see, the “movement direction” (angle) of a point would be the angular coefficient of two dots, one dot at the end of the vector and the other one at the begnning.
Cause we can only find the angle with two dots, so we can make a line, since the direction vector would be (B-A), where A and B are the points I already told you about.
We can calcule this using the formula of the angular coefficient of a line:
m = Tan θ = Δy / Δx
And that is simply:
Tan θ = (yB – yA) / (xB – xA)
Where θ is the “movement direction” (angle) and (x,y) are the coordinates of the points A and B.
Talking about the quadrant, you will only need to use the trigonometric circle to know the sinal of the value of Tan θ, so take a look at this image:
And of course, after you find the value of Tan θ, you will need to use it to find the arctan θ, and that will be your final answer.
Related
I'm writing a program that can draw a line between two points with filled circles. The circles:
- shouldn't overlap each other
- be as close together as possible
- and the centre of each circle should be on the line.
I've written a function to produce the circles, however I'm having trouble calculating position of each circle so that they are correctly lined up
void addCircles(scrPt endPt1, scrPt endPt2)
{
float xLength, yLength, length, cSquare, slope;
int numberOfCircles;
// Get the x distance between the two points
xLength = abs(endPt1.x - endPt2.x);
// Get the y distance between the two points
yLength = abs(endPt1.y - endPt2.y);
// Get the length between the points
cSquare = pow(xLength, 2) + pow(yLength, 2);
length = sqrt(cSquare);
// calculate the slope
slope = (endPt2.y - endPt1.y) / (endPt2.x - endPt1.x);
// Find how many circles fit inside the length
numberOfCircles = round(length / (radius * 2) - 1);
// set the position of each circle
for (int i = 0; i < numberOfCircles; i++)
{
scrPt circPt;
circPt.x = endPt1.x + ((radius * 2) * i);
circPt.y = endPt1.y + (((radius * 2) * i) * slope);
changeColor();
drawCircle (circPt.x, circPt.y);
}
This is what the above code produces:
I'm quite certain that the issue lies with this line, which sets the y value of the circle:
circPt.y = endPt1.y + (((radius * 2) * i) * slope);
Any help would be greatly appreciated
I recommend to calculate the direction of the line as a unit vector:
float xDist = endPt2.x - endPt1.x;
float yDist = endPt2.y - endPt1.y;
float length = sqrt(xDist*xDist + yDist *yDist);
float xDir = xDist / length;
float yDir = yDist / length;
Calculate the distance from one center point to the next one, numberOfSegments is the number of sections and not the number of circles:
int numberOfSegments = (int)trunc( length / (radius * 2) );
float distCpt = numberOfSegments == 0 ? 0.0f : length / (float)numberOfSegments;
A center point of a circle is calculated by the adding a vector the the start point of the line. The vector pints in the direction of the line and its length is given, by the distance between 2 circles multiplied by the "index" of the circle:
for (int i = 0; i <= numberOfSegments; i++)
{
float cpt_x = endPt1.x + xDir * distCpt * (float)i;
float cpt_y = endPt1.y + yDir * distCpt * (float)i;
changeColor();
drawCircle(cpt_x , cpt_y);
}
Note, the last circle on a line may be redrawn, by the first circle of the next line. You can change this by changing the iteration expression of the for loop - change <= to <:
for (int i = 0; i < numberOfSegments; i++)
In this case at the end of the line won't be drawn any circle at all.
Let say I've 5 points, where p0 and p4 are fixed with values 0.0 and 4.0:
0 | 1.0 | 2.0 | 3.0 | 4
The points in the middle can change, but they must stretch the others once moving.
So for a stretch "to right", it must enlarge the prev values around the moving point and press the next ones between the moving point and the last point, keeping the proportions between each points.
I've write this code which move the 3° point to 2.5 from its original 2.0 x-position:
const int numPoints = 5;
double points[numPoints] = { 0.0, 1.0, 2.0, 3.0, 4.0 };
int stretchedPoint = 2;
double prevX = points[stretchedPoint];
points[stretchedPoint] = 2.5;
std::cout<< points[0];
for (int prevPoint = 1; prevPoint < numPoints - 1; prevPoint++) {
// prev points
if (prevPoint < stretchedPoint) {
double ratio = points[stretchedPoint] / prevX;
points[prevPoint] *= ratio;
// next points
} else if (prevPoint > stretchedPoint) {
double ratio = (points[numPoints - 1] - prevX) / (points[numPoints - 1] - points[stretchedPoint]);
points[prevPoint] *= ratio;
}
std::cout << " | " << points[prevPoint];
}
std::cout << " | " << points[numPoints - 1];
which give to me right result for prev points:
0 | 1.25 | 2.5 | 0.76 | 4
but when I try to apply the "same-wrapped-math" for the next points, I get a non-proportional scaling, which give weird results (4?)
Can anyone help me?
You forgot about non-zero starting point
points[prevPoint] = points[stretchedPoint] + ratio * (points[prevPoint] - prevX)
Note that same logic should be applied to previos points, if start value is non-zero
In general, to apply linear interpolation for initial X0..X1 interval and final X0new..X1new interval, one have to use
(Xnew - X0new) / (X1new - X0new) = (X - X0) / (X1 - X0)
so
XNew = X0new + (X1new - X0new) * (X - X0) / (X1 - X0)
What you did on the left side of the point (and which is working) can be rewritten somehow like this:
// double ratio = (points[stretchedPoint] - 0) / (prevX - 0);
// points[prevPoint] = 0 + ratio * (points[prevPoint] - 0);
To achieve exactly the dual on the right side, it should be:
} else if (prevPoint > stretchedPoint) {
double ratio = (points[numPoints - 1] - points[stretchedPoint]) /
(points[numPoints - 1] - prevX);
points[prevPoint] = points[numPoints - 1] -
ratio * (points[numPoints-1] - points[prevPoint]);
}
I have been trying to implement the meanshift algorithm for tracking objects, and have gone through the concepts involved.
As per now I have managed to successfully generate a backprojected stream from my camera with a single channel hue roi histogram and a single channel hue video stream which seems fine, I know there is a meanshift function within the opencv library but I am trying to implement one myself using the data structures provided in opencv, calculating the moments and computing the mean centroid of the search window.
But for some reason I am unable to locate the problem within my code as it keeps on converging to the upper left corner of my video stream for any input roi (region of interest) to be tracked. Following is a code snippet of the function for calculating the centroid of the search window where I feel the problem lies but not sure what it is, I would really appreciate if someone can point me in the right direction:
void moment(Mat &backproj, Rect &win){
int x_c, y_c, x_c_new, y_c_new;
int idx_row, idx_col;
double m00 = 0.0 , m01 = 0.0 , m10 = 0.0 ;
double res = 1.0, TOL = 0.003 ;
//Set the center of search window as the center of the probabilistic image:
y_c = (int) backproj.rows / 2 ;
x_c = (int) backproj.cols / 2 ;
//Centroid search solver until residual below certain tolerance:
while (res > TOL){
win.width = (int) 80;
win.height = (int) 60;
//First array element at position (x,y) "lower left corner" of the search window:
win.x = (int) (x_c - win.width / 2) ;
win.y = (int) (y_c - win.height / 2);
//Modulo correction since modulo of negative integer is negative in C:
if (win.x < 0)
win.x = win.x % backproj.cols + backproj.cols ;
if (win.y < 0)
win.y = win.y % backproj.rows + backproj.rows ;
for (int i = 0; i < win.height; i++ ){
//Traverse along y-axis (height) i.e. rows ensuring wrap around top/bottom boundaries:
idx_row = (win.y + i) % (int)backproj.rows ;
for (int j = 0; j < win.width; j++ ){
//Traverse along x-axis (width) i.e. cols ensuring wrap around left/right boundaries:
idx_col = (win.x + j) % (int)backproj.cols ;
//Compute Moments:
m00 += (double) backproj.at<uchar>(idx_row, idx_col) ;
m10 += (double) backproj.at<uchar>(idx_row, idx_col) * i ;
m01 += (double) backproj.at<uchar>(idx_row, idx_col) * j ;
}
}
//Compute new centroid coordinates of the search window:
x_c_new = (int) ( m10 / m00 ) ;
y_c_new = (int) ( m01 / m00 );
//Compute the residual:
res = sqrt( pow((x_c_new - x_c), 2.0) + pow((y_c_new - y_c), 2.0) ) ;
//Set new search window centroid coordinates:
x_c = x_c_new;
y_c = y_c_new;
}
}
It's my second ever query on stackoverflow so please excuse me for any guidelines that I forgot to follow.
EDIT
changed m00 , m01 , m10 to block level variables within WHILE-LOOP instead of function level variables, thanks to Daniel Strul for pointing it out but the problem still remains. Now the search window jumps around the frame boundaries instead of focusing on the roi.
void moment(Mat &backproj, Rect &win){
int x_c, y_c, x_c_new, y_c_new;
int idx_row, idx_col;
double m00 , m01 , m10 ;
double res = 1.0, TOL = 0.003 ;
//Set the center of search window as the center of the probabilistic image:
y_c = (int) backproj.rows / 2 ;
x_c = (int) backproj.cols / 2 ;
//Centroid search solver until residual below certain tolerance:
while (res > TOL){
m00 = 0.0 , m01 = 0.0 , m10 = 0.0
win.width = (int) 80;
win.height = (int) 60;
//First array element at position (x,y) "lower left corner" of the search window:
win.x = (int) (x_c - win.width / 2) ;
win.y = (int) (y_c - win.height / 2);
//Modulo correction since modulo of negative integer is negative in C:
if (win.x < 0)
win.x = win.x % backproj.cols + backproj.cols ;
if (win.y < 0)
win.y = win.y % backproj.rows + backproj.rows ;
for (int i = 0; i < win.height; i++ ){
//Traverse along y-axis (height) i.e. rows ensuring wrap around top/bottom boundaries:
idx_row = (win.y + i) % (int)backproj.rows ;
for (int j = 0; j < win.width; j++ ){
//Traverse along x-axis (width) i.e. cols ensuring wrap around left/right boundaries:
idx_col = (win.x + j) % (int)backproj.cols ;
//Compute Moments:
m00 += (double) backproj.at<uchar>(idx_row, idx_col) ;
m10 += (double) backproj.at<uchar>(idx_row, idx_col) * i ;
m01 += (double) backproj.at<uchar>(idx_row, idx_col) * j ;
}
}
//Compute new centroid coordinates of the search window:
x_c_new = (int) ( m10 / m00 ) ;
y_c_new = (int) ( m01 / m00 );
//Compute the residual:
res = sqrt( pow((x_c_new - x_c), 2.0) + pow((y_c_new - y_c), 2.0) ) ;
//Set new search window centroid coordinates:
x_c = x_c_new;
y_c = y_c_new;
}
}
The reason your algorithms always converges to the upper left corner independently of the input data is that m00, m10 and m01 are never reset to zero:
On iteration 0, for each moment variable m00, m10 and m01, you compute the right value m0
Between iteration 0 and iteration 1 , the moments variables are not reset and keep their previous value
Thus, on iteration 1, for each moment variable m00, m10 and m01, you actually sum the new moment with the old one and obtain ( m0 + m1 )
On iteration 2, you carry on summing the new moments on top of the previous ones and obtain ( m0 + m1 + m2 )
And so on, iteration by iteration.
At the very least, the moment variables should be reset at the beginning of each iteration.
Ideally, they should not be function-level variables but should rather be block-level variables, as they have no use outside the loop iterations (except for debugging purpose):
while (res > TOL){
...
double m00 = 0.0, m01 = 0.0, m10 = 0.0;
for (int i = 0; i < win.height; i++ ){
...
EDIT 1
The reason for the second problem you encounter (the ROI jumping all around the place) is that the computations of the moments are based on the relative coordinates i and j.
Thus, what you compute is [ avg(j) , avg(i) ], wher as what you really want is [ avg(y) , avg(x) ]. To solve this issue, I had proposed a first solution. I"ve replaced it by a much simpler solution below.
EDIT 2
The simplest solution is to add the coordinates of the ROI corner right at the end of each iteration:
x_c_new = win.x + (int) ( m10 / m00 ) ;
y_c_new = win.y + (int) ( m01 / m00 );
I did use the findcontours() method to extract contour from the image, but I have no idea how to calculate the curvature from a set of contour points. Can somebody help me? Thank you very much!
While the theory behind Gombat's answer is correct, there are some errors in the code as well as in the formulae (the denominator t+n-x should be t+n-t). I have made several changes:
use symmetric derivatives to get more precise locations of curvature maxima
allow to use a step size for derivative calculation (can be used to reduce noise from noisy contours)
works with closed contours
Fixes:
* return infinity as curvature if denominator is 0 (not 0)
* added square calculation in denominator
* correct checking for 0 divisor
std::vector<double> getCurvature(std::vector<cv::Point> const& vecContourPoints, int step)
{
std::vector< double > vecCurvature( vecContourPoints.size() );
if (vecContourPoints.size() < step)
return vecCurvature;
auto frontToBack = vecContourPoints.front() - vecContourPoints.back();
std::cout << CONTENT_OF(frontToBack) << std::endl;
bool isClosed = ((int)std::max(std::abs(frontToBack.x), std::abs(frontToBack.y))) <= 1;
cv::Point2f pplus, pminus;
cv::Point2f f1stDerivative, f2ndDerivative;
for (int i = 0; i < vecContourPoints.size(); i++ )
{
const cv::Point2f& pos = vecContourPoints[i];
int maxStep = step;
if (!isClosed)
{
maxStep = std::min(std::min(step, i), (int)vecContourPoints.size()-1-i);
if (maxStep == 0)
{
vecCurvature[i] = std::numeric_limits<double>::infinity();
continue;
}
}
int iminus = i-maxStep;
int iplus = i+maxStep;
pminus = vecContourPoints[iminus < 0 ? iminus + vecContourPoints.size() : iminus];
pplus = vecContourPoints[iplus > vecContourPoints.size() ? iplus - vecContourPoints.size() : iplus];
f1stDerivative.x = (pplus.x - pminus.x) / (iplus-iminus);
f1stDerivative.y = (pplus.y - pminus.y) / (iplus-iminus);
f2ndDerivative.x = (pplus.x - 2*pos.x + pminus.x) / ((iplus-iminus)/2*(iplus-iminus)/2);
f2ndDerivative.y = (pplus.y - 2*pos.y + pminus.y) / ((iplus-iminus)/2*(iplus-iminus)/2);
double curvature2D;
double divisor = f1stDerivative.x*f1stDerivative.x + f1stDerivative.y*f1stDerivative.y;
if ( std::abs(divisor) > 10e-8 )
{
curvature2D = std::abs(f2ndDerivative.y*f1stDerivative.x - f2ndDerivative.x*f1stDerivative.y) /
pow(divisor, 3.0/2.0 ) ;
}
else
{
curvature2D = std::numeric_limits<double>::infinity();
}
vecCurvature[i] = curvature2D;
}
return vecCurvature;
}
For me curvature is:
where t is the position inside the contour and x(t) resp. y(t) return the related x resp. y value. See here.
So, according to my definition of curvature, one can implement it this way:
std::vector< float > vecCurvature( vecContourPoints.size() );
cv::Point2f posOld, posOlder;
cv::Point2f f1stDerivative, f2ndDerivative;
for (size_t i = 0; i < vecContourPoints.size(); i++ )
{
const cv::Point2f& pos = vecContourPoints[i];
if ( i == 0 ){ posOld = posOlder = pos; }
f1stDerivative.x = pos.x - posOld.x;
f1stDerivative.y = pos.y - posOld.y;
f2ndDerivative.x = - pos.x + 2.0f * posOld.x - posOlder.x;
f2ndDerivative.y = - pos.y + 2.0f * posOld.y - posOlder.y;
float curvature2D = 0.0f;
if ( std::abs(f2ndDerivative.x) > 10e-4 && std::abs(f2ndDerivative.y) > 10e-4 )
{
curvature2D = sqrt( std::abs(
pow( f2ndDerivative.y*f1stDerivative.x - f2ndDerivative.x*f1stDerivative.y, 2.0f ) /
pow( f2ndDerivative.x + f2ndDerivative.y, 3.0 ) ) );
}
vecCurvature[i] = curvature2D;
posOlder = posOld;
posOld = pos;
}
It works on non-closed pointlists as well. For closed contours, you may would like to change the boundary behavior (for the first iterations).
UPDATE:
Explanation for the derivatives:
A derivative for a continuous 1 dimensional function f(t) is:
But we are in a discrete space and have two discrete functions f_x(t) and f_y(t) where the smallest step for t is one.
The second derivative is the derivative of the first derivative:
Using the approximation of the first derivative, it yields to:
There are other approximations for the derivatives, if you google it, you will find a lot.
Here's a python implementation mainly based on Philipp's C++ code. For those interested, more details on the derivation can be found in Chapter 10.4.2 of:
Klette & Rosenfeld, 2004: Digital Geometry
def getCurvature(contour,stride=1):
curvature=[]
assert stride<len(contour),"stride must be shorther than length of contour"
for i in range(len(contour)):
before=i-stride+len(contour) if i-stride<0 else i-stride
after=i+stride-len(contour) if i+stride>=len(contour) else i+stride
f1x,f1y=(contour[after]-contour[before])/stride
f2x,f2y=(contour[after]-2*contour[i]+contour[before])/stride**2
denominator=(f1x**2+f1y**2)**3+1e-11
curvature_at_i=np.sqrt(4*(f2y*f1x-f2x*f1y)**2/denominator) if denominator > 1e-12 else -1
curvature.append(curvature_at_i)
return curvature
EDIT:
you can use convexityDefects from openCV, here's a link
a code example to find fingers based in their contour (variable res) source
def calculateFingers(res,drawing): # -> finished bool, cnt: finger count
# convexity defect
hull = cv2.convexHull(res, returnPoints=False)
if len(hull) > 3:
defects = cv2.convexityDefects(res, hull)
if type(defects) != type(None): # avoid crashing. (BUG not found)
cnt = 0
for i in range(defects.shape[0]): # calculate the angle
s, e, f, d = defects[i][0]
start = tuple(res[s][0])
end = tuple(res[e][0])
far = tuple(res[f][0])
a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # cosine theorem
if angle <= math.pi / 2: # angle less than 90 degree, treat as fingers
cnt += 1
cv2.circle(drawing, far, 8, [211, 84, 0], -1)
return True, cnt
return False, 0
in my case, i used about the same function to estimate the bending of board while extracting the contour
OLD COMMENT:
i am currently working in about the same, great information in this post, i'll come back with a solution when i'll have it ready
from Jonasson's answer, Shouldn't be here a tuple on the right side too?, i believe it won't unpack:
f1x,f1y=(contour[after]-contour[before])/stride
f2x,f2y=(contour[after]-2*contour[i]+contour[before])/stride**2
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