くるくるグラフィックジェネレーター


先日wonderflにポストしたくるくるカール(forked from: Draw worm by mouse gesture)に少し機能を追加して、簡単にコンピュテーションアート風のグラフィックを作成できるジェネレーター(?)を作ってみました。

くるくるグラフィックジェネレーター
操作方法は、マウスダウンで描画開始・マウスアップで描画停止・FULLSCREENボタンで全画面表示・CLEARボタンで全削除・SAVEボタンで背景が黒の画像保存・SAVE(Alpha)ボタンで背景が透明の画像保存です。

kurukuru

画像をクリックすると別ウィンドウでFlashアプリケーションが開きます

このアプリケーションを作るにあたってはwonderfl Draw worm by mouse gesture. をForkさせていただきましたので、オリジナルコードのライセンスに基づき以下に全ソースを公開します。
またこのコードのライセンスもオリジナルのライセンスを継承します。

// forked from nutsu's Worm matrix based.
// forked from nutsu's Draw worm by mouse gesture. : http://wonderfl.net/c/9os2
/**
LOVE MATRIX.
a study for drawing curl curve.
license under the GNU Lesser General Public License.
*/

package
{
	import com.adobe.images.PNGEncoder;
	import com.bit101.components.PushButton;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageDisplayState;
	import flash.display.StageScaleMode;
	import flash.events.*;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.navigateToURL;
	import flash.utils.ByteArray;

	[SWF(width = "1280", height = "800", frameRate = "60", backgroundColor = "0")]

	public class Main extends Sprite
	{

		private var _canvas:WormMatrix;
		private var _fr:FileReference = new FileReference();

		public function Main()
		{
			_canvas = new WormMatrix();
			addChild(_canvas);
			//
			var bt1:PushButton = new PushButton ( this,  10 , 10, "CANVAS CLEAR" , _canvas.canvasClear) ;
			var bt2:PushButton = new PushButton ( this, 115 , 10, "SAVE IMAGE", onSave ) ;
			var bt3:PushButton = new PushButton ( this, 220 , 10, "SAVE IMAGE (Alpha)", onSaveAlpha ) ;
			var bt4:PushButton = new PushButton ( this, 350 , 10, "SOURCE CODE", function():void{navigateToURL(new URLRequest("http://www.digifie.jp/blog/archives/397"))} ) ;
			var bt5:PushButton = new PushButton ( this,  1280 - 110 , 10, "FULL SCREEN" , fullScreen) ;
			//
			var isFullScreen:Boolean;
			function fullScreen():void
			{
				if(!isFullScreen){
					stage.scaleMode = StageScaleMode.NO_SCALE;
					stage.displayState = StageDisplayState.FULL_SCREEN;
					isFullScreen = true;
					bt4.label = "NOMAL SCREEN"
				}else{
					stage.displayState = StageDisplayState.NORMAL;
					isFullScreen = false;
					bt4.label = "FULL SCREEN"
				}
			}
		}

		private function onSave(e:MouseEvent):void
		{
			var bmd:BitmapData = new BitmapData(1280, 800, false, 0);
			bmd.draw(_canvas);
			fileSave(bmd);
			//
			bmd.dispose();
		}

		private function onSaveAlpha(e:MouseEvent):void
		{
			var bmd:BitmapData = new BitmapData(1280, 800, true, 0x00000000);
			bmd.draw(_canvas);
			fileSave(bmd);
			//
			bmd.dispose();
		}

		private function fileSave(bmd:BitmapData):void
		{
			var png:ByteArray = PNGEncoder.encode(bmd);
			_fr.addEventListener(Event.OPEN, open);
			_fr.addEventListener(ProgressEvent.PROGRESS, progress);
			_fr.addEventListener(Event.COMPLETE, complete);
			_fr.addEventListener(Event.CANCEL, cancel);
			_fr.addEventListener(Event.SELECT, select);
			_fr.addEventListener(IOErrorEvent.IO_ERROR, ioError);
			var date:Date = new Date;
			_fr.save(png, "export_image_" + date.getTime() + ".png");
			//
			function open(e:Event):void
			{
				//
			}
			function progress(e:ProgressEvent):void
			{
				//
			}
			function complete(e:Event):void
			{
				removedEventListener();
			}
			function cancel(e:Event):void
			{
				removedEventListener();
			}
			function select(e:Event):void
			{
				//
			}
			function ioError(e:IOErrorEvent):void
			{
				removedEventListener();
			}
			//
			function removedEventListener():void
			{
				_fr.removeEventListener(Event.OPEN, open)
				_fr.removeEventListener(ProgressEvent.PROGRESS, progress)
				_fr.removeEventListener(Event.COMPLETE, complete)
				_fr.removeEventListener(Event.CANCEL, cancel);
				_fr.removeEventListener(Event.SELECT, select);
				_fr.removeEventListener(IOErrorEvent.IO_ERROR, ioError)
			}
		}
	}
}

//package {
    import flash.events.MouseEvent;
    import frocessing.color.ColorHSV;
    import frocessing.display.F5MovieClip2DBmp;
    import frocessing.geom.FMatrix2D;

    internal class WormMatrix extends F5MovieClip2DBmp
	{

        private var vms:Array;
        private var MAX_NUM:int = 150;
        private var N:Number = 120;
        private var px:Number;
        private var py:Number;
		private var t:Number = 0;

        public function WormMatrix ()
		{
            super( true, 0 );
            vms = [];
        }

        public function check():void
        {
            var x0:Number = mouseX;
            var y0:Number = mouseY;
            var vx:Number = x0 - px;
            var vy:Number = y0 - py;
            var len:Number = min( mag( vx, vy ), 50 );

            if( len<10 ) return;

            var mtx:FMatrix2D = new FMatrix2D();
            mtx.rotate( atan2( vy, vx ) );
            mtx.translate( x0, y0 );

            createObj( mtx, len );

            px = x0;
            py = y0;
        }

        public function createObj( mtx:FMatrix2D, len:Number ):void
        {
			var angle:Number = random(PI/180, PI/2);
            if( Math.random() > 0.5 ) angle *= -1;
            var tmt:FMatrix2D = new FMatrix2D();
            tmt.scale( 0.95, 0.95 );
            tmt.rotate( angle );
            tmt.translate( len, 0 );
            var w:Number = 0.5;

            var obj:WormObject = new WormObject();
            obj.c1x = obj.p1x = -w * mtx.c + mtx.tx;
            obj.c1y = obj.p1y = -w * mtx.d + mtx.ty;
            obj.c2x = obj.p2x =  w * mtx.c + mtx.tx;
            obj.c2y = obj.p2y =  w * mtx.d + mtx.ty;
            obj.vmt = mtx;
            obj.tmt = tmt;
            obj.r = angle;
            obj.w = len * .1;
            obj.count = 0;

            vms.push( obj );
            if( vms.length > MAX_NUM )
                vms.shift();
        }

        public function setup():void
        {
 			size( 1280, 800 );
            background(0, 0);
            noStroke();
            px = mouseX;
            py = mouseY;
        }

		public function canvasClear(e:MouseEvent):void
		{
			background(0, 0);
			vms = [];
		}

        public function draw():void
        {

			stroke(0xFFFFFFFF, .4);

            var len:int = vms.length;
            for( var i:int=0; i<len; i++ )
            {
                var o:WormObject = vms[i];
                if( o.count<N && isMousePressed){
                    drawWorm( o );
                    o.count++;
                }else{
                    len--;
                    vms.splice( i, 1 );
                    i--;
                }
            }

            check();
        }

        public function drawWorm( obj:WormObject ):void
        {

			var color:ColorHSV = new ColorHSV(t, 0.6, 1, 0.1);
			t += 0.1;

			if( Math.random()>0.9 ){
                obj.tmt.rotate( -obj.r*2 );
                obj.r *= -1;
            }
            obj.vmt.prepend( obj.tmt );
            var cc1x:Number = -obj.w*obj.vmt.c + obj.vmt.tx;
            var cc1y:Number = -obj.w*obj.vmt.d + obj.vmt.ty;
            var pp1x:Number = (obj.c1x+cc1x)/2;
            var pp1y:Number = (obj.c1y+cc1y)/2;
            var cc2x:Number = obj.w*obj.vmt.c + obj.vmt.tx;
            var cc2y:Number = obj.w*obj.vmt.d + obj.vmt.ty;
            var pp2x:Number = (obj.c2x+cc2x)/2;
            var pp2y:Number = (obj.c2y+cc2y)/2;
            beginFill( uint(color), .7 );
            moveTo( obj.p1x, obj.p1y );
            curveTo( obj.c1x, obj.c1y, pp1x, pp1y );
            lineTo( pp2x, pp2y );
            curveTo( obj.c2x, obj.c2y, obj.p2x, obj.p2y );
            closePath();
            endFill();
            obj.c1x = cc1x;
            obj.c1y = cc1y;
            obj.p1x = pp1x;
            obj.p1y = pp1y;
            obj.c2x = cc2x;
            obj.c2y = cc2y;
            obj.p2x = pp2x;
            obj.p2y = pp2y;
        }
    }
//}

import frocessing.geom.FMatrix2D;

internal class WormObject{
    public var c1x:Number;
    public var c1y:Number;
    public var c2x:Number;
    public var c2y:Number;
    public var p1x:Number;
    public var p1y:Number;
    public var p2x:Number;
    public var p2y:Number;
    public var w:Number;
    public var r:Number;
    public var count:int;
    public var vmt:FMatrix2D;
    public var tmt:FMatrix2D;
}

描いた絵はPNG画像として保存できますので、ぜひ遊んでみてください。