
/*-----------------------------------------------------------------------------
	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
