Added help for Pen and updated some other docs
[supercollider.git] / HelpSource / Classes / Pen.schelp
blob75c77ee52bdc72151290888c6c2b54e062ce3d40
1 class:: Pen
2 summary:: Draw custom graphics
3 redirect:: implClass
4 categories:: GUI
5 related:: Classes/QPenPrinter
7 description::
8 A class which allows you to draw custom graphics on a UserView or Window.
10 The following methods must be called within a link::Classes/Window#-drawFunc:: or a link::Classes/UserView#-drawFunc:: function, and will only be visible once the window or the view is refreshed. Each call to link::Classes/Window#-refresh:: or link::Classes/UserView#-refresh:: will 'overwrite' all previous drawing by executing the currently defined function, unless link::Classes/UserView#-clearOnRefresh:: is set to code::false::
12 classmethods::
13 private:: key
15 subsection:: Construct path
16 The following methods define paths. You will need to call link::#*stroke:: or link::#*fill:: to actually draw them.
18 method:: moveTo
19 Move the Pen to point.
20 argument:: point
21 An instance of link::Classes/Point::
23 method:: lineTo
24 Draw a line (define a path) from the current position to point.
25 argument:: point
26 An instance of link::Classes/Point::
28 method:: line
29 Draw a line (define a path) from p1 to p2. Current position will be p2.
30 argument:: p1
31 An instance of link::Classes/Point::
32 argument:: p2
33 An instance of link::Classes/Point::
35 method:: curveTo
36 Draws a cubic bezier curve from the current position to point. 
37 strong::cpoint1:: and strong::cpoint2:: are control points determining the curve's curvature.
38 argument:: endPoint
39 An instance of link::Classes/Point::
40 argument:: cPoint1
41 An instance of link::Classes/Point::
42 argument:: cPoint2
43 An instance of link::Classes/Point::
45 method:: quadCurveTo
46 Draws a quad bezier curve from the current position to point. 
47 strong::cpoint1:: is a control point determining the curve's curvature.
48 argument:: endPoint
49 An instance of link::Classes/Point::
50 argument:: cPoint1
51 An instance of link::Classes/Point::
53 method:: arcTo
54 Draws an arc of a circle using a radius and tangent points.
55 argument:: point1
56 The end point of the first tangent line. Its start point is the current position. An instance of link::Classes/Point::
57 argument:: point2
58 The end point of the second tangent line. Its start point is point1. An instance of link::Classes/Point::
59 argument:: radius
60 The radius of the arc.
61 discussion::
62 example:
63 code::
65 var w = Window("arcTo", Rect(100, 200, 300, 300)).front;
66 var r= 15;
67 w.drawFunc = { |v|
68     Pen.fillColor = Color.blue;
69     Pen.strokeColor = Color.red;
70     Pen.moveTo(150@150);
71     Pen.arcTo(200@150, 200@225, r);
72     Pen.arcTo(200@225, 100@225, r);
73     Pen.arcTo(100@225, 100@150, r);
74     Pen.arcTo(100@150, 150@150, r);
75     Pen.lineTo(150@150);
76     Pen.fillStroke;
81 method:: addArc
82 Draw an arc around the link::Classes/Point:: strong::center::, at strong::radius:: number of pixels. strong::startAngle:: and strong::arcAngle:: refer to the starting angle and the extent of the arc, and are in radians [0..2pi].
83 discussion::
84 example:
85 code::
87 w = Window.new.front;
88 w.view.background_(Color.white);
89 w.drawFunc = {
90     Pen.translate(100, 100);
91     10.do{
92         // set the Color
93         Pen.color = Color.red(rrand(0.0, 1), rrand(0.0, 0.5));
94         Pen.addArc((100.rand)@(100.rand), rrand(10, 100), 2pi.rand, pi);
95         Pen.perform([\stroke, \fill].choose);
96     }
98 w.refresh;
102 method:: addWedge
103 Draw a wedge around the link::Classes/Point:: strong::center::, at strong::radius:: number of pixels. strong::startAngle:: and strong::sweepLength:: refer to the starting angle and the extent of the arc, and are in radians [0..2pi].
104 discussion::
105 example:
106 code::
108 w = Window.new.front;
109 w.view.background_(Color.white);
110 w.drawFunc = {
111     Pen.translate(100, 100);
112     10.do{
113         // set the Color
114         Pen.color = Color.blue(rrand(0.0, 1), rrand(0.0, 0.5));
115         Pen.addWedge((100.rand)@(100.rand), rrand(10, 100), 2pi.rand, 2pi.rand);
116         Pen.perform([\stroke, \fill].choose);
117     }
119 w.refresh;
123 method:: addAnnularWedge
124 Draw an annular wedge around the link::Classes/Point:: strong::center::, from strong::innerRadius:: to strong::outerRadius:: in pixels. strong::startAngle:: and strong::sweepLength:: refer to the starting angle and the extent of the arc, and are in radians [0..2pi].
125 discussion::
126 example:
127 code::
129 w = Window.new.front;
130 w.view.background_(Color.white);
131 w.drawFunc = {
132     Pen.translate(100, 100);
133     1000.do{
134         // set the Color
135         Pen.color = Color.green(rrand(0.0, 1), rrand(0.0, 0.5));
136         Pen.addAnnularWedge(
137             (100.rand)@(100.rand),
138             rrand(10, 50),
139             rrand(51, 100),
140             2pi.rand,
141             2pi.rand
142         );
143         Pen.perform([\stroke, \fill].choose);
144     }
146 w.refresh;
150 method:: addRect
151 Adds a link::Classes/Rect:: to the drawing.
152 discussion::
153 example:
154 code::
156 w = Window.new.front;
157 w.view.background_(Color.white);
158 w.drawFunc = {
159     80.do{
160         // set the Color
161         Pen.color = Color.green(rrand(0.0, 1), rrand(0.0, 0.5));
162         Pen.addRect(
163             Rect(20, 20, (w.bounds.width-40).rand, (w.bounds.height-40).rand)
164         );
165         Pen.perform([\stroke, \fill].choose);
166     }
168 w.refresh;
172 method:: addOval
173 Adds an Oval shape that fits inside the link::Classes/Rect:: to the current path.
176 subsection:: Draw the path
178 method:: stroke
179 Outline the previous defined path.
180 discussion::
181 example:
182 code::
184 w = Window.new.front;
185 w.view.background_(Color.white);
186 w.drawFunc = {
187     // set the Color
188     Pen.strokeColor = Color.red;
189     Pen.moveTo(200@100);
191     Pen.lineTo(250@200);
192     Pen.lineTo(300@200);
193     Pen.lineTo(200@250);
194     Pen.lineTo(100@200);
195     Pen.lineTo(150@200);
196     Pen.lineTo(200@100);
198     Pen.stroke
200 w.refresh;
204 method:: fill
205 Fill the previous defined path. 
206 discussion::
207 example:
208 code::
210 w = Window.new.front;
211 w.view.background_(Color.white);
212 w.drawFunc = {
213     // set the Color
214     Pen.fillColor = Color.red;
215     Pen.moveTo(200@100);
217     Pen.lineTo(250@200);
218     Pen.lineTo(300@200);
219     Pen.lineTo(200@250);
220     Pen.lineTo(100@200);
221     Pen.lineTo(150@200);
222     Pen.lineTo(200@100);
224     Pen.fill
226 w.refresh;
230 method:: draw
231 Draw the previous defined path using any of the following options:
232 argument:: style
233 table::
234 ## 0 || fill
235 ## 1 || fill using even-odd rule
236 ## 2 || stroke
237 ## 3 || fill and stroke the current path
238 ## 4 || fill and stroke using even-odd rule
240 discussion::
241 example:
242 code::
244 w = Window.new.front;
245 w.view.background_(Color.white);
246 w.drawFunc = {
247     // set the Color
248     Pen.fillColor = Color.red;
249     Pen.moveTo(200@100);
251     Pen.lineTo(250@200);
252     Pen.lineTo(300@200);
253     Pen.lineTo(200@250);
254     Pen.lineTo(100@200);
255     Pen.lineTo(150@200);
256     Pen.lineTo(200@100);
258     Pen.draw(4); // fill and then stroke
260 w.refresh;
264 method:: fillStroke
265 Fill and stroke the current path. Shortcut to the draw(3) method.
268 subsection:: Construct and draw
269 These methods do not require separate calls to link::#*stroke:: or link::#*fill::.
271 method:: strokeRect
272 Strokes a link::Classes/Rect:: into the window.
274 method:: fillRect
275 Draws a filled link::Classes/Rect:: into the window.
277 method:: strokeOval
278 Strokes an oval into the window.
280 method:: fillOval
281 Draws a filled oval into the window.
283 subsection:: Gradients
285 method:: fillAxialGradient
286 Fills an Axial gradient.
287 discussion::
288 example:
289 code::
291 w = Window.new.front;
292 w.drawFunc = {
293     // fill the gradient
294     Pen.addRect(w.view.bounds.insetBy(30));
295     Pen.fillAxialGradient(w.view.bounds.leftTop, w.view.bounds.rightBottom, Color.rand, Color.rand);
297 w.refresh;
301 method:: fillRadialGradient
302 Fills a Radial gradient.
303 discussion::
304 example:
305 code::
307 w = Window.new.front;
308 w.drawFunc = {
309     // fill the gradient
310     Pen.addOval(w.view.bounds.insetBy(30));
311     Pen.fillRadialGradient(w.view.bounds.center,
312         w.view.bounds.center, 0, w.bounds.width, Color.rand, Color.rand);
314 w.refresh;
318 subsection:: Graphics State Methods
320 The following commands transform the graphics state, i.e. they effect all subsequent drawing commands. These transformations are cumulative, i.e. each command applies to the previous graphics state, not the original one.
322 method:: translate
323 Translate the coordinate system to have its origin moved by x,y
324 discussion::
325 example:
326 code::
328 w = Window.new.front;
329 w.view.background_(Color.white);
330 w.drawFunc = {
331     Pen.strokeColor = Color.blue;
332     Pen.translate(200,100); // 0@0 is now 200@100
333     Pen.moveTo(0@0);
334     Pen.lineTo(50@100);
335     Pen.lineTo(100@100);
336     Pen.lineTo(0@150);
337     Pen.lineTo(-100@100);
338     Pen.lineTo(-50@100);
339     Pen.lineTo(0@0);
340     Pen.stroke
342 w.refresh;
345 Cumulative translations:
346 code::
348 w = Window.new.front;
349 w.view.background_(Color.clear);
350 w.drawFunc = {
351     Pen.strokeColor = Color.black;
352     35.do { // draw 35 lines
353         Pen.moveTo(0@0);
354         Pen.lineTo(50@350);
355         Pen.translate(10, 0); // shift 10 to the right every time
356         Pen.stroke
357     }
359 w.refresh;
363 method:: scale
364 Scales subsequent drawing. x and y are scaling factors (i.e. 1 is normal, 2 is double size, etc.).
365 discussion::
366 example:
367 code::
369 w = Window.new.front;
370 w.view.background_(Color.white);
371 w.drawFunc = {
372     Pen.strokeColor = Color.green;
373     Pen.translate(200,100);
374     Pen.scale(0.5, 2);
375     Pen.moveTo(0@0); // you have to set a starting point...
376     Pen.lineTo(50@100);
377     Pen.lineTo(100@100);
378     Pen.lineTo(0@150);
379     Pen.lineTo(-100@100);
380     Pen.lineTo(-50@100);
381     Pen.lineTo(0@0);
382     Pen.stroke
384 w.refresh;
388 method:: skew
389 Skews subsequent drawing. x and y are skewing factors (i.e. 1 is normal).
390 discussion::
391 example:
392 code::
394 w = Window.new.front;
395 w.view.background_(Color.white);
396 w.drawFunc = {
397     Pen.fillColor = Color.green(0.5, 0.8);
398     Pen.translate(200,100);
399     Pen.skew(0.5, 0.2);
400     Pen.moveTo(0@0); // you have to set a starting point...
401     Pen.lineTo(50@100);
402     Pen.lineTo(100@100);
403     Pen.lineTo(0@150);
404     Pen.lineTo(-100@100);
405     Pen.lineTo(-50@100);
406     Pen.lineTo(0@0);
407     Pen.fill
409 w.refresh;
413 method:: rotate
414 Rotates subsequent drawing around the link::Classes/Point:: code::x@y:: by the amount strong::angle:: in radians [0..2pi].
415 discussion::
416 example:
417 code::
419 w = Window.new.front;
420 w.view.background_(Color.white);
421 c = 0;
422 w.drawFunc = {
423     Pen.translate(220, 200);
424     10.do({
425         Pen.translate(0,10);
426         Pen.fillColor = Color.hsv(c.fold(0, 1), 1, 1, 0.5);
427         Pen.moveTo(0@0); // you have to set a starting point...
428         Pen.lineTo(50@100);
429         Pen.lineTo(100@100);
430         Pen.lineTo(0@150);
431         Pen.lineTo(-100@100);
432         Pen.lineTo(-50@100);
433         Pen.lineTo(0@0);
434         Pen.fill;
435         Pen.rotate(0.2pi);
436         c = c + 0.1;
437     });
439 w.refresh;
443 method:: matrix
444 Set transformation matrix to transform coordinate system.
445 See link::#matrix_example:: for an example.
446 argument:: matrixArray
447 A matrix array in the form
448 code::
449 [ zoomX, shearingY, shearingX, zoomY, translateX, translateY ]
452 method:: width
453 Sets the width of the Pen for the whole stroke
455 method:: use
456 Draw function, and then revert to the previous graphics state. This allows you to make complex transformations of the graphics state without having to explicitly revert to get back to 'normal'.
457 discussion::
458 example:
459 code::
461 // modified by an example of Stefan Wittwer
462 w = Window.new.front;
463 w.view.background_(Color.white);
464 w.drawFunc = {
465     //paint origin
466     Pen.fillColor = Color.gray(0, 0.5);
467     Pen.addArc(0@0, 20, 0, 2pi);
468     Pen.fill;
469     Pen.width = 10;
471     Pen.use { // draw something complex...
472         Pen.width = 0.5;
473         Pen.translate(100,100);
474         Pen.fillColor = Color.blue;
475         Pen.addArc(0@0, 10, 0, 2pi);
476         Pen.fill;
477         20.do{
478             Pen.moveTo(0@0);
479             Pen.lineTo(100@0);
480             Pen.strokeColor = Color.red(0.8, rrand(0.7, 1));
481             Pen.stroke;
482             Pen.skew(0, 0.1);
483         };
484     };
486     // now go on with all params as before
487     // translation, skewing, width, and color modifications do not apply
488     Pen.line(10@120, 300@120);
489     Pen.stroke;
491 w.refresh
495 method:: path
496 Make a path, consisting of the drawing made in function.
497 note::
498 Unfortunately not working for now... 
499 (there's no Pen.endPath which currently is used in this method)
502 method:: beginPath
503 Discard any previous path.
505 method:: beginTransparencyLayer
506 Begins a new transparency layer. Transparency layers are useful when you want to apply an effect to a group of objects or create a composite graphic. See link::#transparency_layer_example:: for an example.
508 method:: endTransparencyLayer
509 Ends the current transparency layer.
511 method:: clip
512 Use the previously defined path as a clipping path.
513 discussion::
514 example:
515 code::
517 w = Window.new.front;
518 w.view.background_(Color.white);
519 w.drawFunc = {
520     // outline the clipping path
521     Pen.moveTo(110@110);
522     Pen.lineTo(290@110);
523     Pen.lineTo(290@240);
524     Pen.lineTo(110@240);
525     Pen.lineTo(110@110);
526     // now clip
527     Pen.clip;
529     // everything else we draw is now clipped
530     Pen.fillColor = Color.yellow;
531     Pen.fillRect(Rect(0,0,400,400));
532     Pen.fillColor = Color.red;
533     Pen.moveTo(200@100);
535     Pen.lineTo(250@200);
536     Pen.lineTo(300@200);
537     Pen.lineTo(200@250);
538     Pen.lineTo(100@200);
539     Pen.lineTo(150@200);
541     Pen.fill;
543 w.refresh;
547 method:: smoothing
548 Turns on/off anti-aliasing.
549 discussion::
550 example:
551 code::
553 var w = Window("smoothing", Rect(100, 200, 500, 300)).front;
554 w.view.background_(Color.white);
555 w.drawFunc = { |v|
556     Pen.strokeColor = Color.grey(0.25);
557     Pen.smoothing_(false); //no anti-aliasing
558     50.do{|i|
559         Pen.moveTo(50@50.rrand(250));
560         Pen.lineTo(250@50.rrand(250));
561     };
562     Pen.stroke;
563     Pen.smoothing_(true); //anti-aliasing (default)
564     50.do{|i|
565         Pen.moveTo(250@50.rrand(250));
566         Pen.lineTo(450@50.rrand(250));
567     };
568     Pen.stroke;
573 method:: setShadow
574 Will fill the current path with a shadow.
575 You should use this option between Pen.push / Pen.pop (or Pen.use)
577 method:: joinStyle
578 Set the lines joining style according to the defined options:
579 table::
580 ## 0 || miter
581 ## 1 || round
582 ## 2 || bevel
585 method:: capStyle
586 Set the lines joining style according to the defined options:
587 table::
588 ## 0 || butt
589 ## 1 || round
590 ## 2 || square
592 discussion::
593 example:
594 code::
596 w = Window.new.front;
597 w.view.background_(Color.white);
598 StaticText(w, Rect(0,0,180,20))
599     .string_(" Change Line Cap & Join Styles: ");
600 y = PopUpMenu(w, Rect(180,0,130,20))
601     .items_(["Butt - Miter", "Round - Round", "Square - Bevel"])
602     .action_({w.refresh});
603 w.drawFunc = {
604     Pen.strokeColor = Color.red;
605     Pen.width_(8.0);
606     Pen.capStyle_(y.value);
607     Pen.joinStyle_(y.value);
608     Pen.moveTo(200@100);
609     Pen.lineTo(250@200);
610     Pen.lineTo(300@200);
611     Pen.lineTo(200@250);
612     Pen.lineTo(100@200);
613     Pen.lineTo(150@200);
614     Pen.lineTo(200@100);
615     Pen.stroke;
617 w.refresh;
621 method:: alpha
622 Set the opacity level.
624 method:: blendMode
625 Set the blending mode to use.
626 See link::#blending_modes:: for more information.
628 method:: lineDash
629 Set the line dash pattern. 
630 pattern should be a link::Classes/FloatArray:: of values that specify the lengths of the painted segments and not painted segments.
632 examples::
633 Simple rotating and scaling:
634 code::
636         w = Window("Pen Rotation and Scaling", Rect(128, 64, 360, 360));
637         w.drawFunc = {
638                 Pen.use {
639                         // use the same rect for everything, just scale and rotate
640                         var r = Rect(0,0,200,80);
641                         Pen.fillColor = Color.black;
642                         // offset all subsequent co-ordinates
643                         Pen.translate(80,20);
644                         Pen.fillRect(r);
645                         Pen.fillColor = Color.red;
646                         // scale all subsequent co-ordinates
647                         Pen.scale(0.8, 0.8);
648                         Pen.translate(8,10);
649                         // rotate all subsequent co-ordinates
650                         Pen.rotate(0.1pi);
651                         Pen.fillRect(r);
652                         Pen.strokeColor = Color.blue;
653                         // lather, rinse, repeat
654                         Pen.scale(0.8, 0.8);
655                         Pen.rotate(0.1pi);
656                         Pen.width = 3;
657                         Pen.strokeRect(r);
658                         Pen.fillColor = Color.yellow(1,0.5);
659                         Pen.scale(0.8, 0.8);
660                         Pen.rotate(0.1pi);
661                         Pen.translate(20,-20);
662                         Pen.fillOval(r);
663                 }
664         };
666         w.front;
669 Redraw at random interval, different every time:
670 code::
672 var w, run = true;
673 w = Window("my name is... panel", Rect(128, 64, 800, 800));
674 w.view.background = Color.white;
675 w.onClose = { run = false; };
676 w.front;
677 w.drawFunc = {
678         Pen.use {
679                 Pen.width = 0.2;
680                 400.do {
681                         Pen.beginPath;
682                         Pen.moveTo(Point(10.rand * 80 + 40, 10.rand * 80 + 40));
683                         Pen.lineTo(Point(10.rand * 80 + 40, 10.rand * 80 + 40));
684                         Pen.stroke;
685                 };
686         };
689 { while { run } { w.refresh; 1.0.rand.wait } }.fork(AppClock)
693 code::
695 var w, run = true;
696 w = Window("my name is... panel", Rect(128, 64, 800, 500));
697 w.view.background = Color.white;
698 w.onClose = { run = false; };
699 w.front;
700 w.drawFunc = {
701         Pen.use {
702                 Pen.width = 2;
703                 80.do {
704                         Pen.width = rrand(0,4) + 0.5;
705                         Pen.beginPath;
706                         Pen.moveTo(Point(800.rand, 500.rand));
707                         Pen.lineTo(Point(800.rand, 500.rand));
708                         Pen.stroke;
709                 };
710         };
713 { while { run } { w.refresh; 1.0.rand.wait } }.fork(AppClock)
718 subsection:: Animation
720 Uses random seed to 'store' data
721 By reseting the seed each time the same random values and shapes are generated for each 'frame'
722 These can then be subjected to cumulative rotation, etc., by simply incrementing the phase var.
723 code::
725 // By James McCartney
726 var w, h = 700, v = 700, seed, run = true, phase = 0;
727 w = Window("wedge", Rect(40, 40, h, v), false);
728 w.view.background = Color.rand(0,0.3);
729 w.onClose = { run = false }; // stop the thread on close
730 w.front;
731 // store an initial seed value for the random generator
732 seed = Date.seed;
733 w.drawFunc = {
734         Pen.width = 2;
735         Pen.use {
736                 // reset this thread's seed for a moment
737                 thisThread.randSeed = Date.seed;
738                 // now a slight chance of a new seed or background color
739                 if (0.006.coin) { seed = Date.seed; };
740                 if (0.02.coin) { w.view.background = Color.rand(0,0.3); };
741                 // either revert to the stored seed or set the new one
742                 thisThread.randSeed = seed;
743                 // the random values below will be the same each time if the seed has not changed
744                 // only the phase value has advanced
745                 Pen.translate(h/2, v/2);
746                 // rotate the whole image
747                 // negative random values rotate one direction, positive the other
748                 Pen.rotate(phase * 1.0.rand2);
749                 // scale the rotated y axis in a sine pattern
750                 Pen.scale(1, 0.3 * sin(phase * 1.0.rand2 + 2pi.rand) + 0.5 );
751                 // create a random number of annular wedges
752                 rrand(6,24).do {
753                         Pen.color = Color.rand(0.0,1.0).alpha_(rrand(0.1,0.7));
754                         Pen.beginPath;
755                         Pen.addAnnularWedge(Point(0,0), a = rrand(60,300), a + 50.rand2, 2pi.rand 
756                                 + (phase * 2.0.rand2), 2pi.rand);
757                         if (0.5.coin) {Pen.stroke}{Pen.fill};
758                 };
759         };
762 // fork a thread to update 20 times a second, and advance the phase each time
763 { while { run } { w.refresh; 0.05.wait; phase = phase + 0.01pi;} }.fork(AppClock)
767 code::
769 var w, phase = 0, seed = Date.seed, run = true;
770 w = Window("my name is... panel", Rect(128, 64, 800, 800));
771 w.view.background = Color.blue(0.4);
772 w.onClose = { run = false; };
773 w.front;
774 w.drawFunc = {
775         Pen.use {
776                 if (0.02.coin) { seed = Date.seed; };
777                 thisThread.randSeed = seed;
778                 Pen.strokeColor = Color.white;
779                 200.do {
780                         var a = 4.rand;
781                         var b = 24.rand;
782                         var r1 = 230 + (50 * a);
783                         var a1 = 2pi / 24 * b + phase;
784                         var r2 = 230 + (50 * (a + 1.rand2).fold(0,3));
785                         var a2 = 2pi / 24 * (b + (3.rand2)).wrap(0,23) + phase;
786                         Pen.width = 0.2 + 1.0.linrand;
787                         Pen.beginPath;
788                         Pen.moveTo(Polar(r1, a1).asPoint + Point(400,400));
789                         Pen.lineTo(Polar(r2, a2).asPoint + Point(400,400));
790                         Pen.stroke;
791                 };
792                 thisThread.randSeed = Date.seed;
793                 40.do {
794                         var a = 4.rand;
795                         var b = 24.rand;
796                         var r1 = 230 + (50 * a);
797                         var a1 = 2pi / 24 * b + phase;
798                         var r2 = 230 + (50 * (a + 1.rand2).fold(0,3));
799                         var a2 = 2pi / 24 * (b + (3.rand2)).wrap(0,23) + phase;
800                         Pen.width = 0.2 + 1.5.linrand;
801                         Pen.beginPath;
802                         Pen.moveTo(Polar(r1, a1).asPoint + Point(400,400));
803                         Pen.lineTo(Polar(r2, a2).asPoint + Point(400,400));
804                         Pen.stroke;
805                 };
806         };
809 { while { run } { w.refresh; 0.1.wait; phase = phase + (2pi/(20*24)) } }.fork(AppClock)
814 code::
816 var w, h = 800, v = 600, seed = Date.seed, phase = 0, zoom = 0.7, zoomf = 1, run = true;
817 w = Window("affines", Rect(40, 40, h, v));
818 w.view.background = Color.blue(0.4);
819 w.onClose = { run = false };
820 w.front;
821 w.drawFunc = {
822         thisThread.randSeed = Date.seed;
823         if (0.0125.coin) { seed = Date.seed; phase = 0; zoom = 0.7; zoomf = exprand(1/1.01, 1.01); }
824         { phase = phase + (2pi/80); zoom = zoom * zoomf; };
825         thisThread.randSeed = seed;
826         Pen.use {
827                 var p1 = Point(20.rand2 + (h/2), 20.rand2 + (v/2));
828                 var p2 = Point(20.rand2 + (h/2), 20.rand2 + (v/2));
829                 var xscales = { exprand(2** -0.1, 2**0.1) } ! 2;
830                 var yscales = { exprand(2** -0.1, 2**0.1) } ! 2;
831                 var xlates = { 8.rand2 } ! 2;
832                 var ylates = { 8.rand2 } ! 2;
833                 var rots = { 2pi.rand + phase } ! 2;
834                 var xform;
835                 xscales = (xscales ++ (1/xscales)) * 1;
836                 yscales = (yscales ++ (1/yscales)) * 1;
837                 xlates = xlates ++ xlates.neg;
838                 ylates = ylates ++ xlates.neg;
839                 rots = rots ++ rots.neg;
840                 xform = {|i| [xlates[i], ylates[i], rots[i], xscales[i], yscales[i]] } ! 4;
841                 Pen.strokeColor = Color.grey(1,0.5);
842                 Pen.width = 8.linrand + 1;
843                 Pen.translate(400, 400);
844                 Pen.scale(zoom, zoom);
845                 Pen.translate(-400, -400);
846                 1200.do {
847                         var p, rot, xlate, ylate, xscale, yscale;
848                         Pen.width = 8.linrand + 1;
849                         Pen.beginPath;
850                         #rot, xlate, ylate, xscale, yscale = xform.choose;
851                         Pen.translate(xlate, ylate);
852                         Pen.rotate(rot, h/2, v/2);
853                         Pen.scale(xscale, yscale);
854                                 Pen.moveTo(p1);
855                                 Pen.lineTo(p2);
856                         Pen.stroke;
857                 };
858         };
861 { while { run } { w.refresh; 0.05.wait; } }.fork(AppClock)
866 subsection:: Matrix example
867 code::
869 var controlWindow, w;
870 var r, a, b, c, d, matrix = [1, 0, 0, 1, 10, 10];
871 var sliders, spex, name;
873 w = Window.new.front;
874 w.view.background_(Color.white);
876 // create a controller-window 
877 controlWindow = Window("matrix controls", Rect(400,200,350,120));
878 controlWindow.front;
880 // determine the rectangle to be drawn
881 r = Rect.fromPoints(a = 0 @ 0, c = 180 @ 180);
882 b = r.leftBottom;
883 d = r.rightTop;
885 // the drawFunc
886 w.drawFunc = {
887     Pen.strokeColor = Color.red;
888     Pen.matrix = matrix;
889     Pen.width = 5;
890     Pen.strokeRect(r);
891     Pen.strokeOval(r);
892     Pen.color = Color.blue;
893     Pen.width = 0.1;
894     Pen.line(a, c);
895     Pen.line(b, d);
896     Pen.stroke;
898     Pen.font = Font( "Helvetica-Bold", 12 );
899     Pen.stringAtPoint( "A", a - 6 );
900     Pen.stringAtPoint( "B", b - 6 );
901     Pen.stringAtPoint( "C", c - (0@6) );
902     Pen.stringAtPoint( "D", d - (0@6) );
904     Pen.font = Font( "Helvetica", 10 );
905     Pen.stringInRect( "a matrix test", r.moveBy( 50, 50 ));
908 controlWindow.view.decorator = sliders = FlowLayout(controlWindow.view.bounds);
909 spex = [
910     [0, 2.0].asSpec,
911     [0, 2.0].asSpec,
912     [0, 2.0].asSpec,
913     [0, 2.0].asSpec,
914     [0, 200.0].asSpec,
915     [0, 200.0].asSpec
917 name = #[zoomX, shearingY, shearingX, zoomY, translateX, translateY];
918 6.do { |i|
919     EZSlider(controlWindow, 300 @ 14, name[i], spex[i], { |ez| var val;
920             val = ez.value;
921             [i, val.round(10e-4)].postln;
922             matrix.put(i, val);
923             w.refresh; // reevaluate drawFunc function
924     }, matrix[i]);
925     sliders.nextLine;
927 w.refresh;
931 subsection:: Transparency layer example
932 code::
934 w = Window.new("Transparency Layer test", Rect(400,400,430,450)).front;
935 w.drawFunc = {
936     Color.blue.setStroke;
938     Pen.use {
939     Pen.setShadow(2@2, 10, Color.black);
940     Pen.beginTransparencyLayer;
942     Color.red.setFill;
943     Pen.addOval(Rect(20,40,100,100));
944     Pen.fill;
946     Color.green.setFill;
947     Pen.addOval(Rect(30,70,100,100));
948     Pen.fill;
950     Color.blue.setFill;
951     Pen.addOval(Rect(60,40,100,100));
952     Pen.fill;
954     "With Transparency Layer".drawCenteredIn(Rect(30, 40, 100, 100), Font.default, Color.white);
955     Pen.endTransparencyLayer;
956     };
958     Pen.use {
959     Pen.translate(200, 0);
960     Pen.setShadow(2@2, 10, Color.black);
963     Color.red.setFill;
964     Pen.addOval(Rect(20,40,100,100));
965     Pen.fill;
967     Color.green.setFill;
968     Pen.addOval(Rect(30,70,100,100));
969     Pen.fill;
971     Color.blue.setFill;
972     Pen.addOval(Rect(60,40,100,100));
973     Pen.fill;
975     "Without Transparency Layer".drawCenteredIn(Rect(30, 40, 100, 100), Font.default, Color.white);
976     };
978     Pen.use {
979         Pen.translate(0, 200);
980         Pen.setShadow(2@2, 10, Color.black);
981         Pen.beginTransparencyLayer;
983         Pen.push;
984         Pen.addOval(Rect(20,40,170,170));
985         Pen.fillAxialGradient(w.view.bounds.leftTop, w.view.bounds.rightBottom, Color.rand, Color.rand);
986         Pen.pop;
988         "With Transparency Layer".drawCenteredIn(Rect(20,40,170,170), Font.default, Color.white);
990         Pen.endTransparencyLayer;
991     };
993     Pen.use {
994         Pen.translate(200, 200);
995         Pen.setShadow(2@2, 10, Color.black);
996         Pen.addOval(Rect(20,40,170,170));
997         Pen.fillAxialGradient(w.view.bounds.leftTop, w.view.bounds.rightBottom, Color.rand, Color.rand);
999         "Without Transparency Layer".drawCenteredIn(Rect(20,40,170,170), Font.default, Color.white);
1000     };
1002 w.refresh;
1006 subsection:: Blending modes
1007 code::
1009 different blend modes:
1010 OS X 10.4 and > Only
1011 --------------------
1012 0 - Normal
1013 1 - Multiply
1014 2 - Screen
1015 3 - Overlay
1016 4 - Darken
1017 5 - Lighten
1018 6 - ColorDodge
1019 7 - ColorBurn
1020 8 - SoftLight
1021 9 - HardLight
1022 10 - Difference
1023 11 - Exclusion
1024 12 - Hue
1025 13 - Saturation
1026 14 - Color
1027 15 - Luminosity
1029 OS 10.5 and > Only
1030 --------------------
1031 16 - Clear
1032 17 - Copy
1033 18 - SourceIn
1034 19 - SourceOut
1035 20 - SourceATop
1036 21 - DestinationOver
1037 22 - DestinationIn
1038 23 - DestinationATop
1039 24 - XOR
1040 25 - PlusDarker
1041 26 - PlusLighter
1045         var blendMode=0, blendString="Normal";
1046         w = Window.new.front;
1047         m = SCPopUpMenu(w, Rect(10, w.view.bounds.height - 30, 150, 20));
1048         m.items = [
1049                 "0 - Normal",
1050                 "1 - Multiply",
1051                 "2 - Screen",
1052                 "3 - Overlay",
1053                 "4 - Darken",
1054                 "5 - Lighten",
1055                 "6 - ColorDodge",
1056                 "7 - ColorBurn",
1057                 "8 - SoftLight",
1058                 "9 - HardLight",
1059                 "10 - Difference",
1060                 "11 - Exclusion",
1061                 "12 - Hue",
1062                 "13 - Saturation",
1063                 "14 - Color",
1064                 "15 - Luminosity"
1065         ];
1066         
1067         m.action_({|view|
1068                 blendMode = view.value;
1069                 blendString = view.items.at(blendMode);
1070                 w.refresh;
1071         });
1072         w.drawFunc = {
1073                 80.do{|i|
1074                         Pen.use {
1075                         Pen.blendMode_(blendMode);
1076                         Pen.color = Color.green(0.6, 0.10);
1077                         Pen.addRect(
1078                                 Rect(20, 20, 20 + (i*4), 20 + (i*4));
1079                         );
1080                         Pen.fill;
1081                         };
1082                 }
1083         };
1084         w.refresh;