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.