/* * Created on 01-Oct-2004 by Ryan McNally */ package com.speckled.specksim.gui.imp; import java.util.Arrays; import javax.media.opengl.GLAutoDrawable; import javax.vecmath.Point3f; import com.ryanm.config.Configurator; import com.ryanm.config.ValueListener; import com.ryanm.config.imp.AbstractConfigurator; import com.ryanm.glvisualiser.GLMaterial; import com.ryanm.glvisualiser.imp.ConicalEdge; import com.speckled.specksim.SpeckPosition; import com.speckled.specksim.imp.state.NeighbourhoodProcessor; import com.speckled.specksim.state.StateSink; /** * @author ryanm */ public class NeighbourhoodRenderer extends AbstractStateRenderer { private NeighbourhoodRendererConfigurator conf; private ConicalEdge edge; private ConicalEdge symmetricEdge; private static float lengthOffset = 0; private static float width = 0.005f; public String getName() { return "Neighbourhood"; } @Override public void constructGLObjects( GLAutoDrawable drawable ) { GLMaterial mat = new GLMaterial(); mat.setAmbientReflectivity( 0, 0, 1, 1 ); mat.setDiffuseReflectivity( 0, 0, 1, 1 ); mat.setSpecularReflectivity( 1, 1, 1, 1 ); mat.setShininess( 100 ); edge = new ConicalEdge( visualiser, mat, 5, 1, 0 ); GLMaterial mat2 = new GLMaterial(); mat2.setAmbientReflectivity( 0, 0, 1, 1 ); mat2.setDiffuseReflectivity( 0, 0, 1, 1 ); mat2.setSpecularReflectivity( 1, 1, 1, 1 ); mat2.setShininess( 100 ); symmetricEdge = new ConicalEdge( visualiser, mat2, 5, 1, 1 ); } @Override protected void compileSubLists( GLAutoDrawable drawable, StateSink state ) { edge.recompile(); symmetricEdge.recompile(); } @Override public void renderState( GLAutoDrawable drawable, StateSink state ) { NeighbourhoodProcessor neigh = ( NeighbourhoodProcessor ) state.getProcessor( NeighbourhoodProcessor.NAME ); int[][] neighbourIndices = neigh.getNeighbourIndices(); if( neighbourIndices != null && neighbourIndices.length > 0 ) { SpeckPosition[] positions = state.getState().getPositions(); Point3f v = new Point3f(), w = new Point3f(); // for each speck for( int i = 0; i < positions.length; i++ ) { if( state.isIncluded( i ) ) { v.set( positions[ i ].position ); // render a link to each of its neighbours int[] array = neighbourIndices[ i ]; if( array != null ) { for( int j = 0; j < array.length; j++ ) { if( array[ j ] != -1 && state.isIncluded( array[ j ] ) ) { w.set( positions[ array[ j ] ].position ); if( isLinked( array[ j ], i, neighbourIndices ) ) { if( array[ j ] < i ) { symmetricEdge.render( v, lengthOffset, w, lengthOffset, width, i, array[ j ] ); } } else { edge.render( v, lengthOffset, w, lengthOffset, width, i, array[ j ] ); } } } } } } } } /** * Check if there is a link from A to B * * @param indexA * The index of speck A * @param indexB * The index of speck B * @param neighbourhoods * The neighbourhoods * @return true if there is a neighbourhood link between A and B */ private boolean isLinked( int indexA, int indexB, int[][] neighbourhoods ) { if( neighbourhoods[ indexA ] != null ) { return Arrays.binarySearch( neighbourhoods[ indexA ], indexB ) >= 0; } return false; } public Configurator getConfigurator() { if( conf == null ) { conf = new NeighbourhoodRendererConfigurator(); } return conf; } /** * @author ryanm */ private class NeighbourhoodRendererConfigurator extends AbstractConfigurator implements ValueListener { private static final String WIDTH = "Edge Width"; private static final String LENGTH_OFFSET = "Length Offset"; private static final String SUBDIVISIONS = "Subdivisions"; private NeighbourhoodRendererConfigurator() { super( NeighbourhoodRenderer.this.getName(), "Draws neighbourhood links" ); addVariable( WIDTH ); addVariable( LENGTH_OFFSET ); addVariable( SUBDIVISIONS ); Configurator ec = edge.getConfigurator( "Edge material" ); Configurator sec = symmetricEdge.getConfigurator( "Symmetric edge material" ); ec.addValueListener( this, true ); sec.addValueListener( this, true ); addVariable( ec ); addVariable( sec ); setType( WIDTH, float.class ); setDescription( WIDTH, "The width of the edge" ); setRange( WIDTH, new Float[] { new Float( 0 ), new Float( 0.02f ) } ); setType( LENGTH_OFFSET, float.class ); setDescription( LENGTH_OFFSET, "The offset at the start and end of the edge" ); setRange( LENGTH_OFFSET, new Float[] { new Float( 0 ), new Float( 0.1 ) } ); setType( SUBDIVISIONS, int.class ); setDescription( SUBDIVISIONS, "The number of subdivisions in the edge" + " more looks better but is slower" ); setRange( SUBDIVISIONS, new Integer[] { new Integer( 3 ), new Integer( 15 ) } ); } @Override protected void applyValue( String name, Object value ) { if( name.equals( WIDTH ) ) { width = ( ( Number ) value ).floatValue(); } else if( name.equals( LENGTH_OFFSET ) ) { lengthOffset = ( ( Number ) value ).floatValue(); } else if( name.equals( SUBDIVISIONS ) ) { int subs = ( ( Number ) value ).intValue(); edge.setSubdivisions( subs ); symmetricEdge.setSubdivisions( subs ); } setDirty(); visualiser.refresh(); } @Override public Object retrieveValue( String name ) { if( name.equals( WIDTH ) ) { return new Float( width ); } else if( name.equals( LENGTH_OFFSET ) ) { return new Float( lengthOffset ); } else if( name.equals( SUBDIVISIONS ) ) { return new Integer( edge.getSubdivisions() ); } return null; } public void valueChanged( Configurator conf, String name ) { // our subconfigurators have changed setDirty(); visualiser.refresh(); } } }