forked from: 画像のアウトラインを一筆書きじゃない
// forked from xlune's 画像のアウトラインを一筆書き
package {
import __AS3__.vec.Vector;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.CapsStyle;
import flash.display.Graphics;
import flash.display.JointStyle;
import flash.display.LineScaleMode;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLRequest;
import flash.system.LoaderContext;
[SWF(width=465, height=465, frameRate=15, backgroundColor=0xFFFFFF)]
public class main extends Sprite
{
//画面幅
private var _width:uint = 465;
//画面高さ
private var _height:uint = 465;
//精度
private var _prec:uint = 1;
//描画スピード
private var _speed:uint = 3;
//閾値
private var _threshold:uint = 0xCC;
//ガイド画像
private var _imgUrl:String = 'http://farm4.static.flickr.com/3366/3409173374_d1a4af8142.jpg';
private var _img:Loader;
private var _guide:BitmapData;
private var _canvas:Sprite;
private var _gp:Graphics;
private var _pointMap:Vector.<Vector.<Boolean>>;
private var _pointGroups:Vector.<Vector.<Point>>;
private var _drowGroup:Vector.<Point>;
private var _basePoint:Point;
private var _closePoint:Point;
/**
* コンストラクタ
*/
public function main()
{
var request:URLRequest = new URLRequest(_imgUrl);
var context:LoaderContext = new LoaderContext(true);
_img = new Loader();
_img.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
_guide = new BitmapData(_width, _height, false, 0x00FFFFFF);
_canvas = new Sprite();
_gp = _canvas.graphics;
_gp.lineStyle(1, 0xFF0000, 1, true, LineScaleMode.NONE, CapsStyle.ROUND, JointStyle.MITER, 2);
try {
_img.load(request, context);
} catch (error:Error) {
trace("image load failed.");
}
}
/**
* Event Complete Handler
*/
private function completeHandler(event:Event):void
{
_img.x = _width / 2 - _img.width / 2;
_img.y = _height / 2 - _img.height / 2;
_img.alpha = 0.1;
var gMatrix:Matrix = new Matrix();
gMatrix.tx = _img.x;
gMatrix.ty = _img.y;
_guide.draw(_img, gMatrix);
makePoints();
addChild(_img);
addChild(_canvas);
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
/**
* Event EnterFrame Handler
*/
private function enterFrameHandler(event:Event):void
{
if(_pointGroups.length > 0 || _drowGroup.length > 0)
{
var drowPoint:Point;
if(!_drowGroup || _drowGroup.length == 0)
{
_basePoint = new Point(0, 0);
_drowGroup = Vector.<Point>(_pointGroups.shift());
_drowGroup.sort(drowPointSort);
drowPoint = Point(_drowGroup.shift());
if(_closePoint)
{
_gp.lineTo(_closePoint.x, _closePoint.y);
}
_gp.moveTo(drowPoint.x, drowPoint.y);
_closePoint = _basePoint = drowPoint;
}
else
{
_drowGroup.sort(drowPointSort);
drowPoint = Point(_drowGroup.shift());
if(Point.distance(_basePoint, drowPoint) > Math.sqrt(_prec*_prec*2))
{
if(_closePoint)
{
_gp.lineTo(_closePoint.x, _closePoint.y);
}
_gp.moveTo(drowPoint.x, drowPoint.y);
_closePoint = drowPoint;
}
else
{
_gp.lineTo(drowPoint.x, drowPoint.y);
}
_basePoint = drowPoint;
}
}
else
{
_img.visible = false;
this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
/**
* 境界点ソート
*/
private function drowPointSort(a:Point, b:Point):int
{
var numA:Number = Point.distance(_basePoint, a);
var numB:Number = Point.distance(_basePoint, b);
if(numA > numB)
{
return 1;
}
else if(numA < numB)
{
return -1;
}
return 0;
}
/**
* 境界点生成
*/
private function makePoints():void
{
var groupNum:uint = 0;
_pointMap = new Vector.<Vector.<Boolean>>(uint(_height/_prec));
_pointGroups = new Vector.<Vector.<Point>>();
for(var i:uint=0,iMax:uint=_pointMap.length; i<iMax; i++)
{
_pointMap[i] = new Vector.<Boolean>(uint(_width/_prec));
}
for(i=0,iMax=_pointMap.length; i<iMax; i++)
{
for(var j:uint=0,jMax:uint=_pointMap[i].length; j<jMax; j++)
{
if(_pointMap[i][j] != true && isFillPoint(j * _prec, i * _prec))
{
_pointGroups[groupNum] = new Vector.<Point>();
groupAgent(j, i, groupNum++);
}
}
}
}
/**
* 境界点をグループ化
*/
private function groupAgent(xNum:int, yNum:int, groupNum:uint):void
{
if(0 <= xNum && xNum < uint(_width/_prec)
&& 0 <= yNum && yNum < uint(_height/_prec))
{
if(_pointMap[yNum][xNum] != true)
{
_pointMap[yNum][xNum] = true;
if(isFillPoint(xNum * _prec, yNum * _prec)){
if(isUniquePoint(xNum * _prec, yNum * _prec))
{
_pointGroups[groupNum].push(new Point(xNum * _prec, yNum * _prec));
}
groupAgent(xNum, yNum-1, groupNum);
groupAgent(xNum+1, yNum, groupNum);
groupAgent(xNum, yNum+1, groupNum);
groupAgent(xNum-1, yNum, groupNum);
}
}
}
}
/**
* 境界点を抽出
*/
private function isUniquePoint(posX:int, posY:int):Boolean
{
if(isFillPoint(posX, posY))
{
if(!isFillPoint(posX, posY-_prec)
|| !isFillPoint(posX+_prec, posY)
|| !isFillPoint(posX, posY+_prec)
|| !isFillPoint(posX-_prec, posY))
{
return true;
}
}
return false;
}
/**
* 2値化
*/
private function isFillPoint(posX:int, posY:int):Boolean
{
if(posX < 0 || posX > _width) return false;
if(posY < 0 || posY > _height) return false;
var color:uint = _guide.getPixel(posX, posY);
color = (((color >> 16) & 0xff) + ((color >> 8) & 0xff) + (color & 0xff)) / 3;
if(color < _threshold) return true;
return false;
}
}
}