IKvFX
Based off original work by author @TMaeda
/**
* Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/s1FT
*/
// Edit by BradSedito:
// [ Made white version; changed line behavior ]
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import frocessing.core.F5BitmapData2D;
import com.greensock.*;
import com.greensock.easing.*;
[SWF(width=465,height=465,backgroundColor=0xffffff,frameRate=60)]
public class IKvFX extends Sprite
{
private var back : F5BitmapData2D;
private var canvas : F5BitmapData2D;
private var smoke : BitmapData;
private var hairs : Vector.<Tail> = new Vector.<Tail>;
private var blur : BlurFilter = new BlurFilter(6, 6, 3);
private var colorTrans : ColorTransform = new ColorTransform(1.0,1.0,1.0, 0.9);
private var center : Point = new Point();
private var isDown : Boolean = false;
private var tempPos : Point = new Point();
private var lastPos : Point = new Point();
private var rootPos : Point = new Point();
private var zeroPos : Point = new Point(0, 0);
private var plusBall : Point = new Point();
private var negative : Boolean = false;
private var selIndex : int = -1;
private var rad : Number = 0;
private var smokePos : Point = new Point();
public function IKvFX()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event=null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, enterFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
back = new F5BitmapData2D(stage.stageWidth, stage.stageHeight, true, 0);
addChild(new Bitmap(back.bitmapData));
canvas = new F5BitmapData2D(stage.stageWidth, stage.stageHeight, true, 0xffffff);
addChild( new Bitmap(canvas.bitmapData) );
smoke = new BitmapData(32, 32, true, 0x80808080);
smoke.perlinNoise(16, 16, 8, 0, true, false, 7, true);
//addChild( new Bitmap(smoke));
center.x = stage.stageWidth / 2;
center.y = stage.stageHeight / 2;
for (var i : int = 0 ; i < 5; i++) {
hairs[i] = new Tail();
hairs[i].init(center, 270, 8, 32+Math.random()*16, 1, false);
}
rootPos.x = stage.stageWidth*3/5;
rootPos.y = stage.stageHeight*3/4;
plusBall.x = 100;
plusBall.y = 100;
}
private function dist(a : Point, b : Point): Number
{
return Math.sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
private function mouseDown(e:MouseEvent):void
{
lastPos.x = e.stageX;
lastPos.y = e.stageY;
selIndex = (dist(lastPos, plusBall) < 8) ? 0 : (dist(lastPos, rootPos) < 8) ? 1 : -1;
isDown = true;
}
private function mouseUp(e:MouseEvent):void
{
if ((dist(lastPos, plusBall) < 1) && (selIndex == 0)) {
negative = !negative;
}
isDown = false;
}
private function smokeMoveTo(x : int, y : int) : void
{
smokePos.x = x;
smokePos.y = y;
}
private function smokeLineTo(x : int, y : int, p : int, acoeff : Number) : int
{
var a : Number;
var dx : int = x - smokePos.x, dy : int = y - smokePos.y;
var adx : int = Math.abs(dx), ady : int = Math.abs(dy);
var d : int = 0;
var i : int, j : int;
var x0 : int, y0 : int, x1 : int, y1 : int, xsign : int, ysign : int;
var sx : int = p % 32, sy : int = (p / 32) % 32;
var col : int, src : int;
xsign = smokePos.x > x ? -1 : 1;
ysign = smokePos.y > y ? -1 : 1;
if (adx > ady) {
x0 = xsign > 0 ? smokePos.x : x;
y0 = xsign > 0 ? smokePos.y : y;
x1 = xsign < 0 ? smokePos.x : x;
y1 = xsign < 0 ? smokePos.y : y;
//ysign = xsign < 0 ? -ysign : ysign;
j = smokePos.y;
for ( i = smokePos.x; i != x; i += xsign) {
d += ady;
if (d > adx) {
d -= adx;
j += ysign;
}
src = canvas.bitmapData.getPixel32(i, j) & 0xff;
col = smoke.getPixel32(sx, sy) & 0xff;
sx = (sx+1) & 0x1f;
if (sx == 0) sy = (sy + 1) & 0x1f;
a = Math.min(acoeff*255, Math.max(col , src )& 0xff);
canvas.bitmapData.setPixel32(i, j, (0xff << 24) | (a << 16) | (a << 8) | a);
p++;
}
} else if (dy != 0) {
x0 = ysign > 0 ? smokePos.x : x;
y0 = ysign > 0 ? smokePos.y : y;
x1 = ysign < 0 ? smokePos.x : x;
y1 = ysign < 0 ? smokePos.y : y;
//xsign = ysign < 0 ? -xsign : xsign;
j = smokePos.x;
for ( i = smokePos.y; i != y; i += ysign ) {
d += adx;
if (d > ady) {
d -= ady;
j += xsign;
}
src = canvas.bitmapData.getPixel32(i, j) & 0xff;
col = smoke.getPixel32(sx, sy) & 0xff;
//col = (0xff-(col & 0xff)) << 24 | (0xffffff);
sx = (sx+1) & 0x1f;
if (sx == 0) sy = (sy+1) & 0x1f;
a = Math.min(acoeff*255, Math.max(col , src));
canvas.bitmapData.setPixel32(j,i, (0xff << 24) | (a << 16) | (a << 8) | a);
p++;
}
}
smokePos.x = x;
smokePos.y = y;
return p;
}
private var smoke_state : int = 0;
private function enterFrame(e:Event):void
{
var i : int, j : int;
var hair_len : int;
var hair_cnt : int = hairs.length;
var p : int = smoke_state;
smoke_state = (32 * 32 + smoke_state - 4) % (32 * 32);
if (isDown) {
switch (selIndex) {
case 0: {
plusBall.x = stage.mouseX;
plusBall.y = stage.mouseY;
break;
}
case 1: {
rootPos.x += (stage.mouseX-rootPos.x)/4;
rootPos.y += (stage.mouseY-rootPos.y)/4;
break;
}
}
}
rad += Math.random();
back.bitmapData.fillRect(back.bitmapData.rect, 0);
back.beginDraw();
back.beginFill(0xff8000);
back.noStroke();
back.circle(rootPos.x, rootPos.y, 2);
back.endFill();
back.stroke(0xff, 0xff, 0xff);
back.noFill();
back.circle(rootPos.x, rootPos.y, 8);
back.circle(plusBall.x, plusBall.y, 8);
back.moveTo(plusBall.x - 3, plusBall.y);
back.lineTo(plusBall.x + 3, plusBall.y);
if (!negative) {
back.moveTo(plusBall.x , plusBall.y - 3);
back.lineTo(plusBall.x , plusBall.y + 3);
}
back.endDraw();
canvas.beginDraw();
canvas.bitmapData.lock();
for (i = 0; i < hair_cnt; i++) {
var tail : Tail = hairs[i];
tempPos.x = rootPos.x + Math.cos(rad) * 4;
tempPos.y = rootPos.y + Math.sin(rad) * 8;
tail.stepFrame(tempPos, 0, negative? null : plusBall);
hair_len = tail.tails.length;
smokeMoveTo(tail.rootpos.x, tail.rootpos.y);
// canvas.moveTo(tail.rootpos.x, tail.rootpos.y);
for (j = 0; j < hair_len; j++) {
canvas.stroke(0xff*j/hair_len, 0x80, 0x40);
p = smokeLineTo(tail.tails[j].term.x, tail.tails[j].term.y, p, (hair_len - j) / hair_len);
//canvas.lineTo(tail.tails[j].term.x, tail.tails[j].term.y);
}
}
canvas.bitmapData.unlock();
canvas.endDraw();
canvas.bitmapData.applyFilter(canvas.bitmapData, canvas.bitmapData.rect, zeroPos, blur);
canvas.bitmapData.colorTransform(canvas.bitmapData.rect, colorTrans);
}
}
}
import flash.geom.Point;
class Tail
{
import com.greensock.*;
import com.greensock.easing.*;
public var tails : Vector.<TailItem>;
public var rootpos : Point = new Point();
public var rootangle : Number;
private var flexAngle : Number;
private var segLen : int;
public var segCnt : int;
private var temppos1 : Point = new Point();
private var temppos2 : Point = new Point();
private var befterm : Point = new Point();
private var autoWave : Boolean;
private var waveState : int;
private var absorb : Number = 0;
private const maxlen : int = 24;
public function Tail()
{
tails = new Vector.<TailItem>;
}
public function init(pos : Point, angle : Number, seglen : int, len : int, flexlevel : Number, autowave : Boolean = true ) : void
{
flexAngle = Math.min(flexlevel, 1.0)*2 * Math.PI;
rootangle = angle * Math.PI / 180;
segCnt = Math.min(maxlen, len);
segLen = seglen;
autoWave = autowave;
waveState = 0;
rootpos.x = pos.x;
rootpos.y = pos.y;
TweenMax.to(this, 1, {glowFilter:{color:0x000000, quality:3}});
for (var i : int = 0; i < segCnt; i++) {
/*if (i < tails.length)*/ tails[i] = new TailItem;
tails[i].pos.x = pos.x;
tails[i].pos.y = pos.y;
tails[i].len = segLen+Math.random()*(segLen);
tails[i].angle = 0;
}
}
public function tailPos(index : int, /*out*/ pos : Point, term : Point = null) : void
{
if (index < segCnt) {
pos.x = tails[index].pos.x;
pos.y = tails[index].pos.y;
if (term) {
term.x = tails[index].term.x;
term.y = tails[index].term.y;
}
}
}
public function stepFrame(newpos : Point, rotate_angle : Number, tail_target : Point = null) : void
{
var i : int, cosa : Number, sina : Number, rad : Number;
var angle : Number = rootangle+rotate_angle;
waveState = (waveState + 1) & 0x00;
if (autoWave) {
if (waveState >= 0x20) {
angle = angle + (0x20 - (waveState & 0x00) - 0x10)*Math.PI/180;
} else {
angle = angle + (waveState-0x10)*Math.PI/180;
}
}
if (tail_target) {
absorb = 1.0;
} else if (absorb > 0) {
absorb /= 2;
} else absorb = 0;
rootpos.x = newpos.x;
rootpos.y = newpos.y;
for (i = 0; i < segCnt; i++) {
if (i == 0) {
tails[i].pos.x = newpos.x;
tails[i].pos.y = newpos.y;
if (autoWave) {
tails[i].pos.x += Math.random() * 8-4;
tails[i].pos.y += Math.random() * 8-4;
}
} else {
tails[i].pos.x += (tails[i-1].term.x-tails[i].pos.x)/((i*absorb/4+1));
tails[i].pos.y += (tails[i-1].term.y-tails[i].pos.y)/((i*absorb/4+1));
}
computeUnitVector(Math.cos(angle) * segLen, Math.sin(angle) * segLen, 0, 0, temppos1);
if (!computeUnitVector(tails[i].term.x, tails[i].term.y, tails[i].pos.x, tails[i].pos.y, temppos2)) {
temppos2.x = temppos1.x;
temppos2.y = temppos1.y;
}
cosa = dotProduct(temppos1.x, temppos1.y, temppos2.x, temppos2.y);
sina = crossProductZ(temppos1.x, temppos1.y, temppos2.x, temppos2.y);
if (cosa < -1) rad = Math.PI; else if (cosa > 1) rad = 0; else rad = Math.acos(cosa);
//trace(cosa + " " + rad);
if (cosa < 0) rad = Math.PI-rad;
if (sina < 0) rad = -rad;
//trace(">"+cosa + " " + rad);
if ((rad < 0) && ( -flexAngle > rad)) rad = -flexAngle;
if ((rad > 0) && ( flexAngle < rad)) rad = flexAngle;
rad += (0-rad) /4;
tails[i].angle = angle+rad;
angle = tails[i].angle;
tails[i].term.x = tails[i].pos.x + Math.cos(angle)*tails[i].len;
tails[i].term.y = tails[i].pos.y + Math.sin(angle)*tails[i].len;
}
if (tail_target) {
befterm.x = tail_target.x;
befterm.y = tail_target.y;
for (i = segCnt - 1; i > 0; i--) {
temppos2.x = tails[i].term.x + (befterm.x - tails[i].term.x) / ((segCnt - i)/2 + 1) ;
temppos2.y = tails[i].term.y + (befterm.y - tails[i].term.y) / ((segCnt - i)/2 + 1) ;
if (computeUnitVector(temppos2.x, temppos2.y, tails[i].pos.x, tails[i].pos.y, temppos1)) {
tails[i].term.x = temppos2.x;
tails[i].term.y = temppos2.y;
tails[i].pos.x = temppos2.x - (temppos1.x)*tails[i].len;
tails[i].pos.y = temppos2.y - (temppos1.y)*tails[i].len;
befterm.x = tails[i].pos.x;
befterm.y = tails[i].pos.y;
}
}
}
}
public function computeUnitVector(x0 : Number, y0 : Number, x1 : Number, y1 : Number, /*out*/ u : Point) : Boolean
{
var len : Number = Math.sqrt( (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1) );
if (len > 0) {
u.x = (x0 - x1) / len;
u.y = (y0 - y1) / len;
return true;
} else return false;
}
public function dotProduct(x0 : Number, y0 : Number, x1 : Number, y1 : Number) : Number
{
return x0 * x1 + y0 * y1;
}
public function crossProductZ(x0 : Number, y0 : Number, x1 : Number, y1 : Number) : Number
{
return x0 * y1 - y0 * x1;
}
}
class TailItem
{
public var pos : Point = new Point();
public var angle : Number;
public var term : Point = new Point();
public var len : Number;
}