Add your own alternative version
Stats
183.3K views 5.7K downloads 47 bookmarked
Posted
30 Nov 2002

Comments and Discussions


When we increase the size of the line then it does not meet to the center when arrow is drawn, please help whether we need to change the formula






Very good method. I think it can be better if adding the following two sides:
1. Check the condition when start point and end point are the same point. Currently you code can only draw a line from (0,0) to this point. It will be confused to users. Maybe you can just draw nothing for this condition.
2. Your methods can be with better encapsulation. For example, when user wants to use your code, he has to use dc.MoveTo() to set start point. May be you can use the following methods:
void ArrowTo(HDC hDC, int start_x, int start_y, int end_x, int end_y, ARROWSTRUCT *pArrow);
void ArrowTo(HDC hDC, const POINT *lpFrom, const POINT *lpTo, ARROWSTRUCT *pArrow);
to replace original methods:
void ArrowTo(HDC hDC, int x, int y, ARROWSTRUCT *pArrow);
void ArrowTo(HDC hDC, const POINT *lpTo, ARROWSTRUCT *pArrow);
Of course, you have to add dc.MoveTo() in the modified methods.
Just 2 minor suggestions. Thanks again to your methods.





This is really dated, but I noticed that the arrow heads were often not quite drawn properly (aligned with the line) so I poked into the math and created another version that corrects the anomaly for the filled arrow head version:
void ArrowTo (HDC hDC, const POINT *lpTo, ARROWSTRUCT *pA) {
POINT pFrom, ptPoly[3];
// return if called with zero arrow head angle (prevents math error)
if (pA>Theta == 0) return;
// get the "from point"
MoveToEx(hDC, 0, 0, &pFrom);
// set the arrow tip point
ptPoly[0].x = lpTo>x;
ptPoly[0].y = lpTo>y;
// set the two arrow base points
double alpha = atan2(ptPoly[0].y  pFrom.y, ptPoly[0].x  pFrom.x);
double theta = pA>Theta/2.;
double factor = (double)pA>nWidth/2./sin(pA>Theta/2.);
ptPoly[1].x = ptPoly[0].x  (int)(factor*cos(alphatheta));
ptPoly[1].y = ptPoly[0].y  (int)(factor*sin(alphatheta));
ptPoly[2].x = ptPoly[0].x  (int)(factor*cos(alpha+theta));
ptPoly[2].y = ptPoly[0].y  (int)(factor*sin(alpha+theta));
// draw arrow head
MoveToEx (hDC, pFrom.x, pFrom.y, NULL);
LineTo (hDC, ptPoly[0].x, ptPoly[0].y);
Polygon (hDC, ptPoly, 3);
}





Does it work when line width larger than 1 pixel?





Hi,
The sample project runs fine in debug mode. When compiled in release mode (vc++ 2005), the arrows go 'beserk' and are drawn all over the screen. I haven't had a chance to debug it, but it looks most disturbing.
Anyone else experience this?





Alright I have no idea what's going on with this, but I change the POINT definitions to CPoint, the arrow is drawn fine in release mode.
Another fix is putting the statement:
for (int i = 0; i < 2; i++) {
}
below the declarations in the draw routine. I was using the loop to attempt to zero out all the POINT[3] indexes. But then just deleted the zero'ing out line and for some bizarre reason the arrow still gets drawn ok now. But if you take the empty loop out, or iterate to only 1, the arrow goes back to getting drawn incorrectly. Maybe my compiler is broken.





Okay, first off, thanks for catching this. I'm just starting to work in VS 2005 and just getting back to working in Windows, so it's nice to have an excuse to dig around.
Second, I used POINT so that others wouldn't have to be using MFC. If it works with CPoint, then obviously there's something wacky going on with the definitions... I might look into this...
Third, about the forloop, uh, holy crap. ?! That's pretty messed up. I have no idea what to suggest there.
J
"I am the Lorax. I speak for the trees."





Yeah it is really messed up that that would 'fix' it. I have noticed other really weird behavior by vc++ 2005 in release compilations though  and beyond that I have had a lot of bizarre problems using the software itself. Sometimes it will take 5 minutes to save a small project, sometimes the mouse pointer will disappear for 5 minutes. Sometimes text will just turn different colors and switch into some bizarre characters. This is a legally purchased version too!





Hi,
i just found two solutions.
1) change all float to double and tanf to tan
2) disable optimization in debug mode: Properties> Configuration Properties>C/C++>Optimization set to Disabled
regards,
Clemens





Hello, everyone.
I fixed the error on the release mode in VC++ 2005. At the 40th line, I added parentheses like this:
// build the line vector
vecLine[0] = (float)(aptPoly[0].x  pFrom.x);
vecLine[1] = (float)(aptPoly[0].y  pFrom.y);
I think VC++ 2005 does something wrong for the speed optimization. Anyway, it works in my computer.





Just for the folks who need to draw their own arrows, this was a method I used:
#define ARROW_SIZE (5.0)
void DrawArrow(CDC *pDC, const CPoint& ptPoint, const CPoint& ptBase)
{
double slopy , cosy , siny;
slopy = atan2((double)(ptBase.y  ptPoint.y), (double)(ptBase.x  ptPoint.x));
cosy = cos(slopy);
siny = sin(slopy);
CPoint pts[3];
pts[0] = ptPoint;
pts[1].x = ptPoint.x + (int)(ARROW_SIZE * cosy  (ARROW_SIZE / 2.0 * siny) + 0.5);
pts[1].y = ptPoint.y + (int)(ARROW_SIZE * siny + (ARROW_SIZE / 2.0 * cosy) + 0.5);
pts[2].x = ptPoint.x + (int)(ARROW_SIZE * cosy + ARROW_SIZE / 2.0 * siny + 0.5);
pts[2].y = ptPoint.y  (int)(ARROW_SIZE / 2.0 * cosy  ARROW_SIZE * siny + 0.5);
pDC>Polygon(pts, 3);
}
This is just FYI and somewhat related to this article. It's benefit is a little less floating point math. I picked it up a long time ago and modified it. I can't take credit as being the original author.





Don't forget to draw the tail
dc>MoveTo(m_beginPt);
dc>LineTo(m_endPt);





Hello,
I am looking for a simple MFC Control with Draw Area.
I would like to:
0) place this Control in CDialog, CFormView
1) draw CDC Objects on it (e.g. CDC::Polyline, CDC::Rectangle)
2) Copy Drawing to Clipboard
3) Save Drawing to EMF
Many Thanks,
Jessica





This is very good artical but missing one point.
The length of arrow cannot be controlled by program.
This may be missing point.
can any one correct it?.





If I understand correctly the article, the length of the arrowhead depends on the width and the angle, and the length of the arrow itself is given by the distance between the current point (MoveTo?) and the specified endpoint.
One possible improvement would be to specify the width of the arrow shaft.





Hemant replied privately but since the answer can be of interest for other people, I answer here:
"thanks for urgent reply.
i am saying about length of arrow head but not about
total length of arrow.
what about arrow head length"
As I said, the length of the arrow head can be calculated from the other parameters. Now, perhaps the interface can be improved to allow specifying also length and width instead of angle and width.
If my memory of trigonometry is serving me well, the length of the arrow head is deducted from the formula:
width / 2 = length * tan(angle / 2)
So length = width / 2 / tan(angle / 2)
To get an arrow head of a given width and length, you must give an angle = 2 * atan(width / length / 2)
Hoping my math is accurate...





Regardless of the ease or difficulty of the math required to calculate this, the fact that the API doesn't let you define that makes it kind of useless.
Point taken! I'll make the mods when I get home.
J
May the bear never have cause to eat you.





I agree. The parameters I used were the ones I used to work out the math. I forgot that the user might not feel like trying to remember trig when drawing arrows.
I will make the changes as soon as I can.
J
May the bear never have cause to eat you.





Greetings,
Please can someone help me. I need to know how to change the height of the arrowhead.
This is quite important.
... and urgent
Thanks,
Robert





You'll have to do a bit of trig. The relationship is this (I'm pretty sure, though it's early...):
tan( theta / 2 ) = width / ( 2 * height )
Set two and solve for the third.
J
"I am the Lorax. I speak for the trees."





Hi thanks for the response,
My big problem is that I left school so long ago and have been working in business type applications that trig and geometry knowledge is now something foreign to me
I left school when we still used log books and slide rules ... we did not even know what calculus was ... let alone be able to spell it or use it.
Please could you help with how I can change the code associated with the article to set the arrow height ... or at least what I can change to decrease the height of the arrowhead.
Thanks again,
Robert





I mean arrow HEAD height





Another way to draw predefined objects at any angle and size is to use matrix transformations, which are commonly used in computer graphics.
First, you would define your object as an array of points in some logical coordinates, for example in inches.
Then, to transform this object to new coordinate system with new origin, size and angle, you would for each point:
1. Scale point coordinates to the right scale. For example, if you defined your object in inches and transforming point to device coordiantes, then your ScaleFactor = number of device pixels per inch.
x2 = ScaleFactor * x1;
y2 = ScaleFactor * y1
2. Rotate point by a given angle:
x3 = cos(angle)*x2 + sin(angle)*y2;
y3 = sin(angle)*x2 + cos(angle)*y2;
3. Translate point to new origin
x4 = x3 + x_origin;
y4 = y3 + y_origin;
Very simple and versitile!





This probably would have been much easier. Never crossed my mind  despite my recent foray into Direct3D...
J
May the bear never have cause to eat you.






General News Suggestion Question Bug Answer Joke Praise Rant Admin Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

