controlp5/src/controlP5/Chart.java

562 lines
16 KiB
Java
Executable File

package controlP5;
/**
* controlP5 is a processing gui library.
*
* 2006-2015 by Andreas Schlegel
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* @author Andreas Schlegel (http://www.sojamo.de)
* @modified ##date##
* @version ##version##
*
*/
import java.util.Iterator;
import java.util.LinkedHashMap;
import processing.core.PApplet;
import processing.core.PGraphics;
/**
* Use charts to display float array data as line chart, yet experimental, but see the
* ControlP5chart example for more details.
*
* @example controllers/ControlP5chart
*/
public class Chart extends Controller< Chart > {
public final static int LINE = 0;
public final static int BAR = 1;
public final static int BAR_CENTERED = 2;
public final static int HISTOGRAM = 3;
public final static int PIE = 4;
public final static int AREA = 5;
protected final LinkedHashMap< String , ChartDataSet > _myDataSet;
protected float resolution = 1;
protected float strokeWeight = 1;
protected float _myMin = 0;
protected float _myMax = 1;
/**
* Convenience constructor to extend Chart.
*
* @example use/ControlP5extendController
* @param theControlP5
* @param theName
*/
public Chart( ControlP5 theControlP5 , String theName ) {
this( theControlP5 , theControlP5.getDefaultTab( ) , theName , 0 , 0 , 200 , 100 );
theControlP5.register( theControlP5.papplet , theName , this );
}
protected Chart( ControlP5 theControlP5 , ControllerGroup< ? > theParent , String theName , float theX , float theY , int theWidth , int theHeight ) {
super( theControlP5 , theParent , theName , theX , theY , theWidth , theHeight );
setRange( 0 , theHeight );
_myDataSet = new LinkedHashMap< String , ChartDataSet >( );
getCaptionLabel( ).align( LEFT, BOTTOM_OUTSIDE ).paddingX = 0;
}
public Chart setRange( float theMin , float theMax ) {
_myMin = theMin;
_myMax = theMax;
return this;
}
public Chart setColors( String theSetIndex , int ... theColors ) {
getDataSet( ).get( theSetIndex ).setColors( theColors );
return this;
}
public Chart addData( ChartData theItem ) {
return addData( getFirstDataSetIndex( ) , theItem );
}
private String getFirstDataSetIndex( ) {
return getDataSet( ).keySet( ).iterator( ).next( );
}
private String getLastDataSetIndex( ) {
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String last = null;
while ( it.hasNext( ) ) {
last = it.next( );
}
return last;
}
public Chart addData( String theSetIndex , ChartData theItem ) {
getDataSet( theSetIndex ).add( theItem );
return this;
}
public Chart addData( float theValue ) {
ChartData cdi = new ChartData( theValue );
getDataSet( getFirstDataSetIndex( ) ).add( cdi );
return this;
}
public Chart addData( String theSetIndex , float theValue ) {
ChartData cdi = new ChartData( theValue );
getDataSet( theSetIndex ).add( cdi );
return this;
}
public Chart addData( ChartDataSet theChartData , float theValue ) {
ChartData cdi = new ChartData( theValue );
theChartData.add( cdi );
return this;
}
// array operations see syntax
// http://www.w3schools.com/jsref/jsref_obj_array.asp
/**
* adds a new float at the beginning of the data set.
*/
public Chart unshift( float theValue ) {
return unshift( getFirstDataSetIndex( ) , theValue );
}
public Chart unshift( String theSetIndex , float theValue ) {
if ( getDataSet( theSetIndex ).size( ) > ( getWidth() / resolution ) ) {
removeLast( theSetIndex );
}
return addFirst( theSetIndex , theValue );
}
public Chart push( float theValue ) {
return push( getFirstDataSetIndex( ) , theValue );
}
public Chart push( String theSetIndex , float theValue ) {
if ( getDataSet( theSetIndex ).size( ) > ( getWidth() / resolution ) ) {
removeFirst( theSetIndex );
}
return addLast( theSetIndex , theValue );
}
public Chart addFirst( float theValue ) {
return addFirst( getFirstDataSetIndex( ) , theValue );
}
public Chart addFirst( String theSetIndex , float theValue ) {
ChartData cdi = new ChartData( theValue );
getDataSet( theSetIndex ).add( 0 , cdi );
return this;
}
public Chart addLast( float theValue ) {
return addLast( getFirstDataSetIndex( ) , theValue );
}
public Chart addLast( String theSetIndex , float theValue ) {
ChartData cdi = new ChartData( theValue );
getDataSet( theSetIndex ).add( cdi );
return this;
}
public Chart removeLast( ) {
return removeLast( getFirstDataSetIndex( ) );
}
public Chart removeLast( String theSetIndex ) {
return removeData( theSetIndex , getDataSet( theSetIndex ).size( ) - 1 );
}
public Chart removeFirst( ) {
return removeFirst( getFirstDataSetIndex( ) );
}
public Chart removeFirst( String theSetIndex ) {
return removeData( theSetIndex , 0 );
}
public Chart removeData( ChartData theItem ) {
removeData( getFirstDataSetIndex( ) , theItem );
return this;
}
public Chart removeData( String theSetIndex , ChartData theItem ) {
getDataSet( theSetIndex ).remove( theItem );
return this;
}
public Chart removeData( int theItemIndex ) {
removeData( getFirstDataSetIndex( ) , theItemIndex );
return this;
}
public Chart removeData( String theSetIndex , int theItemIndex ) {
if ( getDataSet( theSetIndex ).size( ) < 1 ) {
return this;
}
getDataSet( theSetIndex ).remove( theItemIndex );
return this;
}
public Chart setData( int theItemIndex , ChartData theItem ) {
getDataSet( getFirstDataSetIndex( ) ).set( theItemIndex , theItem );
return this;
}
public Chart setData( String theSetItem , int theItemIndex , ChartData theItem ) {
getDataSet( theSetItem ).set( theItemIndex , theItem );
return this;
}
public Chart addDataSet( String theName ) {
getDataSet( ).put( theName , new ChartDataSet( theName ) );
return this;
}
public Chart setDataSet( ChartDataSet theItems ) {
setDataSet( getFirstDataSetIndex( ) , theItems );
return this;
}
public Chart setDataSet( String theSetIndex , ChartDataSet theChartData ) {
getDataSet( ).put( theSetIndex , theChartData );
return this;
}
public Chart removeDataSet( String theIndex ) {
getDataSet( ).remove( theIndex );
return this;
}
public Chart setData( float ... theValues ) {
setData( getFirstDataSetIndex( ) , theValues );
return this;
}
public Chart setData( String theSetIndex , float ... theValues ) {
if ( getDataSet( ).get( theSetIndex ).size( ) != theValues.length ) {
getDataSet( ).get( theSetIndex ).clear( );
for ( int i = 0 ; i < theValues.length ; i++ ) {
getDataSet( ).get( theSetIndex ).add( new ChartData( 0 ) );
}
}
int n = 0;
resolution = ( float ) getWidth() / ( getDataSet( ).get( theSetIndex ).size( ) - 1 );
for ( float f : theValues ) {
getDataSet( ).get( theSetIndex ).get( n++ ).setValue( f );
}
return this;
}
public Chart updateData( float ... theValues ) {
return setData( theValues );
}
public Chart updateData( String theSetIndex , float ... theValues ) {
return setData( theSetIndex , theValues );
}
public LinkedHashMap< String , ChartDataSet > getDataSet( ) {
return _myDataSet;
}
public ChartDataSet getDataSet( String theIndex ) {
return getDataSet( ).get( theIndex );
}
public float[] getValuesFrom( String theIndex ) {
return getDataSet( theIndex ).getValues( );
}
public ChartData getData( String theIndex , int theItemIndex ) {
return getDataSet( theIndex ).get( theItemIndex );
}
public int size( ) {
return getDataSet( ).size( );
}
@Override
public void onEnter( ) {
}
@Override
public void onLeave( ) {
}
@Override
public Chart setValue( float theValue ) {
// TODO Auto-generated method stub
return this;
}
public Chart setStrokeWeight( float theWeight ) {
strokeWeight = theWeight;
for ( ChartDataSet c : getDataSet( ).values( ) ) {
c.setStrokeWeight( theWeight );
}
return this;
}
public float getStrokeWeight( ) {
return strokeWeight;
}
/**
* ?
*
* @param theValue
* @return
*/
public Chart setResolution( int theValue ) {
resolution = theValue;
return this;
}
public int getResolution( ) {
return ( int ) resolution;
}
/**
* @exclude
*/
@Override
@ControlP5.Invisible
public Chart updateDisplayMode( int theMode ) {
return updateViewMode( theMode );
}
/**
* @exclude
*/
@ControlP5.Invisible
public Chart updateViewMode( int theMode ) {
_myDisplayMode = theMode;
switch ( theMode ) {
case ( DEFAULT ):
_myControllerView = new ChartViewPie( );
break;
case ( IMAGE ):
// _myDisplay = new ChartImageDisplay();
break;
case ( SPRITE ):
// _myDisplay = new ChartSpriteDisplay();
break;
case ( CUSTOM ):
default:
break;
}
return this;
}
public class ChartViewBar implements ControllerView< Chart > {
public void display( PGraphics theGraphics , Chart theController ) {
theGraphics.pushStyle( );
theGraphics.fill( getColor( ).getBackground( ) );
theGraphics.rect( 0 , 0 , getWidth( ) , getHeight( ) );
theGraphics.noStroke( );
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String index = null;
float o = 0;
while ( it.hasNext( ) ) {
index = it.next( );
float s = getDataSet( index ).size( );
for ( int i = 0 ; i < s ; i++ ) {
theGraphics.fill( getDataSet( index ).getColor( i ) );
float ww = ( ( getWidth() / s ) );
float hh = PApplet.map( getDataSet( index ).get( i ).getValue( ) , _myMin , _myMax , 0 , getHeight( ) );
theGraphics.rect( o + i * ww , getHeight( ) , ( ww / getDataSet( ).size( ) ) , -PApplet.min( getHeight( ) , PApplet.max( 0 , hh ) ) );
}
o += ( ( getWidth() / s ) ) / getDataSet( ).size( );
}
theGraphics.popStyle( );
}
}
public class ChartViewBarCentered implements ControllerView< Chart > {
public void display( PGraphics theGraphics , Chart theController ) {
theGraphics.pushStyle( );
theGraphics.fill( getColor( ).getBackground( ) );
theGraphics.rect( 0 , 0 , getWidth( ) , getHeight( ) );
theGraphics.noStroke( );
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String index = null;
float o = 0;
int n = 4;
int off = ( getDataSet( ).size( ) - 1 ) * n;
while ( it.hasNext( ) ) {
index = it.next( );
int s = getDataSet( index ).size( );
float step = ( float ) getWidth() / ( float ) ( s );
float ww = step - ( getWidth() % step );
ww -= 1;
ww = PApplet.max( 1 , ww );
for ( int i = 0 ; i < s ; i++ ) {
theGraphics.fill( getDataSet( index ).getColor( i ) );
ww = ( ( getWidth() / s ) * 0.5f );
float hh = PApplet.map( getDataSet( index ).get( i ).getValue( ) , _myMin , _myMax , 0 , getHeight( ) );
theGraphics.rect( -off / 2 + o + i * ( ( getWidth() / s ) ) + ww / 2 , getHeight( ) , ww , -PApplet.min( getHeight( ) , PApplet.max( 0 , hh ) ) );
}
o += n;
}
theGraphics.popStyle( );
}
}
public class ChartViewLine implements ControllerView< Chart > {
public void display( PGraphics theGraphics , Chart theController ) {
theGraphics.pushStyle( );
theGraphics.fill( getColor( ).getBackground( ) );
theGraphics.rect( 0 , 0 , getWidth( ) , getHeight( ) );
theGraphics.noFill( );
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String index = null;
while ( it.hasNext( ) ) {
index = it.next( );
theGraphics.stroke( getDataSet( index ).getColor( 0 ) );
theGraphics.strokeWeight( getDataSet( index ).getStrokeWeight( ) );
theGraphics.beginShape( );
float res = ( ( float ) getWidth( ) ) / ( getDataSet( index ).size( ) - 1 );
for ( int i = 0 ; i < getDataSet( index ).size( ) ; i++ ) {
float hh = PApplet.map( getDataSet( index ).get( i ).getValue( ) , _myMin , _myMax , getHeight( ) , 0 );
theGraphics.vertex( i * res , PApplet.min( getHeight( ) , PApplet.max( 0 , hh ) ) );
}
theGraphics.endShape( );
}
theGraphics.noStroke( );
theGraphics.popStyle( );
getCaptionLabel( ).draw( theGraphics , 0 , 0 , theController );
}
}
public class ChartViewArea implements ControllerView< Chart > {
public void display( PGraphics theGraphics , Chart theController ) {
theGraphics.pushStyle( );
theGraphics.fill( getColor( ).getBackground( ) );
theGraphics.rect( 0 , 0 , getWidth( ) , getHeight( ) );
theGraphics.noStroke( );
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String index = null;
while ( it.hasNext( ) ) {
index = it.next( );
float res = ( ( float ) getWidth( ) ) / ( getDataSet( index ).size( ) - 1 );
theGraphics.fill( getDataSet( index ).getColor( 0 ) );
theGraphics.beginShape( );
theGraphics.vertex( 0 , getHeight( ) );
for ( int i = 0 ; i < getDataSet( index ).size( ) ; i++ ) {
float hh = PApplet.map( getDataSet( index ).get( i ).getValue( ) , _myMin , _myMax , getHeight( ) , 0 );
theGraphics.vertex( i * res , PApplet.min( getHeight( ) , PApplet.max( 0 , hh ) ) );
}
theGraphics.vertex( getWidth( ) , getHeight( ) );
theGraphics.endShape( PApplet.CLOSE );
}
theGraphics.noStroke( );
theGraphics.popStyle( );
}
}
public class ChartViewPie implements ControllerView< Chart > {
public void display( PGraphics theGraphics , Chart theController ) {
theGraphics.pushStyle( );
theGraphics.pushMatrix( );
Iterator< String > it = getDataSet( ).keySet( ).iterator( );
String index = null;
while ( it.hasNext( ) ) {
index = it.next( );
float total = 0;
for ( int i = 0 ; i < getDataSet( index ).size( ) ; i++ ) {
total += getDataSet( index ).get( i ).getValue( );
}
float segment = TWO_PI / total;
float angle = -HALF_PI;
theGraphics.noStroke( );
for ( int i = 0 ; i < getDataSet( index ).size( ) ; i++ ) {
theGraphics.fill( getDataSet( index ).getColor( i ) );
float nextAngle = angle + getDataSet( index ).get( i ).getValue( ) * segment;
// a tiny offset to even out render artifacts when in smooth() mode.
float a = PApplet.max( 0 , PApplet.map( getWidth( ) , 0 , 200 , 0.05f , 0.01f ) );
theGraphics.arc( 0 , 0 , getWidth( ) , getHeight( ) , angle - a , nextAngle );
angle = nextAngle;
}
theGraphics.translate( 0 , ( getHeight( ) + 10 ) );
}
theGraphics.popMatrix( );
theGraphics.popStyle( );
}
}
public Chart setView( int theType ) {
switch ( theType ) {
case ( PIE ):
setView( new ChartViewPie( ) );
break;
case ( LINE ):
setView( new ChartViewLine( ) );
break;
case ( BAR ):
setView( new ChartViewBar( ) );
break;
case ( BAR_CENTERED ):
setView( new ChartViewBarCentered( ) );
break;
case ( AREA ):
setView( new ChartViewArea( ) );
break;
default:
System.out.println( "Sorry, this ChartView does not exist" );
break;
}
return this;
}
@Override
public String getInfo( ) {
return "type:\tChart\n" + super.toString( );
}
@Override
public String toString( ) {
return super.toString( ) + " [ " + getValue( ) + " ]" + " Chart " + "(" + this.getClass( ).getSuperclass( ) + ")";
}
}
/* NOTES what is the difference in meaning between chart and graph
* http://answers.yahoo.com/question/index?qid=20090101193325AA3mgMl
*
* more charts to implement: from https://vimeo.com/groups/oaod/videos/60013194 (44:40) scatter
* plot, star plot, histogram, dendrogram, box plot, physical map, tree, 2d 3d isosurfaces table,
* half matrix, graph, hierarchical pie, line graph, numeric matrix, heat map, permutation matrix
* bar graph, radial graph, */