/**
* Copyright J.J ( http://wonderfl.net/user/J.J )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/jjoU
*/
package {
import flash.display.Shape;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextField;
import flash.filters.DropShadowFilter;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.display.Sprite;
/**
* @author JavidJafari
* is ported from haXe version
* use "+" and "-" to zoomIn and zoomOut
*/
public class Plotter extends Sprite {
private var w : Number;
private var h : Number;
private var upper : Vec2D;
private var lower : Vec2D;
private var graph : GPlotter;
private var m : Vec2D;
private var plots:Vector.<Vec2D>
public var isDown:Boolean = false;
private var offset:Vec2D=new Vec2D()
private var mouse:Vec2D=new Vec2D();
public var keyIsDown : Boolean = false;
private var btn : Sprite;
private var txt :TextField;
private var equation : String;
private var dot : Shape;
public function Plotter() {
dot = new Shape();
dot.graphics.beginFill(0xffaa00);
dot.graphics.drawCircle(0, 0, 3);
dot.graphics.endFill();
addChild(dot)
w=stage.stageWidth;
h=stage.stageHeight;
btn=new Sprite()
btn.graphics.beginFill(0xaa0000)
btn.graphics.drawRect(10, 5, 50, 20)
addChild(btn)
txt = new TextField();
var dr:DropShadowFilter = new DropShadowFilter(1);
txt.filters = [dr];
btn.filters=[dr]
var texf:TextFormat = new TextFormat();
texf.font = "Consolas";
txt.setTextFormat(texf);
txt.defaultTextFormat = texf;
txt.text =equation= "x^2";
txt.width=400
//txt.borderColor = 0x000000;
//txt.border = true;
txt.type = TextFieldType.INPUT;
txt.height = 20;
txt.y=4
txt.x=btn.width+btn.x+15
addChild(txt)
var label:TextField=new TextField()
label.textColor=0xffffff
label.filters=[dr]
label.text="draw"
label.selectable=false;
label.x=btn.width/2.5
label.y=btn.height/4
label.setTextFormat(texf)
btn.addChild(label)
btn.addEventListener(MouseEvent.MOUSE_DOWN, ondwon)
Evaulator.eval("5+4")
Display.setCanvas(this);
upper=new Vec2D();
lower=new Vec2D(w,h);
graph=new GPlotter(upper, lower);
m=graph.getOrigin().clone();
this.stage.addEventListener(Event.ENTER_FRAME, loop)
stage.addEventListener(KeyboardEvent.KEY_DOWN, keydown)
stage.addEventListener(KeyboardEvent.KEY_UP, keyup)
stage.addEventListener(MouseEvent.MOUSE_DOWN, md)
stage.addEventListener(MouseEvent.MOUSE_UP, mu)
render()
}
private function ondwon(event : MouseEvent) : void {
equation=txt.text;
render()
}
private function mu(event : MouseEvent) : void {
isDown = false;
}
private function md(event : MouseEvent) : void {
mouse.setTo(mouseX,mouseY)
if(mouse.x>0 && mouse.x<160 && mouse.y>0 && mouse.y<30 ){
}else {
offset = mouse.to(graph.getOrigin());
isDown = true;
}
}
private function keyup(event : KeyboardEvent) : void {
isDown = false;
keyIsDown = false;
}
private function keydown(event : KeyboardEvent) : void {
var kd:int = event.keyCode;
if (kd == 107 || kd == 187) {
graph.zoomIn();
keyIsDown = true;
isDown = true;
}else if (kd == 109 || kd==189) {
isDown = true;
keyIsDown = true;
graph.zoomOut();
//render();
}
}
private function loop(event : Event) : void {
if (isDown) {
m.setTo(mouseX,mouseY);
m.add(offset);
if(!keyIsDown) graph.setOrign(m);
render();
}
var i:int = 0;
var lens:int = plots.length;
var isit:Vec2D = null;
var mps:Number = mouseX;
var l:Number = mps - 1;
var r:Number = mps + 1;
while (i < lens) {
var a:Vec2D = plots[i];
if (check(a)) {
if (a.x > l && a.x < r) isit = a;
}
i++;
}
if (isit != null ) {
dot.x = isit.x;
dot.y = isit.y;
}
else {
dot.x = -10;
dot.y =-400;
}
}
private function drawFunc():void{
plots=graph.getPlots(equation)
var i:int = 0;
var lens:int = plots.length - 1;
while (i < lens) {
var a:Vec2D = plots[i];
var b:Vec2D = plots[i + 1];
// if (b.y > lower.y) b.y = lower.y + 2;
// else if (b.y < upper.y) b.y = upper.y;
if (check(a) && check(b)) {
Display.drawLine(a, b,0x11aaff);
}
//0x11aaff
i++;
}
}
private function check(a:Vec2D): Boolean {
var offs:Number = 20;
var a1:Boolean = (a.x<=-offs || a.x>=w+offs);
if (a1) return false;
var a2:Boolean = (a.y<=-offs || a.y>=h+offs);
if (a2) return false;
return true;
}
public function render():void {
var O:Vec2D=graph.getOrigin();
Display.clear();
Display.drawGrid(GPlotter.SCALE_FACTOR,O);
Display.drawDot(O);
Display.drawLine2(O.x, 0, O.x, lower.y);
Display.drawLine2(0, O.y, lower.x, O.y);
drawFunc();
}
}
}
import flash.display.Sprite;
import flash.display.Graphics;
//Display
class Display {
private static var w:Number;
private static var h:Number;
private static var canvas:Graphics;
public static function setCanvas(s:Sprite):void {
canvas = s.graphics;
h = s.stage.stageHeight;
w = s.stage.stageWidth;
}
public static function drawLine2(ax:Number,ay:Number,bx:Number,by:Number,c:uint=0,r:Number=1):void {
canvas.lineStyle(r, c);
canvas.moveTo(ax, ay);
canvas.lineTo(bx, by);
canvas.endFill();
}
public static function drawLine(a:Vec2D,b:Vec2D,c:uint=0,r:Number=1):void {
canvas.lineStyle(r, c);
canvas.moveTo(a.x, a.y);
canvas.lineTo(b.x, b.y);
canvas.endFill();
}
public static function drawDot(v:Vec2D, r:Number = 2, c:uint=0):void {
canvas.beginFill(c);
canvas.lineStyle(1, c);
canvas.drawCircle(v.x, v.y, r);
canvas.endFill();
}
public static function clear():void {canvas.clear();}
public static function drawGrid(offset:Number,orign:Vec2D):void {
var x:Number = orign.x;
var y:Number = orign.y;
while (x < w) {
if(!(x<0)){
drawLine2(x, 0, x, h, 0xcccccc);
}
x += offset;
}
x = orign.x-offset;
while (x >0) {
if(!(x>w)){
drawLine2(x, 0, x, h, 0xcccccc);}
x -= offset;
}
while (y < h) {
if(!(y<0)) {
drawLine2(0, y, w, y, 0xcccccc);
}
y += offset;
}
y = orign.y - offset;
while (y >0) {
if(!(y>h)) {
drawLine2(0, y, w, y, 0xcccccc);
}
y -= offset;
}
}
}
//GPlotter
class GPlotter{
public var nativeBound:AABB;
public var mapBound:AABB;
public static var SCALE_FACTOR:Number=10;
private var ORIGIN:Vec2D;
private var plots:Vector.<Vec2D>;
private var ft:Number=.5;
private var w:Number;
private var h:Number;
private var theX:Variable;
public function GPlotter(min:Vec2D,max:Vec2D){
w=max.x;
h=max.y;
plots=new Vector.<Vec2D>();
for(var i:int=0;i<w+1;i++){
plots.push(new Vec2D(i,0))
}
trace(plots.length)
var half:Vec2D=max.scaleBy(.5);
Coordinate.setTranslationVector(half);
Coordinate.setScaleFactor(SCALE_FACTOR);
this.nativeBound=new AABB(min, max);
this.mapBound=new AABB(Coordinate.toMap(min.clone()), Coordinate.toMap(max.clone()));
ORIGIN=Coordinate.toNative(new Vec2D(0,0));
theX=Evaulator.getVariable("x");
}
public function zoomIn():void {
var min:Vec2D = Coordinate.toNative(mapBound.min);
var max:Vec2D = Coordinate.toNative(mapBound.max);
SCALE_FACTOR++;
if (SCALE_FACTOR <= 2) SCALE_FACTOR = 2;
ft = Coordinate.map(SCALE_FACTOR, 100, 2, .05, .5);
if (ft < .05) ft = .05;
if (ft > .5) ft = .5;
Coordinate.setScaleFactor(SCALE_FACTOR);
mapBound.min = Coordinate.toMap(min);
mapBound.max =Coordinate.toMap(max);
}
public function zoomOut():void {
var min:Vec2D = Coordinate.toNative(mapBound.min);
var max:Vec2D = Coordinate.toNative(mapBound.max);
SCALE_FACTOR--;
if (SCALE_FACTOR <= 2) SCALE_FACTOR = 2;
ft = Coordinate.map(SCALE_FACTOR, 100, 2, .05, .5);
if (ft < .05) ft = .05;
if (ft > .5) ft = .5;
Coordinate.setScaleFactor(SCALE_FACTOR);
mapBound.min = Coordinate.toMap(min);
mapBound.max = Coordinate.toMap(max);
}
public function getOrigin():Vec2D{return ORIGIN;}
public function setOrign(v:Vec2D):void {
var o1:Vec2D = ORIGIN.clone();
o1 = Coordinate.toMap(o1);
ORIGIN.x = v.x;
ORIGIN.y = v.y;
var o2:Vec2D = v;
o2 = Coordinate.toMap(o2);
o2.sub(o1)
var delta:Vec2D = o2;
//o1.to(o2);
mapBound.min.sub(delta);
mapBound.max.sub(delta);
}
public function getPlots(equations:String="cos(x)+sin(x)"):Vector.<Vec2D>{
var o:Vec2D = Coordinate.toMap(ORIGIN.clone());
//var ex:EReg = ~/jp/g;
var x:Number =mapBound.min.x;
var saghf:Number = mapBound.max.x;
var fact:Number = (saghf - x)*(1/w);
var p:Number = 0;
while (p<=w) {
x += fact;
//Evaulator.changeVariable("x", x);
theX.setValue(x);
var t:Number = Evaulator.eval(equations);
//Math.cos(x)+Math.sin(x)
// Evaulator.eval(equations);
var y:Number = t;
var v:Vec2D =plots[p];
v.y=y;
v.y+=o.y;
//to native coord
v.y*=-Coordinate.t
v.y+=Coordinate.indica.y;
p++;
}
//ORIGIN=Coordinate.toNative(o);
return plots;
}
}
//GeoGebra Tools
class Coordinate {
public static var t:Number;
public static var indica:Vec2D;
public static function setTranslationVector(v:Vec2D):void{
indica=v;
}
public static function setScaleFactor(sf:Number):void{
t=sf;
}
public static function toNative(v:Vec2D):Vec2D {
v.x *=t;
v.y *=-t;
v .add(indica);
return v;
}
public static function toMap(v:Vec2D):Vec2D {
v.sub(indica);
v.x /= t;
v.y /= -t;
return v;
}
public static function map(X:Number, a1:Number, a2:Number, b1:Number, b2:Number):Number {
//a a1 + b = b1
//a a2 + b = b2
// -a a2 -b=-b2
//a a1 -a a2=b1-b2
//a(a1-a2)=b1-b2
var a:Number = (b1 - b2) / (a1 - a2);
var b:Number = b1 - (a * a1);
return (a * X) + b;
}
}
class AABB{
//Axis-Aligned Bounding Box
public var min:Vec2D;
public var max:Vec2D;
public function AABB(min:Vec2D,max:Vec2D){
this.min=min;
this.max=max;
}
public function contains(v:Vec2D):Boolean {
if (v.x<min.x || v.x>max.x) return false;
if (v.y<max.y || v.y>min.y) return false;
return true;
}
}
class Vec2D {
public var x:Number;
public var y:Number
public function Vec2D(x:Number=0,y:Number=0):void{
this.x=x;
this.y=y;
}
public function add(v:Vec2D):void{
this.x+=v.x;this.y+=v.y;
}
public function sub(v:Vec2D):void{
this.x-=v.x;this.y-=v.y;
}
public function addition(v:Vec2D):Vec2D{
return new Vec2D(this.x+v.x,this.y+v.y)
}
public function subtraction(v:Vec2D):Vec2D{
return new Vec2D(this.x-v.x,this.y-v.y)
}
public function to(v:Vec2D):Vec2D{
return new Vec2D(v.x-this.x,v.y-this.y)
}
public function scale(k:Number):void{
this.x*=k;
this.y*=k;
}
public function scaleBy(k:Number):Vec2D{
return new Vec2D(this.x*k,this.y*k);
}
public function dot(v:Vec2D):Number{
return this.x*v.x+this.y*v.y;
}
public function cross(v:Vec2D):Number{
return this.x*v.y-this.y*v.x;
}
public function length():Number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
public function normalize():void{
var len:Number=1/this.length();
this.x*=len;
this.y*=len;
}
public function clone():Vec2D{
return new Vec2D(this.x,this.y);
}
public function setTo(x:Number,y:Number):void{this.x=x;this.y=y;}
}
//Expression Evaulator
import flash.utils.Dictionary;
class Evaulator {
private static var exp:Vector.<ExpEntity>;
private static var clone:Vector.<ExpEntity>;
private static var operations:Dictionary;
private static var functions:Dictionary;
private static var opCount:int=0;
private static var idx:IndexData;
private static var variables:Dictionary;
private static var isInitialized:Boolean=false;
private static function initialiZe():void{
exp=new Vector.<ExpEntity>()
variables=new Dictionary();
operations=new Dictionary()
functions=new Dictionary();
idx=new IndexData();
//add variables
addVariable(new PI());
addVariable(new X());
//add operators
addOperator(new Division());
addOperator(new Multiplication());
addOperator(new Power());
addOperator(new Subtraction());
addOperator(new Addition());
//add functions
addFunction(new Sineous());
addFunction(new Cosinus());
addFunction(new Tangent());
addFunction(new Square());
addFunction(new Logarithem());
addFunction(new Absolute());
addFunction(new Exponen());
}
public static function eval(expr:String):Number{
if(isInitialized==false){
initialiZe();
isInitialized=true;
}
expr="("+expr+")";
exp=toArray(expr)
var opLen:int=opCount;
while(hasOpenParenthesis()){
idx=getDeepestExpression(0, exp.length);
for(var i:int=0;i<opLen;i++){
idx=doOperation(operations[i], idx.start, idx.end)
}
if(exp[idx.start - 1].type==EntityType.STRING){
var ch:String = exp[idx.start - 1].stringData;
//make sure is function
if (ch.length > 1) {
var func:MathFunction=functions[ch] as MathFunction;
var ex:ExpEntity=exp[idx.start];
ex.setFloat(func.doOperation(ex.floatData))
//exp[idx.start].setFloat(func.doOperation(exp[idx.start].floatData));
}
//remove parant
exp.splice(idx.start - 1, 1);
exp.splice(idx.end - 1, 1);
}
}
//trace(exp.length)
return exp[0].floatData;
}
private static function copy(ar:Vector.<ExpEntity>):Vector.<ExpEntity>{
var len:int=ar.length;
var nw:Vector.<ExpEntity>=new Vector.<ExpEntity>()
for (var i : int = 0; i < len; i++) {
nw.push(ar[i])
}
return nw;
}
public static function print(ar:Vector.<ExpEntity>):void{
var buf:String="";
for each (var o :ExpEntity in ar) {
buf+=o.toString()+",";
}
trace(buf)
}
private static function doOperation(op:Operator,lo:int,hi:int):IndexData{
var i:int=lo;
while(i<hi){
// var ex:ExpEntity=exp[i];
if(exp[i].type==EntityType.STRING && exp[i].stringData==op.getName()){
var l:ExpEntity=exp.splice(i-1, 1)[0];
var r:ExpEntity=exp.splice(i, 1)[0];
exp[i-1].setFloat(op.doOperation(l.floatData, r.floatData));
l.destroy()
r.destroy();
hi-=2;
i--;
}
i++;}
idx.start=lo;
idx.end=hi;
return idx;
}
private static function getDeepestExpression(lo:int,hi:int):IndexData{
idx.start=0;
idx.end=0;
var len:int=exp.length;
var index:int=-1;
for(var i:int=lo;i<hi;i++){
var ex:ExpEntity=exp[i];
if (ex.type == EntityType.STRING) {
var str:String = ex.stringData;
var ch:String = str.charAt(exp[i].stringData.length-1);
if (ch == "(") index = i;
}
}
idx.start = ++index;
var subexp:String = "";
while (index < hi) {
if (exp[index].type == EntityType.STRING && exp[index].stringData == ")") break;
index++;
}
idx.end = index;
return idx;
}
private static function toArray(expr:String):Vector.<ExpEntity>{
exp.length=0;
var expression:Vector.<ExpEntity>=exp;
var i:int=0;
var len:int=expr.length;
while(i<len){
var buf:String = "";
var ex:ExpEntity;
if (isOperand(expr.charAt(i))) {
buf="";
while (isOperand(expr.charAt(i))) { buf += expr.charAt(i++); }
i--;
ex = new ExpEntity();
ex.setFloat(parseFloat(buf));
expression.push(ex);
}else if (isLetter(expr.charAt(i))) {
buf="";
while (isLetter(expr.charAt(i))) {
buf += expr.charAt(i);
if (expr.charAt(i++) == "(") break;
}
i--;
if (existVariable(buf)) {
ex= new ExpEntity();
var vari:Variable=variables[buf] as Variable;
ex.setFloat(vari.getValue());
expression.push(ex);
}
else {
ex = new ExpEntity();
ex.setString(buf);
expression.push(ex);
};
}else {
ex= new ExpEntity();
ex.setString(expr.charAt(i));
expression.push(ex);
}
i++;}
return expression;
}
private static function existVariable(s:String):Boolean{
return variables[s]!=undefined;
}
private static function isOperand(ch:String):Boolean{
var f:Number=parseFloat(ch);
return !isNaN(f);
}
private static function hasOpenParenthesis():Boolean {
var len:int=exp.length;
for(var i:int=0;i<len;i++){
var o:ExpEntity=exp[i];
if(o.type==EntityType.STRING){
if (o.stringData.charAt(o.stringData.length - 1) == "(") return true;
}
}
return false;
}
private static function isLetter(c:String):Boolean{
var s:int = c.charCodeAt(0);
//94 is the charCode of ^
return (s >= 65 && s <= 122 && s!=94) || c=="(";
}
public static function addOperator(op:Operator):void {
operations[opCount++]=op;
}
public static function addVariable(vari:Variable):void {
variables[vari.getName()]= vari;
}
public static function changeVariable(key:String, value:Number):void {
var vr:Variable=variables[key] as Variable;
vr.setValue(value);
}
public static function getVariable(key:String):Variable{
var vr:Variable=variables[key] as Variable;
return vr;
}
public static function addFunction(func:MathFunction):void {
functions[func.getName()+"("]= func;
}
}
class IndexData {
public function IndexData(){};
public var start:int;
public var end:int;
}
class EntityType{
public static var STRING:String="string";
public static var NUMBER:String="number";
}
class ExpEntity{
public var type:String;
public var floatData:Number;
public var stringData:String;
public function setString(value:String):void{
this.type=EntityType.STRING;
this.stringData=value;
this.floatData=NaN;
}
public function setFloat(value:Number):void{
this.type=EntityType.NUMBER;
this.floatData=value;
this.stringData=null;
}
public function toString():String {
if(type==EntityType.NUMBER){
return floatData + "";
}
else return stringData;
}
public function destroy():void {
floatData =NaN
stringData = null;
}
}
//interface s
interface Operator {
function getPriority():int;
function getName():String;
function doOperation(a:Number,b:Number):Number;
}
interface MathFunction {
function getName():String;
function doOperation(a:Number):Number;
}
interface Variable{
function getName():String;
function getValue():Number;
function setValue(a:Number):void;
}
//variables
class X implements Variable{
private var name:String;
private var value:Number;
public function X(){
this.name="x";
this.value=2;
}
public function getName():String{return this.name;}
public function getValue():Number{return this.value}
public function setValue(a:Number):void{this.value=a;}
}
class PI implements Variable{
private var name:String;
private var value:Number;
public function PI(){
this.name="pi";
this.value=Math.PI;
}
public function getName():String{return this.name;}
public function getValue():Number{return this.value}
public function setValue(a:Number):void{this.value=a;}
}
//Operators
class Power implements Operator{
private var name:String;
private var priority:int;
public function Power(){
this.priority=1;
this.name="^";
}
public function getPriority():int {return priority;};
public function getName():String{return name;}
public function doOperation(a:Number,b:Number):Number{
return Math.pow(a, b);
}
}
class Multiplication implements Operator{
private var name:String;
private var priority:int;
public function Multiplication(){
this.priority=1;
this.name="*";
}
public function getPriority():int {return priority;};
public function getName():String{return name;}
public function doOperation(a:Number,b:Number):Number{
return a*b;
}
}
class Division implements Operator{
private var name:String;
private var priority:int;
public function Division(){
this.priority=1;
this.name="/";
}
public function getPriority():int {return priority;};
public function getName():String{return name;}
public function doOperation(a:Number,b:Number):Number{
return a/b;
}
}
class Addition implements Operator{
private var name:String;
private var priority:int;
public function Addition(){
this.priority=3;
this.name="+";
}
public function getPriority():int {return priority;};
public function getName():String{return name;}
public function doOperation(a:Number,b:Number):Number{
return a+b;
}
}
class Subtraction implements Operator{
private var name:String;
private var priority:int;
public function Subtraction(){
this.priority=4;
this.name="-";
}
public function getPriority():int {return priority;};
public function getName():String{return name;}
public function doOperation(a:Number,b:Number):Number{
return a-b;
}
}
//function(s)
class Sineous implements MathFunction{
private var name:String;
public function Sineous(){
this.name="sin"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.sin(a);
}
}
class Cosinus implements MathFunction{
private var name:String;
public function Cosinus(){
this.name="cos"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.cos(a);
}
}
class Tangent implements MathFunction{
private var name:String;
public function Tangent(){
this.name="tan"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.tan(a);
}
}
class Logarithem implements MathFunction{
private var name:String;
public function Logarithem(){
this.name="log"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.log(a);
}
}
class Exponen implements MathFunction{
private var name:String;
public function Exponen(){
this.name="exp"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.exp(a);
}
}
class Absolute implements MathFunction{
private var name:String;
public function Absolute(){
this.name="abs"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.abs(a);
}
}
class Square implements MathFunction{
private var name:String;
public function Square(){
this.name="sqrt"
}
public function getName():String{return this.name;}
public function doOperation(a:Number):Number{
return Math.sqrt(a);
}
}