3D Self Organizing Map - Arrange 2D Array
/**
* Copyright greentec ( http://wonderfl.net/user/greentec )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qZ83
*/
// forked from greentec's Self Organizing Map - Arrange 2D Array
// forked from greentec's Self Organizing Map
package
{
import away3d.cameras.Camera3D;
import away3d.containers.Scene3D;
import away3d.containers.View3D;
import away3d.controllers.HoverController;
import away3d.debug.AwayStats;
import away3d.entities.Mesh;
import away3d.lights.DirectionalLight;
import away3d.materials.ColorMaterial;
import away3d.materials.lightpickers.StaticLightPicker;
import away3d.primitives.SkyBox;
import away3d.primitives.SphereGeometry;
import away3d.primitives.CubeGeometry;
import away3d.textures.BitmapCubeTexture;
import away3d.utils.Cast;
import com.bit101.components.Label;
import flash.display.*;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
/**
* ...
* @author ypc
*/
[SWF(width = "465", height = "465")]
public class Main extends Sprite
{
private var view:View3D;
private var scene:Scene3D;
private var camera:Camera3D;
private var awayStats:AwayStats;
private var cameraController:HoverController;
private var light:DirectionalLight;
private var lightPicker:StaticLightPicker;
private var skyboxTexture:BitmapCubeTexture;
private var nodeSphereArray:Array;
//navigation variables
private var move:Boolean = false;
private var lastPanAngle:Number;
private var lastTiltAngle:Number;
private var lastMouseX:Number;
private var lastMouseY:Number;
private var outputNeuronWidthNum:int = 25;
private var outputNeuronHeightNum:int = 25;
private var outputNeuronNum:int = outputNeuronWidthNum * outputNeuronHeightNum;
private var inputNeuronNum:int = 25000;
public var inputLayer:Layer;
public var outputLayer:Layer;
public var startLearningRate:Number = 0.05;
public var learningRate:Number = 0.05;
public var oneFrameIter:int = 10;
public var iterMax:int = inputNeuronNum / oneFrameIter - 1;
public var iter:int = -1;
public var startRadius:Number = (outputNeuronWidthNum + outputNeuronWidthNum) / 2;
public var radius:Number = startRadius;
public var timeConstant:Number = iterMax / Math.log(startRadius);
public var proceedNumLabel:Label;
public var resetLabel:Label;
//public var resetButton:PushButton;
//private var source:BitmapData = new BitmapData(465, 465, false, 0x000000);
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
Wonderfl.disable_capture();
//addChild(new Bitmap(source));
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
proceedNumLabel = new Label(this, 465 - 100, 10, "");
resetLabel = new Label(this, 465 - 100, 22, "Press R to Reset");
//resetButton = new PushButton(this, 465 - 100, 30, "Reset", onReset);
initEngine();
initLight();
initMaterial();
initObject();
initListener();
initNeuron();
}
private function initEngine():void
{
view = new View3D();
view.antiAlias = 4;
scene = view.scene;
camera = view.camera;
camera.lens.far = 5000;
cameraController = new HoverController(camera);
cameraController.distance = 1000;
//cameraController.minTiltAngle = 10;
//cameraController.maxTiltAngle = 90;
cameraController.panAngle = 45;
cameraController.tiltAngle = 20;
awayStats = new AwayStats(view);
}
private function initLight():void
{
light = new DirectionalLight( -0.5, -1, -1);
light.color = 0xffffff;
light.ambient = 1;
scene.addChild(light);
lightPicker = new StaticLightPicker([light]);
}
private function initMaterial():void
{
var noiseBitmapData:BitmapData = new BitmapData(64, 64, false);
noiseBitmapData.noise(Math.random() * int.MAX_VALUE, 0, 64, 7, true);
var red:BitmapData = new BitmapData(64, 64, false, 0x990000);
red.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
var green:BitmapData = new BitmapData(64, 64, false, 0x009900);
green.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
var blue:BitmapData = new BitmapData(64, 64, false, 0x000099);
blue.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
var yellow:BitmapData = new BitmapData(64, 64, false, 0x999900);
yellow.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
var magenta:BitmapData = new BitmapData(64, 64, false, 0x990099);
magenta.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
var cyan:BitmapData = new BitmapData(64, 64, false, 0x009999);
cyan.draw(noiseBitmapData, null, null, BlendMode.SUBTRACT);
//var bitmap:Bitmap = new Bitmap(red);
//bitmap.x = stage.stageWidth - bitmap.width;
//addChild(bitmap);
skyboxTexture = new BitmapCubeTexture(Cast.bitmapData(red),
Cast.bitmapData(green),
Cast.bitmapData(blue),
Cast.bitmapData(yellow),
Cast.bitmapData(magenta),
Cast.bitmapData(cyan));
//
}
private function initObject():void
{
scene.addChild(new SkyBox(skyboxTexture));
nodeSphereArray = [];
var nodeSphere:Mesh;
var i:int;
var red:int;
var green:int;
var blue:int;
var tx:int;
var ty:int;
var colorMaterial:ColorMaterial;
for (i = 0; i < outputNeuronNum; i += 1)
{
//nodeSphere = new Mesh(new SphereGeometry(20), new ColorMaterial(Math.random() * 0x0000ff));
tx = i % outputNeuronWidthNum;
ty = i / outputNeuronWidthNum;
red = tx / outputNeuronWidthNum * 255;
green = ty / outputNeuronWidthNum * 255;
blue = Math.abs(tx - ty) / outputNeuronWidthNum * 255;
colorMaterial = new ColorMaterial(red << 16 | green << 8 | blue);
colorMaterial.lightPicker = lightPicker;
nodeSphere = new Mesh(new SphereGeometry(20), colorMaterial);
//nodeSphere = new Mesh(new CubeGeometry(30, 30, 30, 1, 1, 1, false), colorMaterial);
nodeSphereArray.push(nodeSphere);
scene.addChild(nodeSphere);
}
}
private function initListener():void
{
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(Event.RESIZE, resizeHandler);
stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
addEventListener(Event.ENTER_FRAME, render);
addChild(view);
addChild(awayStats);
}
private function onKeyDown(e:KeyboardEvent):void
{
if (e.keyCode == 82) //R or r : Reset!
{
onReset();
}
}
private function initNeuron():void
{
var i:int;
var neuron:Neuron;
inputLayer = new Layer();
for (i = 0; i < inputNeuronNum; i += 1)
{
neuron = new Neuron(3);
inputLayer.neurons.push(neuron);
}
outputLayer = new Layer();
for (i = 0; i < outputNeuronNum; i += 1)
{
neuron = new Neuron(3);
outputLayer.neurons.push(neuron);
}
}
private function moveNode():void
{
var i:int;
var nodeSphere:Mesh;
for (i = 0; i < outputNeuronNum; i += 1)
{
nodeSphere = nodeSphereArray[i];
nodeSphere.x = outputLayer.neurons[i].w[0] * 1000;
nodeSphere.y = outputLayer.neurons[i].w[1] * 1000;
nodeSphere.z = outputLayer.neurons[i].w[2] * 1000;
}
}
private function render(e:Event):void
{
if (move)
{
cameraController.panAngle = 0.3 * (stage.mouseX - lastMouseX) + lastPanAngle;
cameraController.tiltAngle = 0.3 * (stage.mouseY - lastMouseY) + lastTiltAngle;
}
learn();
moveNode();
view.render();
//view.renderer.queueSnapshot(source);
}
private function learn():void
{
var i:int;
var j:int;
var input:Neuron;
var neuron:Neuron;
var bmuNumber:Number;
var bmuIndex:int;
var dx:int;
var dy:int;
var v:Number;
var N:Number;
var dist:Number;
var influence:Number;
var bmuNeuron:Neuron;
iter += 1;
if (iter <= iterMax)
{
for (i = 0; i < oneFrameIter; i += 1)
{
input = inputLayer.neurons[iter * oneFrameIter + i];
bmuNumber = 99999;
for (j = 0; j < outputNeuronNum; j += 1)
{
neuron = outputLayer.neurons[j];
neuron.redDist = input.w[0] - neuron.w[0];
neuron.greenDist = input.w[1] - neuron.w[1];
neuron.blueDist = input.w[2] - neuron.w[2];
neuron.euclideanDist = Math.sqrt(neuron.redDist * neuron.redDist + neuron.greenDist * neuron.greenDist + neuron.blueDist * neuron.blueDist);
if (bmuNumber > neuron.euclideanDist)
{
bmuNumber = neuron.euclideanDist;
bmuIndex = j;
}
}
bmuNeuron = outputLayer.neurons[bmuIndex];
//trace(bmuIndex, bmuNeuron, bmuNeuron.w[0], bmuNeuron.w[1]);
//var num:int = 0;
for (j = 0; j < outputNeuronNum; j += 1)
{
if (j != bmuIndex)
{
neuron = outputLayer.neurons[j];
dx = (bmuIndex % outputNeuronWidthNum) - (j % outputNeuronWidthNum);
dx = (dx ^ dx >> 31) - (dx >> 31); // == MAth.abs(dx)
dy = int(bmuIndex / outputNeuronWidthNum) - int(j / outputNeuronWidthNum);
dy = (dy ^ dy >> 31) - (dy >> 31);
//dist = Math.sqrt(dx * dx + dy * dy);
dist = dx + dy;
if (dist < radius)
{
influence = Math.exp(( -1 * Math.sqrt(dist)) / (2 * radius * iter));
neuron.w[0] += learningRate * influence * neuron.redDist;
neuron.w[1] += learningRate * influence * neuron.greenDist;
neuron.w[2] += learningRate * influence * neuron.blueDist;
//trace(neuron.w[0], neuron.w[1]);
//num += 1;
}
}
//neuron.w[0] = neuron.w[0] > 1 ? 1 : (neuron.w[0] < -1 ? -1 : neuron.w[0]);
//neuron.w[1] = neuron.w[1] > 1 ? 1 : (neuron.w[1] < -1 ? -1 : neuron.w[1]);
//v = (dx + dy) / 18;
//N = Math.exp( -v);
//neuron.w[0] += learningRate * N * neuron.redDist;
//neuron.w[1] += learningRate * N * neuron.greenDist;
}
//trace(num);
//trace(bmuNeuron.w[0], bmuNeuron.w[1]);
}
//drawNodes(i-1, bmuIndex);
learningRate = startLearningRate * Math.exp( -iter / timeConstant);
radius = startRadius * Math.exp( -iter / timeConstant);
proceedNumLabel.text = "Proceed #" + String(iter) + "/" + String(iterMax);
}
//else
//{
//if (hasEventListener(Event.ENTER_FRAME) == true)
//{
//removeEventListener(Event.ENTER_FRAME, onLoop);
//}
//}
}
private function onMouseWheel(e:MouseEvent):void
{
cameraController.distance -= e.delta * 15;
if (cameraController.distance < 500)
{
cameraController.distance = 500;
}
else if (cameraController.distance > 3000)
{
cameraController.distance = 3000;
}
}
private function onMouseDown(e:MouseEvent):void
{
lastPanAngle = cameraController.panAngle;
lastTiltAngle = cameraController.tiltAngle;
lastMouseX = stage.mouseX;
lastMouseY = stage.mouseY;
move = true;
}
private function onMouseUp(e:MouseEvent):void
{
move = false;
}
private function resizeHandler(e:Event):void
{
view.width = stage.stageWidth;
view.height = stage.stageHeight;
}
private function onReset():void
{
var i:int;
if (hasEventListener(Event.ENTER_FRAME) == true)
{
removeEventListener(Event.ENTER_FRAME, render);
}
var neuron:Neuron;
for (i = 0; i < inputNeuronNum; i += 1)
{
neuron = inputLayer.neurons[i];
neuron.resetW(2);
}
for (i = 0; i < outputNeuronNum; i += 1)
{
neuron = outputLayer.neurons[i];
neuron.resetW(2);
}
iter = 0;
learningRate = startLearningRate;
radius = startRadius;
if (hasEventListener(Event.ENTER_FRAME) == false)
{
addEventListener(Event.ENTER_FRAME, render);
}
}
}
}
Class
{
/**
* ...
* @author ypc
*/
class Neuron
{
public var w:Object;
public var input:Number;
public var output:Number;
public var euclideanDist:Number;
public var redDist:Number;
public var greenDist:Number;
public var blueDist:Number;
public function Neuron(n:int)
{
this.w = new Object();
if (n > 0)
{
var i:int;
for (i = 0; i < n; i += 1)
{
this.w[i] = Math.random() * 2 - 1;
}
}
}
public function resetW(n:int):void
{
var i:int;
for (i = 0; i < n; i += 1)
{
this.w[i] = Math.random() * 2 - 1;
}
}
}
}
Class
{
/**
* ...
* @author ypc
*/
class Layer
{
public var neurons:Array;
public function Layer()
{
this.neurons = [];
}
}
}