In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Rubik's Cube

shuffle
score
basicView + QuadRender
smooth orbiting
Get Adobe Flash player
by jozefchutka 02 Apr 2010
/**
 * Copyright jozefchutka ( http://wonderfl.net/user/jozefchutka )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/351d
 */

// shuffle
// score
// basicView + QuadRender
// smooth orbiting

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.core.math.Number3D;
    
    [SWF(width="465", height="465", frameRate="30", backgroundColor="#FFFFFF")]
    
    public class RCubeApp extends Sprite
    {
        public static const W:uint = 465;
        public static const H:uint = 465;
        
        private var rcube:RCube = new RCube();
        private var isCameraRotating:Boolean;
        private var previousMousePoint:Point;
        
        public function RCubeApp()
        {
            super();
            
            addChild(rcube);
            rcube.interactive = true;
            rcube.init(true);
            //rcube.shuffle(4);
            rcube.addEventListener(RCubeEvent.SOLVED, solvedHandler);
            
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
        }
        
        private function solvedHandler(event:RCubeEvent):void
        {
            //trace('solved');
        }
        
        private function mouseUp(event:Event):void
        {
            if(isCameraRotating)
                rcube.renderFull();
            isCameraRotating = false;
            rcube.release();
        }
        
        private function mouseDown(event:Event):void
        {
            if(event.target != stage)
                return;
            isCameraRotating = true;
            previousMousePoint = new Point(mouseX, mouseY);
        }
        
        private function mouseMove(event:MouseEvent):void
        {
            if(!isCameraRotating)
                return;
            
            rcube.moveCamera(
                (previousMousePoint.x - mouseX) / 2, 
                (previousMousePoint.y - mouseY) / 2);
            rcube.renderFast();
            
            previousMousePoint = new Point(mouseX, mouseY);
        }
    }
}

import __AS3__.vec.Vector;

import flash.events.Event;

import org.papervision3d.cameras.Camera3D;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.utils.Mouse3D;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.render.QuadrantRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;
import flash.display.Sprite;
import org.papervision3d.view.BasicView;
import caurina.transitions.Tweener;

internal class RCube extends Sprite
{
    private var viewport:Viewport3D;
    //private var view:BasicView = new BasicView(RCubeApp.W, RCubeApp.H, true, true);
    private var scene:Scene3D = new Scene3D();
    private var camera:Camera3D = new Camera3D(110, 10, 5000);
    private var rendererFast:BasicRenderEngine = new BasicRenderEngine();
    private var rendererFull:QuadrantRenderEngine 
        = new QuadrantRenderEngine(QuadrantRenderEngine.CORRECT_Z_FILTER);
    private var cubes:Vector.<Cube> = new Vector.<Cube>();
    private var mouse:Mouse3D;
    private var rotationDart:Plane;
    
    private var _solved:Boolean = true;
    private var isRotating:Boolean = false;
    
    public static const COLORS:Object = {front:0xf5272b, back:0x016eb1, 
        left:0xfed720, right:0x57ab4b, top:0xf0f0f0, bottom:0xfaa01f};
    //0x000000, 0xD80505, 0xFF9900, 0xFFFFFF, 0xFFFF00, 0x0018EE, 0x1CA91B
        
    public static const CUBE_SIZE:uint = 180;
    public static const CUBE_SPACE:uint = 10;
    private static const CUBE_SIDES:Array = ["front", "back", "left", "right", "top", "bottom"];
    private static const CUBE_BITMAP_SIZE:uint = 100;
    private static const DART_WIDTH:uint = CUBE_SIZE * 5;
    private static const DART_HEIGHT:uint = CUBE_SIZE;
    private static const ROTAION_TIME:Number = 1;
    private static const SHUFFLE_ROTAION_TIME:Number = 0.5;
    
    public static const X:String = "x";
    public static const Y:String = "y";
    public static const Z:String = "z";
    
    private var pressPosition:Number3D;
    private var pressCube:Cube;
    private var coursePosition:Number3D;
    private var courseCube:Cube;
    private var cameraPitch:Number = 60;
    private var cameraYaw:Number = -50;
    
    private var shufflesLeft:uint = 0;
    
    public var useShadow:Boolean = false;
    public var useShadedMaterial:Boolean = false;
    public var interactive:Boolean = false;
    
    public function RCube()
    {
        super();
    }
    
    public function init(render:Boolean = true):void
    {
        initViewport();
        initMouse();
        initCubes();
        initDart();
        initCamera();
        if(render)
            renderFull();
    }
    
    public function destroy():void
    {
        for each(var cube:Cube in cubes)
        {
            cube.removeEventListener(InteractiveScene3DEvent.OBJECT_PRESS, cubePressHandler);
            cube.removeEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release);
            cube.removeEventListener(InteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDE, release);
            cube.removeEventListener(InteractiveScene3DEvent.OBJECT_OVER, cubeOverHandler);
        }
    }
    
    public function get solved():Boolean
    {
        return _solved;
    }
    
    private function set solved(value:Boolean):void
    {
        _solved = value;
        var type:String = value ? RCubeEvent.SOLVED : RCubeEvent.UNSOLVED;
        dispatchEvent(new RCubeEvent(type));
    }
    
    public function renderFast():void
    {
        rendererFast.renderScene(scene, camera, viewport);
        dispatchEvent(new RCubeEvent(RCubeEvent.RENDER));
    }
    
    public function renderFull():void
    {
        rendererFull.renderScene(scene, camera, viewport);
        //view.singleRender();
        dispatchEvent(new RCubeEvent(RCubeEvent.RENDER));
    }
    
    public function moveCamera(x:Number = 0, y:Number = 0):void
    {
        cameraPitch += y;
        cameraPitch %= 360;
        cameraPitch = cameraPitch > 0 ? cameraPitch : 0.0001;
        cameraPitch = cameraPitch < 180 ? cameraPitch : 179.9999;
        
        cameraYaw += x;
        cameraYaw %= 360;
        
        camera.zoom = 80;
        camera.orbit(cameraPitch, cameraYaw, true, 
            cubeActual(new Number3D(0, 0, 0)));
    }
    
    private function solvedCheck():void
    {
        var result:Boolean = !isRotating;
        for(var x:int = -1; x <= 1; x++)
        for(var y:int = -1; y <= 1; y++)
        for(var z:int = -1; z <= 1; z++)
        if(cubeActual(new Number3D(x, y, z)).name 
            != cubeName(new Number3D(x, y, z)))
            result = false;
        private::solved = result;
    }
    
    private function initViewport():void
    {
        viewport = new Viewport3D(RCubeApp.W, RCubeApp.H, false, true);
        viewport.buttonMode = true;
        addChild(viewport);
        
        //addChild(view);
    }
    
    private function initMouse():void
    {
        Mouse3D.enabled = true;
        mouse = viewport.interactiveSceneManager.mouse3D;
    }
    
    private function initCamera():void
    {
        moveCamera();
    }
    
    private function initDart():void
    {
        var material:ColorMaterial = new ColorMaterial(0x0);
        material.doubleSided = true;
        material.smooth = true;
        rotationDart = new Plane(material, DART_WIDTH, DART_HEIGHT, 20, 1);
        rotationDart.visible = false;
        scene.addChild(rotationDart);
    }
    
    private function initCubes():void
    {
        for(var x:int = -1; x <= 1; x++)
        for(var y:int = -1; y <= 1; y++)
        for(var z:int = -1; z <= 1; z++)
            scene.addChild(createCube(new Number3D(x, y, z)));
    }
    
    private function createCube(position:Number3D):Cube
    {
        var cube:Cube = new Cube(materialList(position), 
            CUBE_SIZE, CUBE_SIZE, CUBE_SIZE, 1, 1, 1, Cube.ALL);
        cube.name = cubeName(position);
        cube.x = position.x * (CUBE_SIZE + CUBE_SPACE);
        cube.y = position.y * (CUBE_SIZE + CUBE_SPACE);
        cube.z = position.z * (CUBE_SIZE + CUBE_SPACE);
        cube.useOwnContainer = true;
        cube.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, cubePressHandler);
        cube.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release);
        cube.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE_OUTSIDE, release);
        cube.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, cubeOverHandler);
        cubes.push(cube);
        return cube;
    }
    
    private function materialList(position:Number3D):MaterialsList
    {
        var useMaterial:Boolean;
        var materials:MaterialsList = new MaterialsList();
        var i:uint = 0;
        var material:ColorMaterial;
        for each(var side:String in CUBE_SIDES)
        {
            useMaterial = (side=='front' && position.z == 1)
                || (side=='back' && position.z == -1)
                || (side=='left' && position.x == -1)
                || (side=='right' && position.x == 1)
                || (side=='top' && position.y == 1)
                || (side=='bottom' && position.y == -1)
            material = useMaterial 
                ? new ColorMaterial(COLORS[side])
                : new ColorMaterial(0x0);
            material.opposite = true;
            material.oneSide = true;
            material.smooth = useMaterial;
            material.interactive = useMaterial;
            materials.addMaterial(material, side);
        }
            
        return materials;
    }
    
    public static function cubeName(position:Number3D):String
    {
        return position.x.toString() 
            + position.y.toString()
            + position.z.toString();
    }
    
    public function cubeActual(position:Number3D):Cube
    {
        for each(var cube:Cube in cubes)
            if(Math.round(cube.x) == position.x * (CUBE_SIZE + CUBE_SPACE)
                && Math.round(cube.y) == position.y * (CUBE_SIZE + CUBE_SPACE) 
                && Math.round(cube.z) == position.z * (CUBE_SIZE + CUBE_SPACE))
                return cube;
        return null;
    }
    
    private function wrap(list:Vector.<Cube>):DisplayObject3D
    {
        var wrapper:DisplayObject3D = new DisplayObject3D();
        wrapper.useOwnContainer = true;
        for each(var cube:Cube in list)
        {
            scene.removeChild(cube);
            //view.scene.removeChild(cube);
            if(cube.parent)
                cube.parent.removeChild(cube);
            wrapper.addChild(cube);
        }
        scene.addChild(wrapper);
        //view.scene.addChild(wrapper);
        return wrapper;
    }
    
    private function rotateWrapper(wrapper:DisplayObject3D, 
        rotationX:Number=0, rotationY:Number=0, rotationZ:Number=0):void
    {
        if(isRotating)
            return;
            
        isRotating = true;
        var time:Number = shufflesLeft ? SHUFFLE_ROTAION_TIME : ROTAION_TIME;
        Tweener.addTween(wrapper, {time:time, transition: "easeInOutSine",
            onComplete:rotationComplete, onUpdate:renderFast,
            rotationX:rotationX, rotationY:rotationY, rotationZ:rotationZ});
    }
    
    private function rotationComplete():void
    {
        isRotating = false;
        unwrapAll();
        if(shufflesLeft && shufflesLeft--)
        {
            if(shufflesLeft)
                return shuffle(shufflesLeft);
            else
                dispatchEvent(new RCubeEvent(RCubeEvent.SHUFFLED, true));
        }
        renderFull();
        solvedCheck();
    }
    
    private function unwrapAll():void
    {
        for each(var wrapper:DisplayObject3D in scene.children)
        {
            if(wrapper is Cube || wrapper is Plane)
                continue;
            for each(var cube:Cube in wrapper.children)
                reparentCube(cube);
            scene.removeChild(wrapper);
        }
    }
    
    private function reparentCube(cube:Cube):void
    {
        cube.copyTransform(
            Matrix3D.multiply(Matrix3D.inverse(new Matrix3D()), cube.world));
        cube.parent.removeChild(cube);
        scene.addChild(cube);
    }
    
    private function layersByAxis(axis:String):Object
    {
        var k:int; 
        var layers:Object = {};
        layers[-1] = new Vector.<Cube>();
        layers[0] = new Vector.<Cube>();
        layers[1] = new Vector.<Cube>();
        
        for(var x:int = -1; x <= 1; x++)
        for(var y:int = -1; y <= 1; y++)
        for(var z:int = -1; z <= 1; z++)
        {
            k = axis == X ? x : (axis == Y ? y : z);
            layers[k].push(cubeActual(new Number3D(x, y, z)));
        }
        return layers;
    }
    
    public function rotateAnimation(axis:String, quadrant:int, rotaion:int):void
    {
        if(isRotating)
            return;
            
        var wrapper:DisplayObject3D;
        var layers:Object = layersByAxis(axis);
        
        for(var layerValue:String in layers)
        {
            wrapper = wrap(layers[layerValue]);
            if(int(layerValue) == quadrant)
                rotateWrapper(wrapper, 
                    axis == X ? rotaion : 0, 
                    axis == Y ? rotaion : 0, 
                    axis == Z ? rotaion : 0);
        }
    }
    
    public function rotate(axis:String, quadrant:int, rotaion:int):void
    {
        var wrapper:DisplayObject3D;
        var layers:Object = layersByAxis(axis);
        
        for(var layerValue:String in layers)
        {
            wrapper = wrap(layers[layerValue]);
            if(int(layerValue) == quadrant)
                wrapper['rotation' + axis.toUpperCase()]= rotaion;
        }
        renderFull();
    }
    
    public function shuffle(steps:uint):void
    {
        shufflesLeft = steps;
        var axises:Array = [X, Y, Z];
        var quadrants:Array = [-1, 0, 1];
        var rotations:Array = [90, -90];
        var axis:String = axises[Math.floor(Math.random() * 3)];
        var quadrant:int = quadrants[Math.floor(Math.random() * 3)];
        var rotation:int = rotations[Math.floor(Math.random() * 2)];
        rotateAnimation(axis, quadrant, rotation);
    }
    
    private function cubePressHandler(event:InteractiveScene3DEvent):void
    {
        if(!interactive || isRotating)
            return;
        pressCube = Cube(event.displayObject3D);
        pressPosition = getMousePosition(mouse);
        courseCube = null;
        coursePosition = null;
    }
    
    private function cubeOverHandler(event:InteractiveScene3DEvent):void
    {
        if(!interactive || isRotating)
            return;
        courseCube = Cube(event.displayObject3D);
        coursePosition = getMousePosition(mouse);
        
        if(isRotating || !possibleRotation(pressPosition, 
            coursePosition, pressCube, courseCube))
        {
            courseCube = null;
            coursePosition = null;
            rotationDart.visible = false;
        }
        else
        {
            var face:String = getFaceAxis(pressPosition, coursePosition);
            transformDart(face, pressCube, courseCube);
            rotationDart.visible = true;
        }
        renderFull();
    }
    
    public function release(... rest):void
    {
        if(!interactive || isRotating)
            return;
        if(courseCube)
        {
            var face:String = getFaceAxis(pressPosition, coursePosition);
            var course:String = getCourseAxis(pressCube, courseCube);
            var rotationAxis:String = getRemainingAxis(face, course);
            var quadrant:int = positionToQuadrant(pressCube[rotationAxis]);
            var rotation:int = getRotation(pressCube, courseCube, face);
            rotateAnimation(rotationAxis, quadrant, rotation);
        }
        pressPosition = null;
        pressCube = null;
        courseCube = null;
        coursePosition = null;
        rotationDart.visible = false;
        renderFull();
    }
    
    public function transformDart(face:String, cube1:Cube, cube2:Cube):void
    {
        var course:String = getCourseAxis(cube1, cube2);
        var lower:Boolean = cube1[course] < cube2[course];
        var rotation:String = getRemainingAxis(face, course);
        rotationDart[course] = 0;
        rotationDart[rotation] = cube1[rotation];
        rotationDart[face] = cube1[face] * 1.6;
        
        if(face == Z && course == X)
            return rotateDart(0, lower ? 0 : 180, 0);
        if(face == Z && course == Y)
            return rotateDart(0, lower ? 0 : 180, 90);
        if(face == X && course == Y)
            return rotateDart(90, lower ? 0 : 180, 90);
        if(face == X && course == Z)
            return rotateDart(90, lower ? -90 : 90, 90);
        if(face == Y && course == X)
            return rotateDart(90, lower ? 0 : 180, 0);
        if(face == Y && course == Z)
            return rotateDart(90, lower ? -90 : 90, 0);
    }
    
    private function rotateDart(x:int, y:int, z:int):void
    {
        rotationDart.rotationX = x;
        rotationDart.rotationY = y;
        rotationDart.rotationZ = z;
    }
    
    public static function positionToQuadrant(value:Number):int
    {
        return Math.min(Math.max(Math.round(
            (value - CUBE_SPACE) / CUBE_SIZE), -1), 1);
    }
    
    public static function get faceAxisPosition():int
    {
        return CUBE_SIZE * 3 / 2 + CUBE_SPACE;
    }
    
    public static function getMousePosition(mouse:Mouse3D):Number3D
    {
        return new Number3D(mouse.x, mouse.y, mouse.z);
    }
    
    public static function getFaceAxis(pos1:Number3D, pos2:Number3D):String
    {
        var r1x:int = Math.round(Math.abs(pos1.x));
        var r1y:int = Math.round(Math.abs(pos1.y));
        var r1z:int = Math.round(Math.abs(pos1.z));
        var r2x:int = Math.round(Math.abs(pos2.x));
        var r2y:int = Math.round(Math.abs(pos2.y));
        var r2z:int = Math.round(Math.abs(pos2.z));
        
        if(r1x == r2x && r1x == faceAxisPosition)
            return X;
        if(r1y == r2y && r1y == faceAxisPosition)
            return Y;
        if(r1z == r2z && r1z == faceAxisPosition)
            return Z;
        return null;
    }
    
    public static function getCourseAxis(cube1:Cube, cube2:Cube):String
    {
        var r1x:int = Math.round(cube1.x);
        var r1y:int = Math.round(cube1.y);
        var r1z:int = Math.round(cube1.z);
        var r2x:int = Math.round(cube2.x);
        var r2y:int = Math.round(cube2.y);
        var r2z:int = Math.round(cube2.z);
        
        if(r1x == r2x && r1y == r2y)
            return Z;
        if(r1x == r2x && r1z == r2z)
            return Y;
        if(r1y == r2y && r1z == r2z)
            return X;
        return null;
    }
    
    public static function getRemainingAxis(axis1:String, axis2:String)
        :String
    {
        if(axis1 != X && axis2 != X)
            return X;
        if(axis1 != Y && axis2 != Y)
            return Y;
        return Z;
    }
    
    public static function getRotation(cube1:Cube, cube2:Cube, 
        faceAxis:String):int
    {
        var courseAxis:String = getCourseAxis(cube1, cube2);
        var d:Boolean = cube1[courseAxis] > cube2[courseAxis];
        if((faceAxis == X && courseAxis == Y && cube1[faceAxis] > 0)
            || (faceAxis == X && courseAxis == Z && cube1[faceAxis] < 0)
            || (faceAxis == Y && courseAxis == X && cube1[faceAxis] < 0)
            || (faceAxis == Y && courseAxis == Z && cube1[faceAxis] > 0)
            || (faceAxis == Z && courseAxis == X && cube1[faceAxis] > 0)
            || (faceAxis == Z && courseAxis == Y && cube1[faceAxis] < 0))
            d = !d;
        return d ? 90 : -90;
    }
    
    public static function possibleRotation(position1:Number3D, 
        position2:Number3D, cube1:Cube, cube2:Cube):Boolean
    {
        if(!position1 || cube1 == cube2)
            return false;
            
        var face:String = getFaceAxis(position1, position2);
        if(!face)
            return false;
        
        var course:String = getCourseAxis(cube1, cube2);
        if(!course || course == face)
            return false;
        return true;
    }
}

internal class RCubeEvent extends Event
{
    public static const RENDER:String = "RCubeEventRENDER";
    public static const SELECTED:String = "RCubeEventSELECTED";
    public static const SHUFFLED:String = "RCubeEventSHUFFLED";
    public static const SOLVED:String = "RCubeEventSOLVED";
    public static const UNSOLVED:String = "RCubeEventUNSOLVED";
    
    private var _data:Object;
    
    public function RCubeEvent(type:String, bubbles:Boolean=false, 
        cancelable:Boolean=false, data:Object=null)
    {
        super(type, bubbles, cancelable);
        
        _data = data;
    }
    
    public function get data():Object
    {
        return _data;
    }
}