|
|
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.