/*****************************************************************************/
/* PolyView.m								     */
/* interface file for PolyView class of RandPoly application		     */
/* Carl F. Sutter   September 1989					     */
/* RandPoly draws polygons continuously in the view. 			     */
/* Use of the Alpha channel is accomplished by drawing first in a bitmap,    */
/* and then compositing to the screen. Drawing in the icon and limiting      */
/* window resizing are also shown.  DPSDoUserPath is used for polygons.      */
/*****************************************************************************/

#import "PolyView.h"
#import "Animator.h"
#import <appkit/appkit.h>

@implementation PolyView

// newFrame - create an instance of PolyView, and initialize it
+ newFrame:(NXRect *)rect
   {
   self = [super newFrame:rect];
   nNumCorners = 5;
   [self createOpsArray];
   bAlpha = YES;
   flAlpha = 1.0;
   bDrawInIcon = NO;
   nFill = FILL_FILL;
   nBorder = BORDER_YES;
   srandom( time( 0 ) );  /* randomize the random number generator */
   timer = [Animator newChronon:0.0 adaptation:0.0 target:self
                         action:(SEL)"drawPolygons:" autoStart:YES
	              eventMask:NX_ALLEVENTS];
   bmpPoly = [Bitmap newSize:bounds.size.width :bounds.size.height
		        type:NX_UNIQUEBITMAP];
   return( self );
   } /* newFrame 8/16/89 CFS */
   
   
// appDidInit - Delegate message sent after application is set up.
// The Views's window instance variable is now set, so it's size can be 
// found.  Now find the minimum window size for a view of 1x1, since the
// bounds instance variable is used to size each polygon.
- appDidInit:(id)sender
   {
   NXRect	nxrWinFrame;
  
   [window getFrame:&nxrWinFrame];	
   minWindowSize.width = nxrWinFrame.size.width - bounds.size.width + 1.0;
   minWindowSize.height = nxrWinFrame.size.height - bounds.size.height + 1.0;
   return( self );
   } /* appDidInit 8/16/89 CFS */
   

// windowWillResize - Delegate message sent from main window.
// Prevent window from getting so small that the view size is zero.
- windowWillResize:sender toSize:(NXSize *)frameSize
   {
   frameSize->width  = MAX( frameSize->width,  minWindowSize.width );
   frameSize->height = MAX( frameSize->height, minWindowSize.height );
   return( self );
   } /* windowWillResize 9/14/89 CFS */
   

// windowDidResize - Delegate message sent from main window.
// After sizing, free and re-allocate image storage bitmap.
- windowDidResize:sender;
   {
   [bmpPoly resize:bounds.size.width :bounds.size.height];
   return( self );
   } /* windowDidResize 9/14/89 CFS */
   

// These are the outlet initialization methods made by the IB
- setCornersDisplay:anObject
   {
   cornersDisplay = anObject;
   return( self );
   }   
   
- setAlphaDisplay:anObject;
   {
   alphaDisplay = anObject;
   return( self );
   }  
   
- setDelayDisplay:anObject;
   {
   delayDisplay = anObject;
   return( self );
   }


// setCorners - set the number of corners for the following polygons
- setCorners:sender
   {
   nNumCorners = MIN( MAXCORNERS, MAX( 2, [sender intValue]) ); /* limit value */
   [cornersDisplay setIntValue:nNumCorners];
   [self createOpsArray];
   return( self );
   }
 
   
// setAlpha - set the alpha value for the next and following polygons   
- setAlpha:sender
   {
   flAlpha = [sender floatValue];
   [alphaDisplay setFloatValue:flAlpha];
   return( self );
   } /* setAlpha 9/14/89 CFS */
   
   
// setDelay - set the delay time between polygons
// to do this, free the current Animator instance and make a new one
- setDelay:sender;
   {
   double	dDelay;
   
   [timer free];
   dDelay = [sender doubleValue];
   [delayDisplay setDoubleValue:dDelay];
   timer = [Animator newChronon:dDelay adaptation:0.0 target:self 
	                 action:(SEL)"drawPolygons:" autoStart:YES 
	              eventMask:NX_ALLEVENTS];
   return( self );
   } /* setDelay 9/14/89 CFS */


// setBAlpha - set whether or not to use the bitmap and compositing
- setBAlpha:sender;
   {
   bAlpha = !bAlpha;
   return( self );
   } /* setBAlpha 9/15/89 CFS */


// drawInTheIcon - set whether to draw polygons in the icon too
- drawInTheIcon:sender
   {
   bDrawInIcon = !bDrawInIcon;
   return( self );
   }


// setFill - set the fill type
- setFill:sender
   {
   nFill = [[sender selectedCell] tag];
   } /* setFill 9/28/89 CFS */


// setBorder - set whether to draw a border or not
- setBorder:sender;
   {
   nBorder = [[sender selectedCell] tag];
   } /* setFill 9/28/89 CFS */
  

// drawPolygons - draw polygons until any other events occur
- drawPolygons:(id)sender
   {
   Window	*winIcon;	// window of Applications icon
   NXRect	nxrIcon;	// frame of icon window
   
   [self lockFocus];
   
   /* this do loop prevents locking and unlocking the view focus while     */
   /* there are no pending events.  This saves time, since locking is slow */
   /* Note that turning alpha off, and icon-drawing off is fastest.        */
   do {
      if (bAlpha)
         {
	 /* clear and draw a new polygon in the off-screen bitmap */
         [bmpPoly lockFocus];
         PScompositerect (0.0, 0.0, bounds.size.width, bounds.size.height,
                          NX_CLEAR);
         [self drawOnePolygon:bounds];
         [bmpPoly unlockFocus];
	 /* composite the polygon to the screen - the only way to use alpha */
         [bmpPoly composite:NX_SOVER toPoint:&bounds.origin];
         }
      else
         [self drawOnePolygon:bounds]; /* just draw one poly. to the screen */
   
      if (bDrawInIcon)
         {
	 /* get the icon window, it's size, and draw a poly in it's view */
         winIcon = [NXApp appIcon];
         [winIcon getFrame:&nxrIcon];
         [[winIcon contentView] lockFocus];
         [self drawOnePolygon:nxrIcon];
         [winIcon flushWindow];
         [[winIcon contentView] unlockFocus];
         }
      
      [window flushWindow];  /* send main view buffer to the screen */
      /* repeat if delay is zero, and no events are in the queue */
      } while(([delayDisplay floatValue] == 0.0) && ![timer shouldBreak]);
      
   [self unlockFocus];
   return( self );
   } /* drawPolygons 9/20/89 CFS */


// Draw one polygon in the current focus
// note: even when not compositing, the alpha value does have an effect on color
- drawOnePolygon:(NXRect)nxrBounds
   {
   short	bbox[4];		// bounding box
   short	data[MAXCORNERS*2];	// Array of data points for user path
   int		i;
   
   /* set up the bounding box array */
   bbox[0] = nxrBounds.origin.x;
   bbox[1] = nxrBounds.origin.y;
   bbox[2] = nxrBounds.origin.x + nxrBounds.size.width;
   bbox[3] = nxrBounds.origin.y + nxrBounds.size.height;
   
   /* put random x and y coordinates in the data array */
   for (i=0; i<nNumCorners; i++)
      {
      data[i * 2]     = random() % (int)nxrBounds.size.width;;
      data[i * 2 + 1] = random() % (int)nxrBounds.size.height;
      }

   /* draw the new polygon */
   PSsetgray( (float)random() / (float)RAND_MAX );
   PSsetalpha( flAlpha );
   if (nFill == FILL_FILL)
      DPSDoUserPath( data, nNumCorners*2, dps_short, ops, nNumCorners+1,
	  	     bbox, dps_ufill );
   if (nFill == FILL_EOFILL)
      DPSDoUserPath( data, nNumCorners*2, dps_short, ops, nNumCorners+1,
	  	     bbox, dps_ueofill );

   if (nBorder == BORDER_YES)
      {
      PSsetgray( NX_BLACK ); 
      PSsetalpha( 1.0 ); /* opaque */
      DPSDoUserPath( data, nNumCorners*2, dps_short, ops, nNumCorners+1,
	  	     bbox, dps_ustroke );
      }

   return( self );
   } /* drawOnePolygon 9/28/89 CFS */


// createOpsArray - Create an ops[] array that has moveto as the first element and
// a lineto per edge and finally a closepath. Everytime we change the number
// of edges we need to fix this array. This array gets passed into
// DPSDoUserPath as the array of PS operators...
- createOpsArray 
   {
   int i;
   
   ops[0] = dps_moveto;
   for (i=1; i<nNumCorners; i++) ops[i] = dps_lineto;
   ops[nNumCorners] = dps_closepath;
   } /* createOpsArray 9/28/89 CFS */


@end
