
abstract class Visualization
{
  float value=0;
  float value2=-1000;
  
  PVector posOrigin = new PVector(100,100);
  float valueMin=0;
  float valueMax=0;
  float value2Min=0; //value 2 for twodimensional visualization or second displayed value
  float value2Max=0;
  
  float valueMinRecord = Float.NaN; //nan means no value here
  float valueMaxRecord = Float.NaN;
  float value2MinRecord = Float.NaN; //nan means no value here
  float value2MaxRecord = Float.NaN;
  boolean showMinMax=false;
  
  //default colors (not all used by every implementation)
  
  
  color cmain = color(255,255,255);
  color cmain2 = color(255,255,255);
  color cscale = color(100,100,100);
  color cborder = color(200,200,200);
  color cmin = color(0,150,0);
  color cmax = color(150,0,0);
  color ctext = color(255,255,255);
  
  int textsize=12;
  float textWidthScale=1.0/2*this.textsize/2; //*text.length()*
  int showdecimals=2;
  
  String title="";
  String title2="";
  String valueUnit="";
  String value2Unit="";
  
  
  public abstract void drawVis();
  public void setValue(float pv){
    //this.value=constrain(pv,valueMin,valueMax);
    this.value=pv;
    if (this.showMinMax){
      if (Float.isNaN(this.valueMinRecord) || this.value<this.valueMinRecord){
        this.valueMinRecord=this.value;        
      }
      if (Float.isNaN(this.valueMaxRecord) || this.value>this.valueMaxRecord){
        this.valueMaxRecord=this.value;        
      }
    }
  }
  
  public void setValue2(float pv){
    //this.value2=constrain(pv,valueMin,valueMax);
    this.value2=pv;
    if (this.showMinMax){
      if (Float.isNaN(this.value2MinRecord) || this.value2<this.value2MinRecord){
        this.value2MinRecord=this.value2;        
      }
      if (Float.isNaN(this.value2MaxRecord) || this.value2>this.value2MaxRecord){
        this.value2MaxRecord=this.value2;        
      }
    }
  }
  
  public float getValueNormalized() {
    return (constrain(this.value,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
  }
  public float getValueMinNormalized() {
    return (constrain(this.valueMinRecord,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
  }
  public float getValueMaxNormalized() {
    return (constrain(this.valueMaxRecord,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
  }
  
  public float getValue2Normalized() {
    return (constrain(this.value2,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
  }
  public float getValue2MinNormalized() {
    return (constrain(this.value2MinRecord,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
  }
  public float getValue2MaxNormalized() {
    return (constrain(this.value2MaxRecord,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
  }
  
  public void setShowMinMax(boolean pshowMinMax){
    this.showMinMax = pshowMinMax;
  }
  
  public void setcmain(color pc){
    this.cmain=pc;
  }
  public void setcmain2(color pc){
    this.cmain2=pc;
  }
  public void setcscale(color pc){
    this.cscale=pc;
  }
  public void setcborder(color pc){
    this.cborder=pc;
  }
  public void setcmin(color pc){
    this.cmin=pc;
  }
  public void setcmax(color pc){
    this.cmax=pc;
  }
  public void setctext(color pc){
    this.ctext=pc;
  }
  
  public void setTitle(String pt) {
    this.title=pt;
  }
  
  public void setTitle2(String pt) {
    this.title2=pt;
  }
  
  public void setValueUnit(String pt) {
    this.valueUnit=pt;
  }
  
  public void setValue2Unit(String pt) {
    this.value2Unit=pt;
  }
  
  public void setshowdecimals(int pd) {
    this.showdecimals=pd;
  }
  
  public String getFormattedValue(double pv){
    if( ( (int)(pv*100)/100.0 )%1==0){
      return ""+(int)pv;
    }else{ //has decimals
      return String.format("%."+this.showdecimals+"f", pv); //limit decimal places
    }
  }
  
}

public class BarV extends Visualization {
  PVector size = new PVector(10,100);
  
  public BarV(int px, int py, int pw, int ph, float pvmin, float pvmax) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //lower left corner
    this.size = new PVector(pw,ph); //to the right and up
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    fill(super.cmain); noStroke();
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueNormalized()) );
    
    
    if (!Float.isNaN(super.valueMinRecord)){
      stroke(super.cmin);
      line(super.posOrigin.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) );
    }
    if (!Float.isNaN(super.valueMaxRecord)){
      stroke(super.cmax);
      line(super.posOrigin.x, super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()),  super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()) );
    }
    
    noFill(); stroke(this.cborder);
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    text(super.getFormattedValue(super.valueMin)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2);
    text(super.getFormattedValue(super.valueMax)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y+super.textsize/2);
    textAlign(LEFT);
    text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2-this.size.y/2); //display value
    
    //Title
    textAlign(CENTER);
    text(super.title, super.posOrigin.x+this.size.x/2, super.posOrigin.y-this.size.y-1);
    
  }
}



public class BarV_cmd extends Visualization {
  PVector size = new PVector(10,100);
  
  public BarV_cmd(int px, int py, int pw, int ph, float pvmin, float pvmax) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //lower left corner
    this.size = new PVector(pw,ph); //to the right and up
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    fill(super.cmain); noStroke();
    int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
    rect(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueNormalized()) );
    
    
    if (!Float.isNaN(super.valueMinRecord)){
      stroke(super.cmin);
      line(super.posOrigin.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) );
    }
    if (!Float.isNaN(super.valueMaxRecord)){
      stroke(super.cmax);
      line(super.posOrigin.x, super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()),  super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()) );
    }
    
    noFill(); stroke(this.cborder);
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
    line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy); //zero line
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    //text(super.getFormattedValue(super.valueMin),super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2);
    //text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y+super.textsize/2);
    textAlign(RIGHT);
    text(super.getFormattedValue(super.value),super.posOrigin.x+this.size.x/2+super.textWidthScale*3,super.posOrigin.y+super.textsize); //display value
    textAlign(LEFT);
    text(valueUnit,super.posOrigin.x+this.size.x/2+super.textWidthScale*3,super.posOrigin.y+super.textsize); //display unit on the right without disturbing alignment
    
    
    //Title
    textAlign(LEFT);
    text(super.title, super.posOrigin.x, super.posOrigin.y-this.size.y-1);
    
  }
}




public class BarH extends Visualization {
  PVector size = new PVector(10,100);
  
  public BarH(int px, int py, int pw, int ph,float pvmin, float pvmax) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //lower left corner
    this.size = new PVector(pw,ph); //to the right and up
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    fill(super.cmain); noStroke();
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueNormalized()),super.posOrigin.y- this.size.y );
    
    if (!Float.isNaN(super.valueMinRecord)){
      stroke(super.cmin);
      line(super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y- this.size.y );
    }
    if (!Float.isNaN(super.valueMaxRecord)){
      stroke(super.cmax);
      line(super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y- this.size.y );
    }
    
    noFill(); stroke(super.cborder);
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    text(super.getFormattedValue(super.valueMin)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.valueMin).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    text(super.getFormattedValue(super.valueMax)+valueUnit,super.posOrigin.x+this.size.x-super.getFormattedValue(super.valueMax).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    
    
    //Title
    text(super.title, super.posOrigin.x+this.size.x/2-super.title.length()*super.textWidthScale,super.posOrigin.y+this.size.y+1);
    
  }
}




public class BarH_cmd extends Visualization {
  PVector size = new PVector(10,100);
  
  public BarH_cmd(int px, int py, int pw, int ph,float pvmin, float pvmax) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //lower left corner
    this.size = new PVector(pw,ph); //to the right and up
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    int zerox=(int)map(0,super.valueMin,super.valueMax,0,this.size.x);
    
    fill(super.cmain); noStroke();
    rect(super.posOrigin.x+zerox,super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueNormalized()),super.posOrigin.y- this.size.y );
    
    if (!Float.isNaN(super.valueMinRecord)){
      stroke(super.cmin);
      line(super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y- this.size.y );
    }
    if (!Float.isNaN(super.valueMaxRecord)){
      stroke(super.cmax);
      line(super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y- this.size.y );
    }
    
    noFill(); stroke(super.cborder);
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    //text(super.getFormattedValue(super.valueMin),super.posOrigin.x-super.getFormattedValue(super.valueMin).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    //text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x-super.getFormattedValue(super.valueMax).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
    
    
    //Title
    text(super.title, super.posOrigin.x+this.size.x/2-super.title.length()*super.textWidthScale,super.posOrigin.y+this.textsize+1);
    
  }
}

public class Tacho extends Visualization {
  int size;
  
  
  public Tacho(int px, int py, int psize, float pvmin, float pvmax) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.value2Min=pvmin; //same scale
    super.value2Max=pvmax; //same scale
    super.posOrigin= new PVector(px,py); //circle center
    this.size = psize; //radius from the center
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    stroke(super.cmain);
    float angle=PI-super.getValueNormalized()*PI; //0=right, positive=CCW
    line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*this.size*0.8 ,super.posOrigin.y-sin(angle)*this.size*0.8); //draw tacho needle
    
    if (super.value2!=-1000) {
      stroke(super.cmain2);
      float angle2=PI-super.getValue2Normalized()*PI; //0=right, positive=CCW
      line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle2)*this.size*0.8 ,super.posOrigin.y-sin(angle2)*this.size*0.8); //draw tacho needle
    }
    
    if (!Float.isNaN(super.valueMinRecord)){
      stroke(this.cmin);
      float angleMin=PI-super.getValueMinNormalized()*PI; //0=right, positive=CCW
      line(super.posOrigin.x+cos(angleMin)*this.size*0.65 ,super.posOrigin.y-sin(angleMin)*this.size*0.65,super.posOrigin.x+cos(angleMin)*this.size*0.75 ,super.posOrigin.y-sin(angleMin)*this.size*0.75); //draw tacho needle min
    }
    if (!Float.isNaN(super.valueMaxRecord)){
      stroke(this.cmax);
      float angleMax=PI-super.getValueMaxNormalized()*PI; //0=right, positive=CCW
      line(super.posOrigin.x+cos(angleMax)*this.size*0.65 ,super.posOrigin.y-sin(angleMax)*this.size*0.65,super.posOrigin.x+cos(angleMax)*this.size*0.75 ,super.posOrigin.y-sin(angleMax)*this.size*0.75); //draw tacho needle max
    }
    
    stroke(this.cscale);
    fill(super.ctext);
    float _steps=0.1;
    for (float i=0;i<=1+_steps; i+=_steps){
       float a=PI-PI*i;
       line(super.posOrigin.x+cos(a)*this.size*0.85 ,super.posOrigin.y-sin(a)*this.size*0.85 ,super.posOrigin.x+cos(a)*this.size ,super.posOrigin.y-sin(a)*this.size);
       String _text=super.getFormattedValue(super.valueMin+(super.valueMax-super.valueMin)*i);
       text(_text,super.posOrigin.x+cos(a)*this.size*1.1-_text.length()*super.textWidthScale-1 ,super.posOrigin.y-sin(a)*this.size*1.1+super.textsize/2-1);
    }
    
    //text   
    fill(super.cmain);
    text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2-this.size*0.3);
    if (super.value2!=-1000) { //value2 is used
      fill(super.cmain2);
      text(super.getFormattedValue(super.value2)+value2Unit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2*4-this.size*0.3);
    }
    
    //Title
    textAlign(CENTER);
    fill(super.ctext);
    text(super.title, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5);
    if (super.title2!="") {
      text(super.title2, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5*2);
    }
    
  }
}


public class Direction extends Visualization {
  int size;
  float angleoffset;
  float minvisiblevalue;
  float maxvisiblevalue;
  
  public Direction(int px, int py, int psize,float pvmin, float pvmax, float pvlmin, float pvlmax, float pangleoffset) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //center
    this.size = psize; //radius from the center
    
    super.value2Min=pvlmin;
    super.value2Max=pvlmax;
    
    this.angleoffset=pangleoffset;
    
    this.minvisiblevalue=valueMin;
    this.maxvisiblevalue=valueMax;
  }
  
  public Direction(int px, int py, int psize,float pvmin, float pvmax, float pvlmin, float pvlmax, float pangleoffset, float pminvisiblevalue, float pmaxvisiblevalue) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //center
    this.size = psize; //radius from the center
    
    super.value2Min=pvlmin;
    super.value2Max=pvlmax;
    
    this.angleoffset=pangleoffset;
    
    this.minvisiblevalue=pminvisiblevalue;
    this.maxvisiblevalue=pmaxvisiblevalue;
  }
  
  
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    stroke(super.cborder); noFill();
    if (this.minvisiblevalue==super.valueMin & this.maxvisiblevalue==super.valueMax) {
      ellipseMode(RADIUS);  //centerx, centery, width,height for ellipse
      ellipse(super.posOrigin.x, super.posOrigin.y, this.size,this.size);
    }else{
      arc(super.posOrigin.x, super.posOrigin.y, this.size*2,this.size*2, this.angleoffset+2*PI -this.minvisiblevalue/super.valueMin*PI, 2*PI+this.angleoffset +this.maxvisiblevalue/super.valueMax*PI, PIE);
    }
    
    stroke(super.cmain);
    float angle=map(super.getValueNormalized(),0,1,0,2*PI)+this.angleoffset;
    //float _vecsize=this.size*( (super.value2-super.value2Min)/(super.value2Max-super.value2Min));
    float _vecsize=this.size*super.getValue2Normalized();
    line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize);
    
    line(super.posOrigin.x+cos(angle-0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle-0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize); //arrow
    line(super.posOrigin.x+cos(angle+0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle+0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize);
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize*1.5*1);
    if (super.value2<super.value2Max){ //display only if in use
      text("l="+super.getFormattedValue(super.value2)+valueUnit,super.posOrigin.x-("l="+super.getFormattedValue(super.value2)).length()*super.textWidthScale,super.posOrigin.y+super.textsize*1.5*2);
    }
    
    //Title
    text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5*3);
  }
}




public class GraphRoll extends Visualization {
  PVector size;
  float[] valuearray;
  float[] valueMinarray;
  float[] valueMaxarray;
  int arraypos;
  int recordevery=1; //minimum value =1, the higher the slower
  int recordevery_counter=0;
  
  public GraphRoll(int px, int py, int psx, int psy,float pvmin, float pvmax,int precordevery) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //center
    this.size = new PVector(psx,psy);
    
    this.recordevery= precordevery;
    
    this.valuearray = new float[psx]; //array size equals window width
    this.arraypos=0; //points to position to write to next (current value is at arraypos-1)
    this.valueMinarray = new float[psx];
    this.valueMaxarray = new float[psx];
  }
  
  
  
  public void drawVis() {
    //update history
    this.recordevery_counter++;
    if (this.recordevery_counter>=this.recordevery){
      this.valuearray[this.arraypos]=super.getValueNormalized();
      this.arraypos++;
      this.arraypos%=this.valuearray.length;
      this.recordevery_counter=0;
      
      if (super.showMinMax) {
        this.valueMinarray[this.arraypos]=super.getValueMinNormalized();
        this.valueMaxarray[this.arraypos]=super.getValueMaxNormalized();
        super.valueMinRecord=Float.NaN;
        super.valueMaxRecord=Float.NaN;
      }
    }
    
    
    rectMode(CORNERS);
    textSize(super.textsize);
    
    stroke(super.cborder);
    noFill();
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-this.size.y);
    int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
    line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy);
    
     noFill();
    int _cpos=this.arraypos;
    int _x=0; //position of _ya
    while (_cpos!=((this.arraypos-1+this.valuearray.length)%this.valuearray.length)) { //go trough all values starting at oldest value
      float _ya=this.valuearray[_cpos];
      float _yb=this.valuearray[(_cpos+1)%this.valuearray.length];
      
      //float _yaMin=this.valueMinarray[_cpos];
      float _ybMin=this.valueMinarray[(_cpos+1)%this.valuearray.length];
      float _ybMax=this.valueMaxarray[(_cpos+1)%this.valuearray.length];
      
      
      if (super.showMinMax) {
        stroke(super.cmin);
        //line(super.posOrigin.x+_x,super.posOrigin.y-_yaMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y);
        line(super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
        stroke(super.cmax);
        line(super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMax*this.size.y);
      }
      
      stroke(super.cmain);
      line(super.posOrigin.x+_x,super.posOrigin.y-_ya*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
      
      
      
      _x++;
      _cpos++;
      _cpos%=this.valuearray.length;
    }
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    text(super.getFormattedValue(super.value)+valueUnit, super.posOrigin.x+this.size.x-50, super.posOrigin.y+super.textsize*1.5);
    text(super.getFormattedValue(super.valueMin)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y+super.textsize*0.5-1);
    text(super.getFormattedValue(super.valueMax)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.size.y+super.textsize*0.5-1);
    //Title
    text(super.title, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5);
  }
}




public class GraphRoll_minimal extends Visualization {
  PVector size;
  float[] valuearray;
  float[] valueMinarray;
  float[] valueMaxarray;
  int arraypos;
  int recordevery=1; //minimum value =1, the higher the slower
  int recordevery_counter=0;
  
  public GraphRoll_minimal(int px, int py, int psx, int psy,float pvmin, float pvmax,int precordevery) {
    super.valueMin=pvmin;
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //center
    this.size = new PVector(psx,psy);
    
    this.recordevery= precordevery;
    
    this.valuearray = new float[psx]; //array size equals window width
    this.arraypos=0; //points to position to write to next (current value is at arraypos-1)
    this.valueMinarray = new float[psx];
    this.valueMaxarray = new float[psx];
  }
  
  
  
  public void drawVis() {
    //update history
    this.recordevery_counter++;
    if (this.recordevery_counter>=this.recordevery){
      this.valuearray[this.arraypos]=super.getValueNormalized();
      this.arraypos++;
      this.arraypos%=this.valuearray.length;
      this.recordevery_counter=0;
      
      if (super.showMinMax) {
        this.valueMinarray[this.arraypos]=super.getValueMinNormalized();
        this.valueMaxarray[this.arraypos]=super.getValueMaxNormalized();
        super.valueMinRecord=Float.NaN;
        super.valueMaxRecord=Float.NaN;
      }
    }
    
    
    rectMode(CORNERS);
    textSize(super.textsize);
    
    stroke(super.cborder);
    noFill();
    rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-this.size.y);
    int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
    int dotlength=10;
    float dotlengthpercentage=0.2; //0 to 1. the lower the less dot length
    for(int _xdot=(int)super.posOrigin.x; _xdot+dotlength<=super.posOrigin.x+this.size.x; _xdot+=dotlength) {
      //line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy); //zero line
      line(_xdot,super.posOrigin.y-zeroy,_xdot+dotlength*dotlengthpercentage,super.posOrigin.y-zeroy); //zero line
    }
    
     noFill();
    int _cpos=this.arraypos;
    int _x=0; //position of _ya
    while (_cpos!=((this.arraypos-1+this.valuearray.length)%this.valuearray.length)) { //go trough all values starting at oldest value
      float _ya=this.valuearray[_cpos];
      float _yb=this.valuearray[(_cpos+1)%this.valuearray.length];
      
      //float _yaMin=this.valueMinarray[_cpos];
      float _ybMin=this.valueMinarray[(_cpos+1)%this.valuearray.length];
      float _ybMax=this.valueMaxarray[(_cpos+1)%this.valuearray.length];
      
      
      if (super.showMinMax) {
        stroke(super.cmin);
        //line(super.posOrigin.x+_x,super.posOrigin.y-_yaMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y);
        line(super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
        stroke(super.cmax);
        line(super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMax*this.size.y);
      }
      
      stroke(super.cmain);
      line(super.posOrigin.x+_x,super.posOrigin.y-_ya*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
      
      
      
      _x++;
      _cpos++;
      _cpos%=this.valuearray.length;
    }
    
    //text
    textAlign(LEFT);
    fill(super.ctext);
    text(super.getFormattedValue(super.value)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.valuearray[(_cpos)%this.valuearray.length]*this.size.y);
    //text(super.getFormattedValue(super.valueMin), super.posOrigin.x+this.size.x+1, super.posOrigin.y+super.textsize*0.5-1);
    //text(super.getFormattedValue(super.valueMax), super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.size.y+super.textsize*0.5-1);
    //Title
    text(super.title, super.posOrigin.x, super.posOrigin.y-this.size.y+super.textsize);
  }
}
  
public class VectorDisplay extends Visualization {
  int size = 100;
  
  public VectorDisplay(int px, int py, int psize, float pvmax) {
    super.valueMax=pvmax;
    super.posOrigin= new PVector(px,py); //center
    this.size=psize;
  }
  
  public void drawVis() {
    rectMode(CORNERS);
    textSize(super.textsize);
    
    stroke(super.cmain);
    strokeWeight(4);
    float _x=super.posOrigin.x+this.value/super.valueMax*this.size;
    float _y=super.posOrigin.y+this.value2/super.valueMax*this.size;
    line(super.posOrigin.x,super.posOrigin.y,_x,_y);
    
  }
}
