flash on 2013-2-6

by yurij.shaulov
♥0 | Line 371 | Modified 2013-02-06 22:55:21 | MIT License
play

ActionScript3 source code

/**
 * Copyright yurij.shaulov ( http://wonderfl.net/user/yurij.shaulov )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/vmyD
 */

// forked from makc3d's ericlin's EquationDisplayer

package {

    import flash.display.*;

    import flash.geom.*;

    import com.bit101.components.*;

    public class Test extends Sprite {

                public var ta:TextArea;

        public function Test () {

            ta = new TextArea (this, 10, 10,

                "ans=(-b+sqrt(b^2-4ac))/2a\n"+

                "ax^3+bx^2+cx+d=0\n"+

                "3+sqrt(sqrt(a^2+b^2))+pow(a,1/3)+exp(1234/5)\n"+

                "Y^4*(0.04/2)/39-cos(PI/2)=pow(2000/3*5,3)+sqrt(4/2)+sin(2)"

            );

            ta.width = 445; ta.height = 100;

            

            new PushButton (this, 10, 120, "render", render);

        }

        private function render (...fu):void {

            if (numChildren == 3) {

                removeChildAt (2);

            }

            

            var canvas:Sprite = new Sprite;

            canvas.x = 10; canvas.y = 140; addChild (canvas);

            

            var offset:int = 0;

            var lines:Array = ta.text.split (/[\n\r]+/);

            for (var i:int = 0; i < lines.length; i++) {

                var clip:EquationDisplayer = new EquationDisplayer;

                canvas.addChild (clip);

                clip.setEquation (lines[i]);

                clip.render ();

                var b:Rectangle = clip.getBounds(canvas);

                clip.y += offset-b.y;

                offset += clip.height+5;

            }

        }

    }

}



import flash.display.*;

import flash.geom.*;

import flash.text.*;



// port of http://ericlin2.tripod.com/math2/eqDisplayert.html

// this is dumb port: everything is public, needless code is possible



class EquationDisplayer extends Sprite {

    public var eqObject:EquationObject;

    public var equation:String = "";

    public function setEquation(str:String):void {

        eqObject = new EquationObject(str);

        equation = str;

    }

    public function setEquationObject(obj:EquationObject):void {

        eqObject = obj;

        equation = eqObject.equation;

    }

    public function getEquationObject():EquationObject {

        return eqObject;

    }

    public function clean():void {

        while (numChildren > 0) removeChildAt (0);

    }

    //######################################################

    public function render():void {

        clean();

        var obj:EquationObject = eqObject;

        var info:Object = obj.info;

        if (info == null) {

            return;

        }

        if (info.type == "operator") {

            operatorLayout(info);

            return;

        }

        if (info.type == "function") {

            functionLayout(info);

            return;

        }

        stringLayout(info);

    }

    //#######################################################

    public function lineUp(...args):void {

        //clips in arguments

        var offset:int = 0;

        for (var k:int = 0; k<args.length; k++) {

            var clip:Sprite = args[k];

            var b:Rectangle = clip.getBounds(clip.parent);

            clip.x += offset-b.x;

            offset += clip.width;

        }

    }

    //#########################################################

    public function operatorLayout(info:Object):void {

        var hiddenMultiplier:String = "\u0012";

        var operatorChar:String = info.typeData;

        var childObjects:Array = info.childObjects;

        var clip1:EquationDisplayer = new EquationDisplayer; addChild (clip1);

        clip1.setEquationObject(childObjects[0]);

        clip1.render();

        var clip2:EquationDisplayer = new EquationDisplayer; addChild (clip2);

        clip2.setEquationObject(childObjects[1]);

        clip2.render();

        //------------------------

        if (operatorChar == "^") {

            var b:Rectangle = clip1.getBounds(this);

            clip2.y = b.y+5;

            clip2.scaleX = clip2.scaleY=0.60;

            lineUp(clip1, clip2);

            return;

        }

        if (operatorChar == "/") {

            var w:Number = Math.max(clip1.width, clip2.width);

            var b1:Rectangle = clip1.getBounds(this);

            var b2:Rectangle = clip2.getBounds(this);

            var line:Sprite = attachMovie("divide", this);

            w = line.width = w+6;

            clip1.y += b1.y;

            clip1.x += (w/2-(b1.right+b1.x)/2);

            clip2.y += b2.bottom+3;

            clip2.x += (w/2-(b2.right+b2.x)/2);

            return;

        }

        //others

        var opClip:Sprite;

        if (operatorChar == "*") {

            opClip = attachMovie("multiply", this);

        } else if (operatorChar == hiddenMultiplier) {

            opClip = attachMovie("empty", this);

        } else {

            opClip = attachMovie("char", this, operatorChar);

        }

        lineUp(clip1, opClip, clip2);

    }

    //#####################################################

    public function functionLayout(info:Object):void {

        var functionName:String = info.typeData;

        var childObjects:Array = info.childObjects;

        var clipArray:Array = [];

        for (var i:int = 0; i<childObjects.length; i++) {

            var clip0:EquationDisplayer = clipArray[i]= new EquationDisplayer;

            addChild (clip0);

            clip0.setEquationObject(childObjects[i]);

            clip0.render();

        }

        //-----------------------

        if (functionName == "sqrt") {

            var line:Sprite = attachMovie("sqrtLine", this);

            var hook:Sprite = attachMovie("sqrtHook", this);

            var clip:Sprite = clipArray[0];

            var b:Rectangle = clip.getBounds(this);

            clip.y -= (b.y+b.bottom)/2;

            clip.x = hook.width+3;

            line.x = hook.width;

            hook.height = clip.height;

            var b2:Rectangle = hook.getBounds(this);

            line.y = b2.y;

            line.width = clip.width+6;

            return;

        }

        //-----------------------------------------------

        if (functionName == "pow") {

            var d:Number = 2;

            var clip1:Sprite = clipArray[0];

            var clip2:Sprite = clipArray[1];

            b = clip1.getBounds(this);

            //var offset = clip1._width;

            if (childObjects[0].info.type != "string") {

                //-------- add parenthesis to clip1 to avoid this condition: pow(3+4,2); ->2+4^2

                var leftParen:Sprite = attachMovie("char", this, "(");

                var rightParen:Sprite = attachMovie("char", this, ")");

                lineUp(leftParen, clip1, rightParen, clip2);

                leftParen.height = rightParen.height=2*Math.max(b.bottom, -b.y);

            } else {

                lineUp(clip1, clip2);

            }

            clip2.y = b.y+5;

            clip2.scaleX = clip2.scaleY=0.60;

            return;

        }

        //------------------------------------------------

        if (functionName == "exp") {

            clip1 = attachMovie("exp", this);

            clip2 = clipArray[0];

            clip2.x = clip1.width+4;

            b = clip1.getBounds(this);

            clip2.y = b.y;

            clip2.scaleX = clip2.scaleY=0.60;

            return;

        }

        //--------------------------------------------------

        d = 50;

        var funClip:Sprite = attachMovie("char", this, functionName);

        leftParen = attachMovie("char", this, "(");

        rightParen = attachMovie("char", this, ")");

        var tempArray:Array = [funClip, leftParen];

        var yMin:Number = 0;

        var yMax:Number = 0;

        for (var k:int = 0; k<clipArray.length; k++) {

            tempArray.push(clipArray[k]);

            b = clipArray[k].getBounds(this);

            if (b.y<yMin) {

                yMin = b.y;

            }

            if (b.bottom>yMax) {

                yMax = b.bottom;

            }

            if (k<clipArray.length-1) {

                var comaClip:Sprite = attachMovie("char", this, ",");

                tempArray.push(comaClip);

            }

        }

        tempArray.push(rightParen);

        lineUp.apply(null, tempArray);

        rightParen.height = leftParen.height=2*Math.max(yMax, -yMin);

    }

    //#######################################################

    public function stringLayout(info:Object):void {

        //trace("info="+info);

        //trace("stringData="+info.stringData);

        var str:String = info.typeData;

        //trace("---------------end str="+str);

        if ((str == "PI") || (str == "pi")) {

            attachMovie("pi", this);

            return;

        }

        var clip:Sprite = attachMovie("char", this, str);

    }

    public function attachMovie (movie:String, to:Sprite, label:String = null):Sprite {

        // this imitates original FLA assets

        var s:Sprite = new Sprite; s.graphics.lineStyle (0, 0);

        switch (movie) {

            case "divide":

                s.graphics.lineTo (25, 0);

                s.graphics.lineStyle();

                s.graphics.beginFill(0,0);

                s.graphics.drawRect(0,-1,25,1);

                s.graphics.endFill();

            break;

            case "multiply":

                s.graphics.moveTo (2, -2);

                s.graphics.lineTo (7, 3);

                s.graphics.moveTo (7, -2);

                s.graphics.lineTo (2, 3);

                s.graphics.lineStyle();

                s.graphics.beginFill(0,0);

                s.graphics.drawRect(0,-3,10,6);

                s.graphics.endFill();

            break;

            case "char":

            case "exp":

            case "pi":

                var italic:Boolean = (movie != "char");

                var tf:TextField = new TextField;

                tf.y = -14;

                tf.selectable = true;

                tf.autoSize = TextFieldAutoSize.LEFT;

                tf.defaultTextFormat = new TextFormat ('Times New Roman', 20, 0, false, italic);

                switch (movie) {

                    case "char":

                        tf.text = label;

                    break;

                    case "exp":

                        tf.text = "e";

                    break;

                    case "pi":

                        tf.text = "\u03C0";

                    break;

                }

                s.addChild(tf);

            break;

            case "sqrtLine":

                s.graphics.lineTo(43,0);

                s.graphics.lineStyle();

                s.graphics.beginFill(0,0);

                s.graphics.drawRect(0,-5.5,43.5,5.5);

                s.graphics.endFill();

            break;

            case "sqrtHook":

                s.graphics.moveTo(1,4);

                s.graphics.lineTo(2,1);

                s.graphics.lineTo(4,8);

                s.graphics.lineTo(10.5,-7.5);

            break;

        }

        to.addChild(s);

        return s;

    }

}



class EquationObject {

    public function EquationObject(str:String) {

        equation = str;

        parse();

    }

    //#########################################################

    public var equation:String = "";

    public var hiddenMultiplier:String = "\u0012";

    public var info:Object = {type:"string", typeData:"", childObjects:{}};

    //type: operator, function and string; and childObject

    //equation supplied by initObject

    public var operatorSet:String = "+-=><*/"+hiddenMultiplier+"^+-";

    //becareful, it is important  for the order of operator in this operatorSet 

    //the first  +- is for addition and subtracftion and the last +- is for sign

    //###############################################################

    public function setEquation(str:String):void {

        if (str == null) {

            str = "";

        }

        equation = str;

        parse();

    }

    //##############################################################

    public function parse():void {

        if (equation == null) {

            return;

        }

        var str:String = equation;

        //trim space

        str = str.split(" ").join("");

        //-----------------------------------------------------

        //handle the hidden multiplier such as (a-b)(c+d); there is an omitted multiplier between parenthesis block

        var strArr:Array = str.split(")");

        for (var k:int = 1; k<strArr.length; k++) {

            if (strArr[k].substr(0, 1) == "(") {

                strArr[k] = hiddenMultiplier+strArr[k];

            }

        }

        str = strArr.join(")");

        var str2:String = maskParenthesis(str);

        //search for  +,-,=,>,<    , skip string in parenthesis and cut into pieces accordingly

        //after the first step ,  only function and parenthesis and variable or number is left

        for (k = 0; k<operatorSet.length; k++) {

            var op:String = operatorSet.charAt(k);

            var start:int = str.length-1;

            var i:int = 0;

            while ((i=str2.lastIndexOf(op, start))>=0) {

                if ((k<=1) && operatorSet.indexOf(str2.charAt(i-1))>=0) {

                    //something like 3*-4;  

                    start = i-1;

                    continue;

                }

                //split  to recursive function and return;

                var lStr:String = str.substr(0, i);

                var rStr:String = str.substr(i+1, str.length-i-1);

                //trace(lStr+"["+op+"]"+rStr);

                var childObjects:Array = [];

                childObjects[0] = new EquationObject(lStr);

                childObjects[1] = new EquationObject(rStr);

                info = {type:"operator", typeData:op, childObjects:childObjects};

                return;

            }

        }

        //------------------------------------------

        //now we get only the parenthesis,function and pure number/variable here;

        start = str.indexOf("(");

        if (start>=0) {

            var functionName:String = (str.substr(0, start)).toLowerCase();

            var contentInParenthesis:String = str.substr(start+1, str.length-start-2);

            //trace("content="+contentInParenthesis);

            //there might be more than one parameters separated by ',', so parese the parameter and create clip for each parameter;

            childObjects = parseFunctionArgument(contentInParenthesis);

            info = {type:"function", typeData:functionName, childObjects:childObjects};

            //layoutManager.layout(info);

            //here arrange these clips, parenthesis and function name

            return;

        }

        //---------------------------------------------

        //After those steps , only pure number, and variable left here now

        info = {type:"string", typeData:str};

        //layoutManager.layout(info);

        //most of the time, we display Number, but sometime we need to convert PI  into symbol, so I add this function here

    }

    //#################################################

    public function parseFunctionArgument(str:String):Array {

        // cut arguments into parameters

        var childObjects:Array = [];

        var start:int = 0;

        var paramStr:Array = [];

        var parenthesisStack:int = 0;

        for (var i:int = 0; i<str.length; i++) {

            var char:String = str.substr(i, 1);

            if (char == "(") {

                parenthesisStack++;

            }

            if (char == ")") {

                parenthesisStack--;

            }

            if ((char == ",") && parenthesisStack == 0) {

                paramStr.push(str.substr(start, i-start));

                start = i+1;

            }

        }

        paramStr.push(str.substr(start, str.length-start));

        for (var j:int = 0; j<paramStr.length; j++) {

            childObjects[j] = new EquationObject(paramStr[j]);

        }

        return childObjects;

    }

    //---------------------------------------------------

    public function maskParenthesis(str:String):String {

        var parenthesisStack:int = 0;

        //prepare to skip the first level parenthesis

        var temp:Array = [];

        for (var i:int = 0; i<str.length; i++) {

            var char:String = str.substr(i, 1);

            if (char == ")") {

                parenthesisStack++;

            }

            if (char == "(") {

                parenthesisStack--;

            }

            if (parenthesisStack == 0) {

                temp.push(char);

            } else {

                temp.push("(");

            }

        }

        if (parenthesisStack != 0) {

            trace("parenthesis not match");

            return "";

        }

        var str2:String = temp.join("");

        //trace(str+"=="+str2);

        return str2;

    }

}