Arrowheads
Volume Number: | | 1
|
Issue Number: | | 9
|
Column Tag: | | C WORKSHOP
|
How to Draw Arrowheaded Lines
How to Draw Arrowheaded Lines
While writing a new application, I discovered I needed to have lines with arrowheads (like the ones we all love in MacDraw). Not only that, I needed to have the line stop on the surface of an object (usually an oval or rectangle).
The first place to look, of course, is to see how MacDraw did it. After using MacDraw for some time in various applications, I was surprised to see that the arrowheads were not 3-pointed polygons but actually small wedges! This was discovered after placing an arrowhead on a thick line and noticing the curvature of the arrowhead.
Keeping with the Macintosh spirit, I decided to make the call similar to the ones used in QuickDraw (i.e. - Line and LineTo). Instead of using a LineTo call, this routine may be substituted and all of the LineTo's changed to ArrowLineTo's.
The parameter passed to it is the absolute horizontal and vertical coordinates of the end point of the line (as in LineTo). The end point is also where the arrowhead will be drawn.
The basic algorithm is simple -
1). Compute the slope of the line
2). Draw the line
3). Point the wedge in the opposite direction of the line (to create the effect of an arrowhead).
4). Draw the wedge
There is a fantastic Mac routine called PtToAngle that will compute the angle for you! All you need to do is to place a rectangle around the starting point, make the call, and the angle is returned to you (in degrees)!
The following code (written in Mac C) is pretty much self explanatory. You could experiment with the constants arrowWidth and arrowLength (as shown in Fig. 1) to vary the width and length of the arrowhead. Note - this routine could be changed so that these constants are passed as parameters, for applications that need varying sizes of arrowheads.
/************************************************************************
ArrowLineTo.c
Copyright 1985, FlottWare
************************************************************************/
#include "MacCDefs.h"// Mac ROM data structure definitions
#include "Events.h"
#include "Window.h"
extern struct P_Str *CtoPstr();
int strlen(str) char *str;
{int i=0; while (str[i++]); return i-1;}
/*------------------------------------------------------------------------
Global Data
------------------------------------------------------------------------*/
#define False 0
#define True 0xFF
Rect windowRect = {40,4,336,508}; // Window Rectangle
WindowPtr windowPtr; // Window Pointer
EventRecord event; // Current event record
/*------------------------------------------------------------------------
main()
------------------------------------------------------------------------*/
main()
{
if (CatchSignal()) // Is the user quitting this application?
ExitToShell(); // Go to Finder when done
init(); // Init Mac
while (True) // Poll events forever
{
SystemTask();// Perform System duties
if (GetNextEvent(everyEvent, &event)) // Get the next event
{
switch ( event.what ) // Which event is it?
{
case autoKey:
case keyDown: // Key was pressed
case mouseDown: // Mouse Button pressed
{
Signal("All Done"); // Y - quit application
break;
} // case autoKey,keyDown,mouseDown:
} // switch (event.what)
} // if (Getevent)
} // while (true)
} // main
/************************************************************************
Windows
***********************************************************************/
/*----------------------------------------------------------------------
init() -
----------------------------------------------------------------------*/
init()
{
// Open a window
windowPtr = NewWindow(0,&windowRect,CtoPstr("Hit Key or Mouse to Quit "),True,
documentProc,-1,True,0);
SetPort(windowPtr);
#define centerH 250
#define centerV 150
#define offset 50
// Draw some lines with arrowheads
MoveTo(centerH,centerV);
ArrowLineTo(centerH-offset,centerV);
MoveTo(centerH,centerV);
ArrowLineTo(centerH-offset,centerV+offset);
MoveTo(centerH,centerV);
ArrowLineTo(centerH,centerV+offset);
MoveTo(centerH,centerV);
ArrowLineTo(centerH+offset,centerV+offset);
MoveTo(centerH,centerV);
ArrowLineTo(centerH+offset,centerV);
MoveTo(centerH,centerV);
ArrowLineTo(centerH+offset,centerV-offset);
MoveTo(centerH,centerV);
ArrowLineTo(centerH,centerV-offset);
MoveTo(centerH,centerV);
ArrowLineTo(centerH-offset,centerV-offset);
} // end init()
/*----------------------------------------------------------------------
ArrowLineTo(horiz,vert)
This routine draws a line from the current pen location
to point (h,v) with an arrowhead (wedge) on the end of it.
Inputs : horiz - horizontal coord of end point
vert - vertical coord of end point
Written by: Rick Flott Mac C (Consulair) V
1.0
----------------------------------------------------------------------*/
ArrowLineTo(horiz,vert)
short horiz;
short vert;
{
// Size of starting Rect used in PtToAngle
#define rectOffset 30
// Arrowhead (wedge) constants
// arrowWidth - width of 1/2 arrowhead (in degrees)
// arrowLength - length of arrowhead in pixels
#define arrowWidth 25
#define arrowLength 10
// Rects used in angle calculations
Rect startRect; // starting Rect used in PtToAngle
Rect arrowheadRect; // the Rect that the wedge is drawn in
short arrowAngle;// Angle of arrowhead (in degrees)
Point startPt,endPt; // Start,End points of line
GetPen(&startPt);// Get the current pen location
// Set up a rectangle around the starting point
// (this is needed for the PtToAngle routine)
SetRect (&startRect,startPt.h - rectOffset,
startPt.v - rectOffset,
startPt.h + rectOffset,
startPt.v + rectOffset);
SetPt(&endPt,horiz,vert); // Set up the end point
// Calculate the angle (in degrees) of the line segment
PtToAngle(&startRect,&endPt,&arrowAngle);
LineTo(horiz,vert);// Draw the line
arrowAngle -= (180 + arrowWidth);// Create a arrowhead with a wedge
facing the opposite direction
// Set up a Rect for the wedge around the endpoint
SetRect (&arrowheadRect,endPt.h - arrowLength,
endPt.v - arrowLength,
endPt.h + arrowLength,
endPt.v + arrowLength);
// Draw arrow head (a reversed wedge)
PaintArc(&arrowheadRect,arrowAngle,2*arrowWidth);
} // end ArrowLineTo()