Beautifl - Flash Gallery

Preview

RealCircle -速く、大きく、正確に正円を書く-
sakusan393 2010年6月15日 MIT License
?
一筆書きでいかに正円に近い円を書けるかを競います。  
得点には、正確さだけではなく、速さと、大きさも影響します。  
より速く、より大きく、より正確に正円がかけると得点があがります。  
100点満点で、理論上は満点とるのは可能ですが、まず無理でしょう。。。  
  
・・・正円判定はかなりざっくりです。  
  
ちなみにこれはiPhoneアプリネタを考えてたときに浮かんだのですが、  
すでに似たようなものがあったので、断念したというお話でした。。  
  
勢いで書いたのでソース汚いです。。。  
 
2010/06/15:追記  
理論上100点越えるケースがあったので、越えないようにしました。  
理論上はガンバレば100点だせる、はずw
      /*
 * 一筆書きでいかに正円に近い円を書けるかを競います。
 * 得点には、正確さだけではなく、速さと、大きさも影響します。
 * より速く、より大きく、より正確に正円がかけると得点があがります。
 * 100点満点で、理論上は満点とるのは可能ですが、まず無理でしょう。。。
 * 
 * ・・・正円判定はかなりざっくりです。
 * 
 * ちなみにこれはiPhoneアプリネタを考えてたときに浮かんだのですが、
 * すでに似たようなものがあったので、断念したというお話でした。。
 * 
 * 勢いで書いたのでソース汚いです。。。
 *
 * 2010/06/15:追記
 * 理論上100点越えるケースがあったので、越えないようにしました。
 * 理論上はガンバレば100点だせる、はずw
 * */

package {
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.net.navigateToURL;
    import flash.net.URLRequest;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.utils.escapeMultiByte;
    import flash.utils.getTimer;
    
    [SWF(backgroundColor="0x808080", frameRate="120", width="320", height="480")]
    /**
     * ...
     * @author 393
     */
    public class RealCircle extends Sprite{
        private var _graphic:Graphics;
        private var _bmd:BitmapData;
        private var _bm:Bitmap;
        private var _realBmd:BitmapData;
        private var _realBm:Bitmap;
        private var _shape:Shape = new Shape();
        private var _sGraphic:Graphics;
        private var _centerPoint:Point;
        private var _pointArray:Array = [];
        private var _pointsLength:int;
        private var _realPointArray:Array = [];
        private var _realPointsLength:int;
        private var _radiusArray:Array = [];
        private var _pointDistance:Number = 100000;
        private var _pointDistanceAverage:int;
        
        //評価パラメータ
        private var _drawSpeed:Number = 0;
        private var _drawSize:int = 0;
        private var _drawAccuracy:int = 0;
        
        private var _generalTf:TextField = new TextField();
        private var _accuracyTf:TextField = new TextField();
        private var _speedTf:TextField = new TextField();
        private var _sizeTf:TextField = new TextField();
        private var _infoTf:TextField = new TextField();
        
        //定数
        private const SPEED_MAX:Number = 10;
        private const SPEED_MIN:Number = 1;
        private const SPEED_POINT:Number = 100;
        
        private const SIZE_MAX:Number = 155;
        private const SIZE_MIN:Number = 10;
        private const SIZE_POINT:Number = 300;
        
        private const ACCURACY_MAX:Number = 20;
        private const ACCURACY_MIN:Number = 0;
        private const ACCURACY_POINT:Number = 100;
        
        private const LINE_COLOR:int = 0xFFFF00;
        private const REAL_LINE_COLOR:int = 0xFF0000;
        
        private var _retryButton:PushButton;
        private var _postButton:PushButton;
        private var _totalScoreStr:String;
        private var _info:String  = "正円を書いてください。\n速く、大きく、そして正確に";
        
        private var _isNotCircle:Boolean;
        
        public function RealCircle() {
            addEventListener(Event.ADDED_TO_STAGE, init);
        }
        //初期化処理
        private function init(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            //ステージエリアの描画
            stage.addChild(new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0)));
            //ステージエリアを一番下に置く
            stage.setChildIndex(stage.getChildAt(stage.numChildren-1),0)
            
            //評価用(円を転写)
            _bmd = new BitmapData(stage.stageWidth, stage.stageHeight,false,0x000000);
            _bm = new Bitmap(_bmd);
            //正円用(円を転写)
            _realBmd = new BitmapData(stage.stageWidth, stage.stageHeight,true,0x00000000);
            _realBm = new Bitmap(_realBmd);
            
            //正円を描画用
            _sGraphic = _shape.graphics;
            //円を描画用
            _graphic = this.graphics;
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            
            //リトライ用のボタン
            _retryButton = new PushButton(stage, 50, stage.stageHeight - 100, "retry", onMouseClick);
            _retryButton.visible = false;
            
            //Twitter投稿用のボタン
            _postButton = new PushButton(stage, 160, stage.stageHeight - 100, "post Twitter", postTwitter);
            _postButton.visible = false;
            
            //infoテキストを表示
            stage.addChild(setTf(_infoTf, _info, 10, 10, 24));
        }
        //マウスダウン時の処理
        private function mouseDownHandler(e:MouseEvent):void {
            _drawSpeed = getTimer();
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            _graphic.lineStyle(2, LINE_COLOR);
            _graphic.moveTo(mouseX, mouseY);
            addEventListener(Event.ENTER_FRAME, entarFrameHandler);
        }
        //円を描画
        private function entarFrameHandler(e:Event):void {
            _graphic.lineTo(mouseX, mouseY);
        }
        
        //マウスアップ処理
        private function mouseUpHandler(e:MouseEvent):void {
            //描画時間をセット
            _drawSpeed = (getTimer() - _drawSpeed) / 1000;
            //描画終了
            _graphic.endFill();
            removeEventListener(Event.ENTER_FRAME, entarFrameHandler);
            //イベントの破棄
            stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            //書いた円をbitmapDataに転写
            _bmd.draw(this);
            //graphicに書いた円はクリア
            _graphic.clear();
            //bitmapDataを表示
            addChild(_bm);
            //正円用のshapeを表示
            addChild(_shape);
            
            
            //書いた円の座標ポイントを取得
            setCircleMatrix();
            //理想の正円の中心点を取得
            checkCenterPoint();
            //正円の描画
            drawTrueCircle();
            //正確さを判定
            checkAcuracy();
            //結果を表示
            drawResult();
        }
        //書いた円の座標を配列に追加
        private function setCircleMatrix():void {
            var i:int;
            var j:int;
            for (i = 0; i < stage.stageHeight; i+=2) {
                for (j = 0; j < stage.stageWidth; j+=2) {
                    if (_bmd.getPixel(j, i) == LINE_COLOR) {
                        _pointArray.push(new Point(j, i));
                    }
                }
            }
            _pointsLength = _pointArray.length;
        }
        //書いた円の中心座標を計算
        private function checkCenterPoint():void {
            if (_pointArray.length <= 0) return;
            var i:int;
            var maxX:int = 0;
            var minX:int = stage.stageWidth;
            
            //Y座標の最大値を取得
            var maxY:int = _pointArray[_pointsLength - 1].y;
            //Y座標の最小値を取得
            
            var minY:int = _pointArray[0].y;            
            for (i = 0; i < _pointsLength; i++) {
                //X座標の最大値を取得
                if (maxX < _pointArray[i].x) maxX = _pointArray[i].x;
                //X座標の最小値を取得
                if (minX > _pointArray[i].x) minX = _pointArray[i].x;
            }
            //正円の中心座標
            _centerPoint = new Point((maxX - minX) / 2 + minX, (maxY - minY) / 2 + minY);
        }
        //理想の正円を描画
        private function drawTrueCircle():void {
            var radiusSum:Number = 0;
            var i:int;
            for (i = 0; i < _pointsLength; i++) {
                //各ポイントの半径を取得
                _radiusArray.push(Point.distance(_pointArray[i], _centerPoint));
                radiusSum += _radiusArray[i];
            }
            //平均値を正円の半径とする
            var radiusAverage:Number = radiusSum / _pointsLength;
            _drawSize = radiusAverage;
            //円っぽくなってるか判定
            var halfLength:int = _pointsLength / 2;
            var checkDistanceX:Number;
            var checkDistanceY:Number;
            _isNotCircle = true;
            
            //十分にポイントが無かったら終了
            if (_pointsLength < 50) return;
            
            for (i = 0; i < 10; i++) {
                checkDistanceX = Math.abs(_pointArray[halfLength].x - _pointArray[halfLength + i].x);
                checkDistanceY = Math.abs(_pointArray[halfLength].y - _pointArray[halfLength + i].y);
                if (checkDistanceX > _drawSize * 0.8 && checkDistanceY < 10) {
                    _isNotCircle = false;
                    break;
                }
            }
            
            //グラフィックに正円を描画
            _sGraphic.lineStyle(2, REAL_LINE_COLOR);
            _sGraphic.drawCircle(_centerPoint.x, _centerPoint.y, _drawSize);
            _sGraphic.endFill();
            //ビットマップに転写
            if(!_isNotCircle) _realBmd.draw(_shape);
            addChild(_realBm);
            //グラフィックをクリア
            _sGraphic.clear();
            
            //正円の座標を取得
            var j:int;
            for (i = 0; i < stage.stageHeight; i+=2) {
                for (j = 0; j < stage.stageWidth; j+=2) {
                    if (_realBmd.getPixel(j, i) == 0x00FF0000) {
                        _realPointArray.push(new Point(j, i));
                    }
                }
            }
            _realPointsLength = _realPointArray.length;
        }
        //円の正確さを判定
        private function checkAcuracy():void {
            var i:int;
            var j:int;
            var distanceMax:int;
            var distanceSum:int;
            for (i = 0; i < _pointsLength; i++) {
                var point:Point = _pointArray[i];
                var pointDistanceMin:int = 1000;
                for (j = 0; j < _realPointsLength; j++) {
                    var pointDistance:int = Point.distance(point, _realPointArray[j]);
                    if (pointDistanceMin > pointDistance) {
                        pointDistanceMin = pointDistance;
                    }
                }
                distanceSum += pointDistanceMin;
                if(distanceMax <  pointDistanceMin) distanceMax = pointDistanceMin;
            }
            _drawAccuracy = distanceMax;
        }
        //判定結果を表示
        private function drawResult():void {
            //infoを消す
            _infoTf.text = "";
            
            if (!_isNotCircle) {
                //速度
                var speedStr:String = "draw speed : " + _drawSpeed + " sec";
                //サイズ
                var sizeStr:String = "draw size : " + _drawSize + " px";
                //正確さ
                var accuracyStr:String = "draw accuracy : " + _drawAccuracy  +" px";
            }else {
                speedStr = "";
                sizeStr = "";
                accuracyStr = "";
            }
            addChild(setTf(_sizeTf, sizeStr, 0, 20));
            addChild(setTf(_speedTf, speedStr, 0, 0));
            addChild(setTf(_accuracyTf, accuracyStr, 0, 40));
            
            //総合評価(100点からの減点法)
            var totalPoint:Number = 100;
            //速度
            var speed:Number = _drawSpeed / (Math.pow(SPEED_MAX,2) / SPEED_POINT) - SPEED_MIN * SPEED_MIN;
            if (speed > 20) speed = 20;
            //大きさ
            var size:Number = (Math.pow(SIZE_MAX - _drawSize, 4)) / (Math.pow(SIZE_MAX,4) / SIZE_POINT);
            if (size > 40) size = 40;
            //正確さ
            var accuracy:Number = _drawAccuracy / (Math.pow(ACCURACY_MAX,2) / ACCURACY_POINT);
            if (accuracy > 40) accuracy = 40;
            //合計点
            if (!_isNotCircle) {
                var totalScore:Number = xRoundDown(totalPoint - speed - size - accuracy, 2);
                if (totalScore >= 100) totalScore = 100;
                _totalScoreStr = String(totalScore);
                var totalStf:String = "score : " + totalScore;
                _postButton.visible = true;
            }else {
                totalStf = "";
                _infoTf.text = "もうすこし円っぽく・・・";
            }
            _retryButton.visible = true;
            addChild(setTf(_generalTf , totalStf, 160, 20,24));
        }
        private function xRoundDown(nValue:Number, nDigits:int):Number {
          var nMultiplier:Number = Math.pow(10, nDigits);
          var nResult:Number = Math.ceil(nValue * nMultiplier) / nMultiplier;
          return nResult;
        } 
        private function setTf(tf:TextField,str:String,posX:int = 0, posY:int = 0,size:int = 12):TextField {
            var tfm:TextFormat = new TextFormat();
            tfm.color = 0xFFFFFF;
            tfm.size = size;
            tfm.font = "_明朝";
            tf.defaultTextFormat = tfm;
            
            tf.text = str;
            tf.selectable = false;
            tf.mouseEnabled = false;
            tf.autoSize = "left";
            tf.x = posX;
            tf.y = posY;
            return tf;
        }
        
        
        //retryクリック時の処理
        private function onMouseClick(e:MouseEvent):void {
            _bmd.fillRect(_bmd.rect, 0x000000);
            _realBmd.fillRect(_realBmd.rect, 0x00000000);
            if(_bm.parent) removeChild(_bm);
            if(_realBm.parent) removeChild(_realBm);
            _accuracyTf.text = "";
            _generalTf.text = "";
            _sizeTf.text = "";
            _speedTf.text = "";
            removeChild(_accuracyTf);
            removeChild(_generalTf);
            removeChild(_speedTf);
            removeChild(_sizeTf);
            
            _pointArray = [];
            _pointsLength = 0;
            _realPointArray = [];
            _realPointsLength = 0;
            _radiusArray = [];
            
            _retryButton.visible = false;
            _postButton.visible = false;
            
            _infoTf.text = _info;
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        }
        
        //Twitterへ結果をポスト
        private function postTwitter(e:MouseEvent):void {
            var comment:String = "あなたの描いた正円は" + _totalScoreStr + "点です [RealCircle] ";
            var post:String = comment + " " + "http://wonderfl.net/c/kagi" + " " + "#wonderfl";
            navigateToURL(new URLRequest("http://twitter.com/home?status=" + escapeMultiByte(post)), "_blank");
        }
        
    }

}