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

Zoetrope Generator

ローカルPCのFLV、MP4からゾエトロープ的なものを生成します。
ファイルサイズは最大20MB、ファイル名マルチバイト文字NGです。
サンプルFLV http://310design.org/zoetrope/zoetrope_sample.zip
マウスのX座標によって回転速度が変わります。
DebugCamera3Dを使ってるので、マウス、キーボード操作で視点を動かせます。
@author Yukio Sato (310design.)
/**
 * Copyright 310design ( http://wonderfl.net/user/310design )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/zdXg
 */

package {
    import flash.media.SoundTransform;
    import flash.events.DataEvent;
    import flash.utils.Timer;
    import flash.net.FileFilter;

    import caurina.transitions.Tweener;

    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Cylinder;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.Viewport3D;

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.MouseEvent;
    import flash.events.NetStatusEvent;
    import flash.events.SecurityErrorEvent;
    import flash.events.TimerEvent;
    import flash.geom.Matrix;
    import flash.media.Video;
    import flash.net.FileReference;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.URLRequest;
    import flash.net.URLRequestMethod;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;

    /**
     * ローカルPCのFLV、MP4からゾエトロープ的なものを生成します。
     * ファイルサイズは最大20MB、ファイル名マルチバイト文字NGです。
     * サンプルFLV http://310design.org/zoetrope/zoetrope_sample.zip
     * マウスのX座標によって回転速度が変わります。
     * DebugCamera3Dを使ってるので、マウス、キーボード操作で視点を動かせます。
     * @author Yukio Sato (310design.)
     */
    [SWF(backgroundColor = 0x000000, frameRate = 30)]
    public class ZoetropeGenerator extends Sprite {
        private var camera:DebugCamera3DCustomized;
        private var scene:Scene3D = new Scene3D();
        private var viewport:Viewport3D = new Viewport3D(640, 480, true);
        private var renderer:BasicRenderEngine = new BasicRenderEngine();
        private var file:FileReference;
        private var netstream:NetStream;
        private var connection:NetConnection;
        private var video:Video;
        private var bmdCollection:Vector.<BitmapData> = new Vector.<BitmapData>;
        private var cylinder:DisplayObject3D;
        private var textField:TextField;
        public var vx:Number = 0;
        private static const RADIUS:Number = 800;
        private static const PIC_MAX:uint = 60;
        //private static const UPLOAD_URL:String = "http://localhost/data/";
        private static const UPLOAD_URL:String = "http://310design.org/zoetrope/";

        public function ZoetropeGenerator() {
            //stage.scaleMode = StageScaleMode.NO_SCALE;
            //stage.align = StageAlign.TOP_LEFT;

            var format:TextFormat = new TextFormat();
            format.size = 14;
            format.align = TextFormatAlign.CENTER;
            
            textField = new TextField();
            textField.autoSize = TextFieldAutoSize.CENTER;
            textField.selectable = false;
            textField.textColor = 0xffffff;
            textField.width = stage.stageWidth;
            textField.defaultTextFormat = format;
            textField.text = "Click and select FLV or MP4 on your computer.\r(Max 20MB and the file name must not include multibyte character.)";
            textField.x = (stage.stageWidth - textField.width) * 0.5;
            textField.y = (stage.stageHeight - textField.height) * 0.5;
            addChild(textField);

            file = new FileReference();
            file.addEventListener(Event.SELECT, onSelectFile);
            file.addEventListener(Event.COMPLETE, onUploadFile);
            file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadCompleteDataHandler);
            
            stage.addEventListener(MouseEvent.CLICK, browseFile);
        }

        private function browseFile(event:MouseEvent):void {
            try {
                var fileFilter:FileFilter = new FileFilter("*.flv,*.f4v,*.mp4,*.m4v", "*.flv;*.f4v;*.mp4;*.m4v");
                file.browse([fileFilter]);
            }catch(e:Error) {
                trace(e.message);
            }
        }

        private function onSelectFile(event:Event):void {
            stage.removeEventListener(MouseEvent.CLICK, browseFile);
            var request:URLRequest = new URLRequest(UPLOAD_URL + "upload.php");
            request.method = URLRequestMethod.POST;
            file.upload(request);
            textField.text = "Uploading file...";
        }

        private function onError():void {
            textField.text = "Fail to upload data. Try again.";
            stage.addEventListener(MouseEvent.CLICK, browseFile);
        }

        private function ioErrorHandler(event:IOErrorEvent):void {
            trace(event.text);
            onError();
        }

        private function securityErrorHandler(event:SecurityErrorEvent):void {
            trace(event.text);
            onError();
        }

        private function netStatusHandler(e:NetStatusEvent):void {
            trace(e.info.code);
            if(e.info.code == "NetStream.Play.StreamNotFound") {
                onError();
            }
        }

        private function uploadCompleteDataHandler(event:DataEvent):void {
            trace(event.data);
        }

        private function onUploadFile(event:Event):void {
            connection = new NetConnection();
            connection.connect(null);
            
            netstream = new NetStream(connection);
            netstream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            netstream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            var transform:SoundTransform = netstream.soundTransform;
            transform.volume = 0;
            netstream.soundTransform = transform;
            
            var customClient:Object = new Object();
            netstream.client = customClient;
            customClient.onMetaData = onMetaData;
            
            video = new Video();
            video.attachNetStream(netstream);
            //addChild(video);

            netstream.play(UPLOAD_URL + "upfiles/" + file.name);
            textField.text = "Generating your zoetrope...";
        }

        private function onMetaData(param:Object):void {
            video.width = param.width;
            video.height = param.height;            
            
            var framerate:Number = param.framerate ? param.framerate : param.videoframerate;
            if(isNaN(framerate)) framerate = stage.frameRate;
            var count:uint = Math.floor(Math.min(param.duration * framerate, PIC_MAX));
            var timer:Timer = new Timer(1000 / framerate, count);
            timer.addEventListener(TimerEvent.TIMER, onTimer);
            timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
            timer.start();
        }

        private function onTimer(event:TimerEvent):void {
            var bmd:BitmapData = new BitmapData(video.width, video.height, false, 0);
            var matrix:Matrix = new Matrix();
            matrix.scale(video.width / 320, video.height / 240);
            bmd.draw(video, matrix, null, null, null, true);
            bmdCollection.push(bmd);
        }

        private function onTimerComplete(event:TimerEvent):void {
            netstream.close();
            var bmd360:BitmapData = new BitmapData(unitWidth * bmdCollection.length, unitHeight);
            for (var i:int = 0;i < bmdCollection.length;i++) {
                var source:BitmapData = bmdCollection[i];
                var matrix:Matrix = new Matrix();
                var scale:Number;
                if(source.width / source.height > 3 / 4) {
                    scale = unitHeight / source.height;
                    matrix.scale(scale, scale);
                    matrix.translate((unitWidth - source.width * scale) * 0.5, 0);
                } else {
                    scale = unitWidth / source.width;
                    matrix.scale(scale, scale);
                    matrix.translate(0, (unitHeight - source.height * scale) * 0.5);
                }
                var bmd:BitmapData = new BitmapData(unitWidth + 2, unitHeight);
                bmd.draw(source, matrix, null, null, null, true);
                
                var matrix2:Matrix = new Matrix();
                matrix2.translate(unitWidth * i, 0);
                bmd360.draw(bmd, matrix2, null, null, null, true);
            }
            createZoetrope(bmd360);
            removeChild(textField);
        }

        private function createZoetrope(bmpData:BitmapData):void {
            camera = new DebugCamera3DCustomized(viewport);
            camera.z = -RADIUS - unitHeight * 1.8 - 50;
            camera.y = unitHeight * 0.7;
            camera.forceRotationX(-camera.z * 1.2 / camera.y);
            camera.focus = 20;
            addChild(viewport);
            
            var bmpMaterial:BitmapMaterial = new BitmapMaterial(bmpData);
            bmpMaterial.smooth = true;
            bmpMaterial.doubleSided = true;
            cylinder = new Cylinder(bmpMaterial, RADIUS, unitHeight, bmdCollection.length, 8, RADIUS, false, false);
            scene.addChild(cylinder);
            startRendering();
            stage.addEventListener(MouseEvent.MOUSE_MOVE, startMove);
        }

        private function startMove(event:MouseEvent):void {
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, startMove);
            addEventListener(Event.ENTER_FRAME, enterFrame);
        }

        private function enterFrame(event:Event):void {
            var maxVX:Number = 360 / bmdCollection.length * 1.5;
            var targetVX:Number;
            
            if(Math.abs(mouseX - stage.stageWidth * 0.5) < 50) {
                targetVX = 0;
            } else if(mouseX > stage.stageWidth / 2) {
                targetVX = (mouseX - stage.stageWidth * 0.5 - 50) / (stage.stageWidth * 0.5 - 50) * maxVX;
            } else {
                targetVX = (mouseX - stage.stageWidth * 0.5 + 50) / (stage.stageWidth * 0.5 - 50) * maxVX;
            }
            Tweener.addTween(this, {vx:targetVX, time:1, transition:"easeOutCubic", onUpdate:rotateCylinder});    
        }

        public function rotateCylinder():void {
            cylinder.rotationY += vx;
        }

        public function startRendering():void {
            addEventListener(Event.ENTER_FRAME, onRenderTick);
            viewport.containerSprite.cacheAsBitmap = false;
        }

        protected function onRenderTick(event:Event = null):void {
            renderer.renderScene(scene, camera, viewport);
        }

        public function get unitWidth():Number {
            //return RADIUS * 2 * Math.sin(Math.PI / bmdCollection.length);
            return RADIUS * 2 * Math.PI / bmdCollection.length;
        }

        public function get unitHeight():Number {
            return Math.floor(unitWidth * 4 / 3);
        }
    }
}

import org.papervision3d.cameras.DebugCamera3D;
import org.papervision3d.view.Viewport3D;

class DebugCamera3DCustomized extends DebugCamera3D {
    public function DebugCamera3DCustomized(viewport3D:Viewport3D, fovY:Number = 90, near:Number = 10, far:Number = 5000) {
        super(viewport3D, fovY, near, far);
    }

    public function forceRotationX(rot:Number):void {
        startRotationX = targetRotationX = super.rotationX = rot;
    }
}