/**
* Copyright sanesashi ( http://wonderfl.net/user/sanesashi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/oEQy
*/
// forked from sanesashi's 木のもと forked from: Catmull-Rom スプライン曲線
// forked from sanesashi's forked from: Catmull-Rom スプライン曲線
// forked from sanesashi's Catmull-Rom スプライン曲線
// Catmull-Rom スプライン曲線
// code from http://l00oo.oo00l.com/blog/archives/264
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Point;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.system.LoaderContext;
import flash.geom.Matrix;
import flash.net.URLRequest;
public class Tree extends Sprite
{
private var _canvasWidth:uint = 480;
private var _offsetTop:uint = 70;
private var _point:Point;
private var _angle:Number = 0;
private var _skinLeft:Array = [];
private var _skinRight:Array = [];
public function Tree()
{
if(stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event=null):void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
load();
}
private function load():void
{
var context:LoaderContext = new LoaderContext(true);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, draw);
loader.load(new URLRequest("http://farm5.static.flickr.com/4110/5011491754_be6696dfe1.jpg"), new LoaderContext(true));
}
private function draw(e:Event):void
{
var lineColor:Number = 0x222222;// 縁取り線の色
var lineWidth:Number = 2;// 縁取り線の太さ
var loader:Loader = LoaderInfo(e.target).loader;
var texture:BitmapData = Bitmap(loader.content).bitmapData;
this.trunkTop();
this.trunkMiddle();
this.trunkBottom();
// 先端が配列の中央にくるように配列を結合する
var points:Array = this._skinLeft.reverse().concat(this._skinRight);
(new Texture(this)).draw(texture, points);
(new Spline(this)).draw(this._skinLeft, lineWidth, lineColor);
(new Spline(this)).draw(this._skinRight, lineWidth, lineColor);
}
// 幹の上部
private function trunkTop():void
{
var p:Point = new Point(this._canvasWidth * 0.52, this._offsetTop);// 開始点
var phase:Number = Math.random() > 0.5 ? 0 : Math.PI/0.9;// 初期位相
var angle:Number = - Math.PI * 0.9;// 伸びる方向
const numJoints:Number = 6;// 節の数
var jointLength:Number = this._canvasWidth/numJoints * 0.3;// 節の長さ(初期値)
var skinLeft:Point;
var skinRight:Point;
for(var i:int=0; i < numJoints; i++){
// 幹の次の点
jointLength = jointLength + Math.random()*jointLength/8;
angle = angle + 0.6*Math.sin(Math.PI*i*0.95 + phase) + (Math.random()-0.5)*0.2;
var q:Point = this.nextPoint(p, angle, jointLength);
// 幹の骨から皮の点を求める
skinLeft = this.skinTop(p, angle, 1, jointLength, i);
skinRight = this.skinTop(p, angle, -1, jointLength, i);
this._skinLeft.push(skinLeft);
this._skinRight.push(skinRight);
p = q;
}
// 引継ぎ
this._point = p;
this._angle = angle;
}
private function trunkMiddle():void
{
var p:Point = this._point;// 開始点はtrunkTopの終了点
var phase:Number = 0;// 初期位相
var angle:Number = -Math.PI * 0.3;// 伸びる方向
const numJoints:Number = 9;// 節の数
var jointLength:Number = this._canvasWidth/16;// 節の長さ(初期値)
var skinLeft:Point;
var skinRight:Point;
var angleDiff:Number = angle - this._angle;
for(var i:int=0; i < numJoints; i++){
// 幹の次の点
if(i < 5){
// はじめの5回で徐々に角度をならしていく
jointLength = this._canvasWidth/16 + i * jointLength/16 * Math.random();
angle = this._angle + angleDiff/5*i ;//+ (Math.random()-0.5)*0.3;
}else{
jointLength = jointLength + Math.random()*jointLength/6;
angle = angle + 0.5*Math.sin(Math.PI*i*0.9 + phase) + (Math.random()-0.5)*0.1;
}
var q:Point = this.nextPoint(p, angle, jointLength);
// 幹の骨から皮の点を求める
skinLeft = this.skinMiddle(p, angle, 1, jointLength, i);
skinRight = this.skinMiddle(p, angle, -1, jointLength, i);
this._skinLeft.push(skinLeft);
this._skinRight.push(skinRight);
if(i==0){
this.plot(p);
this.plot(skinLeft);
this.plot(skinRight);
}
p = q;
}
// 引継ぎ
this._point = p;
this._angle = angle;
}
private function trunkBottom():void
{
var p:Point = this._point;// 開始点はtrunkTopの終了点
var phase:Number = 0;// 初期位相
var angle:Number = -Math.PI * 0.3;// 伸びる方向
const numJoints:Number = 20;// 節の数
var jointLength:Number = this._canvasWidth/6;// 節の長さ(初期値)
var skinLeft:Point;
var skinRight:Point;
var angleDiff:Number = angle - this._angle;
// アドホックな調整
p.x += jointLength/4;
p.y += jointLength/2;
for(var i:int=0; i < numJoints; i++){
// 幹の次の点
if(i < 3){
// 徐々に角度をならしていく
jointLength = this._canvasWidth/6 + i * jointLength/8 * Math.random();
angle = this._angle + angleDiff * 0.8 *i ;//+ (Math.random()-0.5)*0.3;
}else{
jointLength = jointLength * 1.2;
angle = angle + 0.4*Math.sin(Math.PI*i*0.3 + phase) + (Math.random()-1)*0.1;
}
var q:Point = this.nextPoint(p, angle, jointLength);
// 幹の骨から皮の点を求める
skinLeft = this.skinBottom(p, angle, 1, jointLength, i);
skinRight = this.skinBottom(p, angle, -1, jointLength, i);
this._skinLeft.push(skinLeft);
this._skinRight.push(skinRight);
if(i>=0){
this.plot(p);
this.plot(skinLeft);
this.plot(skinRight);
}
p = q;
}
}
// 幹の骨から皮の点を求める
private function skinTop(pointTrunk:Point, angleTrunk:Number, dir:Number, d:Number, i:Number):Point
{
var angle:Number = angleTrunk + dir * Math.PI/2;
var distance:Number = d/32 + d/16*i + d/32 * Math.random();
return this.nextPoint(pointTrunk, angle, distance);
}
private function skinMiddle(pointTrunk:Point, angleTrunk:Number, dir:Number, d:Number, i:Number):Point
{
var angle:Number = angleTrunk + dir * Math.PI/2;
var distance:Number = 0;
if(i < 3){
distance = d/2 + d/4*i + d/16 * Math.random();
}else{
distance = d + d/20*i + d/16 * Math.random();
}
return this.nextPoint(pointTrunk, angle, distance);
}
private function skinBottom(pointTrunk:Point, angleTrunk:Number, dir:Number, d:Number, i:Number):Point
{
var angle:Number = angleTrunk + dir * Math.PI/2;
var distance:Number = 0;
if(i < 2){
distance = d + d/8*i + d/16 * Math.random();
}else{
distance = d * 1.1 + d/8 * Math.random();
}
return this.nextPoint(pointTrunk, angle, distance);
}
// 点Pから角度a,距離dの点Qを求める
private function nextPoint(p:Point, angle:Number, distance:Number):Point
{
var q:Point = new Point();
q.x = p.x + distance * Math.cos(angle);
q.y = p.y - distance * Math.sin(angle);
return q;
}
private function plot(p:Point):void
{
var s:Sprite = new Sprite();
s.x = p.x;
s.y = p.y;
s.graphics.beginFill(0xff3366);
s.graphics.drawCircle(0, 0, 2);
s.graphics.endFill();
addChild(s);
}
}
}
import flash.display.Sprite;
import flash.geom.Point;
import flash.display.BitmapData;
// catmullスプライン曲線
class Spline
{
private var _sprite:Sprite
private var _numSegments:uint;//曲線分割数(大きくするとなめらかになる)
public function Spline(sprite:Sprite, numSegments:uint = 5):void
{
this._sprite = sprite;
this._numSegments = numSegments;
}
public function draw(v:Array, width:uint, color:uint):void
{
if(v.length<2) return;
v.splice(0,0,v[0]);
v.push(v[v.length-1]);
for(var i:uint=0; i<v.length-3; i++){
var p0:Point = v[i];
var p1:Point = v[i+1];
var p2:Point = v[i+2];
var p3:Point = v[i+3];
splineTo(width, color, p0, p1, p2, p3);
}
}
private function splineTo(width:uint, color:uint, p0:Point, p1:Point, p2:Point, p3:Point):void
{
this._sprite.graphics.lineStyle(width, color);
this._sprite.graphics.moveTo(p1.x, p1.y);
// 直線数本つなげて曲線っぽく
for(var i:uint = 0; i < this._numSegments; i++){
var t:Number = (i+1) / this._numSegments;
this._sprite.graphics.lineTo(
catmullRom(p0.x, p1.x, p2.x, p3.x, t),
catmullRom(p0.y, p1.y, p2.y, p3.y, t)
);
}
// 最後に起点から終点に直線も引く(といい感じの線になる)
this._sprite.graphics.moveTo(p1.x, p1.y);
this._sprite.graphics.lineTo(p2.x, p2.y);
}
private function catmullRom(p0:Number, p1:Number, p2:Number, p3:Number, t:Number):Number
{
var v0:Number = (p2 - p0) * 0.5;
var v1:Number = (p3 - p1) * 0.5;
return (2*p1 - 2*p2 + v0 + v1)*t*t*t + (-3*p1 + 3*p2 - 2*v0 - v1)*t*t + v0*t + p1;
}
}
class Texture
{
private var _sprite:Sprite
public function Texture(sprite:Sprite):void
{
this._sprite = sprite;
}
public function draw(texture:BitmapData, points:Array):void
{
// bitmapで塗る
this._sprite.graphics.beginBitmapFill(texture);
this._sprite.graphics.moveTo(points[0].x, points[0].y);
for(var i:uint=0; i < points.length; i++){
this._sprite.graphics.lineTo(points[i].x, points[i].y);
}
this._sprite.graphics.endFill();
}
}