/mad-components/MadComponentsStage3D/src/asfiles/GraphPalette.as
http://mad-components.googlecode.com/ · ActionScript · 1 lines · 1 code · 0 blank · 0 comment · 0 complexity · eb124bf4bf29a16027474cecc4cedb34 MD5 · raw file
- /**
* <p>Original Author: Daniel Freeman</p>
*
* <p>Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:</p>
*
* <p>The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.</p>
*
* <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS' OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.</p>
*
* <p>Licensed under The MIT License</p>
* <p>Redistributions of files must retain the above copyright notice.</p>
*/
package asfiles {
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
import flash.utils.Timer;
import com.danielfreeman.madcomponents.Colour;
public class GraphPalette extends SimplePalette {
protected const SETTINGS:String='graphpalette.settings';
protected const BINS:String='graphpalette.bins';
protected static const DELAY:Timer=new Timer(5000,1);
private const kcolours:Array =['0xff0000','0x00ff00','0x0000ff','0xffff00','0xff00ff','0x009999','0xff8000','0x0080ff','0x80ff00','0xff0080','0x00ff80','0x8000ff'];
private const intervals:int=6;
public const scalewidth:int=100;
private const controlsheight:int=100;
private const controlsmargin:int=20;
private const enumlimit:int=30;
public var colours:Array=new Array();
public var gridbe:Shape=new Shape();
public var grph:Sprite=new Sprite();
public var drawax:Shape=new Shape();
public var scale:Sprite=new Sprite();
// public var over:Sprite=new Sprite();
public var controls:Sprite = new Sprite();
private var allintegers:Boolean;
private var possiblenumeration:Boolean;
public var frequencybin:Array;
private var enum:Array;
public var ndatai:int,ndataj:int,nno:int;
public var nmin:Number,nmax:Number,nactualmin:Number;
public var enumerationmode:Boolean=false;
public var frequencymode:Boolean=false;
public var integermode:Boolean=false;
public var colourmode:String=GraphSettings.RAINBOW;
public var mydata:Packet;
public var title:String='';
public var ss:*;
public var datai:int,dataj:int,no:int;
public var min:Number=0,max:Number=Number.MIN_VALUE;
public var actualmin:Number=Number.MAX_VALUE;
public var nminimum:Number,nmaximum:Number,nrange:Number;
public var sminimum:Number,smaximum:Number,srange:Number;
public var swdth:Number,sxoffset:Number;
public var thick:Number=0;
public var freq:Boolean=false;
public var finterval:Number;
public var freqmin:Number=0;
public var freqbin:int;
protected var _setfreqbin:int=-1;
protected var _numberofbins:int;
public var mywidth:int=wdth-frame,myheight:int=hght-frame;
private var scl:Vector.<PrintAt>=new Vector.<PrintAt>();
private var gnumformat:String;
private var dps:int=-1;
private var pre:String='';
public var stack:Boolean=false;
public var threed:Boolean=true;
public var swap:Boolean=false;
private var oldx:int,oldy:int;
public var oldwdth:int,oldhght:int;
public var iam:int;
public var stcol:int=0;
protected var _currencySymbol:String='$';
protected var _precision:int=2;
protected var _scaleColour:uint=0x666666;
protected var _scaleFormat:TextFormat=new TextFormat('Arial',11,_scaleColour);
public var sintervals:int = 6; // number of calibrations
public var shght:Number; // total height (graph)
public var step:Number; // step size (value)
public var soffset:Number = 0; // initial value
protected var _recalculated:Boolean = false;
static public function tint(c:uint,tnt:uint=0xffffff,strength:int=6):uint {
var r:uint,g:uint,b:uint,mask:uint=Math.pow(2,strength)-1;
r=(c & ((255-mask)<<16)) + (tnt & (mask<<16));
g=(c & ((255-mask)<<8)) + (tnt & (mask<<8));
b=(c & (255-mask)) + (tnt & mask);
return r+g+b;
}
protected function recalculate():void {
sminimum=Math.floor(nminimum/step)*step;
sintervals=Math.ceil((max-sminimum)/(step*1.02));
if (sintervals>intervals) {
sintervals=intervals;
}
smaximum=sminimum+step*sintervals;
srange=smaximum-sminimum;
if (srange==0) {
shght=myheight;
}
else {
shght=myheight/srange;
}
soffset=myheight+shght*sminimum;
if (srange==0) {
sxoffset=mywidth*(swdth=-sminimum);
}
else {
sxoffset=mywidth*(swdth=-sminimum/srange);
}
}
public function refresh():void {
resize2(wdth, hght);
}
public function set increment(value:Number):void {
step = value;
recalculate();
_recalculated = true;
}
public function set minimum(value:Number):void {
min = value;
recalculate();
}
public function set maximum(value:Number):void {
max = value;
recalculate();
}
public function get increment():Number {
return step;
}
public function get minimum():Number {
return sminimum;
}
public function get maximum():Number {
return smaximum;
}
public function GraphPalette(screen:Sprite,xx:int,yy:int,select:Packet,ss:*=null,freq:Boolean=false) {
x=xx;y=yy;this.ss=ss;this.freq=freq;
screen.addChild(this);
if (ss==null) mydata=select; else mydata=new Packet(select);
maxandmin();
addChild(gridbe);
addChild(grph);
addChild(drawax);
addChild(scale);
gpredraw();
super(screen,xx,yy);
controls.addEventListener(GraphSettings.SELECTED,controlschanged);
controls.addEventListener(GraphSettings.NEWMODE,newmode);
controls.addEventListener(GraphSettings.START,startcolour);
DELAY.addEventListener(TimerEvent.TIMER,darefresh);
}
public function get textColour():uint
{
return _scaleColour;
}
private function startcolour(ev:MyEvent):void {
stcol=ev.parameters[0];
colours=new Array();
bgredraw();
}
private function controlschanged(ev:MyEvent):void {
var actn:String=ev.parameters[0];
if (actn==GraphSettings.THREED) threed=!threed;
else if (actn==GraphSettings.SWAP) swap=!swap;
else if (actn==GraphSettings.STACK) stack=!stack;
drawaxis();
bgredraw();
gridbehind();
}
public function formatit(valu:Number):String {
if (numformat==Cell.PERCENT) return Math.round(valu*Math.pow(10,dps+2))/Math.pow(10,dps)+'%'; else return (valu<0 ? '-' : '')+pre+todps(Math.abs(valu));
}
public function formatit2(valu:Number):String {
if (numformat==Cell.PERCENT) return Math.round(valu*Math.pow(10,dps+2))/Math.pow(10,dps)+'%'; else return (valu<0 ? '-' : '')+pre+todps(Math.abs(valu),2);
}
private function todps(valu:Number,dps:int=-1):String {
var l:int;
var ret:String;
if (dps<0) dps=this.dps;
if (dps<0) return valu.toString();
else ret=String(Math.round(valu*Math.pow(10,dps))/Math.pow(10,dps));
if (ret.lastIndexOf('.')<0) {l=dps;ret+='.';}
else l=dps-(ret.length-ret.lastIndexOf('.'))+1;
for (var i:int=0;i<l;i++) ret+='0';
ret=ret.substr(0,int(ret).toString().length+3);
return ret;
}
private function set numformat(nfrmt:String):void {
gnumformat=nfrmt;
if (nfrmt=='') numberformat(-1,'');
else if (nfrmt==Cell.CURRENCY) numberformat(_precision,_currencySymbol);
else if (nfrmt==Cell.TWODP) numberformat(_precision,'');
else if (nfrmt==Cell.PERCENT) numberformat(2,'');
else if (nfrmt==Cell.REAL) numberformat(-1,'');
else numberformat(Number(nfrmt));
}
private function get numformat():String {
return gnumformat;
}
private function numberformat(dps:int=-2,pre:String='.'):void {
if (dps!=-2) this.dps=dps;
if (pre!='.') this.pre=pre;
}
public function maxandmin():void {
var cell:Cell;
var numf:String;
datai=mydata.ito-mydata.ifrom+1;
dataj=mydata.jto-mydata.jfrom+1;
no=datai*dataj;
min=0,max=Number.MIN_VALUE;actualmin=Number.MAX_VALUE;
allintegers=true;
possiblenumeration=true;
if ((cell=readcell(0,0))==null) numf=Cell.REAL; else numf=cell.numformat;
for (var i:int=0;i<datai;i++)
for (var j:int=0;j<dataj;j++)
if ((cell=readcell(i,j))!=null) {
if (numf!=cell.numformat) numf='';
if (cell.value>max) max=cell.value;
if (cell.value<min) min=cell.value;
if (cell.value<actualmin) actualmin=cell.value;
if (cell.value!=Math.round(cell.value)) allintegers=false;
if (cell.isvalue) possiblenumeration=false;
} else if (numf!=Cell.REAL) numf='';
numformat=numf;
if (cell!=null) {
_currencySymbol=cell.currencySymbol;
if (numf==Cell.TWODP || numf==Cell.CURRENCY) pre=_currencySymbol;
_precision=cell.precision;
}
if (freq) convertofrequency();
}
private function convertofrequency():Boolean {
ndatai=datai;ndataj=dataj;nno=no;nmin=min;nmax=max;nactualmin=actualmin;
if (possiblenumeration)
if (enumeration()) {enumerationmode=true;frequency();return true;} else return false;
else {frequencymode=true;frequency();return true;}
}
protected function setnumberofbins(ev:MyEvent):void {
_setfreqbin=ev.parameters[0] as int;
darefresh();
}
private function numberofbins():int {
var sortit:Array=new Array();
var count:int=0;
var cell:Cell;
var total:Number=0,diff:Number,mean:Number,power:int,normalised:Number;
for (var i:int=0;i<ndatai;i++)
for (var j:int=0;j<ndataj;j++)
if ((cell=readcell(i,j))!=null && cell.isvalue) sortit.push(cell.value);
sortit.sort();
for (var k:int=0;k<sortit.length-1;k++) if ((diff=sortit[k+1]-sortit[k])>0) {total+=diff;count++;}
if (count>0) {
mean=total/count;
power=Math.pow(10,Math.floor(Math.log(mean)/Math.LN10));
normalised=mean/power;
if (normalised>5) finterval=5*power;
else if (normalised>2) finterval=2*power;
else finterval=1*power;
if (finterval==0) finterval=0.1;
freqmin=Math.floor(nactualmin/finterval)*finterval;
if (isNaN(freqmin)) freqmin=0;
freqbin=Math.ceil((nmax-freqmin)/finterval);
return freqbin;
} else return 1;
}
public function frequency():void {
var i:int,j:int,k:int;
if (frequencymode || enumerationmode) {
min=0,max=Number.MIN_VALUE;actualmin=Number.MAX_VALUE;
var n:int;
var bin:int;
if (_setfreqbin>0) {n=_setfreqbin;enumerationmode=integermode=false;frequencymode=true;finterval=(nmax-freqmin)/_setfreqbin;}
else if (enumerationmode) n=enum.length;
else if (integermode=(allintegers && nmax-nactualmin<=enumlimit)) n=nmax-nactualmin+1;
else _numberofbins=n=numberofbins();
// controls.binsVisible=!integermode && !enumerationmode;
frequencybin=new Array();
if (swap || dataj==1) {
for (k=0;k<n;k++) frequencybin[k]=new Array();
for (j=0;j<ndataj;j++) {
for (k=0;k<n;k++) frequencybin[k][j]=0;
for (i=0;i<ndatai;i++)
if ((bin=whatbin(readcell(i,j)))>=0) {frequencybin[bin][j]++;}
// for (k=0;k<n;k++) trace('frequencybin['+k+']['+j+']='+frequencybin[k][j]);
for (k=0;k<n;k++) {
if (frequencybin[k][j]>max) max=frequencybin[k][j];
if (frequencybin[k][j]<min) min=frequencybin[k][j];
if (frequencybin[k][j]<actualmin) actualmin=frequencybin[k][j];
}
}
datai=n;dataj=ndataj;no=datai*dataj;
// if (max==min) shght=myheight; else shght=myheight/(max-min);
} else {
for (i=0;i<ndatai;i++) {
frequencybin[i]=new Array();
for (k=0;k<n;k++) frequencybin[i][k]=0;
for (j=0;j<ndataj;j++)
if ((bin=whatbin(readcell(i,j)))>=0) {frequencybin[i][bin]++;}
// for (k=0;k<n;k++) trace('frequencybin['+i+']['+k+']='+frequencybin[i][k]);
for (k=0;k<n;k++) {
if (frequencybin[i][k]>max) max=frequencybin[i][k];
if (frequencybin[i][k]<min) min=frequencybin[i][k];
if (frequencybin[i][k]<actualmin) actualmin=frequencybin[i][k];
}
}
datai=ndatai;dataj=n;no=datai*dataj;trace('****frequency datai='+datai+' dataj='+dataj+' no='+no);
}
}
}
public function frequencylabel(n:int):String {
if (enumerationmode) return enum[n];
else if (integermode) return String(n+nactualmin);
else return formatit2(freqmin+n*finterval)+'->'+formatit2(freqmin+(n+1)*finterval);
}
private function addtoenum(cell:Cell):Boolean {
if (cell==null || cell.string=='') return false;
else {
var found:Boolean=false;
for (var p:int=0;p<enum.length && !found;p++) found=enum[p]==cell.string;
if (!found) {enum.push(cell.string);return enum.length>enumlimit;}
else return false;
}
}
private function enumeration():Boolean {
var i:int;
enum=new Array();
var exit:Boolean=false;
for (i=0;i<datai && !exit;i++)
for (var j:int=0;j<dataj && !exit;j++) exit=addtoenum(readcell(i,j));
return !exit && enum.length>0;
}
private function whatbin(cell:Cell):int {
if (cell==null || isNaN(cell.value)) return -1;
else if (enumerationmode) {
var found:Boolean=false;
for (var p:int=0;p<enum.length && !(found=(enum[p]==cell.string));p++) {};
if (found) return p; else return -1;
} else {
if (integermode) return cell.value-nactualmin;
else if (_setfreqbin>0) return Math.min(Math.floor((cell.value-freqmin)/finterval),_setfreqbin-1);
else return Math.min(Math.floor((cell.value-freqmin)/finterval),freqbin-1);
}
}
public function darefresh(ev:*=null):void {
if (ev) {
// trace('>>>>>>>>>ev.type='+ev.type+' _setfreqbin='+_setfreqbin+' _numberofbins='+_numberofbins);
_numberofbins=_setfreqbin;
}
maxandmin();
scaleit();
drawscale(scale);
mywidth=wdth-scaleoffset(wdth,hght)-frame;
drawaxis();
scale.x=drawax.x=gridbe.x=grph.x=frame/2+scaleoffset(wdth,hght)-(scale.visible ? 6 : 0);
bgredraw();
gridbehind();
}
private function newmode(ev:MyEvent):void {
colourmode=ev.parameters[0];
bgredraw();
}
private function rndcol(i:int):uint {
const lo:int=40;
const hi:int=255;
const sp:int=40;
var ret:uint=lo+(i*sp)%(hi-lo);
return ret;
}
private function rndgrey(i:int,invert:Boolean=false):uint {
var col:int=rndcol(i);
if (invert) col=255-col;
return col*65536+col*256+col;
}
public function colour(idx:int,idx2:int=0,mde:String=null):uint {
var col:Object,col2:Object;
if (datai==1) {idx=idx2;idx2=0;}
col=colours[idx%kcolours.length];
if (datai==1 || dataj==1) col2=col; else col2=colours[idx2%kcolours.length];
if (mde==null) mde=colourmode;
if (mde==GraphSettings.RAINBOW) return tint(col!=null ? Number(col) : kcolours[(idx+stcol)%kcolours.length],rndgrey(idx2));
else if (mde==GraphSettings.GREYSCALE0) return tint(col!=null ? Number(col) : rndgrey(idx),rndgrey(idx2),4);
else if (mde==GraphSettings.SUBTLE) return tint(col2!=null ? Number(col2) : kcolours[(idx2+stcol)%kcolours.length],rndgrey(idx));
else return tint(col2!=null ? Number(col2) : rndgrey(idx2,true),rndgrey(idx),4);
}
public function readcell(i:int,j:int):Cell {
if (ss==null) return mydata.readlocal(i,j);
else return ss.readcell(i+mydata.ifrom,j+mydata.jfrom);
}
public function scaleoffset(wdth:int,hght:int):int {
return (scale.visible=wdth>scalewidth) ? scale.width : 0;
}
public function controlsoffset(wdth:int,hght:int):int {
if (controls==null) return 0;
else return (controls.visible=(wdth>scalewidth && hght>controlsheight)) ? controlsmargin : 0;
}
override public function resize(wdth:int,hght:int):void {
myheight=hght-controlsoffset(wdth,hght)-frame;
drawscale(scale);
mywidth=wdth-scaleoffset(wdth,hght)-frame;
scale.x=drawax.x=gridbe.x=grph.x=frame/2+scaleoffset(wdth,hght)-(scale.visible ? 6 : 0);
scale.y=drawax.y=gridbe.y=grph.y=frame/2+controlsoffset(wdth,hght)+4;
// if (freq) darefresh();
gpredraw();bgredraw();gridbehind();
extra=0;
}
public function resize2(wdth:int,hght:int):void {
extra=0;
this.wdth=wdth;
this.hght=hght;
drawscale(scale);
redraw();bgredraw();
}
public function bgredraw():void {}
private function gpredraw():void {
if (no>1 && !(max==0 && min==0)) {
scaleit();
drawaxis();
drawscale(scale);
}
}
private function fit(guess:Number,intervals:int):Number {
var minimum:Number=Math.floor(nminimum/guess)*guess;
var maximum:Number=minimum+intervals*guess;
//may put a small threshold here - want a large value of this.nrange/(maximum-minimum)
if (0.01+maximum<nmaximum || maximum<=0) return 0;
else return nrange/(maximum-minimum);
}
private function scaleit():void {
if (_recalculated) {
recalculate();
return;
}
var intervals:int=Math.floor(hght/20);
var f:Number,maxv:Number,maxi:Number,tempmax:Number;
var range:Number,power:int,topower:Number;
tempmax=max;
if (max<0) max=0;
range=max-min;
if (range==0) {range=1;power=0;topower=1;nmaximum=max=1;nminimum=0;srange=step=sintervals=1;}
else {
power=Math.round(Math.log(range)/Math.LN10);
topower=Math.pow(10,power);
nmaximum=max/topower;
nminimum=min/topower;
nrange=range/(topower*intervals);
maxv=fit(0.1,intervals);maxi=0.1;
if ((f=fit(0.2,intervals))>maxv) {maxv=f;maxi=0.2;}
if ((f=fit(0.5,intervals))>maxv) {maxv=f;maxi=0.5;}
if ((f=fit(0.01,intervals))>maxv) {maxv=f;maxi=0.01;}
if ((f=fit(0.02,intervals))>maxv) {maxv=f;maxi=0.02;}
if ((f=fit(0.05,intervals))>maxv) {maxv=f;maxi=0.05;}
if (maxv==0) {
topower=topower*10;
nminimum=nminimum/10;
nrange=nrange/10;
maxv=fit(0.1,intervals);maxi=0.1;
if ((f=fit(0.2,intervals))>maxv) {maxv=f;maxi=0.2;}
if ((f=fit(0.5,intervals))>maxv) {maxv=f;maxi=0.5;}
if ((f=fit(0.01,intervals))>maxv) {maxv=f;maxi=0.01;}
if ((f=fit(0.02,intervals))>maxv) {maxv=f;maxi=0.02;}
if ((f=fit(0.05,intervals))>maxv) {maxv=f;maxi=0.05;}
}
step=topower*maxi;
sminimum=topower*Math.floor(nminimum/maxi)*maxi;
sintervals=Math.ceil((max-sminimum)/(step*1.02));
if (sintervals>intervals) sintervals=intervals;
smaximum=sminimum+step*sintervals;
srange=smaximum-sminimum;
}
if (srange==0) shght=myheight; else shght=myheight/srange;
soffset=myheight+shght*sminimum;
if (srange==0) sxoffset=mywidth*(swdth=-sminimum); else sxoffset=mywidth*(swdth=-sminimum/srange);
// trace('myheight='+myheight+' soffset='+soffset);
// trace('mywidth='+mywidth+' sxoffset='+sxoffset);
// trace('scale goes from:'+sminimum+' to '+smaximum+' in steps of '+step+' shght='+shght);
max=tempmax;
}
public function gridbehind():void {
var yy:int;
gridbe.graphics.clear();
gridbe.graphics.lineStyle(0,0xddddff);
for (var i:int=0;i<=sintervals;i++) {
yy=myheight-i*step*shght;
gridbe.graphics.moveTo(0,yy);
if (thick>0) {
gridbe.graphics.lineTo(thick,yy-thick);
gridbe.graphics.lineTo(mywidth+thick,yy-thick);
} else gridbe.graphics.lineTo(mywidth,yy);
}
}
public function drawaxis():void {
var yy:int;
drawax.graphics.clear();
drawax.graphics.lineStyle(2,_scaleColour);
drawax.graphics.moveTo(0,0);
drawax.graphics.lineTo(0,myheight);
drawax.graphics.moveTo(0,soffset);
drawax.graphics.lineTo(mywidth,soffset);
scale.graphics.clear();
scale.graphics.lineStyle(2,_scaleColour);
for (var i:int=0;i<=sintervals;i++) {
yy=myheight-i*step*shght;
scale.graphics.moveTo(0,yy);
scale.graphics.lineTo(-2,yy);
}
}
private function numbererror(n:Number):Number {
return Math.round(n*1e6)/1e6;
}
public function drawscale(screen:Sprite):void {
for (var j:int=0;j<scl.length;j++) screen.removeChild(scl[j]);
scl=new Vector.<PrintAt>;
for (var i:int=0;i<=sintervals;i++) {
scl[i]=new PrintAt(screen,-2,myheight-i*step*shght,formatit(numbererror(sminimum+i*step)),TextFieldAutoSize.RIGHT,_scaleFormat);
scl[i].y-=scl[i].height/2;
}
}
public function format():void {
mouseChildren=mouseEnabled=false;
filters=null;
}
public function thumbnail(xx:int,yy:int,sz:int):void {
oldx=x;oldy=y;oldwdth=wdth;oldhght=hght;
x=xx;y=yy;
mouseChildren=mouseEnabled=false;
resize(sz,sz);graphics.clear();edge.visible=false;
filters=nullfilters=null;
}
public function restore(partial:Boolean=false):void {
grph.filters=null;
if (partial) {x=y=0;resize(oldwdth,oldhght);}
else {
x=oldx;y=oldy;
mouseChildren=mouseEnabled=edge.visible=true;
resize(oldwdth,oldhght);
}
redraw();
}
public function contained():void
{
graphics.clear();
edge.visible=hitArea.visible=false;
mouseEnabled=false;
mouseChildren=true;
filters=nullfilters=null;
customcolours(0,uint.MAX_VALUE,uint.MAX_VALUE);
}
override public function destructor():void {
controls.removeEventListener(GraphSettings.SELECTED,controlschanged);
controls.removeEventListener(GraphSettings.NEWMODE,newmode);
controls.removeEventListener(GraphSettings.START,startcolour);
super.destructor();
}
}
}