forked from: Saqoosha challenge for professionals(さらさら 2)
forked from Saqoosha challenge for professionals (diff: 470)
水に漂う感じ
ActionScript3 source code
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/8d5h
*/
// forked from checkmate's Saqoosha challenge for professionals
// 水に漂う感じ
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
public class Main extends Sprite {
// 定数
private const LETTER:String = "さらさら"; // 表示文字列
// パーティクルの色の設定
private function setParticleColor():uint {
var c1:uint = Math.random() * 0xFF;
var c2:uint = Math.random() * 0xFF;
return rgbToHex(0xFF, c1, c2);
}
private const CENTER_X:Number = stage.stageWidth / 2;
private const CENTER_Y:Number = stage.stageHeight / 2;
// 変数
private var particleArray:Array; // パーティクル格納配列(繰り返し使うために待避)
private var copyParticleArray:Array; // パーティクル格納配列(実際に使用する)
private var canvasBitmapData:BitmapData; // 描画 BitmapData
private var pixelizer:Pixelizer; // ピクセル化クラス
// コンストラクタ
public function Main() {
// Particle クラスの初期化
Particle.top = 0;
Particle.bottom = stage.stageHeight;
Particle.left = 0;
Particle.right = stage.stageWidth;
// 描画 BitmapData 生成
canvasBitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
addChild(new Bitmap(canvasBitmapData));
// 案内表示
var signField:TextField = new TextField();
signField.text = "Stage を Click すると Reset します。\nMouse の position で motion が変わります。";
signField.autoSize = TextFieldAutoSize.LEFT;
signField.selectable = false;
signField.blendMode = BlendMode.INVERT;
addChild(signField);
// テキストピクセライズ
var pTextField:PixelizerTextField = new PixelizerTextField();
pTextField.text = LETTER;
var textFormat:TextFormat = new TextFormat(null, 120);
textFormat.letterSpacing = -5;
pTextField.setTextFormat(textFormat);
pixelizer = new Pixelizer();
pixelizer.addEventListener(Event.COMPLETE, scanCompleteHandler);
pixelizer.scan(pTextField.bitmapData);
}
// ピクセライズ完了後の処理
// (ピクセル格納配列の生成)
private function scanCompleteHandler(event:Event):void {
// イテレータ生成
var iter:PixelizerIterator = pixelizer.iterator;
pixelizer = null;
// 配置オフセット計算
var offsetX:Number = (stage.stageWidth - iter.width) / 2;
var offsetY:Number = (stage.stageHeight - iter.height) / 2;
// イテレーション
particleArray = [];
while (iter.hasNext()) {
var color:uint = iter.next();
var alpha:uint = getAlpha(color);
if (alpha > 0x7F) {
particleArray.push(new Particle(iter.x + offsetX, iter.y + offsetY, setParticleColor()));
}
}
// イベント登録
stage.addEventListener(MouseEvent.CLICK, clickHandler);
motion();
}
// 実処理(モーションタイポ)
private function motion():void {
// 待避配列から使用配列を生成
copyParticleArray = [];
for each (var original:Particle in particleArray) {
copyParticleArray.push(original.clone());
}
// 各 particle に動作開始時限値
var n:uint = copyParticleArray.length;
for (var i:int = 0; i < n; i++) {
copyParticleArray[i].counter = 7;
}
// イベント登録
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
// フレームイベント
private function enterFrameHandler(event:Event):void {
var cx:Number = CENTER_X;
var cy:Number = CENTER_Y;
canvasBitmapData.lock();
canvasBitmapData.fillRect(canvasBitmapData.rect, 0x000000);
for each (var particle:Particle in copyParticleArray) {
particle.update(cx - mouseX, cy - mouseY);
canvasBitmapData.setPixel(particle.x, particle.y, particle.color);
}
canvasBitmapData.unlock();
}
// マウスイベント
private function clickHandler(event:MouseEvent):void {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
motion();
}
// RGB → 0xNNNNNN
private function rgbToHex(r:uint, g:uint, b:uint):uint {
r = adjust(r);
g = adjust(g);
b = adjust(b);
return r << 16 | g << 8 | b;
}
// 255 を超えていた場合は 255 に切り捨てる
private function adjust(val:uint):uint {
return Math.min(val, 0xFF);
}
// 32 bit color のアルファ値を取得
private function getAlpha(color:uint):uint {
return (color >> 24) & 0xFF;
}
}
}
// パーティクルクラス
class Particle {
// static 変数
// 物理変数
public static var rate:Number = 0.005; // 拡散割合
public static var friction:Number = 0.5; // 空気抵抗
// ステージ領域
public static var top:uint;
public static var bottom:uint;
public static var left:uint;
public static var right:uint;
// 現在座標
public function get x():Number { return _x; }
private var _x:Number;
public function get y():Number { return _y; }
private var _y:Number;
// 色
public function get color():uint { return _color; }
private var _color:uint;
// 動作開始時限値
public function set counter(value:uint):void {
_counter = value;
}
private var _counter:uint = 0;
// ローカルな物理変数
private var localFriction:Number; // 空気抵抗
// 速度
private var vx:Number = 0;
private var vy:Number = 0;
public function Particle(x:Number, y:Number, color:uint) {
_x = x;
_y = y;
_color = color;
// friction のローカル補正
var frictionOffset:Number = (Math.random() * 8 - 4) / 10;
localFriction = friction + frictionOffset;
}
public function clone():Particle {
return new Particle(_x, _y, _color);
}
public function update(offsetX:Number, offsetY:Number):void {
if (_counter > 0) {
_counter--;
} else {
var randomOffsetX:Number = Math.random() * 0.75;
var randomOffsetY:Number = Math.random() * 0.75;
if (offsetX > 0) randomOffsetX *= -1;
if (offsetY > 0) randomOffsetY *= -1;
vx *= localFriction;
vy *= localFriction;
vx += offsetX * rate;
vy += offsetY * rate;
vx += randomOffsetX;
vy += randomOffsetY;
_x += vx;
_y += vy;
}
}
}
// ピクセル化クラス
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
class Pixelizer extends EventDispatcher {
private var width:uint = 0;
private var height:uint = 0;
private var data:Vector.<uint>;
// イテレータ
public function get iterator():PixelizerIterator {
return new PixelizerIterator(width, height, data);
}
// コンストラクタ
public function Pixelizer() {}
// スキャン
public function scan(bmd:BitmapData):void {
width = bmd.width;
height = bmd.height;
data = bmd.getVector(bmd.rect);
// イベント発行
dispatchEvent(new Event(Event.COMPLETE));
}
}
// ピクセル化したデータのイテレータ
class PixelizerIterator {
// モードフラグ
public static const NEXT:String = "next"; // 正順
public static const PREV:String = "prev"; // 逆順
// next()、nextCol() で取得したデータのX座標
public function get x():int { return positionX; }
private var positionX:int = 0;
// next()、nextRow() で取得したデータのY座標
public function get y():int { return positionY; }
private var positionY:int = 0;
// スキャンサイズ(幅)
public function get width():uint { return _width; }
private var _width:uint;
// スキャンサイズ(高)
public function get height():uint { return _height; }
private var _height:uint;
// データ格納 Vector
private var data:Vector.<uint>;
// 現在のイテレートモード
private var mode:String = "next";
// イテレーションカウンター
private var position:int = 0;
public function PixelizerIterator(width:uint, height:uint, data:Vector.<uint>) {
_width = width;
_height = height;
this.data = data;
}
// データの操作(1個ずつデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNext():Boolean {
return (mode == "prev") ? _hasPrev() : _hasNext();
}
// 外部から呼び出せる next
public function next():uint {
return (mode == "prev") ? _prev() : _next();
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextRow():Boolean {
return (mode == "prev") ? _hasPrevRow() : _hasNextRow();
}
// 外部から呼び出せる next
public function nextRow():Vector.<uint> {
return (mode == "prev") ? _prevRow() : _nextRow();
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextCol():Boolean {
return (mode == "next") ? _hasNextCol() : _hasPrevCol();
}
// 外部から呼び出せる next
public function nextCol():Vector.<uint> {
return (mode == "next") ? _nextCol() : _prevCol();
}
// リセット
public function reset(mode:String = "next"):void {
this.mode = mode;
var offset:uint = (mode == "next") ? 0 : 1;
position = (data.length - 1) * offset;
positionX = (_width - 1) * offset;
positionY = (_height - 1) * offset;
}
// データの操作(1個ずつデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNext():Boolean {
return position < data.length;
}
// mode = "prev" 時の hasNext
private function _hasPrev():Boolean {
return position > 0;
}
// mode = "next" 時の next
private function _next():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position++);
}
// mode = "prev" 時の next
private function _prev():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position--);
}
private function getData(idx:uint):uint {
if (idx >= data.length) {
throw new Error("getData#配列範囲外:" + idx + " length:" + data.length);
}
return data[idx];
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextRow():Boolean {
return positionY < _height;
}
// mode = "prev" 時の hasNext
private function _hasPrevRow():Boolean {
return positionY > -1;
}
// mode = "next" 時の next
private function _nextRow():Vector.<uint> {
return getRow(positionY++);
}
// mode = "prev" 時の next
private function _prevRow():Vector.<uint> {
return getRow(positionY--);
}
// next の実体
private function getRow(val:uint):Vector.<uint> {
if (val >= _height) {
throw new Error("getRow#配列範囲外:" + val + " _height:" + _height);
}
return data.slice(val * _width, (val + 1) * _width);
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextCol():Boolean {
return positionX < _width;
}
// mode = "prev" 時の hasNext
private function _hasPrevCol():Boolean {
return positionX > -1;
}
// mode = "next" 時の next
private function _nextCol():Vector.<uint> {
return getCol(positionX++);
}
// mode = "prev" 時の next
private function _prevCol():Vector.<uint> {
return getCol(positionX--);
}
// next の実体
private function getCol(idx:uint):Vector.<uint> {
if (idx >= _width) {
throw new Error("getCol#配列範囲外:" + idx + " _width:" + _width);
}
var vector:Vector.<uint> = new Vector.<uint>(_height, true);
for (var i:uint = 0; i < _height; i++) {
vector[i] = data[_width * i + idx];
}
return vector;
}
}
// 文字列をピクセル化クラスへ投げ込むためのテキストフィールド
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
class PixelizerTextField extends TextField {
// 透明
private const TRANSPARENT:uint = 0x00000000;
public function PixelizerTextField() {
autoSize = TextFieldAutoSize.LEFT;
}
public function get bitmapData():BitmapData {
// 普通に TextField を BitmapData に draw
var bmd1:BitmapData = new BitmapData(textWidth, textHeight, true, TRANSPARENT);
bmd1.draw(this, new Matrix(1, 0, 0, 1, -2, -2));
// 上記 BitmapData のうち、文字である範囲を Rectangle として取得
var rect:Rectangle = bmd1.getColorBoundsRect(0xFF000000, TRANSPARENT, false);
// 上記 Rectangle 部分のみの BitmapData を生成
var bmd2:BitmapData = new BitmapData(rect.width, rect.height, true, TRANSPARENT);
bmd2.draw(bmd1, new Matrix(1, 0, 0, 1, -rect.x, -rect.y));
bmd1.dispose();
return bmd2;
}
}