Solved

Drawing Arrows

Posted on 1998-07-22
8
383 Views
Last Modified: 2013-11-20
I am working on a MFC application that allows the user to draw some lines to construct a diagram that I will then analyze.  I need to be able to place an arrowhead on the end of a line once the ser selects the end of the line by lifting the mouse button as he/she drags the line around the Cview screen.

I am having trouble developing an algorithm using LineTo and MoveTo functions to draw the arrowhead based on the location of the end point of the line.  I need the arrowhead to be the same size for all lines. The trouble I am having is due to the fact that the line may be drawn at any angle ( 0 degrees - 360 degrees ).

Does anyone have some code or ideas on how to do this?  Is there a way to do it with a bitmap if LineTo and MoveTo do not suffice?
0
Comment
Question by:scs1
  • 4
  • 3
8 Comments
 
LVL 1

Expert Comment

by:eburley
ID: 1319585
this should be worth more, but, the key is that you need to position the arrow vertices and then rotate them to account for the angle of the line.

something like this (mix of real and psuedocode)

// assume here that your line endpoints are know
// as x1,y1  x2,y2, and that you want the arrow at x1,y1 end
// and that int arrowsize represents how far back the arrow
// starts on the line.  note that you'll want to add checks to
// make sure that the arrowhead isn't longer than the line.
// and also note that you should scale the arrow size with the
// line thickness.

// this is the angle of the line (origin at x1,y1)
double theta = atan2(x2-x1,y2-y1); // see the ref on atan2

// now find the vertical and horizontal components of
// a point located arrowsize away from x1,y1
int x3 = x1 + (arrowsize * cos(theta));
int y3 = y1 + (arrowsize * sin(theta));

// now you need to find the other points of the arrowhead:
//        /A
//       /
//      E--M---------------------------
//       \
//        \B
// we just got M, now we need A and B, where the fatness is
// represented by some scale of arrowsize we'll assume fatness
// is already declared, a good value might be .33

// the other points are as follows
int x4 = x3 + ((y3 - y1) * fatness);
int y4 = y3 + ((x3 - x1) * fatness);

// and
int x5 = x3 - ((y3 - y1) * fatness);
int y5 = y3 - ((x3 - x1) * fatness);

// from there, just draw away.
0
 
LVL 1

Expert Comment

by:eburley
ID: 1319586
-- adding email notification
0
 

Author Comment

by:scs1
ID: 1319587
I would offer more points if I had them available.  

Your method works for the case in which your arrow is drawn.  However, think about it being drawn on different angles: 45, 315, 82, etc.  It will not work in all cases.  Also, I want the arrowhead to be from the x2,y2 end.
0
 

Author Comment

by:scs1
ID: 1319588
I would offer more points if I had them available.  

Your method works for the case in which your arrow is drawn.  However, think about it being drawn on different angles: 45, 315, 82, etc.  It will not work in all cases.  Also, I want the arrowhead to be from the x2,y2 end.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 1

Accepted Solution

by:
eburley earned 50 total points
ID: 1319589
actually, it will work, since it takes the angle theta into account.  also, use whatever end points you want as x1,y1 x2,y2.

consider a 45 degree angle

45:
assume line from (0,0) to (100,100)
theta = atan2(100-0,100-0) //0.7854 radians or  45 degrees
we now get a point on the line that is arrowsize pixels from the end.
// assume arrowsize is 10
x3 = 0 + (cos(theta) * arrowsize) // 0.7071 * arrowsize +0 is 7
y4 = 0 + (sin(theta) * arrowsize) // 0.7071 * arrowsize +0 is 7
by pythagorus (7^2 + 7^2) = arrowsize^2,
solving, we get arrowsize of 10. so we'd find the arrow center at 7,7, and it would have a lenght of 10.
0
 

Expert Comment

by:seshu
ID: 1319590
I have written a sample code for the line and the arrow.
What i feel is if u hardcode the length of the arrow head it w'll look awkward when the
lenght of the arrow itself is very small. So i have given a length as a  20% ratio of the
line legth if u don't want this feature eliminate the if statment;


Try this
      CPen penBlack;
      if(penBlack.CreatePen(PS_SOLID,2,m_crGraphColor))
      {
      CPen *pOldPen=pDC->SelectObject(&penBlack);
      
      double PI= (double(22)/double(7));
      y=-y;//To reverse the default coordinate direction of windows
      double theta,theta1,theta2;
      theta=double(atan(y/x));
      pDC->MoveTo(0,0);

      if((x>=0)&&(y>=0))
            theta=theta;
      if((x<=0)&&(y>=0))
            theta=PI+theta;
      if((x<=0)&&(y<=0))
            theta=PI+theta;
      if((x>=0)&&(y<=0))
            theta=2*PI+theta;

      //radius is the length of the complex number from the origin
      double radius;
      radius= sqrt(x*x+y*y);
      double radius2,radius1;

    //radius2 is the length of the arrow
      radius2=radius*0.2;
      if (radius2>25) radius2=25;

      //radius1 is the distance of the origin from the other tip of the arrow
      radius1=sqrt((radius*radius)+(radius2*radius2)-(2*radius2*radius*cos(PI/12)));
      
      double delta =asin(radius2*sin(PI/12)/radius1);  //angle between the line & arrow
      double delta2=asin(radius2*sin(PI/10)/radius1);
      theta2=theta-delta;
      theta1=theta+delta;
    if((x==0)&&(y>0))
      {
            theta1=PI/2+delta2;
            theta2=PI/2-delta2;
      }
      if((x==0)&&(y<0))
      {
            theta1=3*PI/2+delta2;
            theta2=3*PI/2-delta2;
      }
      if((y==0)&&(x>0))
      {
            theta1= delta2;
            theta2= -delta2;
      }
      if((y==0)&&(x<0))
      {
            theta1=PI+delta2;
            theta2=PI-delta2;
      }
      pDC->LineTo(x,y);
      pDC->LineTo(radius1*cos(theta1),radius1*sin(theta1));
      pDC->MoveTo(x,y);
      pDC->LineTo(radius1*cos(theta2),radius1*sin(theta2));
      pDC->SelectObject(pOldPen);
}
0
 
LVL 1

Expert Comment

by:eburley
ID: 1319591
actually, you don't have to worry about what quadrant theta lies in, since the sin and cos will go negative at the appropriate time, so you can make it more efficient.

as for the arrow size, you can use whatever you want to get the arrow size.
 
at any rate, the code I used is from production code of a shipping product.  I'm kinda burned that you won't credit my answer.
0
 

Author Comment

by:scs1
ID: 1319592
Well, thank you both for the effort.  Unfortunately, I was unable to get your methods to work.  eburley, your method seemed great for the cases you presented, but I could not get it to work in all cases. (Probably my fault)  Seshu, I tried your method some last night, but I did not get it to work either.  I had trouble following what you were doing (MoveTo 0,0)  I tried pasting your code in my program and messed with it for a little while, but could not get it to work.

The good news is that I sat down and rethought the problem.  I managed to develop a very simple algorithm which in all cases.  I did take the quadrants into account, though. (I use abs( ) when getting delta y and delta x)

Thanks, for your effort.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
C++ BOOL WINAPI ReadFile fails on windows 10 when reading from USB cable 9 383
maven archtype selection in eclipse 1 53
move a line in eclipse 3 82
wordmultiple challenge 12 121
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…

895 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now