Painting
画像を抽象絵画風に描画
/**
* Copyright chutaicho ( http://wonderfl.net/user/chutaicho )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/9xrm
*/
/**
画像を抽象絵画風に描画
*/
package
{
//import __AS3__.vec.Vector;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.geom.ColorTransform;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.utils.Timer;
[SWF(backgroundColor="0x000000", frameRate="40")]
public class AbstractPainting extends Sprite
{
private const IMAGE_URL:String = "http://captain-inc.heteml.jp/apt_works/code/richter.png";
//-------------------------------------------------------
// const
//-------------------------------------------------------
private const DOTS_MAX:Number = 50; // 1モーションで保持する頂点数
private const FRICTION:Number = 0.98;
private const EDGE_COLOR:uint = 0x666666; // 太いラインのエッジカラー
//-------------------------------------------------------
// vars
//-------------------------------------------------------
private var _drawImage:BitmapData;
private var _dotNumber:Number = 0;
private var _dots:Vector.<Vertex>;
private var _codeWidth:Number = 1;
private var _startX:Number;
private var _startY:Number;
private var _layerA:Sprite; // 上のレイヤー(太いライン)
private var _layerB:Sprite; // 下のレイヤー(細いライン)
private var _drawTarget:Sprite; // ビットマップ描画するターゲット
private var _canvas:BitmapData; // 描画先
private var _holder:Sprite; // 描画したビットマップのいれもの
public function AbstractPainting()
{
init();
}
private function init():void
{
var req:URLRequest = new URLRequest(IMAGE_URL);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( req, new LoaderContext(true));
}
private function loadComplete(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, loadComplete);
var sw:int = stage.stageWidth;
var sh:int = stage.stageHeight;
_startX = sw/2;
_startY = sh/2;
var source:Bitmap = e.target.loader.content as Bitmap;
source.width = sw;
source.height = sh;
_drawImage = new BitmapData(sw,sh, false, 0x000);
_drawImage.draw(source);
_layerA = new Sprite();// 上のレイヤー
_layerB = new Sprite();// 下のレイヤー...ちょっとずらす
_layerB.x = 10;
_layerB.y = 10;
// 上のレイヤーだけちょっと色かえる
var colorTransform:ColorTransform = new ColorTransform(1,1,1,2,20,30,20,20);
_layerB.transform.colorTransform = colorTransform;
_drawTarget = new Sprite();
_drawTarget.addChild(_layerA);
_drawTarget.addChild(_layerB);
_holder = new Sprite();
_holder.x = sw/2;
_holder.z = 300;
addChild(_holder);
_canvas = new BitmapData(sw,sh,false,0xEEEEEE);
var canvasBitmap:Bitmap = new Bitmap(_canvas,"auto",true);
canvasBitmap.x = -232;
_holder.addChild(canvasBitmap);
_dots = new Vector.<Vertex>();
var timer:Timer = new Timer(30, 0);
timer.addEventListener(TimerEvent.TIMER, createDot);
timer.start();
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function randomRange( min:Number, max:Number ):Number
{
return min + Math.random() * (max + 1 - min);
}
//-------------------------------------------------------
// Private Event Handler
//-------------------------------------------------------
private function createDot(e:TimerEvent):void
{
var dot:Vertex = new Vertex();
var px:Number = _startX + randomRange(-40,40);
var py:Number = _startY + randomRange(-40,40);
// ステージからはみ出たら適当な場所へ戻す
if(px > stage.stageWidth || py > stage.stageHeight || px < 0 || py < 0)
{
px = Math.random() * stage.stageWidth;
py = Math.random() * stage.stageHeight;
dot.ajust = true; // 位置変更フラグ
}
_startX = px;
_startY = py;
dot.x = _startX;
dot.y = _startY;
_dots.push(dot);
if (_dotNumber >= DOTS_MAX)
{
var target:Vertex = _dots.shift();
target = null;
}
_dotNumber ++;
}
private function enterFrameHandler(e:Event):void
{
_layerA.graphics.clear();
_layerB.graphics.clear();
var codeWidth:Number = 1; // layerBの線の太さ
var brushThickness:Number = 4; // 線の太さ
var pressure:Number = 0; // 筆圧っぽさ
var l:int = _dots.length;
// 一回まわして角度を計算しておく
for(var i:int = l-1; i > 0; i-- )
{
if( i != l-1 )
{
var dot:Vertex = _dots[i];
var dotB:Vertex = _dots[i-1]; // 一つ前のポイント(基準値)
var disX:Number = dotB.x - dot.x;
var disY:Number = dotB.y - dot.y;
// X:Yの角度から垂直になるポイントへの角度
dot.angle = Math.atan2(disX, disY) + Math.PI / 2;
}
}
// 線の描画部分
for(var j:int = l-1; j > 0; j-- )
{
dot = _dots[j];
var color:uint = _drawImage.getPixel(dot.x, dot.y);
if (j>0)
{
brushThickness += 0.1;
codeWidth += 0.01;
pressure += 0.02;
}
else
{
brushThickness *= FRICTION;
codeWidth *= FRICTION;
pressure *= FRICTION;
}
if( j == l-1 )
{
// はみ出たポイントを戻すタイミングで円を書く
if(dot.ajust)
drawCircle(dot,color);
_layerA.graphics.moveTo(dot.x, dot.y);
_layerB.graphics.moveTo(dot.x, dot.y);
}
else
{
dotB = _dots[j-1];
disX = dotB.x - dot.x;
disY = dotB.y - dot.y;
var mx:Number = dot.x + (disX)*0.4;
var my:Number = dot.y + (disY)*0.4;
var cosA:Number = brushThickness*Math.cos(dot.angle);
var cosB:Number = brushThickness*Math.cos(dotB.angle);
var sinA:Number = brushThickness*Math.sin(dot.angle);
var sinB:Number = brushThickness*Math.sin(dotB.angle);
// 上のレイヤー
_layerB.graphics.lineStyle(codeWidth, color, pressure);
_layerB.graphics.curveTo(dotB.x, dotB.y, mx, my);
_layerB.graphics.lineTo(mx, my);
// 下のレイヤー
if(!dot.ajust)
{
_layerA.graphics.beginFill(color,0.4);
_layerA.graphics.moveTo(mx,my);
_layerA.graphics.lineStyle(0.2, EDGE_COLOR, 0);
_layerA.graphics.moveTo(dot.x + cosA, dot.y + sinA);
_layerA.graphics.lineTo(dot.x - cosA, dot.y - sinA);
_layerA.graphics.lineStyle(0.2, EDGE_COLOR, 0.2);
_layerA.graphics.lineTo(dotB.x - cosB, dotB.y - sinB);
_layerA.graphics.lineStyle(0.2, EDGE_COLOR, 0);
_layerA.graphics.lineTo(dotB.x + cosB, dotB.y + sinB);
_layerA.graphics.lineStyle(0.2, EDGE_COLOR, 0.2);
_layerA.graphics.lineTo(dot.x + cosA, dot.y + sinA);
_layerA.graphics.moveTo(mx, my);
}
}
}
_canvas.draw(_drawTarget);
var center:Number = stage.stageWidth/2;
var p:Number = (center - mouseX)/center* -180;
SimpleTween.addTween(_holder, {rotationY:p});
}
private function drawCircle(target:Vertex, color:uint):void
{
for(var i:int = 0; i<8; i++)
{
var px:Number = target.x + randomRange( -100, 100 );
var py:Number = target.y + randomRange( -100, 100 );
var r:Number = Math.random()* 4;
_layerA.graphics.beginFill(color,1);
_layerA.graphics.drawCircle(px,py,r);
}
}
}
}
import flash.display.DisplayObject;
/**
頂点クラス
*/
class Vertex
{
private const GRAVITY:Number = 0.5; // 重力
private const FRICTION:Number = 0.98; // 減速抵抗
public var x:Number;
public var y:Number;
public var rad:Number = 2;
public var color:uint;
public var randonNum:Number;
public var angle:Number;
public var ajust:Boolean = false;
private var speedX:Number;
private var speedY:Number;
public function Vertex()
{
init();
}
private function init():void
{
var angle:Number = Math.random()*Math.PI*2;
randonNum = Math.random() * 4;
speedX = Math.cos(angle) * randonNum;
speedY = Math.sin(angle) * randonNum;
}
public function upDate():void
{
speedX *= FRICTION;
speedY *= FRICTION;
x += speedX;
y += speedY;
}
}
/**
TWEEN用 あとで使うかも。いまのとこ一回しか使ってない。
*/
class SimpleTween
{
private static const EASE_VALUE:Number = 0.2;
public static function addTween( target:Object, obj:Object ):void
{
for(var istr:String in obj)
{
var a:Number = target[istr];
var b:Number = obj[istr];
target[istr] = a + (b-a) * EASE_VALUE;
}
}
}