Skip to content

Instantly share code, notes, and snippets.

@ulmangt
Last active December 26, 2015 01:59
Show Gist options
  • Save ulmangt/7075276 to your computer and use it in GitHub Desktop.
Save ulmangt/7075276 to your computer and use it in GitHub Desktop.
Demonstrates customizing TimeAxisPainter to display half ticks between labeled ticks on timeline.
import static com.metsci.glimpse.util.units.time.TimeStamp.*;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.TimeZone;
import javax.media.opengl.GL2;
import com.metsci.glimpse.axis.Axis1D;
import com.metsci.glimpse.axis.painter.TimeAxisPainter;
import com.metsci.glimpse.axis.painter.label.TimeAxisLabelHandler;
import com.metsci.glimpse.axis.painter.label.TimeAxisLabelHandler.TimeStruct;
import com.metsci.glimpse.axis.painter.label.TimeAxisLabelHandler.TimeStructFactory;
import com.metsci.glimpse.context.GlimpseBounds;
import com.metsci.glimpse.context.GlimpseContext;
import com.metsci.glimpse.examples.Example;
import com.metsci.glimpse.plot.timeline.StackedTimePlot2D;
import com.metsci.glimpse.plot.timeline.data.Epoch;
import com.metsci.glimpse.support.color.GlimpseColor;
import com.metsci.glimpse.support.settings.OceanLookAndFeel;
import com.metsci.glimpse.util.units.time.Time;
import com.metsci.glimpse.util.units.time.TimeStamp;
import com.metsci.glimpse.util.units.time.format.TimeStampFormat;
public class TimelineHalfTicksExample extends CollapsibleTimelinePlotExample
{
public static void main( String[] args ) throws Exception
{
Example example = Example.showWithSwing( new TimelineHalfTicksExample( ) );
// use the ocean look and feel
example.getCanvas( ).setLookAndFeel( new OceanLookAndFeel( ) );
}
@Override
public StackedTimePlot2D getLayout( )
{
StackedTimePlot2D plot = super.getLayout( );
plot.setTimeAxisPainter( new HalfTickTimeAxisPainter( plot.getEpoch( ) ) );
return plot;
}
public static class HalfTickTimeAxisPainter extends TimeAxisPainter
{
public HalfTickTimeAxisPainter( Epoch epoch )
{
super( new TimeAxisLabelHandler( epoch ) );
}
//@formatter:off
public HalfTickTimeAxisPainter( TimeStampFormat minuteSecondFormat,
TimeStampFormat hourMinuteFormat,
TimeStampFormat hourDayMonthFormat,
TimeStampFormat dayMonthYearFormat,
TimeStampFormat dayFormat,
TimeStampFormat dayMonthFormat,
TimeStampFormat monthYearFormat,
TimeStampFormat yearFormat,
TimeZone timeZone, Epoch epoch )
{
super( new TimeAxisLabelHandler( minuteSecondFormat, hourMinuteFormat, hourDayMonthFormat, dayMonthYearFormat, dayFormat, dayMonthFormat, monthYearFormat, yearFormat, timeZone, epoch ) );
}
//@formatter:on
@Override
public void paintTo( GlimpseContext context, GlimpseBounds bounds, Axis1D axis )
{
super.paintTo( context, bounds, axis );
if ( textRenderer == null ) return;
GL2 gl = context.getGL( ).getGL2( );
int width = bounds.getWidth( );
int height = bounds.getHeight( );
if ( width == 0 || height == 0 ) return;
gl.glMatrixMode( GL2.GL_PROJECTION );
gl.glLoadIdentity( );
gl.glOrtho( -0.5, width - 1 + 0.5f, -0.5, height - 1 + 0.5f, -1, 1 );
gl.glMatrixMode( GL2.GL_MODELVIEW );
gl.glLoadIdentity( );
gl.glColor4fv( tickColor, 0 );
List<TimeStamp> tickTimes = handler.tickTimes( axis, width );
double tickInterval = handler.tickInterval( tickTimes );
///////////// START MODIFIED CODE /////////////
// Tick marks
gl.glBegin( GL2.GL_LINES );
for ( int i = 0; i < tickTimes.size( ); i++ )
{
TimeStamp t = tickTimes.get( i );
double x = axis.valueToScreenPixel( fromTimeStamp( t ) );
gl.glVertex2d( x, height );
gl.glVertex2d( x, height - tickLineLength );
// draw half tick
if ( i < tickTimes.size( ) - 1 )
{
TimeStamp t1 = tickTimes.get( i + 1 );
TimeStamp halfway = t.add( t.durationBefore( t1 ) / 2.0 );
x = axis.valueToScreenPixel( fromTimeStamp( halfway ) );
gl.glVertex2d( x, height );
gl.glVertex2d( x, height - tickLineLength );
}
}
gl.glEnd( );
///////////// END MODIFIED CODE /////////////
if ( showCurrentTimeLabel ) drawCurrentTimeTick( gl, axis, width, height );
GlimpseColor.setColor( textRenderer, textColor );
textRenderer.beginRendering( width, height );
try
{
if ( tickInterval <= Time.fromMinutes( 1 ) )
{
// Time labels
double jTimeText = printTickLabels( tickTimes, axis, handler.getSecondMinuteFormat( ), width, height );
// Date labels
printHoverLabels( tickTimes, axis, handler.getHourDayMonthFormat( ), handler.getHourStructFactory( ), jTimeText, width, height );
}
else if ( tickInterval <= Time.fromHours( 12 ) )
{
// Time labels
double jTimeText = printTickLabels( tickTimes, axis, handler.getHourMinuteFormat( ), width, height );
// Date labels
printHoverLabels( tickTimes, axis, handler.getDayMonthYearFormat( ), handler.getDayStructFactory( ), jTimeText, width, height );
}
else if ( tickInterval <= Time.fromDays( 10 ) )
{
// Date labels
double jTimeText = printTickLabels( tickTimes, axis, handler.getDayFormat( ), width, height );
// Year labels
printHoverLabels( tickTimes, axis, handler.getMonthYearFormat( ), handler.getMonthStructFactory( ), jTimeText, width, height );
}
else if ( tickInterval <= Time.fromDays( 60 ) )
{
// Date labels
double jTimeText = printTickLabels( tickTimes, axis, handler.getMonthFormat( ), width, height );
// Year labels
printHoverLabels( tickTimes, axis, handler.getYearFormat( ), handler.getYearStructFactory( ), jTimeText, width, height );
}
else
{
// Date labels
printTickLabels( tickTimes, axis, handler.getYearFormat( ), width, height );
}
}
finally
{
textRenderer.endRendering( );
}
}
protected TimeStamp getCurrentTime( )
{
return currentTime( );
}
private void printHoverLabels( List<TimeStamp> tickTimes, Axis1D axis, TimeStampFormat format, TimeStructFactory factory, double jTimeText, int width, int height )
{
// text heights vary slightly, making the labels appear unevenly spaced in height
// just use the height of a fixed sample character
Rectangle2D fixedBounds = textRenderer.getBounds( "M" );
double textHeight = fixedBounds.getHeight( );
// Date labels
List<TimeStruct> timeStructs = handler.timeStructs( axis, tickTimes, factory );
for ( TimeStruct time : timeStructs )
{
String text = time.textCenter.toString( format );
Rectangle2D textBounds = textRenderer.getBounds( text );
double textWidth = textBounds.getWidth( );
int iMin = axis.valueToScreenPixel( fromTimeStamp( time.start ) );
int iMax = ( int ) Math.floor( axis.valueToScreenPixel( fromTimeStamp( time.end ) ) - textWidth );
int iApprox = ( int ) Math.round( axis.valueToScreenPixel( fromTimeStamp( time.textCenter ) ) - 0.5 * textWidth );
int i = Math.max( iMin, Math.min( iMax, iApprox ) );
if ( i < 0 || i + textWidth > width ) continue;
int j = ( int ) Math.floor( jTimeText - textHeight - hoverLabelOffset );
textRenderer.draw( text, i, j );
}
}
private double printTickLabels( List<TimeStamp> tickTimes, Axis1D axis, TimeStampFormat format, int width, int height )
{
// text heights vary slightly, making the labels appear unevenly spaced in height
// just use the height of a fixed sample character
Rectangle2D fixedBounds = textRenderer.getBounds( "M" );
double textHeight = fixedBounds.getHeight( );
// Time labels
int jTimeText = Integer.MAX_VALUE;
for ( TimeStamp t : tickTimes )
{
String string = t.toString( format );
Rectangle2D textBounds = textRenderer.getBounds( string );
double textWidth = textBounds.getWidth( );
int i = ( int ) Math.round( axis.valueToScreenPixel( fromTimeStamp( t ) ) - 0.5 * textWidth );
if ( i < 0 || i + textWidth > width ) continue;
int j = ( int ) Math.round( height - tickLineLength - textHeight );
jTimeText = Math.min( jTimeText, j );
textRenderer.draw( string, i, j );
}
return jTimeText;
}
private void drawCurrentTimeTick( GL2 gl, Axis1D axis, int width, int height )
{
int iTick = axis.valueToScreenPixel( fromTimeStamp( getCurrentTime( ) ) );
gl.glColor4fv( currentTimeTickColor, 0 );
gl.glLineWidth( currentTimeLineThickness );
gl.glBegin( GL2.GL_LINES );
gl.glVertex2d( iTick, height );
gl.glVertex2d( iTick, 0 );
gl.glEnd( );
String text = "NOW";
GlimpseColor.setColor( textRenderer, currentTimeTextColor );
textRenderer.beginRendering( width, height );
try
{
textRenderer.draw( text, iTick + 3, 0 + 3 );
}
finally
{
textRenderer.endRendering( );
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment