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.