home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Brush.m -- Pressure sensitive paint brush
-
- by Peter Graffagnino, NeXT Computer Inc.
-
- Brush's responsibilities are
-
- - implement brushMoveTo:... and brushLineTo:... painting protocol.
- - provide target/action methods to tweak brush parameters
-
- You may freely copy, distribute, and reuse the code in this example.
- NeXT disclaims any warranty of any kind, expressed or implied, as to its
- fitness for any particular use.
-
- */
-
-
- #import <appkit/appkit.h>
- #import <math.h>
- #import "Brush.h"
-
- @implementation Brush
-
-
-
- /*
- * fill_tangent_trapezoid: takes two circles of radius r1 and r2 whose centers
- * are separated by (dx,dy) and draws the trapezoid formed by the common
- * tangents of the two circles, and parallel chords of the two circles. This
- * is essentially that shape of a belt around two different sized pullies. The
- * code assumes that the current point is at the center of the first circle
- */
-
- void fill_tangent_trapezoid(double r1, double r2, double dx, double dy)
- {
- double d, eta, nu, x1, y1, x1m, y1m, x2, y2, x2m, y2m;
- double xeta, yeta, xnu, ynu;
-
- d = sqrt(dx * dx + dy * dy);
-
- /*
- * protect against the degenerate case (one circle contained in the other)
- */
- if ((r1 - r2) >= d || (r1 - r2) <= -d)
- return;
-
- eta = (r1 - r2)/d;
- nu = sqrt(1 - eta*eta);
-
- nu /= d; xnu = dx*nu; ynu = dy*nu;
- eta /= d; xeta = dx*eta; yeta = dy*eta;
-
- x1 = xeta - ynu; x2 = dx + r2*x1; x1 *= r1;
- y1 = yeta + xnu; y2 = dy + r2*y1; y1 *= r1;
-
- x1m = xeta + ynu; x2m = dx + r2*x1m; x1m *= r1;
- y1m = yeta - xnu; y2m = dy + r2*y1m; y1m *= r1;
-
- PSrmoveto(x1,y1);
- PSrlineto(x1m - x1, y1m - y1);
- PSrlineto(x2m - x1m, y2m - y1m);
- PSrlineto(x2 - x2m, y2 - y2m);
- PSclosepath();
- PSfill();
- }
-
-
- /*
- * brushMoveTo:... and brushLineTo:... are the basic painting protocol. The
- * assumption is that we are currently lockFocused: appropriately. Our
- * only job is to emit postscript to draw the brush stroke, and return a dirty
- * rectangle in which we drew.
- */
-
-
- - brushMoveTo:(float)x :(float)y withPressure:(float) pressure
- dirtyRect: (NXRect *) dirty
- {
- /*
- * latch the current point as the last point. lastsize is immaterial
- * since the line will be zero length. This allows us to use lineto
- * to plot a single point.
- */
- lastx = x; lasty = y; lastsize = 1.0;
- return [self brushLineTo: x : y withPressure: pressure dirtyRect: dirty];
- }
-
-
- - brushLineTo:(float)x :(float)y withPressure:(float) pressure
- dirtyRect: (NXRect *) dirty
- {
- float size;
-
- NXSetColor(brushColor);
-
- size = pressureCoefficient*pow(pressure,pressureExponent) + minSize;
-
- /* Do the endpoint first */
- PSsetlinewidth(size);
- PSsetlinecap(1);
-
- PSmoveto(x,y); PSclosepath(); PSstroke();
-
- /* Connect the last circle to this one with bimodular tangents (honest) */
- PSmoveto(lastx,lasty);
- fill_tangent_trapezoid(lastsize/2, size/2, x - lastx, y - lasty);
-
- /* calculate dirty rect for flush */
- if(lastx > x) {
- dirty->origin.x = x;
- dirty->size.width = lastx - x;
- } else {
- dirty->origin.x = lastx;
- dirty->size.width = x - lastx;
- }
- if (lasty > y) {
- dirty->origin.y = y;
- dirty->size.height = lasty - y;
- } else {
- dirty->origin.y = lasty;
- dirty->size.height = y - lasty;
- }
- NXInsetRect(dirty, -(size + lastsize + 2.0)/2.0,
- -(size + lastsize + 2.0)/2.0);
- lastx = x; lasty = y; lastsize = size;
- return self;
- }
-
-
- /*
- * Target/action parameter methods. We implement a number of action methods
- * to allow control over our various parameters. We also use these methods
- * to latch initial values from nibified controls, by declaring appropriately
- * named phantom outlets in nib.
- */
-
- - setMinSize: sender
- {
- minSize = [sender floatValue];
- return self;
- }
-
- - setPressureExponent: sender
- {
- pressureExponent = [sender floatValue];
- return self;
- }
-
- - setPressureCoefficient: sender
- {
- pressureCoefficient = [sender floatValue];
- return self;
- }
-
- - setBrushColor: sender
- {
-
- brushColor = [sender color];
- return self;
- }
-
- /*
- * init -- give some reasonable default values. Note that the above
- * methods are called by the outlet-setting pass of nib loading, making
- * the real defaults come from the nib itself
- */
-
- - init
- {
- self = [super init];
- brushColor = NX_COLORBLACK;
- pressureExponent = 2.0;
- minSize = 0.0;
- pressureCoefficient = 12.0;
-
- return self;
- }
-
-