nb-sdouglas

cadnano/


a bit of a drag

2007-08-08
Attachments:

package
{
    import flash.text.*;
    import flash.events.*;
    import fl.events.SliderEvent;
    import flash.display.*;
    import flash.ui.Keyboard;
    import flash.utils.Timer;
    import de.polygonal.ds.*;
    import org.papervision3d.cameras.*;
    import org.papervision3d.events.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.scenes.*;

    //import org.papervision3d.components.as3.utils.ObjectController;

    public class Main extends Sprite {
        // Sizing
        public static const k_nodeRadius:int  = 10;
        public static const k_nodeHeight:int  = 36; // 0.34*21*radius/2
        public static const k_gridWidth:int   = 26;
        public static const k_gridHeight:int  = 26;
        public static const k_gridSpacing:int = k_nodeRadius * 2;

        // Node
        private var graph:Graph;
        private var grid:Array2;
        private var nodeCanvas:Sprite;
        private var nodeNumbers:LinkedStack;
        private var nodeCount:int = 0;
        // Path

        // 3D render
        private var container:Sprite;
        private var scene:Scene3D;
        private var camera:Camera3D;
        private var rootNode:DisplayObject3D;
        private var cylinderList:Array;
        private var angle:Number = 0;
        public static const camera_x_offset:int  = 150;
        public static const camera_y_offset:int  = 260;
        public static const camera_z_offset:int  = k_nodeHeight/2;

        // Misc
        public static var MyriadProFont:Font = new MyriadPro();

        public function Main() {
            configureListeners();
            stage.quality = "HIGH";
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            // 3D stuff
            cylinderList = new Array();

            // Creates scene
            container = new Sprite();
            container.x = 150;
            container.y = 20;
            renderPanel.addChild(container);
            scene = new Scene3D(container);
            rootNode = new DisplayObject3D("rootNode");
            scene.addChild(rootNode);

            // Creates cameras
            camera = new Camera3D();
            camera.zoom = 5;
            camera.focus = 200;
            camera.target.z = camera_z_offset;
            addEventListener(Event.ENTER_FRAME, tick);

            // Creates 2D slice grid
            grid = new Array2(k_gridWidth, k_gridHeight);
            graph = new Graph(grid.size);
            Node.RADIUS = k_nodeRadius;
            slicePanel.addChild(nodeCanvas = new Sprite());
            nodeCanvas.x = nodeCanvas.y = k_nodeRadius * 2;
            nodeNumbers = new LinkedStack();

            // Add a mask for slicePanel
            var rect:Shape = new Shape();
            rect.graphics.lineStyle(1, 0x000000);
            rect.graphics.beginFill(0xff0000);
            rect.graphics.drawRect(10, 10, 310, 570);
            rect.graphics.endFill();
            slicePanel.addChild(rect);
            nodeCanvas.mask = rect;

            nodeCanvas.x = -100;
            nodeCanvas.y = -100;
            nodeCanvas.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown)
            nodeCanvas.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);

            //create rectangular array of nodes
            var i:int = 0;
            var node:Node;
            for (var y:int = 0; y < grid.height; y++)
            {
                for (var x:int = 0; x < grid.width; x++)
                {
                    node = new Node(i);
                    nodeCanvas.addChild(node);

                    if ((x % 2) ^ (y % 2)) {
                        node.y = y*k_nodeRadius*3*1.05 + k_nodeRadius;
                    } else {
                        node.y = y*k_nodeRadius*3*1.05;
                    }
                    node.x = x*k_nodeRadius*1.7320*1.1

                    var tf:TextField = node.getChildAt(0) as TextField;
                    var tfm:TextFormat = tf.getTextFormat();
                    tfm.size = 14;
                    tf.defaultTextFormat = tfm;

                    graph.addNode(node, i);

                    grid.set(x, y, node);
                    i++;

                    node.addEventListener(MouseEvent.MOUSE_MOVE, onMove, false, 1000);
                    node.addEventListener(MouseEvent.MOUSE_OUT,  onMouseOut, false, 1000);
                    node.addEventListener(MouseEvent.CLICK, onClick, false, 1000);
                }
            }
        }

        private function mouseDown(event:MouseEvent):void {
            nodeCanvas.startDrag();
        }


        private function mouseReleased(event:MouseEvent):void {
            nodeCanvas.stopDrag();
        }


        private function addCylinder(p_x:Number, p_y:Number, p_z:Number):void {
            var cylinderIndex:uint = cylinderList.length;
            var newCylinderMaterial:CompositeMaterial = new CompositeMaterial()
            newCylinderMaterial.addMaterial(new ColorMaterial(0x03a3f0));
            newCylinderMaterial.addMaterial(new WireframeMaterial(0x000000));
            var newCylinder:Cylinder = new Cylinder(newCylinderMaterial, k_nodeRadius+2, k_nodeHeight, 10, 6, 0, null);
            newCylinder.rotationX = 90;
            rootNode.addChild(newCylinder);
            cylinderList[cylinderIndex] = newCylinder;
            newCylinder.x = p_x;
            newCylinder.y = p_y;
            newCylinder.z = p_z;
        }

        private function tick(event:Event):void {
            // A tick of the engine
            camera.x = Math.round(Math.cos(angle)*750 + camera_x_offset);
            camera.z = Math.round(Math.sin(angle)*750 + camera_z_offset);
            angle += 0.03;
            scene.renderCamera(camera);
            basePanel.renderText.text = "cam.x: " + camera.z.toString() +
                                        "  cam.z: " + camera.z.toString();
        }

        private function configureListeners():void {
            basePanel.zoomslider.addEventListener(SliderEvent.CHANGE, sliderChanged);
            basePanel.zoomslider.addEventListener(SliderEvent.THUMB_RELEASE, sliderRelease);
            basePanel.focusslider.addEventListener(SliderEvent.CHANGE, focussliderChanged);
            basePanel.focusslider.addEventListener(SliderEvent.THUMB_RELEASE, foucssliderRelease);
        }

        private function sliderRelease(e:SliderEvent):void {
            camera.zoom = e.target.value;
        }

        private function sliderChanged(e:SliderEvent):void {
            basePanel.zoomsliderLabel.text =  e.target.value;
        }
        private function foucssliderRelease(e:SliderEvent):void {
            camera.y = e.target.value;
        }

        private function focussliderChanged(e:SliderEvent):void {
            basePanel.focussliderLabel.text =  e.target.value;
        }

        private function onClick(e:MouseEvent):void
        {
            // check if target is marked
            if (e.target.marked) {
                // check if pairedNeighbor is marked
                if (e.target.pairedNeighbor.marked) {
                    var a:Array = new Array();
                    // unmark and recover numbers
                    a.push(e.target.unmark());
                    a.push(e.target.pairedNeighbor.unmark());
                    a.sort();
                    nodeNumbers.push(a[0]);
                    nodeNumbers.push(a[1]);

                    // unpair
                    e.target.neighbor.pairedNeighbor = null;
                    e.target.pairedNeighbor = null;
                }
            } else if (!e.target.marked) { // target is unmarked
                if (e.target.neighbor == null) {
                    return; // return if no neighbors
                }
                if (!e.target.neighbor.marked) { // if neighbor is unmarked
                    var parity:int;
                    parity = getNodeParity(e.target);

                    //var s:int = nodeNumbers.size();
                    if (nodeNumbers.peek() == null) {
                        nodeNumbers.push(nodeCount);
                        nodeCount += 1;
                        nodeNumbers.push(nodeCount);
                        nodeCount += 1;
                    }

                    var odd:int = nodeNumbers.pop();
                    var even:int = nodeNumbers.pop();
                    // mark nodes
                    if (parity == 1) {
                        e.target.neighbor.mark(even);
                        e.target.mark(odd);
                    } else {
                        e.target.mark(even);
                        e.target.neighbor.mark(odd);
                    }

                    // pair nodes
                    e.target.pairedNeighbor = e.target.neighbor;
                    e.target.neighbor.pairedNeighbor = e.target;

                    // Path update


                    // 3D render
                    addCylinder(e.target.x, -e.target.y, 0);
                    addCylinder(e.target.neighbor.x, -e.target.neighbor.y, 0);
                    centerXY();
                }
            }
        }

        private function getDist(x1:int,x2:int,y1:int,y2:int):int {
            return Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2));
        }

        private function centerXY() {
            var n:Node;
            var maxX:int = 0;
            var maxY:int = 0;
            var minX:int = 9999;
            var minY:int = 9999;

            var midX:int = 0;
            var midY:int = 0;

            for (var row:int = 0; row < grid.height; row++)
            {
                for (var col:int = 0; col < grid.width; col++)
                {
                    n = grid.get(col, row);
                    if (!n.marked) continue;
                    if (n.x > maxX) {
                        maxX = n.x;
                    }
                    if (n.x < minX) {
                        minX = n.x;
                    }
                    if (n.y > maxY) {
                        maxY = n.y;
                    }
                    if (n.y < minY) {
                        minY = n.y;
                    }
                }
            }
            midX = (maxX+minX)/2;
            midY = (maxY+minY)/2;
            camera.target.x = midX;
            camera.target.y = -(midY - camera_y_offset);
            camera.y = -(midY-camera_y_offset);
            trace(camera.y)
        }

        private function getNodeParity(n:Object):int
        {
            var rowcol:Array = [];
            var row:int;
            var col:int;
            var parity:int;
            rowcol = grid.rowcolIndex(n); // get (x,y) coords of node
            row = rowcol[0];
            col = rowcol[1];
            parity = (row % 2) ^ (col % 2); // get parity
            return parity;
        }

        private function getRowColParity(row:int, col:int):int
        {
            return (row % 2) ^ (col % 2);
        }

        private function getRowCol(n:Object):Array
        {
            var rowcol:Array = [];
            rowcol = grid.rowcolIndex(n);
            return rowcol
        }

        private function onMouseOut(e:MouseEvent):void
        {
            var neighbors:Array = new Array();
            var rowcol:Array;
            var row:int;
            var col:int;
            var parity:int;
            rowcol = getRowCol(e.target);
            row = rowcol[0];
            col = rowcol[1]
            parity = getRowColParity(row, col);

            // get row + 1 if valid
            if (row < k_gridWidth-1) {
                neighbors.push(grid.get(row+1,col));
            }

            // get row - 1 if valid
            if (row > 0) {
                neighbors.push(grid.get(row-1,col));
            }

            if (parity == 1) { // get col + 1 if valid
                if (col < k_gridHeight-1) {
                    neighbors.push(grid.get(row,col+1));
                }
            } else { // get col+1 if valid
                if (col > 0) {
                    neighbors.push(grid.get(row,col-1));
                }
            }
            var i:int;
            for (i = 0; i < neighbors.length; i++)
            {
                neighbors[i].unhover()
            }
            e.target.unhover();
        }

        private function onMove(e:MouseEvent):void
        {
            var x:int = e.target.x;
            var y:int = e.target.y;
            var row:int;
            var col:int;
            var rowcol:Array = [];
            var parity:int;
            var dist:int;
            var minDist:int = 9999;
            var neighbor:Node;
            var pairedNeighbor:Node;
            var nearestNeighbor:Node;
            var neighbors:Array = new Array();
            var nearestNeighborIndex:int;
            var info:String = "";

            rowcol = getRowCol(e.target);
            row = rowcol[0];
            col = rowcol[1];
            parity = getRowColParity(row, col);

            info += "mouse:" + e.stageX + "," + e.stageY + "\n";
            info += "node " + e.target.graphIndex + ": " + x + "," + y + "\n";
            info += "marked: " + e.target.marked + "\n";
            info += "neighbor: " + e.target.neighbor +  "\n";
            if (e.target.pairedNeighbor != null) {
                pairedNeighbor = e.target.pairedNeighbor;
                e.target.pairedhover();
                pairedNeighbor.pairedhover();
                basePanel.sliceText.text = info;
                return;
            }

            // get row + 1 if valid
            if (row < k_gridWidth-1) {
                neighbor = grid.get(row+1,col)
                neighbors.push(neighbor);
            }

            // get row - 1 if valid
            if (row > 0) {
                neighbor = grid.get(row-1,col)
                neighbors.push(neighbor);
            }

            if (parity == 1) { // get col + 1 if valid
                if (col < k_gridHeight-1) {
                    neighbor = grid.get(row,col+1)
                    neighbors.push(neighbor);
                }
            } else { // get col+1 if valid
                if (col > 0) {
                    neighbor = grid.get(row,col-1)
                    neighbors.push(neighbor);
                }
            }

            var i:int;
            if (neighbors.length > 0) {
                for (i = 0; i < neighbors.length; i++) {
                    dist = getDist(e.stageX,neighbors[i].x+20,
                                   e.stageY,neighbors[i].y+20);
                    info += "[" + neighbors[i].graphIndex + ":" +
                            dist + "] ";
                    if (neighbors[i].pairedNeighbor != null) {
                        continue;
                    }
                    if (dist < minDist) {
                        minDist = dist;
                        nearestNeighborIndex = i;
                    }
                }

                nearestNeighbor = neighbors[nearestNeighborIndex]
                if (nearestNeighbor == pairedNeighbor) {
                    nearestNeighbor.pairedhover();
                    neighbors.splice(nearestNeighborIndex,1);
                } else if (nearestNeighbor.pairedNeighbor != null) {
                    return;
                } else {
                    nearestNeighbor.hover();
                    e.target.neighbor = nearestNeighbor;
                    e.target.neighbor.neighbor = e.target;
                    neighbors.splice(nearestNeighborIndex,1);
                }

                for (i = 0; i < neighbors.length; i++)
                {
                    neighbors[i].unhover();
                }
            }

            basePanel.sliceText.text = info;
        }
    }
}