ftp.nice.ch/peanuts/GeneralData/Documents/adobe/DPS.Purple.LineDraw.tar.gz#/NX_LineDraw/DrawView.m

This is DrawView.m in view mode; [Download] [Up]

/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) 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 NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	DrawView.m
 *
 *	The purpose of the application is to show different methods for drawing paths and 
 *	the times obtained for each method.  Lines are used in the construction of the paths
 *	but curves and arcs could be used as well.
 *
 *	This file contains the methods for the DrawView class, a subclass of view.
 *	The important methods to note are the methods to draw the lines.
 *
 *	Version:	2.0
 *	Author:	Ken Anderson, Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 */

#import "DrawView.h"
#import "DrawViewWraps.h"
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/TextField.h>
#import <dpsclient/wraps.h>

@implementation DrawView

/*
* A gstate is allocated for this view and the clippling is set to NO.  The gstate is used
* because the view is considered important enough to have one.  The clipping is set to NO
* because it can be expensive.  In addition, the lines are constructed to fall entirely within the view.
* The initialization is done here because the "initailize" method is too late for instance
* variables that are involved in the display of the view.
*/
-initFrame:(const NXRect *) frameRect
{
	[super  initFrame:frameRect]; 

	[self allocateGState]; 
	[self setClipping:NO];
	
	srand(1);
	TotalLines = 0;
		
	PSTrace = Random = NO;

	PSWDefs();
	
	return self;
}

/* Since we allocate a gstate, why not free it.  In all likelihood, the gstate is freed anyways.
* A freeGState method is available, however, so conservative programming dictates that it be used.
*/ 
- free
{
    [self  freeGState];
    
    return [super free];
}

/*
*  This method is used to obtain the ids of the text fields to display the times.
*/
-setMatrixDisplayTimes:anObject;
{
	matrixDisplayTimes = anObject;
	return self;
}

/*
*  These two methods are used to obtain the ids of the line color and line width sliders.
*/

-setSliderColor:anObject
{
	sliderColor = anObject;
	LineColor = [sliderColor floatValue];

	return self;
}

-setSliderWidth:anObject
{
	sliderWidth = anObject;
	LineWidth = [sliderWidth floatValue];

	return self;
}

/* Id of total lines text field. */
-setFieldTotalLines:anObject
{
	fieldTotalLines = anObject;

	return self;
}

/* Messaged from the button to select the line color and width.  The sender is a matrix
*  of buttons (a one cell matrix).  The state of the button is used to key the enabling or
* disabling of the slider controls. At the end, the value of Random is flipped to make it
* sematically correct. Otherwise, YES would be no and NO would be yes.
*/
-selectColorWidth:sender
{
	Random = [[sender selectedCell] state];
	[sliderColor setEnabled:Random];
	[sliderWidth setEnabled:Random];
	Random = !Random;
	
	return self;
}

/* The sliders message these methods.  */
-changeLineColor:sender
{
	LineColor = [sender floatValue];
	
	return self;
}

-changeLineWidth:sender
{
	LineWidth = [sender floatValue];
	
	return self;
}

/* This method changes the title of the menu cell according to the
* value of the PSTrace variable.
*/
-psTrace:sender
{
	if (PSTrace == NO)
		[[sender selectedCell] setTitle:"Trace On"];
	else
		[[sender selectedCell] setTitle:"Trace Off"];
	PSTrace = !PSTrace;
	
	return self;
}

/* Erases the times. Messaged when new lines are made or the lines are cleared. */
-eraseTimes:sender
{
	int		i;
	
	for (i = 0; i < [matrixDisplayTimes cellCount]; i++)
		[[matrixDisplayTimes cellAt:i :0] setStringValue:""];

	return self;
}

/* This method is messaged by the row of buttons to make lines.  The sender is a matrix
* so the tag of the selected cell is used to obtain the number of lines to make.  If the total
* number of lines exceeds the size of the line arrays, then system beep; otherwise, 
* randomly create the lines and populate the arrays.  If Random is set, the line color and
* width are randomly selected with each line.  This is the worst case displaying lines,
* especially with the OptimizedStroke.  If Random is not set then the current values
* of LineColor and LineWidth are used.
*/
-makeLines:sender
{
	int  Numsetlines, j;

	[self  eraseTimes:self];
	
	Numsetlines = [sender selectedTag];

	if (TotalLines >= MAXARRAY)
		NXBeep();
	else
	{
		if (TotalLines + Numsetlines > MAXARRAY)
			Numsetlines = MAXARRAY - TotalLines;

		for (j = TotalLines; j < TotalLines + Numsetlines; j++)
		{
			X[j] = rand () % ((int)bounds.size.width -4) +2;
			Y[j] = rand () % ((int)bounds.size.height -4) +2;
			X1[j] = rand () % ((int)bounds.size.width -4) +2;
			Y1[j] = rand () % ((int)bounds.size.height -4) +2;
			if (Random)
			{
				C[j] = (rand() % 1000) * 0.001;
				W[j] = (rand() % (MAXWIDTH * 10)) * 0.1;
			}
			else
			{
				C[j] = LineColor;
				W[j] = LineWidth;
			}
		}
		
		TotalLines += Numsetlines;
		[fieldTotalLines setIntValue:TotalLines];
	}

	return self;
}

-clearLines:sender
{
	TotalLines = 0;
	[fieldTotalLines setIntValue:TotalLines];

	[self  eraseTimes:self];
	
	return self;
}

/*
*   Messaged by the method drawing matrix of buttons.
*   Messages display which in turn will message drawSelf::. 
*/
-drawViewOne:sender
{
	int	i, row;
	
	row = [sender selectedRow];
		drawFlags.field = 0x80 >> row;
	
	[self display];

	return self;
}

/*  Messaged by "Draw All" button. Messages display which in turn will message drawSelf::.  */
-drawViewAll:sender
{
	drawFlags.field = DRAWALL;

	[self display];

	return self;
}

/*
* Below are five methods.  Each uses a different approach to drawing lines.
* In some cases, the approaches are similar. Each is annotated.
*/

/* This approach uses the single operator function calls which are quite easy to use because
* no wraps are necessary.  The drawback is that each call is a separate message to
* the interpreter whereas a wrap is just one message.  This approach is fine for simple
* drawing but wraps should be used for anything over a few lines and repetitions.
*/
-drawSingleOps:(int) cell
{
	int		ElapsedTime,counter;
	
	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];

	PSWMarkTime(); NXPing ();	
	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), YES);

	PSsetgray (BGCOLOR);
	PSrectfill (0.0, 0.0, bounds.size.width, bounds.size.height);
	PSsetgray (BGSTRCOLOR);
	PSsetlinewidth (BGSTRWIDTH);
	PSrectstroke (0.0, 0.0, bounds.size.width, bounds.size.height);

	for (counter = 0; counter < TotalLines; counter++)
	{
		PSsetlinewidth(W[counter]);
		PSsetgray (C[counter]);
		PSmoveto (X[counter], Y[counter]);	
		PSlineto (X1[counter], Y1[counter]);
		PSstroke ();
	}	

	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

/* This approach creates wraps to erase the view and draw the lines.  The appropriate
* arguments are passed to each wrap.  Compare this method with the one that follows.
* The time difference between the two is slight.  As in the singler operator approach,
* this method is fine for drawings that are displayed infrequently.  For drawings that
* are repeated, binding is suggested.  See the comments in DrawViewWraps.psw
* for more information on binding.
*/
-drawWraps:(int) cell
{
	int		ElapsedTime, counter;
	float		ViewRect[4];
	
	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];

	PSWMarkTime (); NXPing ();
	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), YES);

	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWEraseView (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
		
	for (counter = 0; counter < TotalLines; counter++)
	{
		PSWDrawLine (W[counter], C[counter], X[counter], Y[counter],
			X1[counter], Y1[counter]);
	}

	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

/* The wrap calls used in this approach make use of definitions contained in PSWDefs().
*  The definitions bind their operators which simply means that the name lookup occurs
* at definition time only once rather than with each interpretation of the definition (which
* can occur many times).  This approach is recommended for more than the simplest
* drawing.  Drawings with lines that are of uniform line color and width might benefit
* from the opitimized stroke method.  Random line colors and widths show this to be
* the fastest. 
*/
-drawWrapsBind:(int) cell
{
	int		ElapsedTime, counter;
	float		ViewRect[4];
	
	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];

	PSWMarkTime (); NXPing ();
	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), YES);

	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
		
	for (counter = 0; counter < TotalLines; counter++)
	{
		PSWDrawLineBind (W[counter], C[counter], X[counter], Y[counter],
			X1[counter], Y1[counter]);
	}

	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}	


/*  This approach is uses the interpreter to loop through the array and draw the
* lines.  As a result, a good deal of stack manipulation must be performed in the wrap.
* The timing results show this to be a poor approach.  Not recommended at this time.
*/
-drawWrapsRepeat:(int) cell
{
	int		ElapsedTime;
	float		ViewRect[4];
	
	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];

	PSWMarkTime (); NXPing ();
	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), YES);

	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
	PSWDrawLineRepeatBind (W, C, X, Y, X1, Y1, TotalLines);

	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

/* This approach builds the path with successive moveto's and lineto's until the end of the
* array or a change in line color or width occurs.  At this point, the path is stroked.  The
* timing studies show this to be an efficient approach when the line color and width appear
* in non-random order.  As a result, this approach might be used to display a grid or other
* similar uniform drawing.  NOTE: as of this writing the path stack contains a 1500 point limit.
* Therefore, a check should be included to stroke if the limit has been reached.  Otherwise
* a limiterror may occur.
*/
-drawOptimizedStroke:(int) cell
{
	int		ElapsedTime,counter;
	float		ViewRect[4];
	
	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];

	PSWMarkTime (); NXPing ();
	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), YES);

	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
		
	for (counter = 0; counter < TotalLines; counter++)
	{
		PSWMakeLineBind (X[counter], Y[counter], X1[counter], Y1[counter]);

		if (counter >= TotalLines -1 ||
			C[counter] != C[counter + 1] ||
				W[counter] != W[counter+1])
		{
			PSWStrokeLineBind (W[counter], C[counter]);
		}
	}

	if (PSTrace)
		DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];

	return self;
}

/* Messaged by the "display" method.  This method should not be called directly. */
-drawSelf:(NXRect *)r :(int) count
{	
	float	ViewRect[4];
	
	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);

	if (drawFlags.flags.singleops)
		[self drawSingleOps:0];
	if (drawFlags.flags.wraps)
		[self drawWraps:1];
	if (drawFlags.flags.bind)
		[self drawWrapsBind:2];
	if (drawFlags.flags.repeat)
		[self drawWrapsRepeat:3];
	if (drawFlags.flags.optimized)
		[self drawOptimizedStroke:4];

	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.