SvgConvertTest
package{
import flash.display.*;
import flash.text.*;
import flash.geom.Point;
[SWF(backgroundColor="#ffffff")]
public class EasySvgConvert extends Sprite{
private var canvas:Sprite;
private var source:TextField;
public function EasySvgConvert(){
stage.scaleMode ="noScale";
stage.align = "TL";
canvas = new Sprite();
canvas.x = 100; canvas.y = 300;
addChild(canvas);
source = new TextField();
source.x = 300;
source.width = 200;
source.height = 250;
source.multiline = source.border = true;
addChild(source);
var button:Button = new Button("convert");
button.y = 260;
addChild(button);
button.addEventListener("click", function(event:*):void{
//try{
convert(tf.text);
//}catch(e:Error){
// tf.text = e.toString();
//}
});
var tf:TextField = new TextField();
tf.multiline = tf.border = true;
tf.width = 250;
tf.height = 250;
tf.type = "input";
tf.text = defaultSvg.replace(/\r\n/g, "\n");;
addChild(tf);
}
private function convert(svg:String):void{
canvas.graphics.clear();
while(canvas.numChildren) canvas.removeChildAt(0);
var converter:EasySvgConverter = new EasySvgConverter();
source.text = "";
var ret:String = "";
for each(var path:String in svg.match(/ d="[^"]+"/g)){
path = path.substr(0, path.length - 1).substr(4);
converter.convert(path);
var points:Array = converter.getArray();
var convex:Array = converter.calcConvexHull(points);
draw(points, convex);
ret += getText(points, convex);
}
source.text = "{'areas': [\n" + ret + "]}";
}
private function draw(points:Array, convex:Array):void{
canvas.graphics.beginFill(0x0, .2);
canvas.graphics.lineStyle(1, 0x0, .4);
var moveFlag:Boolean = false;
for each(var pt:Point in points){
if(isNaN(pt.x) || isNaN(pt.y)){
moveFlag = true;
continue;
}
if(moveFlag){
canvas.graphics.moveTo(pt.x, pt.y);
moveFlag = false;
}else{
canvas.graphics.lineTo(pt.x, pt.y);
}
}
canvas.graphics.endFill();
moveFlag = true;
canvas.graphics.lineStyle(1, 0x3399ff);
for each(pt in convex){
if(moveFlag){
canvas.graphics.moveTo(pt.x, pt.y);
moveFlag = false;
}
else{
canvas.graphics.lineTo(pt.x, pt.y);
canvas.addChild(new Circle(pt.x, pt.y));
}
}
}
private function getText(points:Array, convex:Array):String{
convex = convex.reverse();
var ret:String = "{'convex': [";
for each(var pt:Point in convex){
ret += pt.x + "," + pt.y + ",";
}
ret += "], 'pt': [";
for each(pt in points){
ret += pt.x + "," + pt.y + ",";
}
ret += "]},\n";
return ret;
}
}
}
import flash.display.*;
import flash.geom.*;
import flash.text.*;
class EasySvgConverter{
private var svg:String;
private var pos:int;
private var path:Array;
public function convert(_svg:String):void{
svg = _svg;
pos = 0;
path = [];
var token:String;
while((token = getToken()) != ""){
var isUpper:Boolean = (token == token.toUpperCase());
switch(token.toLowerCase()){
case "m":
path.push(new SvgMove(
parseFloat(getToken()), parseFloat(getToken()),
isUpper
));
break;
case "c":
path.push(new SvgBezier(
parseFloat(getToken()), parseFloat(getToken()),
parseFloat(getToken()), parseFloat(getToken()),
parseFloat(getToken()), parseFloat(getToken()),
isUpper
));
break;
case "s":
path.push(new SvgBezier(
0, 0,
parseFloat(getToken()), parseFloat(getToken()),
parseFloat(getToken()), parseFloat(getToken()),
isUpper
));
break;
case "h":
path.push(new SvgLineTo(parseFloat(getToken()), NaN, isUpper));
break;
case "v":
path.push(new SvgLineTo(NaN, parseFloat(getToken()), isUpper));
break;
case "l":
path.push(new SvgLineTo(parseFloat(getToken()), parseFloat(getToken()), isUpper));
break;
case "z":
// assume that start point and end point are equal
break;
default:
throw new Error("not implemented: " + token);
}
}
}
private function getToken():String{
var sub:String = svg.substr(pos);
var matches:Array;
if((matches = sub.match(/^[ \t,\r\n]+/))){
pos += matches[0].length;
sub = sub.substr(matches[0].length);
}
if(sub.match(/^[a-z]/i)){
pos++;
return sub.charAt(0);
}
if((matches = sub.match(/^-?\d*(?:\.\d*)?/))){
pos += matches[0].length;
return matches[0];
}
return "";
}
public function getArray():Array{
var ret:Array = [];
var pt:Point = new Point();
for each(var obj:ISvgDrawable in path){
obj.addArray(ret, pt);
}
return ret;
}
public function calcConvexHull(points:Array):Array{
var ret:Array = [];
// 開始ポイント(Y座標が最大のもの)を求める
var maxY:Number = -Infinity;
var start:Point;
for each(var pt:Point in points){
if(maxY < pt.y){
maxY = pt.y;
start = pt;
}
}
ret.push(start);
// ...
var cur:Point = start;
var vecX:Point = new Point(1, 0);
while(true){
var maxCos:Number = -2;
var nextPt:Point;
for each(pt in points){
if(pt == cur) continue;
// 内積を使って cosθ を求める
var cos:Number = (pt.x - cur.x) * vecX.x + (pt.y - cur.y) * vecX.y;
cos /= Point.distance(pt, cur);
if(cos > maxCos){
maxCos = cos;
nextPt = pt;
}
}
if(start == nextPt) break;
ret.push(nextPt);
vecX = nextPt.subtract(cur);
vecX.normalize(1);
cur = nextPt;
}
return ret;
}
}
interface ISvgDrawable{
function addArray(arr:Array, pt:Point):void;
}
function roundPoint(pt:Point):void{
pt.x = Math.round(pt.x * 10000) / 10000;
pt.y = Math.round(pt.y * 10000) / 10000;
}
class SvgMove implements ISvgDrawable{
public var x:Number;
public var y:Number;
public var absolute:Boolean;
public function SvgMove(_x:Number, _y:Number, _absolute:Boolean){
x = _x;
y = _y;
absolute = _absolute;
}
public function addArray(arr:Array, pt:Point):void{
if(absolute){
pt.x = x; pt.y = y;
}else{
pt.x += x; pt.y += y;
}
roundPoint(pt);
arr.push(new Point(NaN, NaN)); // notify 'moveTo'
arr.push(pt.clone());
}
}
// only draws a line from p1 to p4.
class SvgBezier implements ISvgDrawable{
public var p2x:Number;
public var p2y:Number;
public var p3x:Number;
public var p3y:Number;
public var p4x:Number;
public var p4y:Number;
public var absolute:Boolean;
public function SvgBezier(_p2x:Number, _p2y:Number, _p3x:Number, _p3y:Number, _p4x:Number, _p4y:Number, _absolute:Boolean){
p2x = _p2x; p2y = _p2y;
p3x = _p3x; p3y = _p3y;
p4x = _p4x; p4y = _p4y;
absolute = _absolute;
}
public function addArray(arr:Array, pt:Point):void{
if(absolute){
pt.x = p4x; pt.y = p4y;
}else{
pt.x += p4x; pt.y += p4y;
}
roundPoint(pt);
arr.push(pt.clone());
}
}
class SvgLineTo implements ISvgDrawable{
public var x:Number;
public var y:Number;
public var absolute:Boolean;
public function SvgLineTo(_px:Number, _py:Number, _absolute:Boolean){
x = _px; y = _py;
absolute = _absolute;
}
public function addArray(arr:Array, pt:Point):void{
if(absolute){
if(!isNaN(x)) pt.x = x;
if(!isNaN(y)) pt.y = y;
}else{
if(!isNaN(x)) pt.x += x;
if(!isNaN(y)) pt.y += y;
}
roundPoint(pt);
arr.push(pt.clone());
}
}
class Circle extends Sprite{
public function Circle(x:Number, y:Number){
graphics.beginFill(0x3399ff);
graphics.drawCircle(x, y, 2);
graphics.endFill();
}
}
class Button extends Sprite{
public function Button(label:String){
graphics.beginFill(0xcccccc);
graphics.drawRect(0, 0, 50, 30);
graphics.endFill();
buttonMode = useHandCursor = true;
mouseChildren = false;
var tf:TextField = new TextField();
tf.text = label;
addChild(tf);
}
}
// Hokkaido svg
var defaultSvg:String = '<path i:knockout="Off" fill="#333333" d="M339.326,445.156c0,0-7.5,3.25-10.5-1.5c0,0-1.5-3.125,0-6.375c0,0,1.833-2.375,2.354-4.25c0,0,0.854-7,0-9.375\n'
+ 'c0,0-2.729-0.375-4.729,0c0,0-1.125-1.375-1.25-7.5c0,0,0-3,0.75-8.25c0,0-0.5-2-2.875-4.5c0,0-4.75-3.375-9.875-7\n'
+ 'c0,0-8.875-5.75-11.75-10.75h-3.625c0,0,0.25-2.25-1.75-6.125c0,0-2.125-0.375-5.125,0c0,0,0.875,0.75-5.375,10.125\n'
+'c0,0-7.375,9.125-12.25,10.5c0,0-2.375,4.625-6.875,9.625c0,0-0.375,3.5-2.375,9c0,0-1.25,2.125-3.375,3.625c0,0-2,5.125-2.125,11\n'
+'c0,0.75-2,0-2.875,1.625c0,0-1.875-2-2.75-1.75c0,0-0.625-2.625-2.25-2.75c0,0-1.625,1.125-1.875,5.125c0,0,0.875,0.75,0.625,4.25\n'
+ 'c0,0-0.125,4.75,0,7.875c0,0,0.5,1.25-0.625,3.5c0,0-1.625,1.125-3.625,0.625c0,0-1.375,1.006-2.875,0.128c0,0-2.75,1.196-3.5-0.628\n'
+ 'c0,0-3,0.875-3.25-0.875c0,0-2.75,1-2.625-1.5c0,0,2.125-4.25,4.625-10c0,0,1.25-1.875,1.125-4c0,0-0.75-2.125,1.125-3.625\n'
+ 'c0,0,1-2.5,1.375-5.125c0,0,0.375,0-1.625-1.375c0,0-0.875,0.75,1-5c0,0,1.625-3.5,2.625-8.875c0,0,2.375-8.125,4.5-12.25\n'
+ 'c0,0-1.625-5.75,4.625-9c0,0,4.75-7.75,7.5-13.5c0,0,4.625-10.875,8.375-15.375c0,0,3.75-5.25,5.875-7.125c0,0-0.5-2.5-1.25-7.625\n'
+ 'c0,0-0.5-6.75-1.375-9.75c0,0,0.625-1,0.25-5c0,0-3.375-6-2.5-10c0,0-1.875-0.75-1.625-5.625c0,0,0.625-4,3.875-8.125\n'
+ 'c0,0,0.75-0.375,3.875-4c0,0,2.625-2.125,6.75-2.375c0,0,1.5-0.5,3.75,0l1.875-1.25h1.75l2.75-1.125c0,0,8.125-0.25,8.625,0\n'
+ 's2.75-0.111,2.75-0.111s0.5-1.889,1.5-3.889l1-3.75c0,0,2.375-3.375,5.125-6c0,0,5.75-2.625,9.875-1.25c0,0,6.75,1.875,8.75,4.75\n'
+ 'c0,0-0.167,2.237,1.917,1.487c0,0-0.326,1.03-1.076,1.266c0,0,0.659-0.378-0.466,0.247c0,0,0.675,0.795,2.25,0.875\n'
+ 'c0,0-1.83,0.601-2.875,1.875c0,0-0.625,0.75-0.25,3l-0.125,2l-1.125,1c0,0-0.125,2.25,0.5,4l-1.375,1.5c0,0,1.625-0.375,2,1.875\n'
+ 'c0,0,0.5,1.375,3,1.125c0,0,3.75,0.75,5.625,3c0,0,1.25,1.375,3.375,2.625c0,0,1.5,2.25,2.875,2.5c0,0-0.125,1.25,1.125,2.75\n'
+ 'c0,0-1.125,1.5-3.125,1.875c0,0,0.875,0.875,0,4.875c0,0,0,0.625,3.125-0.75c0,0,3,0.125,4.625,1.125c0,0,1.25,1.875,3.875,4.75\n'
+ 'c0,0,2.125,3.375,2.75,5.75v3.75l-2.875,2l-2.125,1.125c0,0,0.125,2.25-1.75,4.25l-2.25,1.5c0,0-0.125,0-1.25,3\n'
+ 'c0,0,0.375,1.25-3.125,1.375c0,0-4-0.125-5.875-4.5c0,0-2.875-1.125-4.625-4.75l-0.75-3.25l-2.75-3l-1.125-3.375l-3.625,0.125\n'
+ 'c0,0,0.125,1.75-1.125,5.875c0,0-5.625,3.625-8,6.875c0,0-1.75,3.125-1.125,5.75l1.75,2.375c0,0,8,6.25,15.5,13.75\n'
+ 'c0,0,9.75,10.625,12.625,15.75c0,0,2.125,3.125,2.375,9.5c0,0,1.5,4,3.5,5.875c0,0,1,4.625,0.25,12.75c0,0,0.625,0.875,2.125,2.25\n'
+ 'c0,0,0.75,0.5,0.125,3c0,0-2.125-0.75-4.375,0.5c0,0-3.042,1.249-3.875,0.916c0,0-1.833,2.167-2.667,2.917c0,0-0.583,6,1.75,8.583\n'
+ 'c0,0,6.333,3.166,10.083,4.083c0,0,1.442,0.95,3.5,1.334c0,0,4.146,1.343,6.239,2.338c0,0,4.677,0.994,5.177,1.578\n'
+ 'c0,0,1.669,3.104-2.499,4.261c0,0-6.584-0.344-9.418-1.094C356.243,444.822,342.576,444.407,339.326,445.156z"/>';