Annotation of Examples/AppKit/UnderPressure/Brush.m, revision 1.1.1.1

1.1       root        1: /*
                      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: */
                     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 essentially 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:        dirty->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:     

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.