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

Summer Memories Generator

この夏、どこにも行けなかったあなたへ。
* 顔写真と行きたい場所を入力するだけで、思い出の写真をご用意します。
* I can make you a photo album of your summer vacation. If you didn't go anywhere. 
*
*
* STEP1 について
* 複数の顔を検出した場合はランダムでコラージュします。
*
* STEP2 について
* 顔写真が多くなるよう "friends" タグを追加して検索しています。
* 1文字目を "," にすることで入力したタグのみでの検索も可能です。
* 例:,azabu,festival
*
*
* 顔認識API detectFace();
* http://detectface.com/
/**
 * Copyright buccchi ( http://wonderfl.net/user/buccchi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dc0l
 */

// forked from Event's Simple MediaRSS Viewer

/*
 * この夏、どこにも行けなかったあなたへ。
 * 顔写真と行きたい場所を入力するだけで、思い出の写真をご用意します。
 * I can make you a photo album of your summer vacation. If you didn't go anywhere. 
 *
 *
 * STEP1 について
 * 複数の顔を検出した場合はランダムでコラージュします。
 *
 * STEP2 について
 * 顔写真が多くなるよう "friends" タグを追加して検索しています。
 * 1文字目を "," にすることで入力したタグのみでの検索も可能です。
 * 例:,azabu,festival
 *
 *
 * 顔認識API detectFace();
 * http://detectface.com/
 */

package {
    import flash.display.*;
    import flash.events.*;
    import flash.net.URLRequest;
    import flash.net.LocalConnection;
    import flash.geom.ColorTransform;
    import org.libspark.betweenas3.BetweenAS3;
    import org.libspark.betweenas3.tweens.ITween;
    import org.libspark.betweenas3.easing.*;
    import org.libspark.betweenas3.events.TweenEvent;
    import net.hires.debug.Stats;
    import com.bit101.components.*;
    
    [SWF(width="465", height="465", backgroundColor="#000000", frameRate="30")]
    
    public class SummerMemoriesGenerator extends Sprite {
        private var _faceSearcher:FaceSearcher;
        private var _faceList:Vector.<FaceObject>;
        private var _count:uint = 0;
        private var _imageLoader:Loader;
        private var _container:Sprite;
        private var _photoSpr:Sprite;
        private var _photoBm:Bitmap;
        private var _isCompLoad:Boolean;
        private var _isCompDetect:Boolean;
        private var _isCompShow:Boolean = true;
        private var _isFirstTime:Boolean = true;    //初回再生判定
        private var _collageList:Vector.<CollageObject>;
        private var _it:ITween;
        private var _panel:SettingPanel;
        private var _stateLabel:Label;
        
        public function SummerMemoriesGenerator() {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            addEventListener(Event.ADDED_TO_STAGE, init);
            Wonderfl.capture_delay( 10 );
        }

        private function init(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            _faceSearcher = new FaceSearcher();
            _imageLoader = new Loader();
            _container = new Sprite();
            addChild( _container );
            
            _photoSpr = new Sprite();
            _photoBm = new Bitmap();
            _container.addChild( _photoSpr );
            _photoSpr.addChild( _photoBm );
            
            _stateLabel = new Label(this);
            var colorTransform:ColorTransform = _stateLabel.transform.colorTransform;
            colorTransform.color = 0xFFFFFF;
            _stateLabel.transform.colorTransform = colorTransform;
            _stateLabel.x = _stateLabel.y = 10;
            
            _panel = new SettingPanel();
            _container.addChild( _panel );
            _panel.addEventListener(PanelEvent.COMPLETE, panelComplete);
            stage.addEventListener(Event.RESIZE, resizeHandler);
            resizeHandler(null);
        }
        private function resizeHandler(e:Event):void {
            _panel.x = Math.floor((stage.stageWidth/2 -465/2));
            _panel.y = Math.floor((stage.stageHeight/2 -465/2));
        }
        
        private function panelComplete(e:PanelEvent):void {
            e.target.removeEventListener(PanelEvent.COMPLETE, panelComplete);
            _container.removeChild(e.target as SettingPanel);
            _faceList = e.faceList;
            //
            _collageList = new Vector.<CollageObject>();
            for(var i:uint=0; i<e.urlList.length; i++){
                _collageList.push( new CollageObject(e.urlList[i]) );
            }
            _stateLabel.text = "Please wait...";
            loadImage(_count);
        }
        
        private function checkReady():void {
            if( _isFirstTime ){
                //1周目
                if(    _isCompLoad && _count==0 ){
                    _count++;
                    _isCompDetect = false;
                    _isCompLoad = false;
                    detectFace(_count-1);
                    loadImage(_count);
                }else if(( _isCompLoad && _isCompDetect && _count==1) || ( _isCompLoad && _isCompDetect && _isCompShow )){
                    _count++;
                    _isCompDetect = false;
                    _isCompLoad = false;
                    _isCompShow = false;
                    detectFace(_count-1);
                    loadImage(_count);
                    showPhoto(_count-2);
                    if(_stateLabel.visible) _stateLabel.visible = false;
                }
            }else{
                //2周目以降
                if( _collageList.length == 0){
                    _stateLabel.text = "Sorry, memorial photos were not found.";
                }else if( _isCompLoad && _isCompDetect && _isCompShow ){
                    showPhoto(_count-1);
                    if(_count < _collageList.length){
                        _count++;
                    }else{
                        _count = 1;
                    }
                }
            }
        }
        
        /* 写真読み込み */
        private function loadImage(i:uint=0):void {
            if(i < _collageList.length){
                var str:String = _collageList[i].url.replace("_s.jpg", "_z.jpg");
                _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadImageComplete);
                _imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadImageError);
                _imageLoader.load(new URLRequest( str ));
            }else{
                //一周完了
                _isCompLoad = true;
                _isFirstTime = false;
                _stateLabel.visible = false;    //画像が1枚だけだった場合用
                checkReady();
            }
        }
        
        private function loadImageComplete(e:Event):void {
            e.target.removeEventListener(Event.COMPLETE, loadImageComplete);
            e.target.removeEventListener(IOErrorEvent.IO_ERROR, loadImageError);
            if(_imageLoader.contentLoaderInfo.contentType == "image/gif"){
                //gifの場合は画像が見つからなかったと判断
                _collageList.splice(_count, 1);    //リストから削除
                loadImage();
            }else{
                _collageList[_count].content = _imageLoader.content;
                _isCompLoad = true;
                checkReady();
            }
        }
        private function loadImageError(e:IOErrorEvent):void {
            _imageLoader.unload();
        }
        
        /* 顔認識 */
        private function detectFace(i:uint):void {
            var obj:DisplayObject = _collageList[i].content;
            var bmd:BitmapData = new BitmapData(obj.width, obj.height, false, 0xFFFFFF);
            bmd.draw(obj);
            _faceSearcher.detectFace( bmd, i/*, "xml/test_face.xml"*/ );
            _faceSearcher.addEventListener(DetectEvent.SUCCESS, detectTargetFaceComplete);
        }
        
        private function detectTargetFaceComplete(e:DetectEvent):void{
            _faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectTargetFaceComplete);
            _collageList[e.collageObjIndex].bmd = e.bmd;
            _collageList[e.collageObjIndex].xmlObj = e.xmlObj;
            _isCompDetect = true;
            checkReady();
        }
        
        /* 写真フェード */
        private function showPhoto(i:uint):void {
            var resObj:Object = CollageMaker.createCollage( _collageList[i].bmd, _collageList[i].xmlObj, _faceList );
            _photoBm.bitmapData = resObj.collageBmd;
            _photoBm.smoothing = true;
            //拡大の中心を _photoSpr の原点へ移動
            _photoBm.x = -resObj.pivotObj.x;
            _photoBm.y = -resObj.pivotObj.y;
            //
            var stageW:Number = stage.stageWidth;
            var stageH:Number = stage.stageHeight;
            var scale:Number = Math.max( stageW/_photoBm.width, stageH/_photoBm.height );
            _photoSpr.scaleX = _photoSpr.scaleY = scale;
            _photoSpr.x = stageW/2 - _photoSpr.width/2 - _photoBm.x*scale;
            _photoSpr.y = stageH/2 - _photoSpr.height/2 - _photoBm.y*scale;
            //
            var zoom:Number = 1.3;
            var goalX:Number = _photoSpr.x;
            var goalY:Number = _photoSpr.y;
            var pivotX:Number = resObj.pivotObj.x;
            var pivotY:Number = resObj.pivotObj.y;
            var faceW:Number = resObj.pivotObj.w;
            var faceH:Number = resObj.pivotObj.h;
            var limL:Number = (_photoSpr.width-stageW)/2 + faceW/2;
            var limR:Number = (_photoSpr.width-stageW)/2 + stageW - faceW/2;
            var limT:Number = (_photoSpr.height-stageH)/2 + faceH/2;
            var limB:Number = (_photoSpr.height-stageH)/2 + stageH - faceH/2;
            if( pivotX*scale < limL ){
                //左にはみ出している場合
                goalX -= (pivotX*scale - limL);
            }else if( pivotX*scale > limR ){
                //右にはみ出している場合
                goalX -= (pivotX*scale - limR);
            }
            if( pivotY*scale < limT ){
                //上にはみ出している場合
                goalY -= (pivotY*scale - limT);
            }else if( pivotY*scale > limB ){
                //下にはみ出している場合
                goalY -= (pivotY*scale - limB);
            }
            if( _it != null ) _it.stop();
            _it =    BetweenAS3.delay(
                        BetweenAS3.parallel(
                            BetweenAS3.serial(
                                BetweenAS3.tween( _photoSpr, {alpha:1}, {alpha:0}, 3, Linear.linear),
                                BetweenAS3.delay(BetweenAS3.tween( _photoSpr, {alpha:0}, {alpha:1}, 3, Linear.linear), 6)
                            ),
                            BetweenAS3.tween( _photoSpr, {x:goalX, y:goalY, scaleX:scale*zoom, scaleY:scale*zoom}, null, 12, Linear.linear)
                        ),
                    .8);
            _it.addEventListener(TweenEvent.COMPLETE, tweenComplete);
            _it.play();
        }
        private function tweenComplete(e:TweenEvent):void {
            _it.removeEventListener(TweenEvent.COMPLETE, tweenComplete);
            _isCompShow = true;
            checkReady();
        }
    }
}


import flash.display.*;
import flash.events.*;
import flash.net.*;
import com.bit101.components.*;
import flash.geom.Matrix;
import flash.geom.ColorTransform;
import flash.system.LoaderContext;

class SettingPanel extends Sprite {
    private var _fileReference:FileReference;
    private var _faceSearcher:FaceSearcher;
    private var _step1State:Label;    //STEP1の状態を表示
    private var _step2State:Label;    //STEP2の状態を表示
    private var _inputText:InputText;
    private var _rssReader:MediaRSSReader;
    private var _urlList:Array;
    private var _faceBm:Bitmap;
    private var _thumbBm:Bitmap;
    private var _isReadyStep1:Boolean;
    private var _isReadyStep2:Boolean;
    private var _playBtn:PushButton;
    private var _dummyPlayBtn:Sprite;
    private var _checkMark1:Sprite;
    private var _checkMark2:Sprite;
    private var _faceList:Vector.<FaceObject>;
    
    public function SettingPanel() {
        _faceSearcher = new FaceSearcher();
        _rssReader = new MediaRSSReader();
        
        var url:String = "http://assets.wonderfl.net/images/related_images/6/64/6401/640135614df9dab6c1be318fedbd181c38ed2d74";
        var loader:Loader = new Loader();
        loader.load(new URLRequest(url), new LoaderContext(true));
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
    }
    
    private function loadComplete(e:Event):void {
        e.target.removeEventListener(Event.COMPLETE, loadComplete);
        
        var bm:Bitmap = Bitmap(e.target.content);
        addChild( bm );
        
        var s:Shape = new Shape();
        s.graphics.beginFill(0xF3F3F3, 1);
        s.graphics.drawRect(8, 95, 449, 13);
        s.graphics.drawRect(8, 286, 449, 13);
        addChild( s );

        _faceBm = new Bitmap( new BitmapData(50, 50, true, 0x0) );
        _faceBm.x = 10;
        _faceBm.y = 143;
        addChild( _faceBm );
        
        _thumbBm = new Bitmap( new BitmapData(435, 50, true, 0x0) );
        _thumbBm.x = 10;
        _thumbBm.y = 334;
        addChild( _thumbBm );
        
        _fileReference = new FileReference();
        //step1
        var expText1:Label = new Label(this, 10, 92, "Up-load the photograph of your face.");
        changeColor( expText1, 0x000000 );
        var loadImageBtn:PushButton = new PushButton(this, 10, 113, "Load Image", clickLoadImageBtn);
        var webCamBtn:PushButton = new PushButton(this, 115, 113, "Web cam", clickWebCam);
        _checkMark1 = drawCheckMark(14, 201);
        _checkMark1.alpha = .2;
        addChild(_checkMark1);
        _step1State = new Label(this, 38, 200, "-");
        _step1State.alpha = .2;
        //step2
        var expText2:Label = new Label(this, 10, 283, "Input the word that is where you wanted to go in this summer. ");
        changeColor( expText2, 0x000000 );        
        _inputText = new InputText(this, 10, 304, "kamakura");
        var searchBtn:PushButton = new PushButton(this, 218, 304, "Search", clickSearchBtn);
        _inputText.setSize(205, 20);
        _checkMark2 = drawCheckMark(14, 392);
        _checkMark2.alpha = .2;
        addChild(_checkMark2);
        _step2State = new Label(this, 38, 391, "-");
        _step2State.alpha = .2;
        //slideshow
        _playBtn = new PushButton(this, 352, 432, "Play slideshow!", clickPlayBtn);
        _dummyPlayBtn = new Sprite();
        _dummyPlayBtn.graphics.beginFill (0xF3F3F3, .7);
        _dummyPlayBtn.graphics.drawRect(0, 0, _playBtn.width, _playBtn.height);
        _dummyPlayBtn.x = _playBtn.x;
        _dummyPlayBtn.y = _playBtn.y;
        addChild(_dummyPlayBtn);
    }
    
    private function set isReadyStep1(v:Boolean):void {
        _isReadyStep1 = v;
        checkReady();
    }
    private function set isReadyStep2(v:Boolean):void {
        _isReadyStep2 = v;
        checkReady();
    }
    
    /******************************************************/
    /* step1 */
    private function clickLoadImageBtn(e:MouseEvent):void {
        _fileReference.addEventListener(Event.SELECT, selectHandler);
        var fileFilter:FileFilter = new FileFilter("Images", "*.jpg;*.jpeg;*.gif;*.png");
        _fileReference.browse([fileFilter]);
        isReadyStep1 = false;
    }
    private function selectHandler(e:Event):void {
        _fileReference.removeEventListener(Event.SELECT, selectHandler);
        if(_fileReference.size <= 1048576*2){
            _fileReference.addEventListener(Event.COMPLETE, completeHandler);
            _fileReference.load();
        }else{
            //容量オーバー
            _step1State.text = "The capacity of the image is too large...";
            _step1State.alpha = 1;
            changeColor(_step1State, 0xFF0000);
        }
    }
    private function completeHandler(e:Event):void {
        _fileReference.removeEventListener(Event.COMPLETE, completeHandler);
        var ld:Loader = new Loader();
        ld.contentLoaderInfo.addEventListener(Event.INIT, loadCompleteHandler);
        ld.loadBytes(_fileReference.data);
    }
    
    //ローカルイメージ読み込み完了
    private function loadCompleteHandler(e:Event):void {
        e.target.removeEventListener(Event.INIT, loadCompleteHandler);
        //
        var bm:Bitmap = e.target.content as Bitmap;
        bm.smoothing = true;
        var max:Number = Math.min(bm.width, bm.height);
        var scale:Number = 50 / max;
        var mtx:Matrix = new Matrix();
        mtx.translate( (max-bm.width)/2, (max-bm.height)/2 );
        mtx.scale(scale, scale);
        _faceBm.bitmapData.draw(bm, mtx, null, null, null, true);
        startDetectFace( bm.bitmapData );
    }
    
    //webカメラ
    private function clickWebCam(e:Event):void {
        var webCamPanel:WebCamPanel = new WebCamPanel();
        webCamPanel.addEventListener( WebCamEvent.COMPLETE, shotComplete);
        webCamPanel.addEventListener( WebCamEvent.CANCEL, shotCancel);
        addChild( webCamPanel );
    }
    
    private function shotComplete(e:WebCamEvent):void {
        e.target.removeEventListener( WebCamEvent.COMPLETE, shotComplete);
        e.target.removeEventListener( WebCamEvent.CANCEL, shotCancel);
        var scale:Number = 50/e.bmd.width;
        var mtx:Matrix = new Matrix();
        mtx.scale(scale, scale);
        _faceBm.bitmapData.draw(e.bmd, mtx, null, null, null, true);
        startDetectFace( e.bmd );
    }
    private function shotCancel(e:WebCamEvent):void {
        e.target.removeEventListener( WebCamEvent.COMPLETE, shotComplete);
        e.target.removeEventListener( WebCamEvent.CANCEL, shotCancel);
    }
    
    //顔認識
    private function startDetectFace( bmd:BitmapData ):void {
        _faceSearcher.detectFace( bmd, 0/*, "xml/miyazaki_face.xml"*/ );
        _faceSearcher.addEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
        _faceSearcher.addEventListener(DetectEvent.ERROR, detectBaseFaceError);
        _step1State.text = "Recognizing photos on process...";
        _checkMark1.alpha = .2;
        _step1State.alpha = 1;
        changeColor(_step1State, 0x666666);
    }
    private function detectBaseFaceError(e:DetectEvent):void{
        _faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
        _faceSearcher.removeEventListener(DetectEvent.ERROR, detectBaseFaceError);
        //エラー
        _step1State.text = "Couldn't recognize face!";
        changeColor(_step1State, 0xFF0000);
    }
    private function detectBaseFaceComplete(e:DetectEvent):void{
        _faceSearcher.removeEventListener(DetectEvent.SUCCESS, detectBaseFaceComplete);
        _faceSearcher.removeEventListener(DetectEvent.ERROR, detectBaseFaceError);
        _faceList = CollageMaker.clipFace( e.bmd, e.xmlObj );
        //認識成功
        if( _faceList.length == 0){
            _step1State.text = "Couldn't recognize face!";
            changeColor(_step1State, 0xFF0000);
        }else{
            isReadyStep1 = true;
            _step1State.text = (_faceList.length==1)? "Recognized 1 face." : "Recognized "+_faceList.length+" faces.";
            _checkMark1.alpha = 1;
        }
    }
    
    /******************************************************/
    /* step2 */
    private function clickSearchBtn(e:Event):void {
        _rssReader.addEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
        _rssReader.addEventListener(RSSReadEvent.ERROR, feedLoadError);
        var tags:String = _inputText.text;
        var tagList:Array = tags.split(",");
        if(tagList[0]!="") tags = tags + ",friends";
        _rssReader.loadFeed( tags );
        _step2State.text = "Searching for photos...";
        _checkMark2.alpha = 1;
        _step2State.alpha = 1;
        _checkMark2.alpha = .2;
        changeColor(_step2State, 0x666666);
        isReadyStep2 = false;
        _thumbBm.bitmapData.dispose();
        _thumbBm.bitmapData = new BitmapData(435, 50, true, 0x0);
    }
    
    private function feedLoadComplete(e:RSSReadEvent):void {
        _rssReader.removeEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
        _rssReader.removeEventListener(RSSReadEvent.ERROR, feedLoadError);
        
        _urlList = e.urlList;
        _step2State.text = "Find "+_urlList.length+" photos."
        var len:Number = Math.min(_urlList.length, 8);
        for(var i:uint=0; i<len; ++i) {
            var ldr:Loader;
            ldr = new Loader;
            ldr.x = i;    //通し番号として使用
            ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoadComplete);
            ldr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
            ldr.load( new URLRequest(_urlList[i]), new LoaderContext(true));
        }
        _checkMark2.alpha = 1;
        isReadyStep2 = true;
    }
    private function feedLoadError(e:RSSReadEvent):void{
        _rssReader.removeEventListener(RSSReadEvent.SUCCESS, feedLoadComplete);
        _rssReader.removeEventListener(RSSReadEvent.ERROR, feedLoadError);
        //エラー
        _step2State.text = "No photos found.";
        changeColor(_step2State, 0xFF0000);
    }
    private function thumbLoadComplete(e:Event):void {
        e.target.removeEventListener(Event.COMPLETE, thumbLoadComplete);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
        var bm:Bitmap = e.target.content as Bitmap;
        bm.smoothing = true;
        var max:Number = Math.min(bm.width, bm.height);
        var scale:Number = 50 / max;
        var mtx:Matrix = new Matrix();
        mtx.translate( (max-bm.width)/2, (max-bm.height)/2 );
        mtx.scale(scale, scale);
        mtx.translate( (55*e.target.loader.x), 0 );
        _thumbBm.bitmapData.draw(bm, mtx, null, null, null, true);
    }
    private function thumbIoError(e:IOErrorEvent):void {
        e.target.removeEventListener(Event.COMPLETE, thumbLoadComplete);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,thumbIoError);
    }
    
    /******************************************************/
    
    private function clickPlayBtn(e:Event):void {
        dispatchEvent( new PanelEvent(PanelEvent.COMPLETE, _faceList, _urlList) );
    }
    
    /******************************************************/
    
    private function checkReady():void {
        if(_isReadyStep1 && _isReadyStep2){
            _dummyPlayBtn.visible = false;
        }else{
            _dummyPlayBtn.visible = true;
        }
    }
    
    /******************************************************/
    
    private function changeColor(obj:DisplayObject, color:Number):void {
        var colorTransform:ColorTransform = obj.transform.colorTransform;
        colorTransform.color = color;
        obj.transform.colorTransform = colorTransform;
    }
    
    private function drawCheckMark(myX:Number=0, myY:Number=0):Sprite {
        var spr:Sprite = new Sprite();
        spr.graphics.beginFill (0x999999, 1);
        spr.graphics.moveTo(0, 8);
        spr.graphics.lineTo (8, 16);
        spr.graphics.lineTo (21, 3);
        spr.graphics.lineTo (18, 0);
        spr.graphics.lineTo (8, 10);
        spr.graphics.lineTo (3, 5);
        spr.graphics.endFill();
        spr.x = myX;
        spr.y = myY;
        return spr;
    }
}


import flash.events.Event;

class PanelEvent extends Event {
    public static const COMPLETE:String="complete";
    public var faceList:Vector.<FaceObject>;
    public var urlList:Array;
    
    function PanelEvent(type:String, myFaceList:Vector.<FaceObject>, myUrlList:Array) {
        super(type);
        faceList = myFaceList;
        urlList = myUrlList;
    }
    public override function clone():Event {
        return new PanelEvent(type, faceList, urlList);
    }
    public override function toString():String {
        return formatToString("PanelEvent","type","bubbles","cancelable","eventPhase","faceList","urlList");
    }
}
    

import flash.display.*;
import flash.media.Camera;
import flash.media.Video;
import flash.events.Event;
import flash.geom.Matrix;
import com.bit101.components.*;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.ITween;
import org.libspark.betweenas3.events.TweenEvent;
    
class WebCamPanel extends Sprite {
    private var _captureContainer:Sprite;
    private var _uiContainer:Sprite;
    private var _camera:Camera;
    private var _video:Video;
    private var _photo:Sprite;
    private var _bmd:BitmapData;
    
    public function WebCamPanel() {
        _captureContainer = new Sprite();
        _uiContainer = new Sprite();
        _photo = new Sprite();
        addChild( _captureContainer );
        addChild( _uiContainer );
        _captureContainer.graphics.beginFill(0xD8D8D8, 1);
        _captureContainer.graphics.drawRect(0, 0, 465, 465);
        _captureContainer.graphics.beginFill(0xF3F3F3, 1);
        _captureContainer.graphics.drawRect(1, 1, 463, 463);
        _captureContainer.graphics.beginFill(0xCCCCCC, 1);
        _captureContainer.graphics.drawRect(16, 16, 432, 432);
        //
        _uiContainer.graphics.lineStyle(1, 0x000000, .2);
        _uiContainer.graphics.drawEllipse(130, 95, 207, 237);
        _uiContainer.graphics.moveTo(130, 214);
        _uiContainer.graphics.lineTo(130+207, 214);
        _uiContainer.graphics.moveTo(233, 95);
        _uiContainer.graphics.lineTo(233, 95+237);
        
        _camera = Camera.getCamera();
        _camera.setMode(445, 445, 10, false);
        _video = new Video(_camera.width, _camera.height);
        _video.attachCamera(_camera);
        _video.scaleX = -1;
        var margin:Number = Math.floor( (465-_camera.width)/2 );
        _video.x = _camera.width + margin;
        _video.y = margin;
        _captureContainer.addChild(_video);
        var shotBtn:PushButton = new PushButton(_uiContainer, 182, 380, "Shot!", clickShotBtn);
        var closeBtn:PushButton = new PushButton(_uiContainer, 465-margin-21-5, margin+5, "X", clickCloseBtn);
        closeBtn.setSize(21, 20);
    }
    
    private function clickShotBtn(e:Event):void {
        _video.attachCamera(null);    //カメラを停止
        
        _bmd = new BitmapData(465, 465, false, 0x000000);
        _bmd.draw(_captureContainer);
        var bm:Bitmap = new Bitmap( _bmd );
        bm.smoothing = true;
        _photo.addChild( bm );
        addChild( _photo );
        var w:Number = _photo.width;
        var pivot:Number = Math.random()*w;
        var lr:Number = (pivot > w/2)? 1 : -1;
        var rot:Number = Math.random()*60*lr;
        _photo.x = pivot;
        bm.x = -pivot;
        var t:ITween =    BetweenAS3.parallel(
                            BetweenAS3.tween(bm, {transform: {colorTransform: {redOffset: 0, greenOffset: 0, blueOffset: 0}}},
                                             {transform: {colorTransform: {redOffset: 255, greenOffset: 255, blueOffset: 255}}},
                                             1, Sine.easeOut),
                            BetweenAS3.delay( BetweenAS3.tween(_photo, {y:stage.stageHeight+bm.height, rotation:rot }, null, 1, Quart.easeIn), 2)
                        );
        t.addEventListener(TweenEvent.COMPLETE, photoOutComplete);
        t.play();
        dispatchEvent( new WebCamEvent(WebCamEvent.COMPLETE, _bmd.clone()) );
        //
        removeChild( _uiContainer );
        removeChild( _captureContainer );
    }
    
    private function photoOutComplete(e:TweenEvent):void {
        removeChild( _photo );
    }
    
    private function clickCloseBtn(e:Event):void {
        _video.attachCamera(null);    //カメラを停止
        parent.removeChild(this);
        dispatchEvent( new WebCamEvent(WebCamEvent.CANCEL) );
    }
}


import flash.events.Event;
import flash.display.BitmapData;

class WebCamEvent extends Event {
    public static const COMPLETE:String = "complete";
    //public static const SLIDE_COMPLETE = "slideComplete";
    public static const CANCEL:String = "cancel";
    public var bmd:BitmapData;
    
    
    function WebCamEvent(type:String, myBmd:BitmapData=null) {
        super(type);
        bmd = myBmd;
    }
    public override function clone():Event {
        return new WebCamEvent(type, bmd);
    }
    public override function toString():String {
        return formatToString("WebCamEvent","type","bubbles","cancelable","eventPhase","bmd");
    }
}


import flash.display.*;
import flash.events.*;
import com.adobe.images.JPGEncoder;
import flash.utils.ByteArray;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLRequestMethod;

class FaceSearcher extends EventDispatcher {
    private var _jpgEncoder:JPGEncoder;
    private var _bmd:BitmapData;
    private var _collageObjIndex:uint;
    
    public function FaceSearcher() {
        _jpgEncoder = new JPGEncoder( 80 );
    }
    
    public function detectFace( bmd:BitmapData, collageObjIndex:uint=0, localTestUrl:String=null ):void{
        _bmd = bmd;
        _collageObjIndex = collageObjIndex;
        var ba:ByteArray = _jpgEncoder.encode( _bmd );
        var req:URLRequest = new URLRequest();
        req.data = ba;
        req.url = "http://detectface.com/api/detect";
        req.method = URLRequestMethod.POST;
        req.contentType = "image/jpeg";
        var urlLoader:URLLoader = new URLLoader();
        urlLoader.addEventListener(Event.COMPLETE,onCompleteHandler);
        urlLoader.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        if(localTestUrl != null){
            urlLoader.load(new URLRequest( localTestUrl ));
        }else{
            urlLoader.load(req);
        }
    }
    
    private function onCompleteHandler(e:Event):void {
        e.target.removeEventListener(Event.COMPLETE,onCompleteHandler);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        if (e.target.data == "error") {
            dispatchEvent( new DetectEvent(DetectEvent.ERROR) );
        } else {
            //認識成功
            dispatchEvent( new DetectEvent(DetectEvent.SUCCESS, _bmd, e.target.data, _collageObjIndex) );
        }
    }
    private function ioErrorHandler(e:IOErrorEvent):void {
        e.target.removeEventListener(Event.COMPLETE,onCompleteHandler);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        dispatchEvent( new DetectEvent(DetectEvent.ERROR) );
    }
}


import flash.events.Event;
import flash.display.BitmapData;

class DetectEvent extends Event {
    public static const SUCCESS:String="success";
    public static const ERROR:String = "error";
    public var bmd:BitmapData;
    public var xmlObj:Object;
    public var collageObjIndex:uint;
    
    function DetectEvent(type:String, myBmd:BitmapData=null, myXmlObj:Object=null, myCollageObjIndex:uint=0) {
        super(type);
        bmd = myBmd;
        xmlObj = myXmlObj;
        collageObjIndex = myCollageObjIndex;
    }
    public override function clone():Event {
        return new DetectEvent(type, bmd, xmlObj, collageObjIndex);
    }
    public override function toString():String {
        return formatToString("DetectEvent","type","bubbles","cancelable","eventPhase","bmd","xmlObj", "collageObjIndex");
    }
}


import flash.net.URLRequest;
import flash.events.*;
import flash.net.URLLoader;

class MediaRSSReader extends EventDispatcher {
    private var media:Namespace = new Namespace("http://search.yahoo.com/mrss/");
    
    public function MediaRSSReader() {}
    
    public function loadFeed( tag:String ):void {
        var feed:String = "http://api.flickr.com/services/feeds/photos_public.gne?tags="+ tag +"&format=rss_200";
        var ldr:URLLoader = new URLLoader;
        ldr.addEventListener(Event.COMPLETE, loadComplete);
        ldr.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        ldr.load(new URLRequest(feed));
    }
    
    private function loadComplete(e:Event):void {
        e.target.removeEventListener(Event.COMPLETE, loadComplete);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        var imageUriList:Array =  XML(e.target.data)..media::thumbnail.@url.toXMLString().split('\n');
        if(imageUriList[0] != ""){
            dispatchEvent( new RSSReadEvent(RSSReadEvent.SUCCESS, imageUriList) );
        }else{
            dispatchEvent( new RSSReadEvent(RSSReadEvent.ERROR) );
        }
    }
    
    private function ioErrorHandler(e:IOErrorEvent):void {
        e.target.removeEventListener(Event.COMPLETE, loadComplete);
        e.target.removeEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
        dispatchEvent( new RSSReadEvent(RSSReadEvent.ERROR) );
    }
}


import flash.events.Event;

class RSSReadEvent extends Event {
    public static const SUCCESS:String="success";
    public static const ERROR:String = "error";
    public var urlList:Array;
    
    function RSSReadEvent(type:String, myList:Array=null) {
        super(type);
        urlList = myList;
    }
    public override function clone():Event {
        return new RSSReadEvent(type, urlList);
    }
    public override function toString():String {
        return formatToString("RSSReadEvent","type","bubbles","cancelable","eventPhase","urlList");
    }
}


import flash.display.*;
import flash.geom.*;
import com.adobe.protocols.dict.events.MatchEvent;

class CollageMaker {
    private static const MARGIN:Number = 1;
    private static const CIR_MARGIN:Number = 1;
    
    /* 顔を切り抜き */
    public static function clipFace(myBmd:BitmapData, myXmlObj:Object):Vector.<FaceObject> {
        var faceList:Vector.<FaceObject> = new Vector.<FaceObject>();
        var xml:XML = XML(myXmlObj);
        var bmd:BitmapData = myBmd;
        
        var len:int = xml.face.length();
        for(var i:uint=0; i<len; i++){
            if(xml.face[i].features != undefined){
                var ptList:Vector.<Point> = new Vector.<Point>();
                var isStart:Boolean = true;
                var oldX:Number;
                var oldY:Number;
                var featuresXl:XMLList = xml.face[i].features;
                for(var pId:uint=1; pId<=10; pId++){
                    var xl:XMLList = featuresXl.point.(hasOwnProperty("@id") && @id == "F"+pId);
                    ptList.push( new Point(xl.@x, xl.@y) );
                }
                var lastPt:Point = ptList[ptList.length-1];
                //マスク用に面を描画
                var spr:Sprite = new Sprite();
                spr.graphics.beginFill(0xFF0000, 1);
                spr.graphics.moveTo(lastPt.x+(ptList[0].x-lastPt.x)/2, lastPt.y+(ptList[0].y-lastPt.y)/2);
                for(var j:uint=0; j<ptList.length-1; j++){
                    var anchorX:Number = ptList[j].x+(ptList[j+1].x-ptList[j].x)/2;
                    var anchorY:Number = ptList[j].y+(ptList[j+1].y-ptList[j].y)/2;
                    spr.graphics.curveTo(ptList[j].x, ptList[j].y, anchorX, anchorY);
                }
                anchorX = lastPt.x+(ptList[0].x-lastPt.x)/2;
                anchorY = lastPt.y+(ptList[0].y-lastPt.y)/2;
                spr.graphics.curveTo(lastPt.x, lastPt.y, anchorX, anchorY);
                spr.graphics.endFill();
                var maskBmd:BitmapData = new BitmapData(bmd.width, bmd.height, true, 0x00000000);
                maskBmd.draw(spr);
                var diffRect:Rectangle = maskBmd.getColorBoundsRect(0xFFFFFFFF, 0x00000000, false);
                var faceBmd:BitmapData = new BitmapData(diffRect.width+MARGIN*2, diffRect.height+MARGIN*2, true, 0x00000000);
                faceBmd.copyPixels(bmd, diffRect, new Point(MARGIN, MARGIN), maskBmd, new Point(diffRect.x, diffRect.y));
                //両目の位置からいろいろ算出
                var resObj:Object = getRevision( xml.face[i] );
                //
                /* 詳細まで認識した場合用 */
                var faceSpr:Sprite = new Sprite();
                var faceBm:Bitmap = new Bitmap( faceBmd );
                faceBm.smoothing = true;
                //両目の中点を原点に移動
                faceBm.rotation = -resObj.rad * (180/Math.PI);
                //n度回転させた後のずれを算出
                var faceDiffX:Number = diffRect.x-resObj.diffX -MARGIN;
                var faceDiffY:Number = diffRect.y-resObj.diffY -MARGIN;
                faceBm.x = Math.cos(-resObj.rad)*faceDiffX-Math.sin(-resObj.rad)*faceDiffY;
                faceBm.y = Math.cos(-resObj.rad)*faceDiffY+Math.sin(-resObj.rad)*faceDiffX;
                faceSpr.addChild( faceBm );
                //
                /* 大まかな位置しか認識されなかった場合用 */
                var roughSpr:Sprite = new Sprite();
                var roughBm:Bitmap = new Bitmap( faceBmd );
                roughBm.smoothing = true;
                roughBm.rotation = -resObj.rad * (180/Math.PI);
                var roughX:Number = diffRect.x-resObj.diffX-MARGIN;
                var roughY:Number = -spr.height/2-MARGIN;
                roughBm.x = Math.cos(-resObj.rad)*roughX-Math.sin(-resObj.rad)*roughY;
                roughBm.y = Math.cos(-resObj.rad)*roughY+Math.sin(-resObj.rad)*roughX;
                roughSpr.addChild( roughBm );
                //
                /* 顔が見つからなかった場合用 */
                var cirW:Number = diffRect.height/.73;
                var cirH:Number = cirW*1.3;
                var cirMaskSpr:Sprite = new Sprite();
                cirMaskSpr.graphics.beginFill(0xFF0000, 1);
                cirMaskSpr.graphics.drawEllipse(1, 1, cirW-2, cirH-2);
                var mar:Number  = 1;
                var cirMaskBmd:BitmapData = new BitmapData(cirW, cirH, true, 0x00000000);
                cirMaskBmd.draw( cirMaskSpr );
                //
                var cirBaseBmd:BitmapData = new BitmapData(cirW, cirH, false, 0xF3F3F3);
                var diffBaseX:Number = -diffRect.x + (cirW-diffRect.width)/2;
                var diffBaseY:Number = -diffRect.y + (cirH-diffRect.height)/2 - cirH*.05;
                cirBaseBmd.draw(bmd, new Matrix(1, 0, 0, 1, diffBaseX, diffBaseY), null, null, null, true);
                //
                var cirFaceBmd:BitmapData = new BitmapData(cirW+CIR_MARGIN*2, cirH+CIR_MARGIN*2, true, 0x00000000);
                cirFaceBmd.copyPixels(cirBaseBmd, new Rectangle(0, 0, cirW, cirH), new Point(CIR_MARGIN, CIR_MARGIN), cirMaskBmd, new Point(0, 0));
                //
                var cirFaceSpr:Sprite = new Sprite();
                var cirBm:Bitmap = new Bitmap(cirFaceBmd/*cirMaskBmd*/);
                cirBm.smoothing = true;
                cirBm.x = -cirW/2 - CIR_MARGIN;
                cirBm.y = -cirH/2 - CIR_MARGIN;
                cirFaceSpr.addChild( cirBm );
                //
                faceList.push( new FaceObject( faceSpr, roughSpr, cirFaceSpr, resObj.eyeDist) );
            }
        }
        return faceList;
    }
    
    /* 顔をコラージュ */
    public static function createCollage(myBmd:BitmapData, myXmlObj:Object, faceList:Vector.<FaceObject>):Object {
        var rate:Number = 1.5; //倍率(コラージュする写真の画質向上)
        
        var resObj:Object = new Object();
        var xml:XML = XML(myXmlObj);
        var bmd:BitmapData = myBmd;
        var collageBmd:BitmapData = new BitmapData(bmd.width*rate, bmd.height*rate, false, 0xFFFFFF);
        var pivotList:Array = new Array();
        //まずは元イメージを描画
        collageBmd.draw( bmd, new Matrix(rate, 0, 0, rate, 0, 0), null, null, null, true);
        //
        var scale:Number;
        var mtr:Matrix;
        var faceObj:FaceObject;
        
        //xmlをパースして見つかった顔の数だけコラージュ
        var len:int = xml.face.length();
        if(len == 0){
            //1つも認識出来なかった場合
            for(var k:uint=0; k<faceList.length; k++){
                faceObj = faceList[k];
                var cirSpr:Sprite = faceObj.circleSpr;
                var minSize:Number = Math.min(collageBmd.width, collageBmd.height);
                scale = minSize*.15 / cirSpr.height;
                var margin:Number = cirSpr.width*scale/8;
                mtr = new Matrix();
                mtr.scale(scale, scale);
                mtr.translate(cirSpr.width*scale/2 + margin*3 + ((cirSpr.width*scale)+margin)*k, cirSpr.height*scale/2 + margin*3);
                //顔を描画
                collageBmd.draw(cirSpr, mtr, null, null, null, true);
            }
            pivotList.push( {w:0, h:0, x:0, y:0});
        }else if(xml.face.features.length() != 0){
            //詳細まで認識されている顔がある場合、その顔のみ置き換え
            for(var i:uint=0; i<len; i++){
                if(xml.face[i].features != undefined){
                    //コラージュする顔をランダム選択
                    faceObj = faceList[ Math.floor(Math.random()*faceList.length) ];
                    var faceSpr:Sprite = faceObj.detailSpr;
                    //両目の位置からいろいろ算出
                    var res:Object = CollageMaker.getRevision( xml.face[i] );
                    scale = res.eyeDist / faceObj.eyeDist * rate;
                    mtr = new Matrix();
                    mtr.rotate( res.rad );
                    mtr.scale(scale, scale);
                    mtr.translate(res.diffX*rate, res.diffY*rate);
                    //顔を描画
                    collageBmd.draw(faceSpr, mtr, null, null, null, true);
                    pivotList.push( {w:faceSpr.width*scale, h:faceSpr.height*scale, x:res.diffX*rate, y:res.diffY*rate} );
                }
            }
        }else{
            //大まかな位置しか認識されなかった場合、認識したすべての顔を置き換え
            for(var j:uint=0; j<len; j++){
                //コラージュする顔をランダム選択
                faceObj = faceList[ Math.floor(Math.random()*faceList.length) ];
                var roughSpr:Sprite = faceObj.roughSpr;
                //
                scale = xml.face[j].bounds.@height / (roughSpr.height-MARGIN*2) *rate;
                var myX:Number = Number(xml.face[j].bounds.@x) + (xml.face[j].bounds.@width)/2;
                var myY:Number = Number(xml.face[j].bounds.@y) + (xml.face[j].bounds.@height)/2;
                mtr = new Matrix();
                mtr.scale(scale, scale);
                mtr.translate(myX*rate, myY*rate);
                //顔を描画
                collageBmd.draw(roughSpr, mtr, null, null, null, true);
                pivotList.push( {w:roughSpr.width*scale, h:roughSpr.height*scale, x:myX*rate, y:myY*rate} );
            }
        }
        resObj.collageBmd = collageBmd;
        resObj.pivotObj = pivotList[ Math.floor(Math.random()*pivotList.length) ];
        return resObj;
    }
    
    /**
     * 両目の位置からいろいろ算出
     * eyeDist:    両目の距離
     * rad:        両目の角度(ラジアン)
     */
    public static function getRevision( myXl:XML ):Object {
        var resObj:Object = new Object();
        var xl:XMLList;
        xl = myXl.features..point.(hasOwnProperty("@id") && @id == "PR");
        var ptR:Point = new Point( xl.@x, xl.@y );
        xl = myXl.features..point.(hasOwnProperty("@id") && @id == "PL");
        var ptL:Point = new Point( xl.@x, xl.@y );
        var dx:Number = ptR.x-ptL.x;
        var dy:Number = ptR.y-ptL.y;
        var vector:Point = ptL.subtract(ptR);
        resObj.eyeDist = Math.sqrt(dx*dx + dy*dy);
        resObj.diffX = ptR.x - dx/2;
        resObj.diffY = ptR.y - dy/2;
        resObj.rad = Math.atan2(vector.y, vector.x);
        return resObj;
    }
}


import flash.display.Sprite;
import flash.geom.Point;
import flash.display.Bitmap;

class FaceObject {
    private var _faceSpr:Sprite;    //切り抜いた顔
    private var _roughSpr:Sprite;
    private var _circleSpr:Sprite;
    public var eyeDist:Number;    //両目の距離
    
    public function FaceObject(myFaceSpr:Sprite, myRoughSpr:Sprite, myCircleSpr:Sprite, myEyeDist:Number) {
        _faceSpr = myFaceSpr;
        _roughSpr = myRoughSpr;
        _circleSpr = myCircleSpr;
        eyeDist = myEyeDist;
    }
    public function get detailSpr():Sprite {
        _faceSpr.scaleX = _faceSpr.scaleY = 1;
        _faceSpr.x = _faceSpr.y = 0;
        return _faceSpr;
    }
    public function get roughSpr():Sprite {
        _roughSpr.scaleX = _roughSpr.scaleY = 1;
        _roughSpr.x = _roughSpr.y = 0;
        return _roughSpr;
    }
    public function get circleSpr():Sprite {
        _circleSpr.scaleX = _circleSpr.scaleY = 1;
        _circleSpr.x = _circleSpr.y = 0;
        return _circleSpr;
    }
}


import flash.display.BitmapData;
import flash.display.DisplayObject;
    
class CollageObject {
    public var url:String;
    public var bmd:BitmapData;    //元写真
    public var xmlObj:Object;    //顔の位置情報
    public var content:DisplayObject;
    public function CollageObject( myUrl:String ) {
        url = myUrl;
    }
}