This is UphillLine.m in view mode; [Download] [Up]
/*-----------------------------------------------------------------------------
Implementation of uphill line. This draws a line from the lower left
hand corner of the view box to the upper right hand corner, and optionally
adds arrows to the ends of the line.
HISTORY
22Mar93 DM New
-----------------------------------------------------------------------------*/
#import <dpsclient/wraps.h>
#import <math.h>
#import "UphillLine.h"
#import "line.h"
@implementation UphillLine
- drawSelf:(NXRect *)rects :(int)count; // The classic NeXT method
{
/*-----------------------------------------------------------------------------
An "uphill" line, going from the lower left of the box to the upper right.
-----------------------------------------------------------------------------*/
float redComponent, greenComponent, blueComponent, alphaComponent; // The components of the color
NXColor drawingColor; // either the enabled or disabled color
float angle, leg1, leg2; // utility trig things
// Get the line grey and the width set, then start drawing things
drawingColor = ([self isEnabled]) ? enabledColor : disabledColor;
[self setFlipped:NO]; // starts out flipped in buttons.
// fix that and conserve sanity
NXConvertColorToRGBA(drawingColor, &redComponent, &greenComponent, &blueComponent, &alphaComponent);
// Deal with the alpha component. If none is specified, set it to 1.0 (opaque paint);
// otherwise, respect what was set in the color.
if(alphaComponent == NX_NOALPHA)
PSsetalpha(1.0);
else
PSsetalpha(alphaComponent);
PSsetrgbcolor(redComponent, greenComponent, blueComponent);
PSsetlinewidth(lineWidth);
// Figure out the "start" and "end" points; don't want to go all the way to the edge, since
// this screws up the arrows.
angle = atan(bounds.size.height/bounds.size.width);
leg1 = sin(angle) * lineWidth;
leg2 = cos(angle) * lineWidth;
angle = (angle/(2 * M_PI)) * 360;
// Draw arrows on end, if so desired.
if(startArrow)
{
PSArrow(leg2, leg1, 180.0 + angle); // Don't go quite all the way to the edges
PSstroke();
}
if(endArrow)
{
PSArrow(bounds.size.width - leg2, bounds.size.height - leg1, angle);
PSstroke();
}
PSnewpath();
// Some munging around to get the arrows looking neat, if any
if(startArrow) // Don't want square end butts screwing up my arrows
PSmoveto(leg2, leg1);
else
PSmoveto(0, 0);
if(endArrow)
PSlineto(bounds.size.width - leg2,(bounds.size.height - leg1));
else
PSlineto(bounds.size.width, bounds.size.height);
PSstroke();
return self;
}
- mouseDown:(NXEvent*)theEvent; // Mousedown handling
{
/*----------------------------------------------------------------------
Tests to see if the mouse click is within tolerance of being on the
line. Generally if it is not within tolerance, we should pass the
click on the the next responder in the chain.
This uses some semi-obscure math, lifted from the "draw" example.
The angle the line makes with the x-axis (for an uphill line) is
determined; that's just the arctan of the rise over the run
(bounds.y/bounds.x). The angle of the line from the origin to the
mouse click point is found by a similar operation. Now, the diagonal
line from corner to corner and the line from the origin to the point,
together with a line perpindicular to the diagonal running to the
mouse click point, form a right triangle. We know the angle formed
in one corner--that's the difference between the two angles we
determined earlier. We can find the length of the hypotenous--that's
the distance from the origin to the mouse click. From that we
can determine the length of the perpendicular line from the diagonal
to the mouse click, which is the distance we are looking for. If that's
within tolerance, we declare a "hit."
Oh, for a drawing capability in source code comments.
There are some other special cases, as noted below, primarily involving
vertical or horizontal lines or downhill lines.
----------------------------------------------------------------------*/
float diagonalAngle, mouseClickAngle; //angle from x-axis, generally
NXPoint mousePoint; //the click point, usually
float distance; //distance to line
// Convert from window coordinates to local view coordinates.
mousePoint.x = theEvent->location.x; mousePoint.y = theEvent->location.y;
[self convertPoint:&mousePoint fromView:nil];
mousePoint.x = mousePoint.x - bounds.origin.x;
mousePoint.y = mousePoint.y - bounds.origin.y;
// find the angles and the difference between them
diagonalAngle = atan(bounds.size.height/bounds.size.width);
mouseClickAngle = atan(mousePoint.y/mousePoint.x);
distance = sqrt(mousePoint.x*mousePoint.x + mousePoint.y*mousePoint.y)*
sin(fabs(diagonalAngle - mouseClickAngle));
if ((distance - (lineWidth/2.0) - HIT_TOLERANCE) <= 0.0)
return [self performClick:self];
else
return [self passOnEvent:theEvent];
return self; // compiler fodder
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.