## SwingOSC – Java-based GUI classes

This class is meant as an emulation of Pen. last mod: 17-jul-09 sciss

no-op / not working
String -> *bounds | calls a cocoa primitive. there is no replacement in SwingOSC due to the fact that this would need to be implemented asynchronously
different behaviour
stroke | swing's stroke is a tiny bit thicker than cocoa's ; with antialiasing off the stroke is about 1px thicker
color | Color.set does NOT work (it calls a cocoa primitive); instead we use JPen.color_ to set the color (similiarily JPen.strokeColor_ and JPen.fillColor_).
strings | String.drawAtPoint does NOT work ; instead we use JPen.stringAtPoint to draw a string (similiarily JPen.stringInRect, JPen.stringCenteredIn etc.). JPen.font_ to set font. stringCenteredIn, stringLeftJustIn, stringRightJustIn break lines according to the bounding box instead of actually calling stringAtPoint, which is obviously more logic (otherwise you wouldn't need a rect argument. they are still compatible with cocoa Pen if the rect width is greater than the text bounds width) the string drawing commands do not overwrite the strokeColor setting (bug in cocoa)
extended functionality
curveTo, quadCurveTo | have been implemented
known issues / todo
speed | especially for large bodies of drawing functions this can be significantly slower than the cocoa class
string location | the calculation in stringAtPoint is a bit unclear; adding ascent plus descent results in y-position similiar to cocoa, albeit a bit illogic
font size | font sizes and line heights (when using stringInRect) are a little bit smaller than in cocoa

# JPen

Note: please use the abstraction layer Pen if possible!

A helper class to draw on a JSCWindow, a JSCUserView, or a JSCTabletView. It cannot be instantiated but only provides static methods.

The following methods must be called within an JSCWindow-drawHook or a JSCUserView-drawFunc function, and will only be visible once the window or the view is refreshed. Each call to JSCWindow-refresh or JSCUserView-refresh will 'overwrite' all previous drawing by executing the currently defined function.

See also: JSCUserView, JSCWindow, JSCTabletView, Color, JFont, Point, Rect

## Shape Constructing Drawing Methods

The following methods define paths. You will need to call `*stroke` or `*fill` to actually draw them:

### *moveTo

    JPen.moveTo( <(Point) point> )

Moves the pen to point. Use this method to start a new (unconnected) polyline. See `*stroke` for an example.

### *lineTo

    JPen.lineTo( <(Point) point> )

Extends the shape by drawing a line from the current position to a given point. See `*stroke` for an example.

### *line

    JPen.line( <(Point) p1>, <(Point) p2> )

Adds a line between the points `p1` and `p2`. The new current position is set to p2. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            var x1, y1, x2, y2, x1a, y1a, x2a, y2a, txr, tyr, rr;

            JPen.translate( 200, 200 );
            JPen.scale( 0.5, 0.5 );
            x1 = 175.0.bilinrand;
            x2 = 175.0.bilinrand;
            y1 = 175.0.bilinrand;
            y2 = 175.0.bilinrand;
            x1a = 15.0.bilinrand;
            x2a = 15.0.bilinrand;
            y1a = 15.0.bilinrand;
            y2a = 15.0.bilinrand;
            txr = 2.0.bilinrand;
            tyr = 2.0.bilinrand;
            rr  = 0.05pi.bilinrand;
            JPen.moveTo( 175 @ 0 );
            200.do { arg i;
                JPen.translate( txr, tyr );
                JPen.rotate( rr );
                JPen.line( x1 @ y1, x2 @ y2 );
                x1 = x1 + x1a;
                x2 = x2 + x2a;
                y1 = y1 + y1a;
                y2 = y2 + y2a;
            };
            JPen.stroke;
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.75.wait }}.fork( AppClock );
        )

### *curveTo

    JPen.curveTo( <(Point) point>, <(Point) cpoint1>, <(Point) cpoint2> )

Adds a cubic-spline curve from the current position to `point`. `cpoint1` and `cpoint2` are help-points determining the curve's curvature. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            var x1, y1, x2, y2, x1a, y1a, x2a, y2a, txr, tyr, rr;

            JPen.translate( 200, 200 );
            JPen.scale( 0.5, 0.5 );
            x1 = 175.0.bilinrand;
            x2 = 175.0.bilinrand;
            y1 = 175.0.bilinrand;
            y2 = 175.0.bilinrand;
            x1a = 25.0.bilinrand;
            x2a = 25.0.bilinrand;
            y1a = 25.0.bilinrand;
            y2a = 25.0.bilinrand;
            txr = 4.0.bilinrand;
            tyr = 4.0.bilinrand;
            rr  = 0.05pi.bilinrand;
            JPen.moveTo( 175 @ 0 );
            100.do {
                JPen.translate( txr, tyr );
                JPen.rotate( rr );
                JPen.curveTo( 175 @ 0, x1 @ y1, x2 @ y2 );
                x1 = x1 + x1a;
                x2 = x2 + x2a;
                y1 = y1 + y1a;
                y2 = y2 + y2a;
            };
            JPen.stroke;
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.75.wait }}.fork( AppClock );
        )

### *quadCurveTo

    JPen.quadCurveTo( <(Point) point>, <(Point) cpoint1> )

Adds a quadratic-spline curve from the current position to `point`. `cpoint1` is a help-point determining the curve's curvature. Example:

        (
        var ang1a, ang2a, ang1s, ang2s, rad1a, rad2a, trans;

        ang1a   = Array.fill( 4, 0.0 );
        ang2a   = Array.fill( 4, 0.0 );
        ang1s   = Array.fill( 4, 0.0 );
        ang2s   = Array.fill( 4, 0.0 );
        rad1a   = Array.fill( 4, 0.0 );
        rad2a   = Array.fill( 4, 0.0 );

        trans   = [ 225 @ 225, 350 @ 0, 0 @ 350, -350 @ 0 ];

        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        d = 0.0;
        w.drawHook = {
            var ang1, ang2, rad1, rad2;
            var di = 1.0 - d;

            JPen.scale( 0.5, 0.5 );
            4.do({ arg i;
                JPen.translate( trans[ i ].x, trans[ i ].y );
                ang1s[ i ]  = ang1s[ i ] * d + (pi.bilinrand * di);
                ang2s[ i ]  = ang2s[ i ] * d + (pi.bilinrand * di);
                ang1        = ang1s[ i ];
                ang2        = ang2s[ i ];
                rad1        = 150;
                rad2        = 75;
                ang1a[ i ]  = ang1a[ i ] * d + ((pi/16).bilinrand * di);
                ang2a[ i ]  = ang2a[ i ] * d + ((pi/16).bilinrand * di);
                rad1a[ i ]  = (rad1a[ i ] * d + (20.0.bilinrand * di)).clip( -20.0, 20.0 );
                rad2a[ i ]  = (rad2a[ i ] * d + (10.0.bilinrand * di)).clip( -10.0, 10.0 );
                100.do {
                    JPen.moveTo( 0 @ 0 );
                    JPen.quadCurveTo( (ang1.cos * rad1) @ (ang1.sin * rad1),
                                      (ang2.cos * rad2) @ (ang2.sin * rad2) );
                    ang1    = ang1 + ang1a[ i ];
                    ang2    = ang2 + ang2a[ i ];
                    rad1    = (rad1 + rad1a[ i ]).clip( 0, 150 );
                    rad2    = (rad2 + rad2a[ i ]).clip( 0, 150 );
                };
                JPen.stroke;
            });
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            d = 0.97; // animation decay
            { while { run } { w.refresh; 0.1.wait }}.fork( AppClock );
        )

        d = 0.8;    // faster
        d = 0.99;   // really slow

### *arcTo

    JPen.arcTo( <(Point) point1>, <(Point) point2>, <(Number) radius> )

Draws an arc of a circle using a radius and two tangents. The first tangent is formed by the current pen location and `point1`, the second by `point1` and `point2`. In the following example, you can drag the end points of the tangent lines around:

        (
        var w, r, ps, dragIdx, fCross;

        r      = 50; // radius
        w      = JSCWindow( "arcTo", Rect( 100, 200, 400, 400 ), resizable: false );
        ps     = [ 50 @ 200, 350 @ 50, 200 @ 350 ];
        fCross = { arg p, colr;
            JPen.strokeColor = colr;
            JPen.line( p + (-6 @ -6), p + (6 @ 6) );
            JPen.line( p + (-6 @ 6), p + (6 @ -6) );
            JPen.stroke;
        };
        w.drawHook = { var dp10, dp12, dp02, a, b, c, s, th, dist, tp1, tp2, ang, cp;
            JPen.strokeColor = Color.green;
            JPen.moveTo( ps[ 0 ]);
            JPen.lineTo( ps[ 1 ]);
            JPen.lineTo( ps[ 2 ]);
            JPen.stroke;

            JPen.strokeColor = Color.black;
            JPen.moveTo( ps[ 0 ]);
            JPen.arcTo( ps[ 1 ], ps[ 2 ], r );
            JPen.stroke;

            ps.do( fCross.( _, Color.red ));

            // visualize center and touching points
            dp10 = ps[ 0 ] - ps[ 1 ];
            dp12 = ps[ 2 ] - ps[ 1 ];
            dp02 = ps[ 2 ] - ps[ 0 ];
            a    = dp02.rho;
            b    = dp10.rho;
            c    = dp12.rho;
            s    = (a + b + c) / 2;
            th   = sqrt( (s - b) * (s - c) / (s * (s - a)) );
            dist = r / th;
            tp1  = Polar( dist, dp10.theta ).asPoint + ps[ 1 ];
            tp2  = Polar( dist, dp12.theta ).asPoint + ps[ 1 ];
            ang  = theta( (tp1 + tp2) / 2 - ps[ 1 ]);
            dist = sqrt( r.squared + dist.squared );
            cp   = ps[ 1 ] + Polar( dist, ang ).asPoint;

            [ cp, tp1, tp2 ].do( fCross.( _, Color.blue ));
        };
        w.view
            .background_( Color.white )
            .mouseDownAction_({ arg view, x, y;
                var dist, mp, idx;
                mp      = x @ y;
                dist    = ps.collect({ arg p; p.dist( mp )});
                idx     = dist.minIndex;
                dragIdx = if( dist[ idx ] < 6, idx );
            })
            .mouseMoveAction_({ arg view, x, y;
                if( dragIdx.notNil, {
                    ps[ dragIdx ] = x @ y;
                    w.refresh;
                });
            });

        w.front;
        )

### *addArc

    JPen.addArc( <(Point) center>, <(Number) radius>, <(Number) startAngle>, <(Number) arcAngle> )

Adds an arc around the `center`, at `radius` number of pixels. `startAngle` and `arcAngle` refer to the starting angle and the extent of the arc, and are in radians [0..2pi]. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            JPen.translate( 100, 100 );
            10.do {
                JPen.color = Color.red( rrand( 0.0, 1 ), rrand( 0.0, 0.5 ));
                JPen.addArc( 100.rand @ 100.rand, rrand( 10, 100 ), 2pi.rand, pi );
                JPen.perform([ \stroke, \fill ].choose );
            };
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.1.wait }}.fork( AppClock );
        )

### *addWedge

    JPen.addWedge( <(Point) center>, <(Number) radius>, <(Number) startAngle>, <(Number) arcAngle> )

Adds a wedge around the `center`, at `radius` number of pixels. `startAngle` and `arcAngle` refer to the starting angle and the extent of the arc, and are in radians [0..2pi]. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.translate( 100, 100 );
            10.do {
                JPen.color = Color.blue( rrand( 0.0, 1 ), rrand( 0.0, 0.5 ));
                JPen.addWedge( 100.rand @ 100.rand, rrand( 10, 100 ), 2pi.rand, 2pi.rand );
                JPen.perform([ \stroke, \fill ].choose );
            };
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.1.wait }}.fork( AppClock );
        )

### *addAnnularWedge

    JPen.addAnnularWedge( <(Point) center>, <(Number) innerRadius>, <(Number) outerRadius>, <(Number) startAngle>, <(Number) arcAngle> )

Adds an annular wedge around the `center`, from `innerRadius` to `outerRadius` in pixels. `startAngle` and `arcAngle` refer to the starting angle and the extent of the arc, and are in radians [0..2pi]. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            JPen.translate( 100, 100 );
            500.do {
                JPen.color = Color.green( rrand( 0.0, 1 ), rrand( 0.0, 0.5 ));
                JPen.addAnnularWedge(
                    100.rand @ 100.rand,
                    rrand( 10, 50 ),
                    rrand( 51, 100 ),
                    2pi.rand,
                    2pi.rand
                );
                JPen.perform([ \stroke, \fill ].choose );
            };
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 1.0.wait }}.fork( AppClock );
        )

### *addRect

    JPen.addRect( <(Rect) rect> )

Adds a rectangle to the drawing. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            var x1, y1, x2, y2, x1a, y1a, x2a, y2a, txr, tyr, x1o, y1o, x2o, y2o, size;

            JPen.translate( 200, 200 );
            JPen.scale( 0.25, 0.25 );
            JPen.width = 2.0;
            x1 = 175.0.bilinrand;
            x2 = 175.0.bilinrand;
            y1 = 175.0.bilinrand;
            y2 = 175.0.bilinrand;
            x1a = 15.0.rand;
            x2a = 15.0.rand;
            y1a = 15.0.rand;
            y2a = 15.0.rand;
            txr = -15.0.rand;
            tyr = -15.0.rand;
            JPen.moveTo( 175 @ 0 );
            200.do { arg i;
                JPen.translate( txr, tyr );
                x1o = x1;
                y1o = y1;
                x2o = x2;
                y2o = y2;
                x1  = x1 + x1a;
                x2  = x2 + x2a;
                y1  = y1 + y1a;
                y2  = y2 + y2a;
                size    = max( (x2 - x1).abs, (y2 - y1).abs );
                JPen.addRect( Rect( x1, y1, size, size ));
            };
            JPen.stroke;
        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.1.wait }}.fork( AppClock );
        )

### *stroke

    JPen.stroke

Outline the path previously defined with any of the above commands. The outline is stroked using the current pen-width (see `*width`) and current stroke-color. The stroke-color is set using `JPen.strokeColor_` or `JPen.color_` (the latter sets both stroke- and fill-color). Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.red;
            JPen.moveTo( 200 @ 100 );

            JPen.lineTo( 250 @ 200 );
            JPen.lineTo( 300 @ 200 );
            JPen.lineTo( 200 @ 250 );
            JPen.lineTo( 100 @ 200 );
            JPen.lineTo( 150 @ 200 );
            JPen.lineTo( 200 @ 100 );

            JPen.stroke;
        };
        )

### *fill

    JPen.fill

Fills the path previously defined with any of the above commands. The area is filled using the current fill-color. The fill-color is set using `JPen.fillColor_` or `JPen.color_` (the latter sets both stroke- and fill-color). Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.red;
            JPen.moveTo( 200 @ 100 );

            JPen.lineTo( 250 @ 200 );
            JPen.lineTo( 300 @ 200 );
            JPen.lineTo( 200 @ 250 );
            JPen.lineTo( 100 @ 200 );
            JPen.lineTo( 150 @ 200 );
            JPen.lineTo( 200 @ 100 );

            JPen.fill;
        };
        )

### *draw

    JPen.draw( <(Integer) option> )

Fills and/or strokes the previously defined shape. The possible values for `<option>` are:

- `0` : just fill, using the non-zero rule
- `1` : just fill, using the even-odd rule
- `2` : just stroke
- `3` : fill, using the non-zero rule, and stroke
- `4` : fill, using the even-odd rule, and stroke

`JPen.fillStroke` is a synonym for `JPen.draw( 3 )`. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_(Color.white);
        w.drawHook = {
            4.do({ arg option; JPen.use({
                JPen.translate( (option & 1) * 200, (option & 2) * 100 );
                JPen.fillColor = Color.black;
                JPen.stringAtPoint( "option = " ++ option, 30 @ 40 );
                JPen.fillColor = Color.red;
                JPen.moveTo( 100 @  50);
                JPen.lineTo( 100 @ 150);
                JPen.lineTo( 150 @  50);
                JPen.lineTo(  50 @ 100);
                JPen.lineTo( 150 @ 100);
                JPen.draw( option );
            }); });
        };
        w.refresh;
        )

## Basic Shape Drawing Methods

The following methods do not require separate `stroke` or `fill` calls. They directly paint a primitive geometric form.

### *strokeRect

    JPen.strokeRect( <(Rect) rect> )

Strokes the outline of a rectangle. Example:

        (
        w = JSCWindow( "strokeRect", Rect( 128, 64, 360, 360 ));
        w.drawHook = {
            var r;
            r = Rect( 100, 100, 160, 80 );
            JPen.color = Color.black.alpha_( 0.8 );
            JPen.strokeRect( r );
        };
        w.front;
        )

Notice how the coordinates appear to lie between two pixels not in a pixel's center, so the stroke appears blurred. You can avoid this behaviour by shifting the coordinates by 0.5, 0.5:

        (
        w = JSCWindow( "strokeRect", Rect( 128, 64, 360, 360 ));
        w.drawHook = {
            var r;
            JPen.translate( 0.5, 0.5 );  // !
            r = Rect( 100, 100, 160, 80 );
            JPen.color = Color.black.alpha_( 0.8 );
            JPen.strokeRect( r );
        };
        w.front;
        )

### *fillRect

    JPen.fillRect( <(Rect) rect> )

Fills the area of a rectangle. Example:

        (
        w = JSCWindow( "fillRect", resizable: false );
        w.view.background = Color.white;
        w.drawHook = {
            var r;
            200.do({
                JPen.color = Color.black.alpha_( 1.0.rand.pow( 6 ));
                JPen.fillRect( Rect( 200.0.rand, 200.0.rand, 200.0.rand, 200.0.rand ));
            });
        };
        w.front;
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.2.wait }}.fork( AppClock );
        )

### *strokeOval

    JPen.strokeOval( <(Rect) rect> )

Strokes the outline of an ellipse. The ellipse is specified by its bounding (framing) rectangle. Example:

        (
        w = JSCWindow( "strokeOval", Rect( 128, 64, 360, 360 ));
        w.drawHook = {
            var h, v, r;
            v = h = 300.0;
            r = Rect( 100, 100, 160, 80 );
            JPen.width = 10;
            JPen.color = Color.black.alpha_( 0.8 );
            JPen.strokeOval( r );
        };
        w.front;
        )

### *fillOval

    JPen.fillOval( <(Rect) rect> )

Fills the area of an ellipse. The ellipse is specified by its bounding (framing) rectangle. Example:

        (
        w = JSCWindow( "fillOval", resizable: false );
        w.view.background = Color.black;
        w.drawHook = {
            var r;
            200.do({
                JPen.color = Color.white.alpha_( 1.0.rand.pow( 6 ));
                JPen.fillOval( Rect( 200.0.rand, 200.0.rand, 200.0.rand, 200.0.rand ));
            });
        };
        w.front;
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.2.wait }}.fork( AppClock );
        )

## String Drawing Methods

These commands draw text. The font is configured by calling `JPen.font_( <(JFont) font> )`. The text color is taken from `JPen.fillColor_( <(Color) c> )`.

### *stringAtPoint

    JPen.stringAtPoint( <(String) str>, <(Point) point> )

Draws a text `str` string left aligned from a given `point`. There are no line breaks, and the text is vertically aligned such that the given y position referes to the top of the text. Example:

        (
        w = JSCWindow( "stringAtPoint", Rect( 128, 64, 360, 360 ), false );
        w.drawHook = {
            var pt;

            JPen.font = JFont( "Monospaced", 16 );
            JPen.strokeColor = Color.red;

            12.do({
                pt = Point( rrand( 10, 280 ), rrand( 10, 340 ));
                JPen.stringAtPoint( "Evidence", pt );
                JPen.moveTo( pt.translate( -8 @  0 ));
                JPen.lineTo( pt.translate( -2 @  0 ));
                JPen.moveTo( pt.translate(  0 @ (-8) ));
                JPen.lineTo( pt.translate(  0 @ (-2) ));
                JPen.moveTo( pt.translate(  2 @ 0 ));
                JPen.lineTo( pt.translate(  8 @ 0 ));
                JPen.moveTo( pt.translate(  0 @ 2 ));
                JPen.lineTo( pt.translate(  0 @ 8 ));
                JPen.stroke;
            });
        };
        w.front;
        )

### *stringInRect, *stringCenteredIn, *stringLeftJustIn, *stringRightJustIn

    JPen.stringInRect( <(String) str>, <(Rect) rect> )       // left/top aligment
    JPen.stringCenteredIn( <(String) str>, <(Rect) rect> )   // x-centered/y-centered aligment
    JPen.stringLeftJustIn( <(String) str>, <(Rect) rect> )   // left/y-centered aligment
    JPen.stringRightJustIn( <(String) str>, <(Rect) rect> )  // right/y-centered aligment

Draws a text `str` string inside a bouding box `rect` with the given alignment. Example:

        (
        w = JSCWindow( "String in Rect", Rect( 128, 64, 300, 560 ), false ).front;
        w.view.background_(Color.white);
        w.drawHook = {
            var rect, txt;

            txt = "Il arrive que la réalité soit trop complexe pour la transmission orale.";
            JPen.font = JFont( "SansSerif", 14 );

            JPen.strokeColor = Color.blue;
            rect    = Rect( 50, 50, 200, 100 );
            JPen.stringInRect( txt, rect );
            JPen.strokeRect( rect );

            rect = rect.moveBy( 0, rect.height + 20 );
            JPen.stringLeftJustIn( txt, rect );
            JPen.strokeRect( rect );

            rect = rect.moveBy( 0, rect.height + 20 );
            JPen.stringCenteredIn( txt, rect );
            JPen.strokeRect( rect );

            rect = rect.moveBy( 0, rect.height + 20 );
            JPen.stringRightJustIn( txt, rect );
            JPen.strokeRect( rect );
        };
        )

## Graphics State Methods

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.

### *translate

    JPen.translate( <(Number) x>, <(Number) y> )

Translate the coordinate system to have its origin moved by x, y. Note that this is a concatenation with the previous affine transforms, so for example if the coordinate system had previously been rotated by 90 degrees, translating along the x-axis actually moves the origin vertically. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.blue;
            JPen.translate( 200, 100 );

            // 0@0 in current context is now 200@100 regarding the whole view
            JPen.moveTo(     0 @  0 );

            JPen.lineTo(   50 @ 100 );
            JPen.lineTo(  100 @ 100 );
            JPen.lineTo(    0 @ 150 );
            JPen.lineTo( -100 @ 100 );
            JPen.lineTo(  -50 @ 100 );
            JPen.lineTo(    0 @   0 );

            JPen.stroke;
        };
        )

        // cumulative translations
        (
        w = JSCWindow.new.front;
        w.drawHook = {
            // set the Color
            JPen.color = Color.black;
            // draw 35 lines
            35.do {
                JPen.moveTo(  0 @   0 );
                JPen.lineTo( 50 @ 350 );
                // shift 10 to the right every time
                JPen.translate( 10, 0 );
                JPen.stroke;
            };
        };
        )

### *scale

    JPen.scale( <(Number) x>, <(Number) y> )

Scales subsequent drawing. `x` and `y` are scaling factors (i.e. 0.5 is half size or zoom-out, 2 is double size or zoom-in, etc.). Notice that the drawing "pen tip" scales accordingly (see also the `*width` section). Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.green;
            JPen.translate( 200, 100 );
            JPen.scale( 0.5, 2 );
            // you have to set a starting point...
            JPen.moveTo(    0 @   0 );

            JPen.lineTo(   50 @ 100 );
            JPen.lineTo(  100 @ 100 );
            JPen.lineTo(    0 @ 150 );
            JPen.lineTo( -100 @ 100 );
            JPen.lineTo(  -50 @ 100 );
            JPen.lineTo(    0 @   0 );

            JPen.stroke;
        };
        )

### *skew

    JPen.skew(<(Number) x>, <(Number) y> )

Skews subsequent drawing. A value of zero means no skewing. Negative `x` values skew the bottom to the left, negative `y` values skew the right to the top. Positive `x` values skew the bottom to the right, positive `y` values skew the right to the bottom. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.green( 0.5, 0.8 );
            JPen.translate( 200, 100 );
            JPen.skew( 0.5, 0.2 );
            // you have to set a starting point...
            JPen.moveTo(    0 @   0 );

            JPen.lineTo(   50 @ 100 );
            JPen.lineTo(  100 @ 100 );
            JPen.lineTo(    0 @ 150 );
            JPen.lineTo( -100 @ 100 );
            JPen.lineTo(  -50 @ 100 );
            JPen.lineTo(    0 @   0 );

            JPen.stroke;
        };
        )

### *rotate

    JPen.rotate( <(Number) angle>, <(Number) x = 0>, <(Number) y = 0> )

Rotates subsequent drawing around the Point `x @ y` by the amount `angle` in radians [0..2pi] (in clockwise orientation). Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        c = 0;
        w.drawHook = {
            JPen.translate( 220, 200 );

            10.do({
                JPen.translate( 0, 10 );
                // set the Color for all "real" drawing
                JPen.color = Color.hsv( c.fold( 0, 1 ), 1, 1, 0.5 );

                // you have to set a starting point...
                JPen.moveTo(    0 @   0 );

                JPen.lineTo(   50 @ 100 );
                JPen.lineTo(  100 @ 100 );
                JPen.lineTo(    0 @ 150 );
                JPen.lineTo( -100 @ 100 );
                JPen.lineTo(  -50 @ 100 );
                JPen.lineTo(    0 @   0 );

                JPen.fill;
                JPen.rotate( 0.2pi );

                c = c + 0.1;
            });

            c = c - 0.95; // for subsequent animation

        };
        )

        (
            var run = true;
            w.onClose = { run = false }; // closing window stops animation
            { while { run } { w.refresh; 0.05.wait }}.fork( AppClock );
        )

### *matrix_

    JPen.matrix_( <(Array) array> )

Transforms (re-sets) the coordinate system, using the coefficients of an affine-transform matrix.

`array = [a, b, c, d, x, y]`

- `a` : zoomX (1.0 = no zooming, 0.5 = zoom out, 2.0 = zoom in)
- `b` : shearingY (0.0 = no shearing)
- `c` : shearingX
- `d` : zoomY
- `x` : translateX (0.0 = no translation. positive values = move right, negative values = move left)
- `y` : translateY (0.0 = no translation. positive values = move down, negative values = move up)

Successive calls of `*matrix_` concatenate with the previous matrix. Thus, if you need to undo the transforms, you should make use of the `*use` method (see below). Example:

        (
        var controlWindow, w;
        var r, a, b, c, d, matrix = [1, 0, 0, 1, 10, 10];
        var sliders, spex, name;

        w = JSCWindow.new.front;
        w.view.background_(Color.white);

        // create a controller-window
        controlWindow = JSCWindow("matrix controls", Rect(400,200,330,160), resizable: false );
        controlWindow.front;

        // determine the rectangle to be drawn
        r = Rect.fromPoints(a = 0 @ 0, c = 180 @ 180);
        b = r.leftBottom;
        d = r.rightTop;

        // the drawHook
        w.drawHook = {
            JPen.color = Color.red;
                JPen.matrix = matrix;   // !
                JPen.width = 5;
                JPen.strokeRect(r);
                JPen.strokeOval(r);
            JPen.color = Color.blue;
                JPen.width = 0.25;
                JPen.line(a, c);
                JPen.line(b, d);
                JPen.stroke;

            JPen.font = JFont( "Helvetica-Bold", 12 );
            JPen.fillColor = Color.black;
                JPen.stringAtPoint( "A", a - 6 );
                JPen.stringAtPoint( "B", b - 6 );
                JPen.stringAtPoint( "C", c - (0 @ 6) );
                JPen.stringAtPoint( "D", d - (0 @ 6) );

            JPen.font = JFont( "Helvetica-Bold", 10 );
            JPen.stringInRect( "a matrix test", r.moveBy( 50, 50 ));
        };

        controlWindow.view.decorator = sliders = FlowLayout(controlWindow.view.bounds);
        spex = [
            [   -2.0,   2.0 ].asSpec,
            [   -2.0,   2.0 ].asSpec,
            [   -2.0,   2.0 ].asSpec,
            [   -2.0,   2.0 ].asSpec,
            [ -200.0, 200.0 ].asSpec,
            [ -200.0, 200.0 ].asSpec
        ];
        name = #[ zoomX, shearingY, shearingX, zoomY, translateX, translateY ];
        GUI.useID( \swing, { 6.do { arg i;
            EZSlider( controlWindow, 300 @ 22, name[i], spex[i], { arg ez; var val;
                    val = ez.value;
                    [ i, val.round(10e-4) ].postln;
                    matrix[ i ] = val;
                    w.refresh; // reevaluate drawHook function
            }, matrix[ i ]);
            sliders.nextLine;
        }});
    )

### *width_

    JPen.width_( <(Number) width = 1> )

Sets the width in pixels of the pen for the successive `*stroke` calls. The pen "tip" is additionally affected by affine transforms such as scaling and skewing. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.blue( 0.5, 0.5 );
            JPen.translate( 200,100 );
            JPen.width = 10;
            // you have to set a starting point...
            JPen.moveTo(    0 @   0 );

            JPen.lineTo(   50 @ 100 );
            JPen.lineTo(  100 @ 100 );
            JPen.lineTo(    0 @ 150 );
            JPen.lineTo( -100 @ 100 );
            JPen.lineTo(  -50 @ 100 );
            JPen.lineTo(    0 @   0 );

            JPen.stroke;
        };
        )

### *lineDash_

    JPen.lineDash_( <(FloatArray) pattern> )

When shapes are stroked, a pattern can be used to created dashed outlines. The pattern is specified as a FloatArray where the elements alternatingly represent the opaque and transparent parts of the pattern. So if `*width_` represents the width of the stroke, `*lineDash_` represents the lengths of each partial stroke. Both scale according to the affine transform of the graphics context. Example:

        (
        var patterns = [ FloatArray[ 3, 3 ],
                         FloatArray[ 3, 2, 5, 2, 7, 2, 5, 2 ],
                         FloatArray[ 8, 2, 8, 8, 2, 8 ]];
        var width = 0;
        w = JSCWindow( "Dash", Rect( 100, 100, 920, 500 ), false ).front;
        w.view.background = Color.white;
        w.drawHook = {
            JPen.translate( 0.5, 0.5 );
            JPen.width = width;
            patterns.do({ arg pat;
                JPen.use({ 3.do({
                    JPen.lineDash = pat;
                    JPen.addOval( Rect( 10, 10, 50, 50 ));
                    JPen.addRect( Rect( 70, 10, 50, 50 ));
                    JPen.moveTo( 150 @ 10 );
                    JPen.lineTo( 130 @ 60 );
                    JPen.lineTo( 180 @ 10 );
                    JPen.lineTo( 160 @ 60 );
                    JPen.stroke;
                    JPen.translate( 200, 0 );
                    JPen.scale( 1.5, 1.5 );
                })});
                JPen.translate( 0, 150 );
            });
        };
        r = { inf.do({ arg p; width = sin( p / 30 ).squared * 4; w.refresh; 0.03.wait })}.fork( AppClock );
        w.onClose = { r.stop };
        )

### *joinStyle_

    JPen.joinStyle_( <(Integer) options> )

When a polygon is stroked, different approaches to draw the joints between line segments are possible: with join option `0` ("Miter"), the outlines of the stroke a extended until they eventually meet, producing pointed edges (default). With option `1` ("Round"), the edges become round, and with option `2` ("Bevel") they are hard trimmed. Example:

        (
        w = JSCWindow( "Join" ).front;
        w.view.background = Color.white;
        w.drawHook = {
            JPen.translate( 0.5, 0.5 );
            JPen.width = 8;
            [ "miter", "round", "bevel" ].do({ arg name, i;
                JPen.joinStyle = i;
                JPen.moveTo( 100 @ 30 );
                JPen.lineTo( 60  @ 130 );
                JPen.lineTo( 160 @ 30 );
                JPen.lineTo( 120 @ 130 );
                JPen.stroke;
                JPen.stringAtPoint( name, 200 @ 40 );
                JPen.translate( 0, 120 );
            });
        };
        )

### *alpha_

    JPen.alpha_( <(Float) opacity> )

Sets the alpha value (translucency) of the subsequent drawing operations. This is often easier than scaling all fill- and stroke-colors in their alpha dimension, and besides works with other graphics operations such as image painting, as the following example shows:

        (
        i = JSCImage.openURL( "http://sciss.de/swingOSC/application.png" );
        w = JSCWindow.new.front;
        w.drawHook = {
            (1..10).do({ arg j;
                JPen.alpha = j / 10;
                JPen.translate( 24 + 4.bilinrand, 24 + 4.bilinrand );
                JPen.image( i );
            });
        };
        w.onClose = { i.destroy; r.stop };
        r = { inf.do({ w.refresh; 0.02.wait })}.fork( AppClock );
        )

### *use

    JPen.use( <(Function) function> )

Stores the current graphics state, executes a `function`, and then revert to the previous graphics state. This allows you to make complex transformations of the graphics state (e.g. affine transforms, colour changes etc.) without having to explicitly revert to get back to 'normal'. Example:

        (
        // modified by an example of Stefan Wittwer
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            //paint origin
            JPen.color = Color.gray( 0, 0.5 );
            JPen.addArc( 0 @ 0, 20, 0, 2pi );
            JPen.fill;
            JPen.width = 10;

            JPen.use({ // draw something complex...
                JPen.width = 0.5;
                JPen.translate( 100, 100 );
                JPen.color = Color.blue;
                JPen.addArc( 0 @ 0, 10, 0, 2pi );
                JPen.fill;
                20.do {
                    JPen.moveTo(   0 @ 0 );
                    JPen.lineTo( 100 @ 0 );
                    JPen.color = Color.red( 0.8, rrand( 0.7, 1 ));
                    JPen.stroke;
                    JPen.skew( 0, 0.1 );
                };
            });

            // now go on with all params as before
            // translation, skewing, width, and color modifications do not apply
            JPen.line( 10 @ 120, 300 @ 120);
            JPen.stroke;
        };
        )

### *beginPath

    JPen.beginPath

Discards any previously defined path. Example:

        // incomplete arrow
        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // set the Color
            JPen.color = Color.blue;
            JPen.translate( 200, 100 );

            JPen.moveTo(    0 @   0 );
            JPen.lineTo(   50 @ 100 );
            JPen.lineTo(  100 @ 100 );
            // forget what we just drew (the previous 3 lines)
            JPen.beginPath;

            JPen.moveTo(  100 @ 100 );
            JPen.lineTo(    0 @ 150 );
            JPen.lineTo( -100 @ 100 );
            JPen.lineTo(  -50 @ 100 );
            JPen.lineTo(    0 @   0 );

            JPen.stroke;
        };
        )

### *clip

    JPen.clip

Uses the previously defined path as a clipping shape, that is drawing successive shapes will clip them so that only the parts inside the clipping shape are visible. Concatenative calls of `*clip` intersect with the previous clipping. Thus, if you need to undo clippings, you must make use of the `*use` method (see above).. Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            // outline the clipping path
            JPen.addRect( Rect( 110, 110, 180, 30 ));
            JPen.addRect( Rect( 110, 145, 180, 30 ));
            JPen.addRect( Rect( 110, 180, 180, 30 ));
            JPen.addRect( Rect( 110, 215, 180, 30 ));
            // now clip
            JPen.clip;

            // everything else we draw is now clipped
            JPen.color = Color.yellow;
            JPen.fillRect( Rect( 0, 0, 400, 400 ));
            JPen.color = Color.red;
            JPen.moveTo( 200 @ 100 );

            JPen.lineTo( 250 @ 200 );
            JPen.lineTo( 300 @ 200 );
            JPen.lineTo( 200 @ 250 );
            JPen.lineTo( 100 @ 200 );
            JPen.lineTo( 150 @ 200 );

            JPen.fill;
        };
        )

### *smoothing_

    JPen.smoothing_( <(Boolean) flag> )

Turns on/off anti-aliasing. WARNING: non-antialiased string drawing currently looks very different in cocoa and swing (much thicker in swing). Example:

        (
        w = JSCWindow.new.front;
        w.view.background_( Color.white );
        w.drawHook = {
            JPen.width = 2;
            2.do({ arg i;
                JPen.smoothing = i == 1;
                JPen.strokeOval( Rect( 100, 100, 50, 50 ));
                JPen.moveTo( 100 @ 200 );
                JPen.quadCurveTo( 200 @ 300, 100 @ 300 );
                JPen.stroke;
                JPen.translate( 100, 0 );
            });
        };
        )

## Further Examples

    (
    // simple rotating and scaling

        w = JSCWindow( "Pen Rotation and Scaling", Rect( 128, 64, 360, 360 ));
        w.drawHook = {
            var h, v;
            v = h = 300.0;
            JPen.use({
                // use the same rect for everything, just scale and rotate
                var r = Rect( 0, 0, 200, 80 );
                JPen.color = Color.black;
                // offset all subsequent co-ordinates
                JPen.translate( 80, 20 );
                JPen.fillRect( r );
                JPen.color = Color.red;
                // scale all subsequent co-ordinates
                JPen.scale( 0.8, 0.8 );
                JPen.translate( 8, 10 );
                // rotate all subsequent co-ordinates
                JPen.rotate( 0.1pi );
                JPen.fillRect( r );
                JPen.color = Color.blue;
                // lather, rinse, repeat
                JPen.scale( 0.8, 0.8 );
                JPen.rotate( 0.1pi );
                JPen.width = 3;
                JPen.strokeRect( r );
                JPen.color = Color.yellow( 1, 0.5 );
                JPen.scale( 0.8, 0.8 );
                JPen.rotate( 0.1pi );
                JPen.translate( 20, -20 );
                JPen.fillOval( r );
            });
        };

        w.front;
    )

    // redraw at random interval
    // different every time

    (
    var w, run = true;
    w = JSCWindow("my name is... panel", Rect(128, 64, 800, 800));
    w.view.background = Color.white;
    w.onClose = { run = false };
    w.front;
    w.drawHook = {
        JPen.use {
            JPen.width = 0.2;
            400.do {
                JPen.beginPath;
                JPen.moveTo(Point(10.rand * 80 + 40, 10.rand * 80 + 40));
                JPen.lineTo(Point(10.rand * 80 + 40, 10.rand * 80 + 40));
                JPen.stroke;
            };
        };
    };

    { while { run } { w.refresh; 1.0.rand.wait } }.fork(AppClock)

    )

    (
    var w, run = true;
    w = JSCWindow("my name is... panel", Rect(128, 64, 800, 500));
    w.view.background = Color.white;
    w.onClose = { run = false };
    w.front;
    w.drawHook = {
        JPen.use {
            JPen.width = 2;
            80.do {
                JPen.width = rrand(0,4) + 0.5;
                JPen.beginPath;
                JPen.moveTo(Point(800.rand, 500.rand));
                JPen.lineTo(Point(800.rand, 500.rand));
                JPen.stroke;
            };
        };
    };

    { while { run } { w.refresh; 1.0.rand.wait } }.fork(AppClock)

    )

    // Animation

    // Uses random seed to 'store' data
    // By reseting the seed each time the same random values and shapes are generated for each 'frame'
    // These can then be subjected to cumulative rotation, etc., by simply incrementing the phase var.
    (
    // By James McCartney (slightly modified)
    var w, h = 700, v = 700, seed, run = true, phase = 0, bg;
    w = JSCWindow("wedge", Rect(40, 40, h, v), false);
    bg = Color.rand(0,0.3);
    w.onClose = { run = false }; // stop the thread on close
    w.front;
    // store an initial seed value for the random generator
    seed = Date.seed;
    w.drawHook = {
        JPen.width = 2;
        JPen.use {
            // reset this thread's seed for a moment
            thisThread.randSeed = Date.seed;
            // now a slight chance of a new seed or background color
            if (0.006.coin) { seed = Date.seed };
            if (0.02.coin) { bg = Color.rand( 0, 0.3 )};
            JPen.fillColor = bg;
            JPen.fillRect( Rect( 0, 0, h, v ));  // preferably do this rather than switching w.view.background
            // either revert to the stored seed or set the new one
            thisThread.randSeed = seed;
            // the random values below will be the same each time if the seed has not changed
            // only the phase value has advanced
            JPen.translate(h/2, v/2);
            // rotate the whole image
            // negative random values rotate one direction, positive the other
            JPen.rotate(phase * 1.0.rand2);
            // scale the rotated y axis in a sine pattern
            JPen.scale(1, 0.3 * sin(phase * 1.0.rand2 + 2pi.rand) + 0.5 );
            // create a random number of annular wedges
            rrand(6,24).do {
                JPen.color = Color.rand( 0.0, 1.0 ).alpha_( rrand( 0.1, 0.7 ));
                JPen.beginPath;
                JPen.addAnnularWedge(Point(0,0), a = rrand(60,300), a + 50.rand2, 2pi.rand
                    + (phase * 2.0.rand2), 2pi.rand);
                if (0.5.coin) {JPen.stroke}{JPen.fill};
            };
        };
    };

    // fork a thread to update 20 times a second, and advance the phase each time
    { while { run } { w.refresh; 0.05.wait; phase = phase + 0.01pi;} }.fork(AppClock)

    )

    (
    var w, phase = 0, seed = Date.seed, run = true;
    w = JSCWindow("my name is... panel", Rect(128, 64, 800, 800));
    w.view.background = Color.blue(0.4);
    w.onClose = { run = false };
    w.front;
    w.drawHook = {
        JPen.use {
            if (0.02.coin) { seed = Date.seed };
            thisThread.randSeed = seed;
            JPen.color = Color.white;
            200.do {
                var a = 4.rand;
                var b = 24.rand;
                var r1 = 230 + (50 * a);
                var a1 = 2pi / 24 * b + phase;
                var r2 = 230 + (50 * (a + 1.rand2).fold(0,3));
                var a2 = 2pi / 24 * (b + (3.rand2)).wrap(0,23) + phase;
                JPen.width = 0.2 + 1.0.linrand;
                JPen.beginPath;
                JPen.moveTo(Polar(r1, a1).asPoint + Point(400,400));
                JPen.lineTo(Polar(r2, a2).asPoint + Point(400,400));
                JPen.stroke;
            };
            thisThread.randSeed = Date.seed;
            40.do {
                var a = 4.rand;
                var b = 24.rand;
                var r1 = 230 + (50 * a);
                var a1 = 2pi / 24 * b + phase;
                var r2 = 230 + (50 * (a + 1.rand2).fold(0,3));
                var a2 = 2pi / 24 * (b + (3.rand2)).wrap(0,23) + phase;
                JPen.width = 0.2 + 1.5.linrand;
                JPen.beginPath;
                JPen.moveTo(Polar(r1, a1).asPoint + Point(400,400));
                JPen.lineTo(Polar(r2, a2).asPoint + Point(400,400));
                JPen.stroke;
            };
        };
    };

    { while { run } { w.refresh; 0.1.wait; phase = phase + (2pi/(20*24)) } }.fork(AppClock)

    )

    // note: on a fast machine try
    // ... and replace the loop by 600.do
    // hmmmmm!
    (
    var w, h = 800, v = 600, seed = Date.seed, phase = 0, zoom = 0.7, zoomf = 1, run = true;
    w = JSCWindow("affines", Rect(40, 40, h, v), resizable: false );
    w.view.background = Color.blue(0.4);
    w.onClose = { run = false };
    w.front;
    w.drawHook = {
        thisThread.randSeed = Date.seed;
        if (0.0125.coin) { seed = Date.seed; phase = 0; zoom = 0.7; zoomf = exprand(1/1.01, 1.01) }
        { phase = phase + (2pi/80); zoom = zoom * zoomf };
        thisThread.randSeed = seed;
        JPen.use {
            var p1 = Point(20.rand2 + (h/2), 20.rand2 + (v/2));
            var p2 = Point(20.rand2 + (h/2), 20.rand2 + (v/2));
            var xscales = { exprand(2** -0.1, 2**0.1) } ! 2;
            var yscales = { exprand(2** -0.1, 2**0.1) } ! 2;
            var xlates = { 8.rand2 } ! 2;
            var ylates = { 8.rand2 } ! 2;
            var rots = { 2pi.rand + phase } ! 2;
            var xform;
            xscales = (xscales ++ (1/xscales)) * 1;
            yscales = (yscales ++ (1/yscales)) * 1;
            xlates = xlates ++ xlates.neg;
            ylates = ylates ++ xlates.neg;
            rots = rots ++ rots.neg;
            xform = {|i| [xlates[i], ylates[i], rots[i], xscales[i], yscales[i]] } ! 4;
            JPen.color = Color.grey( 1, 0.5 );
            JPen.width = 8.linrand + 1;
            JPen.translate(400, 400);
            JPen.scale(zoom, zoom);
            JPen.translate(-400, -400);
    // JJJ OK this is too heavy
    // 1200.do { }
            200.do {
                var p, rot, xlate, ylate, xscale, yscale;
                JPen.width = 8.linrand + 1;
                JPen.beginPath;
                #rot, xlate, ylate, xscale, yscale = xform.choose;
                JPen.translate(xlate, ylate);
                JPen.rotate(rot, h/2, v/2);
                JPen.scale(xscale, yscale);
                    JPen.moveTo(p1);
                    JPen.lineTo(p2);
                JPen.stroke;
            };
        };
    };

    { while { run } { w.refresh; 0.05.wait }}.fork(AppClock)

    )

    // gimmick by nick collins (from some sc-users list posting):
    (
        var linetext, drawletter;
        var w, h = 800, v = 60, seed = Date.seed, run = true;
        var time, name, sourcestring;
        var yellowness, penwidth;

        //name=[\s,\u,\p,\e,\r,\c,\o,\l,\l,\i,\d,\e,\r];
        //sourcestring= "any lower case text";

        sourcestring= "welcome to supercollider";

        name=Array.fill(sourcestring.size,{arg i; sourcestring[i].asSymbol});

        time=0;

        linetext=('a':[[[0,1],[0.5,0]],[[0.5,0],[1,1]],[[0.25,0.5],[0.75,0.5]]],'b':[[[0,1],[0,0]],[[0,1],[1,1]],[[0,0],[1,0]],[[0,0.5],[0.75,0.5]],[[0.75,0.5],[1,0.75]],[[0.75,0.5],[1,0.25]],[[1,0.75],[1,1]],[[1,0.25],[1,0]]],'c':[[[0,1],[0,0]],[[0,0],[1,0]],[[0,1],[1,1]]],'d':[[[0,1],[0,0]],[[0,0],[0.75,0]],[[0,1],[0.75,1]],[[0.75,1],[1,0.75]],[[0.75,0],[1,0.25]],[[1,0.25],[1,0.75]]],'e':[[[0,0],[0,1]],[[0,0],[1,0]],[[0,1],[1,1]],[[0,0.5],[1,0.5]]],'f':[[[0,0],[0,1]],[[0,0],[1,0]],[[0,0.5],[1,0.5]]],'g':[[[0,1],[0,0]],[[0,0],[1,0]],[[0,1],[1,1]],[[1,1],[1,0.5]],[[0.5,0.5],[1,0.5]]],'h':[[[0,1],[0,0]],[[0,0.5],[1,0.5]],[[1,1],[1,0]]],'i':[[[0,0],[1,0]],[[0.5,0],[0.5,1]],[[0,1],[1,1]]],'j':[[[0,0],[1,0]],[[0.5,0],[0.5,1]],[[0,1],[0.5,1]]],'k':[[[0,1],[0,0]],[[0,0.5],[1,1]],[[0,0.5],[1,0]]],'l':[[[0,1],[0,0]],[[0,1],[1,1]]],'m':[[[0,1],[0,0]],[[0,0],[0.5,0.5]],[[0.5,0.5],[1,0]],[[1,0],[1,1]]],'n':[[[0,1],[0,0]],[[0,0],[1,1]],[[1,1],[1,0]]],'o':[[[0,1],[0,0]],[[0,0],[1,0]],[[0,1],[1,1]],[[1,0],[1,1]]],'p':[[[0,0],[0,1]],[[0,0],[1,0]],[[0,0.5],[1,0.5]],[[1,0],[1,0.5]]],'q':[[[0,0],[0,0.75]],[[0,0],[0.75,0]],[[0,0.75],[0.75,0.75]],[[0.75,0],[0.75,0.75]],[[0.5,0.5],[1,1]]],'r':[[[0,0],[0,1]],[[0,0],[1,0]],[[0,0.5],[1,0.5]],[[1,0],[1,0.5]],[[0,0.5],[1,1]]],'s':[[[0,0],[0,0.5]],[[0,0],[1,0]],[[0,1],[1,1]],[[0,0.5],[1,0.5]],[[1,0.5],[1,1]]],'t':[[[0,0],[1,0]],[[0.5,0],[0.5,1]]],'u':[[[0,1],[0,0]],[[0,1],[1,1]],[[1,0],[1,1]]],'v':[[[0,0],[0.5,1]],[[0.5,1],[1,0]]],'w':[[[0,0],[0.25,1]],[[0.25,1],[0.5,0.5]],[[0.5,0.5],[0.75,1]],[[0.75,1],[1,0]]],'x':[[[0,0],[1,1]],[[0,1],[1,0]]],'y':[[[0,0],[0.5,0.5]],[[0.5,0.5],[1,0]],[[0.5,0.5],[0.5,1]]],'z':[[[0,1],[1,0]],[[0,0],[1,0]],[[0,1],[1,1]]],(" ".asSymbol):[[[0,1],[1,1]],[[0,0.8],[0,1]],[[1,0.8],[1,1]]]);

        w = JSCWindow("welcome", Rect(40, 500, h, v), resizable: false );
        w.view.background = Color.blue(0.5);
        w.onClose = { run = false };
        w.front;

        drawletter= { arg which, startx, starty, xscale=100, yscale,prop=1.0;
            var data;

            yscale= yscale ? xscale;
            data= linetext[which];
            prop=(round((data.size)*prop).asInteger).max(1);
            prop.do({ arg i;
                var val=data[i];
                JPen.beginPath;
                JPen.line( Point( startx + ( xscale * val[ 0 ][ 0 ]), starty + (yscale * val[ 0 ][ 1 ])),
                          Point( startx + (xscale * val[ 1 ][ 0 ]), starty + (yscale * val[ 1 ][ 1 ])));
                JPen.stroke;
            });
        };

        yellowness=rrand(0.7,0.9);
        penwidth=rrand(2,3);

        w.drawHook = {
            JPen.use {
                var xoscil, xsizoscil,yoscil, todraw, usedtime;

                JPen.width= penwidth;
                JPen.color = Color.yellow( yellowness );
                usedtime=time.min(1.0);
                todraw=(round((name.size)*usedtime).asInteger).max(1);
                todraw.do({ arg j;
                    xoscil= sin(2*pi*time+(j*pi*0.13))*140/(1+(10*time));
                    yoscil= sin(2*pi*time+(j*pi*0.03))*200/(1+(200*time));
                    xsizoscil= time*5+5;
                    drawletter.value( name[ j ], 50 + (25 * j) + xoscil, 10 + yoscil, xsizoscil, xsizoscil, usedtime);
                });
            };
        };

        {
            while { time < 2.0 } {
                w.refresh;
                time=(time+0.025); //%2.0;
                0.05.wait;
            }
         }.fork; // (AppClock);
    )

    // funcadelics from Swiki (uses addField method from PenExtensions)
    // not very smooth on slower computers ;-C
    (
        var a, w, i=0, run=true, cfunc, lfunc;
        a = Array.fill2D(10, 20, { arg i, j; (i & j + i) % j  / 10 });

        w = JSCWindow("si", Rect(128, 164, 460, 460), resizable: false );
        w.view.background = Color.black;
        cfunc = { |val| Color(
                    0.2*i.fold(0, 0.4),
                    val*i.fold(0, val.wrap(0, 0.4)),
                    val*i.fold(0, 1.1),
                    0.5
                    )
        };
        lfunc = {|val|  1/ val ** i.fold(0, 1.1) * i.fold(val,2) * 0.5 };
        w.drawHook = {
                    i = i + 0.01;
                    JPen.rotate(i, 240, 240);
                    JPen.translate(100,100);
                    JPen.addField(a, Rect(0, 0, 200, 200), \fillOval, cfunc, lfunc)
        };
        w.front;
        w.onClose = { run = false };
        { while { run } { w.refresh; 0.05.wait }}.fork; // (AppClock);
    )

### More Examples

redFrik drawings: http://swiki.hfbk-hamburg.de:8888/MusicTechnology/833. You'll have to replace `dup( 20000 )` with `dup( 2000 )`, after all there's way too much OSC traffic and probably Java2D being slower than native cocoa painting ...