|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.