お絵描きの4歩(金色のモコモコ)
// forked from nabe's forked from: お絵描きの3歩(金色のミミズ)
// forked from nabe's お絵描きの3歩(金色のミミズ)
// forked from nabe's お絵描きの2歩(金色の弾丸)
// forked from nabe's お絵描きの初歩
// forked from nabe's ソースの雛形
package {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
[SWF(width="465", height="465", backgroundColor="0xAABBCC", frameRate="1")];
public class Program_ extends Sprite {
//全体の処理を受け持つクラス。
private const radius_:Number = 15;
//円の半径。
private var cursor_:Sprite = new Sprite();
//カーソル。
private var drag_:Boolean = false;
//「ドラッグ中」の管理フラグ。
private var work_:Draw_;
//描画処理を受け持つインスタンス。
public function Program_ () {
//コンストラクタ。ここから全体の処理が開始する。
//1.初期化処理の呼び出しを仕込むに留める。
addEventListener(Event.ADDED_TO_STAGE, init_);
}
private function init_ (event_:Event):void {
//初期化処理。
//1.用済みのリスナ登録を解除する。
event_.target.removeEventListener(event_.type, arguments.callee);
//2.ビットマップを配置する。
var W_:uint = stage.stageWidth;
var H_:uint = stage.stageHeight;
var bmd_:BitmapData = new BitmapData(W_, H_, false);
addChild(new Bitmap(bmd_));
//4.カーソルを作成する。
var graphics_:Graphics;
graphics_ = cursor_.graphics;
graphics_.lineStyle(0, 0xFF0000);
graphics_.drawCircle(0, 0, radius_);
addChild(cursor_);
cursor_.startDrag(true);
//5.描画処理の準備をする。
work_ = new Draw_(bmd_, radius_);
//6.コマンドを用意する。
var text_:TextField = new TextField();
text_.autoSize = TextFieldAutoSize.LEFT;
text_.background = true;
text_.text = "Clear!";
addChild(text_);
//7.イベント処理を登録する。
stage.addEventListener(MouseEvent.MOUSE_DOWN, beginDrag_);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onDrag_);
stage.addEventListener(MouseEvent.MOUSE_UP , endDrag_);
text_.addEventListener(MouseEvent.CLICK , onClear_);
}
private function beginDrag_ (event_:MouseEvent):void {
work_.moveTo_(mouseX, mouseY);
drag_ = true;
}
private function onDrag_ (event_:MouseEvent):void {
if (!drag_) return;
work_.lineTo_(mouseX, mouseY);
}
private function endDrag_ (event_:MouseEvent):void {
drag_ = false;
}
private function onClear_ (event_:MouseEvent):void {
work_.clear_();
}
}
}
import flash.display.*;
import flash.geom.*;
import flash.filters.*;
class Draw_ extends Sprite {
//描画処理だけ受け持つクラス。
private var radius_:Number;
//円の半径。
private var target_:BitmapData;
//表示用データ。
private var zBuffer_:BitmapData;
//zバッファ用データ。
private var matrix_:Matrix = new Matrix();
//使い回しマトリクス。
private var cmf_:ColorMatrixFilter = new ColorMatrixFilter([
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0xFF, 0, 0, 0, 0,
]);
//αマスク作成用。
private var lastPos_:Point = new Point();
//前回のカーソル位置。
private var v_stamp:Sprite = new Sprite();
//新規描画用オブジェクト。
private var v_circle:Shape = new Shape();
//球。
private var v_bmd:BitmapData;
//描画補助データ。
private var v_bm:Bitmap;
//棒。
private var z_stamp:Sprite = new Sprite();
//zバッファ更新用オブジェクト。
private var z_circle:Shape = new Shape();
//球。
private var z_rect:Shape = new Shape();
//棒。
public function Draw_ (target_:BitmapData, radius_:Number) {
//コンストラクタ。
this.target_ = target_;
this.radius_ = radius_;
build_();
clear_();
}
public function build_ ():void {
//各種オブジェクトを構築する。
var g_:Graphics;
var rect_:Rectangle = new Rectangle(
-radius_, -radius_, 2 * radius_, 2 * radius_
);
//zバッファ用データ。
//内容を書き換える。
zBuffer_ = target_.clone();
//新規描画用オブジェクト。
matrix_.createGradientBox(
rect_.width, rect_.height, Math.PI/4, rect_.x, rect_.y
);
g_ = v_circle.graphics;
g_.clear();
g_.beginGradientFill(
GradientType.RADIAL,
[0xFFFFF7,0xFFFB8C,0xF7D75A,0xA57100,0x432700],
[1,1,1,1,1],
[0x30,0x60,0x90,0xE0,0xFF],
matrix_,
SpreadMethod.PAD,
InterpolationMethod.RGB,
-0.65
);
g_.drawEllipse(rect_.x, rect_.y, rect_.width, rect_.height);
g_.endFill();
v_stamp.addChild(v_circle);
v_bmd = new BitmapData(1, 2 * radius_, false);
v_bm = new Bitmap(v_bmd);
v_bm.y = -radius_;
v_stamp.addChild(v_bm);
this.addChild(v_stamp);
//getBounds呼び出し用に親の座標系にaddしておく。
//zバッファ更新用オブジェクト。
var color_:Array = [];
var alpha_:Array = [];
var ratio_:Array = [];
var max_:uint = Math.ceil(radius_);
for (var x_:uint = 0; x_ <= max_; x_ += 1) {
color_.push(Math.floor((Math.sqrt(max_ * max_ - x_ * x_)) * 0x7F / max_) * 0x010101 + 0x808080);
alpha_.push(1);
ratio_.push(Math.floor(x_ * 0xFF / max_));
}
matrix_.createGradientBox(
rect_.width, rect_.height, 0, rect_.x, rect_.y
);
g_ = z_circle.graphics;
g_.clear();
g_.beginGradientFill(
GradientType.RADIAL,
color_, alpha_, ratio_,
matrix_,
SpreadMethod.PAD,
InterpolationMethod.RGB,
0
);
g_.drawEllipse(rect_.x, rect_.y, rect_.width, rect_.height);
g_.endFill();
z_stamp.addChild(z_circle);
matrix_.createGradientBox(
1, radius_, Math.PI/2, 0, 0
);
g_ = z_rect.graphics;
g_.clear();
g_.beginGradientFill(
GradientType.LINEAR,
color_, alpha_, ratio_,
matrix_,
SpreadMethod.REFLECT,
InterpolationMethod.RGB,
0
);
g_.drawRect(0, -radius_, 1, 2 * radius_);
g_.endFill();
z_stamp.addChild(z_rect);
}
public function clear_ ():void {
var rect_:Rectangle = new Rectangle(
0, 0, zBuffer_.width, zBuffer_.height
);
zBuffer_.fillRect(rect_, 0);
target_.fillRect(rect_, 0);
}
private function savePos_ (x_:Number, y_:Number):void {
//カーソル位置を記憶する。
lastPos_.x = x_;
lastPos_.y = y_;
}
public function moveTo_ (x_:Number, y_:Number):void {
//開始点に球を配置する。
var bounds_:Rectangle = new Rectangle(
x_ - radius_, y_ - radius_,
2 * radius_, 2 * radius_
);
//描画処理を呼び出す。
const rot_:Number = 0;
copy_(x_, y_, rot_, bounds_, v_circle, z_circle);
savePos_(x_, y_);
}
public function lineTo_ (x_:Number, y_:Number):void {
//現在地点まで線を引く。
var dir_:Point = new Point(x_ - lastPos_.x, y_ - lastPos_.y);
var len_:Number = dir_.length;
var rot_:Number = Math.atan2(dir_.y, dir_.x);
const k_:Number = 180 / Math.PI;
//新規描画用オブジェクト。
v_circle.rotation = -rot_ * k_;
v_circle.x = len_;
matrix_.createBox(1, 1, -rot_, 0, radius_);
v_bmd.draw(v_circle, matrix_);
v_bm.scaleX = len_;
//zバッファ更新用オブジェクト。
z_circle.x = len_;
z_rect.scaleX = len_;
//描画領域を求める。
v_stamp.x = lastPos_.x;
v_stamp.y = lastPos_.y;
v_stamp.rotation = rot_ * k_;
var bounds_:Rectangle = v_stamp.getBounds(this);
//描画処理を呼び出す。
copy_(lastPos_.x, lastPos_.y, rot_, bounds_, v_stamp, z_stamp);
savePos_(x_, y_);
}
public function copy_ (
x_:Number, y_:Number, rot_:Number, bounds_:Rectangle,
v_shape:DisplayObject, z_shape:DisplayObject
):void {
//描画領域の端数を丸める。
bounds_.x = Math.floor(bounds_.x);
bounds_.y = Math.floor(bounds_.y);
bounds_.width = Math.ceil(bounds_.width + 2 * radius_);
bounds_.height = Math.ceil(bounds_.height + 2 * radius_);
//描画領域の左上の座標を求める。
var leftTop_:Point = new Point(
bounds_.x, bounds_.y
);
//作業領域を求める。
var zeroBounds_:Rectangle = new Rectangle(
0, 0, bounds_.x, bounds_.y
);
//作業領域の左上の座標を求める。
var zero_:Point = new Point(0, 0);
//新規描画用の位置・角度を設定する。
matrix_.createBox(1, 1, rot_, x_ - bounds_.x, y_ - bounds_.y);
//作業領域を用意する。
var workBuf_:BitmapData = new BitmapData(
bounds_.width, bounds_.height, false, 0
);
//zバッファの更新内容を用意する。
workBuf_.draw(z_shape, matrix_, null, BlendMode.NORMAL);
//αマスクを用意する。ただし他の作業にも流用する。
var alphaBuf_:BitmapData = new BitmapData(
bounds_.width, bounds_.height, true
);
//現在のzバッファの内容をコピーする。
alphaBuf_.copyPixels(zBuffer_, bounds_, zero_);
//zバッファの更新内容から現在の値を差し引く。
workBuf_.draw(new Bitmap(alphaBuf_), null, null, BlendMode.SUBTRACT);
//αチャンネルを求める。
alphaBuf_.applyFilter(workBuf_, zeroBounds_, zero_, cmf_);
//zバッファを更新する。
workBuf_.draw(z_shape, matrix_, null, BlendMode.NORMAL);
zBuffer_.copyPixels(
workBuf_, zeroBounds_, leftTop_,
alphaBuf_, zero_, false
);
//表示用データを更新する。
workBuf_.draw(v_shape, matrix_, null, BlendMode.NORMAL);
target_.copyPixels(
workBuf_, zeroBounds_, leftTop_,
alphaBuf_, zero_, false
);
}
}