home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / UnderPressure / Brush.m < prev    next >
Text File  |  1992-06-11  |  5KB  |  180 lines

  1. #if 0
  2.  
  3.   Brush.m -- Pressure sensitive paint brush
  4.  
  5.   by Peter Graffagnino, NeXT Computer Inc.
  6.  
  7.   Brush's responsibilities are
  8.  
  9.   - implement brushMoveTo:... and brushLineTo:... painting protocol.
  10.   - provide target/action methods to tweak brush parameters
  11.  
  12.   You may freely copy, distribute, and reuse the code in this example.
  13.   NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  14.   fitness for any particular use.
  15.  
  16. #endif
  17.  
  18.  
  19. #import <appkit/appkit.h>
  20. #import <math.h>
  21. #import "Brush.h"
  22.  
  23. @implementation Brush
  24.  
  25.  
  26.  
  27. /*
  28.  * fill_tangent_trapezoid:  takes two circles of radius r1 and r2 whose centers
  29.  * are separated by (dx,dy) and draws the trapezoid formed by the common
  30.  * tangents of the two circles, and parallel chords of the two circles.  This
  31.  * is esseUCelly that shape of a belt around two different sized pullies.  The
  32.  * code assumes that the current point is at the center of the first circle
  33.  */ 
  34.  
  35. void fill_tangent_trapezoid(double r1, double r2, double dx, double dy)
  36. {
  37.     double d, eta, nu, x1, y1, x1m, y1m, x2, y2, x2m, y2m;
  38.     double xeta, yeta, xnu, ynu;
  39.  
  40.     d = sqrt(dx * dx + dy * dy);
  41.  
  42.     /*
  43.      * protect against the degenerate case (one circle contained in the other)
  44.      */
  45.     if ((r1 - r2) >= d || (r1 - r2) <= -d)
  46.     return;
  47.  
  48.     eta = (r1 - r2)/d;
  49.     nu = sqrt(1 - eta*eta);
  50.     
  51.     nu /= d;     xnu = dx*nu;      ynu = dy*nu;
  52.     eta /= d;    xeta = dx*eta;    yeta = dy*eta;
  53.     
  54.     x1  = xeta - ynu;    x2 = dx + r2*x1;      x1 *= r1;
  55.     y1  = yeta + xnu;    y2 = dy + r2*y1;      y1 *= r1;
  56.     
  57.     x1m = xeta + ynu;   x2m = dx + r2*x1m;    x1m *= r1;
  58.     y1m = yeta - xnu;   y2m = dy + r2*y1m;    y1m *= r1;
  59.     
  60.     PSrmoveto(x1,y1);
  61.     PSrlineto(x1m - x1, y1m - y1);
  62.     PSrlineto(x2m - x1m, y2m - y1m);
  63.     PSrlineto(x2 - x2m, y2 - y2m);
  64.     PSclosepath();
  65.     PSfill();
  66. }
  67.  
  68.  
  69. /*
  70.  * brushMoveTo:... and brushLineTo:... are the basic painting protocol.  The
  71.  * assumption is that we are currently lockFocused: appropriately.  Our
  72.  * only job is to emit postscript to draw the brush stroke, and return a dirty
  73.  * rectangle in which we drew.
  74.  */
  75.  
  76.  
  77. - brushMoveTo:(float)x :(float)y withPressure:(float) pressure
  78.  dirtyRect: (NXRect *) dirty
  79. {
  80.     /*
  81.      * latch the current point as the last point.  lastsize is immaterial
  82.      * since the line will be zero length.  This allows us to use lineto
  83.      * to plot a single point.
  84.      */
  85.     lastx = x; lasty = y; lastsize = 1.0;
  86.     return [self brushLineTo: x : y withPressure: pressure dirtyRect: dirty];
  87. }
  88.     
  89.  
  90. - brushLineTo:(float)x :(float)y withPressure:(float) pressure
  91.  dirtyRect: (NXRect *) dirty
  92. {
  93.     float size;
  94.  
  95.     NXSetColor(brushColor);    
  96.  
  97.     size = pressureCoefficient*pow(pressure,pressureExponent) + minSize;
  98.  
  99.     /* Do the endpoint first */
  100.     PSsetlinewidth(size);
  101.     PSsetlinecap(1);
  102.     
  103.     PSmoveto(x,y); PSclosepath(); PSstroke();
  104.  
  105.     /* Connect the last circle to this one with bimodular tangents (honest) */
  106.     PSmoveto(lastx,lasty);
  107.     fill_tangent_trapezoid(lastsize/2, size/2, x - lastx, y - lasty);
  108.  
  109.     /* calculate dirty rect for flush */
  110.     if(lastx > x) {
  111.     dirty->origin.x = x;
  112. UCfty->size.width = lastx - x;
  113.     } else {
  114.     dirty->origin.x = lastx;
  115.     dirty->size.width = x - lastx;
  116.     }
  117.     if (lasty > y) {
  118.     dirty->origin.y = y;
  119.     dirty->size.height = lasty - y;
  120.     } else {
  121.     dirty->origin.y = lasty;
  122.     dirty->size.height = y - lasty;
  123.     }
  124.     NXInsetRect(dirty, -(size + lastsize + 2.0)/2.0,
  125.         -(size + lastsize + 2.0)/2.0);
  126.     lastx = x; lasty = y; lastsize = size;
  127.     return self;
  128. }
  129.  
  130.  
  131. /*
  132.  * Target/action parameter methods.  We implement a number of action methods
  133.  * to allow control over our various parameters.  We also use these methods
  134.  * to latch initial values from nibified controls, by declaring appropriately
  135.  * named phantom outlets in nib.
  136.  */
  137.  
  138. - setMinSize: sender
  139. {
  140.     minSize = [sender floatValue];
  141.     return self;
  142. }
  143.  
  144. - setPressureExponent: sender
  145. {
  146.     pressureExponent = [sender floatValue];
  147.     return self;
  148. }
  149.  
  150. - setPressureCoefficient: sender
  151. {
  152.     pressureCoefficient = [sender floatValue];
  153.     return self;
  154. }
  155.  
  156. - setBrushColor: sender
  157. {
  158.  
  159.     brushColor = [sender color];
  160.     return self;
  161. }
  162.  
  163. /*
  164.  * init -- give some reasonable default values.  Note that the above
  165.  * methods are called by the outlet-setting pass of nib loading, making
  166.  * the real defaults come from the nib itself
  167.  */
  168.  
  169. - init
  170. {
  171.     self = [super init];
  172.     brushColor = NX_COLORBLACK;
  173.     pressureExponent = 2.0;
  174.     minSize = 0.0;
  175.     pressureCoefficient = 12.0;
  176.  
  177.     return self;
  178. }
  179.     
  180.