This is DrawView.m in view mode; [Download] [Up]
/*
* (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.
* ************************************************************************
*/
/*
* DrawView.m
* Created by Ken Anderson, Ken Fromm
*
* 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.
*/
#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.
*/
+newFrame:(NXRect *) frm
{
self = [super newFrame:frm];
[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:"PS Trace On"];
DPSTraceContext(DPSGetCurrentContext(), YES);
}
else
{
[[sender selectedCell] setTitle:"PS Trace Off"];
DPSTraceContext(DPSGetCurrentContext(), NO);
}
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();
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 ();
}
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();
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]);
}
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();
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]);
}
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();
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);
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();
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]);
}
}
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.