/*
 * (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * This file may be freely copied and redistributed as long as:
 *   1) This entire notice continues to be included in the file, 
 *   2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * PostScript, Display PostScript, and Adobe are registered trademarks of
 * Adobe Systems Incorporated.
 * 
 * ************************************************************************
 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR 
 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY 
 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, 
 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR PARTICULAR PURPOSES AND NONINFINGEMENT OF THIRD PARTY RIGHTS.
 * ************************************************************************
 */


/*
 * DialView.m
 * Created by Ken Fromm
 *
 * This application draws an object in several ways. The first way draws 
 * with the  help of the matrix operation rotate. The second draws with 
 * rotate and user paths. The third draws with basic wraps performing 
 * the calculations on the client side. The fourth draws with the 
 * calculated user paths placed in a buffer and sent to the server a 
 * single request. The fifth follows the same procedure as the previous 
 * method except that the user path is stored in the server and not 
 * resent each time.
 */

#import "DialView.h"
#import "DialViewWraps.h"
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/TextField.h>
#import <appkit/nextstd.h>
#import <math.h>
#import <dpsclient/wraps.h>

@implementation DialView

/* These definitions are for the names of the user path arrays in the 
 * server.  Providing names like this is only to simplify the example. An 
 * optimized solution would probably use user objects and forego names 
 * altogether.
 */
static char *upath1 = {"upath1"};
static char *upath10 = {"upath10"};
static char *upath45 = {"upath45"};
static char *upath90 = {"upath90"};

/* Allocate a gstate and set clipping to NO.  The gstate is allocated 
 * because we do not want to reset the individual drawing variables when 
 * the context is switched back to this view.  The clipping is set to NO 
 * because the drawing methods do not need clipping so its a way to save
 *  display time.
 *
 * The dimensions of the dial are calculated. The bounds of the DialView 
 * is the starting point. Arrays to hold user path point and operator 
 * values are allocated.
 *
 * The PSWDefs() call sends some definitions used in the wraps to the 
 * interpreter.  They will be available in the interpreter when the wraps 
 * call them. 
 */

+ newFrame:(NXRect *) frm
{
	self = [super newFrame:frm]; 
	[self allocateGState]; 
	[self setClipping:NO];

	viewcenter.x =  bounds.size.width/2;
	viewcenter.y = bounds.size.height/2;
	if (viewcenter.x > viewcenter.y)
		maxdim = viewcenter.y - WIDCIRCBRD/2 - CIRCFF ;
	else
		maxdim = viewcenter.x - WIDCIRCBRD/2 -CIRCFF;
	viewcenter.x += bounds.origin.x;
	viewcenter.y += bounds.origin.y;

	NX_MALLOC(pts, float, MAX_PTS); 
	NX_MALLOC(ops, char, MAX_OPS);
	
	PSWDefs ();
	[self setupUserPaths];

	drawFlags.field = 0x80;

	return self;
}

/* Free the gstate upon quitting. */
- free
{
	if (pts)
		NX_FREE(pts);
	if (ops)
		NX_FREE(ops);

   	[self  freeGState]; 
  
   	return [super free];
}

/* Created by interface.  Used to obtain the id's of objects that will 
 * be messaged. */
- setMatrixDegreeTypes:anObject
{
	matrixDegreeTypes = anObject;
	return self;
}

- setMatrixDisplayTimes:anObject
{
	matrixDisplayTimes = anObject;
	return self;
}

/* Calculate the start and end points and place in user path format. Send 
 * the user path to the server and define it in the server. Execute the 
 * user path for drawing. */
static void setupTrigUserPath(
  pts, ops, x, y, startlen, endlen, deg, upathname)
	float		pts[];
	char		ops[];
	float 	x, y, startlen, endlen, deg;
	char		*upathname;
{
	int		i , j, angle;
	
	i = 4; j = 1;
	for (angle = 0; angle < 360; angle += deg)
	{
		pts[i++] = x + (float) cos(angle * RADIANS) * startlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * startlen;
		ops[j++] = dps_moveto;
		
		pts[i++] = x + (float) cos(angle * RADIANS) * endlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * endlen;
		ops[j++] = dps_lineto;
	}

	PSWDefineUserPath(pts, i, ops, j, upathname );
}

- setupUserPaths
{
	
	pts[0] = bounds.origin.x;
	pts[1] = bounds.origin.y;
	pts[2] = bounds.origin.x + bounds.size.width;
	pts[3] = bounds.origin.y + bounds.size.height;
	ops[0] = dps_setbbox;
		
	setupTrigUserPath(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN1, maxdim, DEG1, upath1);
	setupTrigUserPath(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN10, maxdim, DEG10, upath10);
	setupTrigUserPath(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN45, maxdim, DEG45, upath45);
	setupTrigUserPath(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN90, maxdim, DEG90, upath90);

	return self;
}

- eraseTimes:sender
{
	int		i;
	
	for (i = 0; i < [matrixDisplayTimes cellCount]; i++)
		[[matrixDisplayTimes cellAt:i :0] setStringValue:""];

	return self;
}

-drawViewOne:sender
{
	int	i, row;
	
	row = [sender selectedRow];
		drawFlags.field = 0x80 >> row;
	
	[self display];

	return self;
}

-drawViewAll:sender
{
	drawFlags.field = DRAWALL;

	[self display];

	return self;
}

/* Use the same line dimensions except rotate the graphics state before 
 * each line. */
static void drawRotateLines (clr, wid, startlen, endlen, deg)
	float 	clr, wid, startlen, endlen, deg;
{
	int		angle;
	
	for (angle = 0; angle < 360; angle += deg)
		PSWRotate_MakeLine(deg, startlen, 0, endlen, 0);

	PSWStrokePath(clr, wid);
}

- drawRotate:(int) cell 
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);

	PSWMarkTime (); NXPing ();	
		PSgsave();
			PStranslate(viewcenter.x, viewcenter.y);			
			if ([[matrixDegreeTypes cellAt:1 :1] state])			
				drawRotateLines(CLR1, WID1, maxdim * LEN1, maxdim, DEG1);
			if ([[matrixDegreeTypes cellAt:0 :1] state])			
				drawRotateLines(CLR10, WID10, maxdim * LEN10, maxdim, DEG10);
			if ([[matrixDegreeTypes cellAt:1 :0] state])			
				drawRotateLines(CLR45, WID45, maxdim * LEN45, maxdim, DEG45);
			if ([[matrixDegreeTypes cellAt:0 :0] state])			
				drawRotateLines(CLR90, WID90, maxdim * LEN90, maxdim, DEG90);
		PSgrestore();
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
			
	[[matrixDisplayTimes cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/* Put the line description in a user path and then rotate and uappend.
 * Not a good way to do it.  (In other words, don't try this at home.) */
static void drawRotateUserPathLines(pts, ops, clr, wid, startlen, endlen, deg)
	float		pts[];
	char		ops[];
	float 	clr, wid, startlen, endlen, deg;
{
	int		angle;

	pts[0] = pts[4] = startlen;
	pts[2] = pts[6] = endlen;
	pts[1] = -wid/2;
	pts[3] = -pts[1];
	pts[5] = pts[7] = 0;
	
	PSsetgray(clr);
	PSsetlinewidth(wid);
	for (angle = 0; angle < 360; angle += deg)
		PSWRotate_UAppend(deg, pts, 8, ops, 3);

	PSstroke();
}

- drawRotateUserPaths:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);

	PSWMarkTime (); NXPing ();
		ops[0] = dps_setbbox;
		ops[1] = dps_moveto;
		ops[2] = dps_lineto;
	
		PSgsave();
			PStranslate(viewcenter.x, viewcenter.y);
			if ([[matrixDegreeTypes cellAt:1 :1] state])			
				drawRotateUserPathLines(pts, ops, CLR1, WID1, maxdim * LEN1,
					maxdim, DEG1);
			if ([[matrixDegreeTypes cellAt:0 :1] state])			
				drawRotateUserPathLines(pts, ops, CLR10, WID10, maxdim * LEN10,
					maxdim, DEG10);
			if ([[matrixDegreeTypes cellAt:1 :0] state])			
				drawRotateUserPathLines(pts, ops, CLR45, WID45, maxdim * LEN45,
					maxdim, DEG45);
			if ([[matrixDegreeTypes cellAt:0 :0] state])			
				drawRotateUserPathLines(pts, ops, CLR90, WID90, maxdim * LEN90,
					maxdim, DEG90);
		PSgrestore();
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
			
	[[matrixDisplayTimes cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/* Calculate the position of the start and end points and add the line to 
 * the current path.  Not bad for this particular object but the 
 * calculations could become ugly for something more complex. */
static void drawTrigLines (clr, wid, x, y, startlen, endlen, deg)
	float 	clr, wid, x, y, startlen, endlen, deg;
{
	int		angle;
	
	for (angle = 0; angle < 360; angle += deg)
		PSWMakeLine(x + (float) cos(angle * RADIANS) * startlen,
					  y + (float) sin(angle * RADIANS) * startlen,
					  x + (float) cos(angle * RADIANS) * endlen,
					  y + (float) sin(angle * RADIANS) * endlen);

	PSWStrokePath(clr, wid);
}

- drawTrig:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);

	PSWMarkTime (); NXPing ();	
		if ([[matrixDegreeTypes cellAt:1 :1] state])			
			drawTrigLines(CLR1, WID1, viewcenter.x, viewcenter.y, 
				maxdim * LEN1, maxdim, DEG1);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			
			drawTrigLines(CLR10, WID10, viewcenter.x, viewcenter.y, 
				maxdim * LEN10, maxdim, DEG10);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			
			drawTrigLines(CLR45, WID45, viewcenter.x, viewcenter.y, 
				maxdim * LEN45, maxdim, DEG45);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			
			drawTrigLines(CLR90, WID90, viewcenter.x, viewcenter.y, 
				maxdim * LEN90, maxdim, DEG90);
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
					
	[[matrixDisplayTimes cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/* Calculate the start and end points and place in user path format. Send 
 * the entire user path at once. */
static void drawTrigUserPathLines(
  pts, ops, clr, wid, x, y, startlen, endlen, deg)
	float		pts[];
	char		ops[];
	float 	clr, wid, x, y, startlen, endlen, deg;
{
	int		i , j, angle;
	
	i = 4; j = 1;
	for (angle = 0; angle < 360; angle += deg)
	{
		pts[i++] = x + (float) cos(angle * RADIANS) * startlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * startlen;
		ops[j++] = dps_moveto;
		
		pts[i++] = x + (float) cos(angle * RADIANS) * endlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * endlen;
		ops[j++] = dps_lineto;
	}

	PSWUStroke(clr, wid, pts, i, ops, j);
}

- drawTrigUserPaths:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);
		
	PSWMarkTime (); NXPing ();	
		pts[0] = bounds.origin.x;
		pts[1] = bounds.origin.y;
		pts[2] = bounds.origin.x + bounds.size.width;
		pts[3] = bounds.origin.y + bounds.size.height;
		ops[0] = dps_setbbox;
		
		if ([[matrixDegreeTypes cellAt:1 :1] state])			
			drawTrigUserPathLines(pts, ops, CLR1, WID1, viewcenter.x, viewcenter.y,
				maxdim * LEN1, maxdim, DEG1);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			
			drawTrigUserPathLines(pts, ops, CLR10, WID10, viewcenter.x, viewcenter.y,
				maxdim * LEN10, maxdim, DEG10);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			
			drawTrigUserPathLines(pts, ops, CLR45, WID45, viewcenter.x, viewcenter.y,
				maxdim * LEN45, maxdim, DEG45);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			
			drawTrigUserPathLines(pts, ops, CLR90, WID90, viewcenter.x, viewcenter.y,
				maxdim * LEN90, maxdim, DEG90);
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
		
	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

- drawTrigUserPathsServer:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);
		
	PSWMarkTime (); NXPing ();		
		if ([[matrixDegreeTypes cellAt:1 :1] state])			
			PSWDrawUserPath(CLR1, WID1, upath1);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			
			PSWDrawUserPath(CLR10, WID10, upath10);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			
			PSWDrawUserPath(CLR45, WID45, upath45);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			
			PSWDrawUserPath(CLR90, WID90, upath90);
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
		
	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

/* Calculate the start and end points and place in user path format. Send 
 * the entire user path at once using the DPSDoUserPath() call. */
static void drawDPSUserPathLines(
  pts, ops, clr, wid, x, y, startlen, endlen, deg, bbox)
	float		pts[];
	char		ops[];
	float 	clr, wid, x, y, startlen, endlen, deg;
	float		bbox[4];
{
	int		i , j, angle;
	
	i = j = 0;
	for (angle = 0; angle < 360; angle += deg)
	{
		pts[i++] = x + (float) cos(angle * RADIANS) * startlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * startlen;
		ops[j++] = dps_moveto;
		
		pts[i++] = x + (float) cos(angle * RADIANS) * endlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * endlen;
		ops[j++] = dps_lineto;
	}

	PSsetgray(clr);
	PSsetlinewidth(wid);

	DPSDoUserPath(pts, i, dps_float, ops, j, bbox, dps_ustroke);
}

- drawDPSUserPaths:(int) cell;
{
	int			ElapsedTime;

	float		bbox[4];

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);
		
	PSWMarkTime (); NXPing ();		
		bbox[0] = bounds.origin.x;
		bbox[1] = bounds.origin.y;
		bbox[2] = bounds.origin.x + bounds.size.width;
		bbox[3] = bounds.origin.y + bounds.size.height;

		if ([[matrixDegreeTypes cellAt:1 :1] state])			
			drawDPSUserPathLines(pts, ops, CLR1, WID1, viewcenter.x, viewcenter.y,
				maxdim * LEN1, maxdim, DEG1, bbox);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			
			drawDPSUserPathLines(pts, ops, CLR10, WID10, viewcenter.x, viewcenter.y,
				maxdim * LEN10, maxdim, DEG10, bbox);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			
			drawDPSUserPathLines(pts, ops, CLR45, WID45, viewcenter.x, viewcenter.y,
				maxdim * LEN45, maxdim, DEG45, bbox);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			
			drawDPSUserPathLines(pts, ops, CLR90, WID90, viewcenter.x, viewcenter.y,
				maxdim * LEN90, maxdim, DEG90, bbox);
	PSWReturnTime (&ElapsedTime);

	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWStrokePath(CLRCIRCBRD, WIDCIRCBRD);
		
	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

- drawSelf:(NXRect *)r :(int) count
{
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);

	if (drawFlags.flags.rotate)
		[self drawRotate:0];
	if (drawFlags.flags.uappend)
		[self drawRotateUserPaths:1];
	if (drawFlags.flags.trig)
		[self drawTrig:2];
	if (drawFlags.flags.userpaths)
		[self drawTrigUserPaths:3];
	if (drawFlags.flags.server)
		[self drawTrigUserPathsServer:4];
	if (drawFlags.flags.dpsuserpath)
		[self drawDPSUserPaths:5];

	return self;
}

@end
