Rubik's Cube
shuffle
score
basicView + QuadRender
smooth orbiting
/**
* 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;
}
}