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.