In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

光を描いて3Dで回すエフェクト

PerfumeのPV 'baby cruising Love'の中に出てくる光のエフェクトを再現してみました。
* 1:13他 何回か出てきます
* 
* Youtube [PV] "Perfume" baby cruising Love 2nd Enc
* http://www.youtube.com/watch?v=76yVixuKqGY
* 
* マウスドラッグで光を描きます
* ハートの形とか可愛いかも
* 
* http://www.kuma-de.com/blog/1-programming/1-javascript/2009-09-07/1202
/**
 * Copyright KinkumaDesign ( http://wonderfl.net/user/KinkumaDesign )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/hfjv
 */

/*
 * PerfumeのPV 'baby cruising Love'の中に出てくる光のエフェクトを再現してみました。
 * 1:13他 何回か出てきます
 * 
 * Youtube [PV] "Perfume" baby cruising Love 2nd Enc
 * http://www.youtube.com/watch?v=76yVixuKqGY
 * 
 * マウスドラッグで光を描きます
 * ハートの形とか可愛いかも
 * 
 * http://www.kuma-de.com/blog/1-programming/1-javascript/2009-09-07/1202
 */

package  
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.BitmapFilter;
	import flash.filters.BitmapFilterQuality;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.PerspectiveProjection;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Utils3D;
	import flash.geom.Vector3D;
	import flash.sampler.NewObjectSample;
	import flash.text.TextField;
	import flash.text.TextFormat;
	
        [SWF(width = "480", height = "480", backgroundColor = 0x000000, frameRate = "30")]
	public class Main extends Sprite
	{
		public var penSp:Sprite;
		public var canvas:Bitmap;
		public var currentBmd:BitmapData;
		public var lastDrawPoint:Point;
		public var drawPoints:Vector.<DrawPoint>;
		public var proj:Matrix3D;
		public var vout:Vector.<Number> = new Vector.<Number>();
		public var uvts:Vector.<Number> = new Vector.<Number>();
		public var projverts:Vector.<Number> = new Vector.<Number>();
		public var moveCount:int = 0;
		
		public var hsw:Number;
		public var hsh:Number;
		
		public var logText:TextField;
		
		
		public function Main() 
		{
			stage.addEventListener(MouseEvent.MOUSE_DOWN, initHD);
		}
		
		private function initHD(e:MouseEvent):void 
		{
			stage.removeEventListener(MouseEvent.MOUSE_DOWN, initHD);

			hsw = stage.stageWidth / 2;
			hsh = stage.stageHeight / 2;
			
			var perse:PerspectiveProjection = new PerspectiveProjection();
			perse.focalLength = 350;
			proj = perse.toMatrix3D();
			drawPoints = new Vector.<DrawPoint>();
			initCanvas();
			initLog();
			
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHD);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHD);
			stage.addEventListener(Event.ENTER_FRAME, update);
		}
		
		private function initLog():void
		{
			logText = new TextField();
			addChild(logText);
			logText.x = 8;
			logText.y = 6;
			var tf:TextFormat = new TextFormat();
			tf.color = 0xffffff;
			tf.size = 10;
			tf.font = "_gothicMono";
			logText.selectable = false;
			logText.defaultTextFormat = tf;
		}
		
		private function initCanvas():void
		{
			var w:Number = stage.stageWidth;
			var h:Number = stage.stageHeight;
			currentBmd = new BitmapData(w, h, true, 0xff000000);
			canvas = new Bitmap(currentBmd);
			addChild(canvas);
			
			penSp = new Sprite();
			penSp.filters = [getGlowFilter(), getBlurFilter()];
		}
		
        private function getGlowFilter():GlowFilter {
            var color:Number = 0xFFF2D3;
            var alpha:Number = 0.85;
            var blurX:Number = 16;
            var blurY:Number = 16;
            var strength:Number = 3;
            var inner:Boolean = false;
            var knockout:Boolean = false;
            var quality:Number = BitmapFilterQuality.HIGH;

            return new GlowFilter(color,
                                  alpha,
                                  blurX,
                                  blurY,
                                  strength,
                                  quality,
                                  inner,
                                  knockout);
        }
		
        private function getBlurFilter():BlurFilter {
            var blurX:Number = 2;
            var blurY:Number = 2;
            return new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH);
        }

		
		private function update(e:Event):void 
		{
			updateDrawPoints();
			updateDraw();
			
			var r:Rectangle = currentBmd.rect;
			currentBmd.lock();
			currentBmd.fillRect(r, 0x000000);
			currentBmd.draw(penSp, new Matrix(), new ColorTransform(), null, r);
			currentBmd.unlock();
			logText.text = "POINTS:" + drawPoints.length.toString();
		}
		
		public function updateDraw():void
		{
			var g:Graphics = penSp.graphics;
			g.clear();
			var len:int = drawPoints.length;
			var drawPoint:DrawPoint;
			
			var verts:Vector.<Number> = new Vector.<Number>();
			var i:int;
			for (i = 0; i < len; i++) {
				drawPoint = drawPoints[i];
				verts.push(drawPoint.drawX, drawPoint.drawY);
			}
			if (verts.length > 8) {
				drawThickSprine(g, verts, 3);
			}
		}
		
		//参考 FrocessingではじめるActionScriptドローイング 第4回 スケッチしてみよう
		//http://gihyo.jp/design/feature/01/frocessing/0004?page=4
		private function drawThickSprine(g:Graphics, verts:Vector.<Number>, thick:Number):void
		{
			var verts0:Vector.<Number> = new Vector.<Number>();
			var verts1:Vector.<Number> = new Vector.<Number>();
			
			var i:int;
			var len:int = verts.length;
			var vx:Number, vy:Number;
			var pt:Point;
			var px0:Number = verts[0];
			var py0:Number = verts[1];
			var px1:Number, py1:Number;
			var drawPoint:DrawPoint;
			for (i = 2; i + 1 < len; i += 2) {
				px1 = verts[i];
				py1 = verts[i + 1];
				vx = px1 - px0;
				vy = py1 - py0;
				pt = new Point(vx, vy);
				pt.normalize(1);
				verts0.push(Math.floor(px0 + pt.y * thick));
				verts0.push(Math.floor(py0 - pt.x * thick));
				verts1.push(Math.floor(px0 - pt.y * thick));
				verts1.push(Math.floor(py0 + pt.x * thick));
				px0 = px1;
				py0 = py1;
			}
			
			var dVerts0:Vector.<Number> = new Vector.<Number>();
			var dVerts1:Vector.<Number> = new Vector.<Number>();
			
			//初期値
			dVerts0.push(0, 0, verts0[0], verts0[1], verts0[2], verts0[3], verts0[4], verts0[5]);
			dVerts1.push(verts1[4], verts1[5], verts1[2], verts1[3], verts1[0], verts1[1], 0, 0);
			
			//描画
			len -= 2;
			for (i = 6; i + 1 < len; i += 2) {
				dVerts0.shift();
				dVerts0.shift();
				dVerts0.push(verts0[i], verts0[i + 1]);
				dVerts1.unshift(verts1[i], verts1[i + 1]);
				dVerts1.pop();
				dVerts1.pop();
				drawPoint = drawPoints[Math.floor(i / 2)];
				g.beginFill(0xFFFEF1, drawPoint.timeFactor);
				g.moveTo(dVerts0[2], dVerts0[3]);
				splineTo(g, dVerts0, 3);
				g.lineTo(dVerts1[2], dVerts1[3]);
				splineTo(g, dVerts1, 3);
				g.lineTo(dVerts0[2], dVerts0[3]);
				g.endFill();
			}
		}
	
		//参考 Catmull-Rom スプライン曲線
		//http://l00oo.oo00l.com/blog/archives/264
		
		//verts: (x0, y0, x1, y1, x2, y2, x3, y3)
		private function splineTo(g:Graphics, verts:Vector.<Number>, numSegments:int):void
		{
			var i:int;
			var len:int = verts.length;
			var t:Number;
			for (i = 0; i < numSegments; i++) {
				t = (i + 1) / numSegments;
				g.lineTo(
					catmullRom(verts[0], verts[2], verts[4], verts[6], t),
					catmullRom(verts[1], verts[3], verts[5], verts[7], t)
				);
			}
		}
	
		private function catmullRom(p0:Number, p1:Number, p2:Number, p3:Number, t:Number):Number
		{
			var v0:Number = (p2 - p0) * 0.5;
			var v1:Number = (p3 - p1) * 0.5;
			return (2 * p1 - 2 * p2 + v0 + v1) * t * t * t + ( -3 * p1 + 3 * p2 - 2 * v0 - v1) * t * t + v0 * t + p1;
		}
		
		//時間がきたら消去
		public function updateDrawPoints():void
		{
			if (drawPoints.length < 1) {
				return;
			}
			var drawPoint:DrawPoint = drawPoints[0];
			
			while (drawPoint.nextPoint != null){
				drawPoint.aliveTime--;
				if (drawPoint.aliveTime < 1) {
					drawPoints.shift();
				}
				updateDrawPoint(drawPoint);
				drawPoint = drawPoint.nextPoint;
			}
			updateDrawPoint(drawPoint);
			drawPoint.aliveTime--;
			if (drawPoint.aliveTime < 1) {
				drawPoints.shift();
			}
		}
		
		//時間に応じてY軸回転
		private function updateDrawPoint(drawPoint:DrawPoint):void
		{
			var mat:Matrix3D = new Matrix3D();
			mat.appendRotation((1 - drawPoint.timeFactor) * 540, Vector3D.Y_AXIS);
			mat.appendTranslation(0, 0, 500);
			mat.transformVectors(Vector.<Number>([drawPoint.x, drawPoint.y, drawPoint.z]), vout);
			Utils3D.projectVectors(proj, vout, projverts, uvts);
			drawPoint.drawX = projverts[0] + hsw;
			drawPoint.drawY = projverts[1] + hsh;
		}
		
		private function mouseUpHD(e:MouseEvent):void 
		{
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHD);
		}
		
		private function mouseDownHD(e:MouseEvent):void 
		{
			stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHD);
		}
		
		private function mouseMoveHD(e:MouseEvent = null):void 
		{
			//制御点をあまりとりすぎないように間引く
			moveCount++;
			if (moveCount % 3 != 0) {
				return;
			}
			moveCount = 0;
			
			//領域を超えたら描画しない
			if (stage.mouseX > stage.stageWidth || stage.mouseY > stage.stageHeight) {
				return;
			}
			
			var drawPoint:DrawPoint = new DrawPoint(stage.mouseX -hsw, stage.mouseY -hsh);
			if (drawPoints.length > 0) {
				drawPoints[drawPoints.length - 1].nextPoint = drawPoint;
			}
			drawPoints.push(drawPoint);
		}
	}
}

import flash.display.Sprite;

class DrawPoint 
{
	public var x:Number;
	public var y:Number;
	public var z:Number;
	public var aliveTime:int = 300;
	public var nextPoint:DrawPoint;
	public var drawX:Number;
	public var drawY:Number;
	public function get timeFactor():Number 
	{
		return aliveTime / 300;
	}
	
	public function DrawPoint(x:Number, y:Number) {
		this.x = x;
		this.y = y;
		this.z = 0;
	}
}