package editor;
import com.clarkparsia.empire.annotation.InvalidRdfException;
import com.clarkparsia.empire.annotation.RdfGenerator;
import com.complexible.common.openrdf.model.ExtGraph;
import com.complexible.common.openrdf.model.Graphs;
import org.drools.owl.conyard.ConstructionYardImpl;
import org.drools.owl.conyard.Painting;
import org.drools.owl.conyard.PaintingImpl;
import org.drools.owl.conyard.Site;
import org.drools.owl.conyard.SiteImpl;
import org.drools.owl.conyard.Stair;
import org.drools.owl.conyard.StairImpl;
import org.drools.owl.conyard.WeldingTorch;
import org.drools.owl.conyard.WeldingTorchImpl;
import org.drools.semantics.Thing;
import org.drools.semantics.util.editor.FactGraphAnalyzer;
import org.openrdf.rio.RDFFormat;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.assignment.DataShapeAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.action.layout.Layout;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.ToolTipControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.PolygonRenderer;
import prefuse.render.Renderer;
import prefuse.render.ShapeRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphicsLib;
import prefuse.util.PrefuseLib;
import prefuse.util.force.ForceSimulator;
import prefuse.util.ui.JForcePanel;
import prefuse.util.ui.JValueSlider;
import prefuse.visual.AggregateItem;
import prefuse.visual.AggregateTable;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Visualizer extends JFrame {
public static final String graph = "graph";
public static final String nodes = "graph.nodes";
public static final String edges = "graph.edges";
public static final String AGGR = "aggregates";
private Graph model;
private Object object;
public Visualizer( Thing object ) {
this.object = object;
model = new FactGraphAnalyzer().analyzeObject( object );
try {
org.openrdf.model.Graph tripples = RdfGenerator.asRdf(object);
ExtGraph ext = Graphs.extend( tripples );
ext.write( System.out, RDFFormat.TURTLE );
} catch (InvalidRdfException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
public static void main( String[] arg ) {
Visualizer viz = new Visualizer( createObject() );
viz.showFrame();
}
void showFrame() {
// UILib.setPlatformLookAndFeel();
this.getContentPane().add( new JScrollPane( demo( model ) ) );
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.pack();
this.setVisible( true );
}
public JComponent demo( final Graph g ) {
// create a new, empty visualization for our data
final Visualization vis = new Visualization();
VisualGraph vg = vis.addGraph( graph, g );
vis.setValue( edges, null, VisualItem.INTERACTIVE, Boolean.TRUE );
TupleSet focusGroup = vis.getGroup( Visualization.FOCUS_ITEMS );
focusGroup.addTupleSetListener( new TupleSetListener() {
public void tupleSetChanged( TupleSet ts, Tuple[] add, Tuple[] rem )
{
for ( int i=0; i<rem.length; ++i ) {
if ( vis.isInGroup( (VisualItem) rem[i], nodes ) ) {
( (VisualItem) rem[i] ).setFixed( false );
}
}
for ( int i=0; i<add.length; ++i ) {
( (VisualItem) add[i] ).setFixed( false );
( (VisualItem) add[i] ).setFixed( true );
}
vis.run( "draw" );
}
});
// set up the renderers
DefaultRendererFactory factory = new DefaultRendererFactory();
factory.setDefaultRenderer( new ShapeRenderer( 20 ) {
public void render( Graphics2D graphics2D, VisualItem visualItem ) {
super.render( graphics2D, visualItem );
if ( visualItem.getGroup().equals( nodes ) ) {
graphics2D.setColor( Color.black );
String lab = (String) visualItem.get( "label" );
if ( lab != null ) {
graphics2D.drawString( lab,
(float) visualItem.getBounds().getMinX(),
(float) visualItem.getBounds().getCenterY() );
}
}
}
} );
EdgeRenderer edger = new EdgeRenderer( Constants.EDGE_TYPE_LINE, Constants.EDGE_ARROW_FORWARD ) {
@Override
public void render( Graphics2D graphics2D, VisualItem visualItem ) {
super.render( graphics2D, visualItem );
graphics2D.setFont( visualItem.getFont() );
graphics2D.setColor( Color.black );
String lab = (String) visualItem.get( "label" );
if ( lab != null ) {
graphics2D.drawString( lab, (float) visualItem.getBounds().getMinX(), (float) visualItem.getBounds().getCenterY() );
}
}
};
edger.setArrowHeadSize( 40, 40 );
factory.setDefaultEdgeRenderer( edger );
Renderer polyR = new PolygonRenderer( Constants.POLY_TYPE_CURVE );
( (PolygonRenderer) polyR ).setCurveSlack( 0.05f );
factory.add( "ingroup('aggregates')", polyR );
vis.setRendererFactory( factory );
// -- set up the actions ----------------------------------------------
int maxhops = 20, hops = 5;
final GraphDistanceFilter filter = new GraphDistanceFilter( graph, hops );
ActionList draw = new ActionList();
draw.add( filter );
draw.add( new ColorAction( nodes, VisualItem.FILLCOLOR, ColorLib.rgb(200,200,255) ) );
draw.add( new ColorAction( nodes, VisualItem.STROKECOLOR, 0) );
draw.add( new ColorAction( nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0) ) );
draw.add( new ColorAction( edges, VisualItem.FILLCOLOR, ColorLib.gray(200) ) );
draw.add( new ColorAction( edges, VisualItem.STROKECOLOR, ColorLib.gray(100) ) );
draw.add( new DataShapeAction( nodes, "type" ) );
ColorAction aStroke = new ColorAction(AGGR, VisualItem.STROKECOLOR);
aStroke.setDefaultColor(ColorLib.gray(200));
aStroke.add("_hover", ColorLib.rgb(255,100,100));
int[] palette = new int[] {
ColorLib.rgba(255,200,0,25)
};
ColorAction aFill = new DataColorAction( AGGR, "id",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette );
draw.add( aStroke );
draw.add( aFill );
ColorAction fill = new ColorAction(nodes,
VisualItem.FILLCOLOR, ColorLib.rgb(200,200,255));
fill.add("_fixed", ColorLib.rgb(255,100,100));
fill.add("_highlight", ColorLib.rgb(255,200,125));
AggregateTable at = vis.addAggregates(AGGR);
at.addColumn( VisualItem.POLYGON, float[].class );
at.addColumn( "id", int.class );
// add nodes to aggregates
Iterator<Node> nodes = vg.nodes();
Map<Integer, AggregateItem> aggTable = new HashMap<Integer, AggregateItem>();
while ( nodes.hasNext() ) {
Node node = nodes.next();
if ( node.get( "aggId" ) != null ) {
int aggId = (Integer) node.get( "aggId" );
if ( ! aggTable.containsKey( aggId ) ) {
AggregateItem agg = (AggregateItem) at.addItem();
agg.set( "id", aggId );
aggTable.put( aggId, agg );
}
aggTable.get( aggId ).addItem( (VisualItem) node );
}
}
ForceDirectedLayout fdl = new ForceDirectedLayout( graph );
ForceSimulator fsim = fdl.getForceSimulator();
fsim.getForces()[0].setParameter(0, -10f);
fsim.getForces()[0].setParameter( 1, 250f );
fsim.getForces()[0].setParameter(2, -0.1f);
fsim.getForces()[1].setParameter( 0, 0.025f );
fsim.getForces()[2].setParameter(0, 1e-4f);
fsim.getForces()[2].setParameter(1, 150f);
// fsim.getForces()[0].setParameter(6, 2);
ActionList animate = new ActionList(Activity.INFINITY);
animate.add( fdl );
animate.add( new AggregateLayout( AGGR ) );
animate.add( aFill );
animate.add( fill );
animate.add(new RepaintAction());
ActionList repaint = new ActionList();
repaint.add( new RepaintAction() );
// finally, we register our ActionList with the Visualization.
// we can later execute our Actions by invoking a method on our
// Visualization, using the name we've chosen below.
vis.putAction( "draw", draw );
vis.putAction( "layout", animate );
vis.putAction( "repaint", repaint );
vis.runAfter( "draw", "layout" );
// --------------------------------------------------------------------
// STEP 4: set up a display to show the visualization
Display display = new Display(vis);
display.setSize(1024,768);
display.setForeground(Color.GRAY);
display.setBackground(Color.WHITE);
// main display controls
display.addControlListener( new FocusControl( 1 ) {
@Override
public void itemClicked( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) || visualItem.getGroup().contains( edges ) ) {
return;
}
super.itemClicked( visualItem, mouseEvent );
}
});
display.addControlListener( new DragControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new PanControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new ZoomControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new WheelZoomControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new ZoomToFitControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new NeighborHighlightControl() {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
display.addControlListener( new ToolTipControl( "iri" ) {
@Override
public void itemDragged( VisualItem visualItem, MouseEvent mouseEvent ) {
if ( visualItem.getGroup().contains( AGGR ) ) {
return;
}
super.itemDragged( visualItem, mouseEvent ); //To change body of overridden methods use File | Settings | File Templates.
}
});
//
EditControlAdapter editor = new EditControlAdapter( g );
display.addControlListener( editor );
display.setForeground(Color.GRAY);
display.setBackground(Color.WHITE);
// --------------------------------------------------------------------
// STEP 5: launching the visualization
// create a panel for editing force values
final JForcePanel fpanel = new JForcePanel(fsim);
final JValueSlider slider = new JValueSlider( "Distance", 0, maxhops, hops );
slider.addChangeListener(new ChangeListener() {
public void stateChanged( ChangeEvent e ) {
filter.setDistance( slider.getValue().intValue() );
vis.run( "draw" );
}
});
slider.setBackground( Color.WHITE );
slider.setPreferredSize( new Dimension( 300,30 ) );
slider.setMaximumSize( new Dimension( 300,30 ) );
Box cf = new Box(BoxLayout.Y_AXIS);
cf.add( slider );
cf.setBorder( BorderFactory.createTitledBorder( "Connectivity Filter" ) );
fpanel.add( cf );
fpanel.add( Box.createVerticalGlue() );
// create a new JSplitPane to present the interface
JSplitPane split = new JSplitPane();
split.setLeftComponent( display );
split.setRightComponent( fpanel );
split.setOneTouchExpandable( true );
split.setContinuousLayout( false );
split.setDividerLocation( 0.75 );
JPanel main = new JPanel();
main.setLayout( new BorderLayout() );
main.add( split, BorderLayout.CENTER );
main.add( editor.getPanel(), BorderLayout.PAGE_END );
// position and fix the default focus node
NodeItem focus = (NodeItem)vg.getNode(0);
PrefuseLib.setX(focus, null, 400);
PrefuseLib.setY(focus, null, 250);
focusGroup.setTuple(focus);
// now we run our action list and return
return main;
}
private static Thing createObject() {
Painting paint = new PaintingImpl();
paint.addEndsOn( new Date() );
paint.addHasComment( "This is a test !" );
paint.addOid( "123" );
Site site = new SiteImpl();
site.addLocatedAt( new ConstructionYardImpl() );
site.addHasNote( "Do not come here!" );
site.addHasNote( "This is dangerous!" );
((PaintingImpl) paint).getEndsOn();
Stair stair = new StairImpl();
stair.setStairLength( 30 );
stair.setOid( "st1" );
// stair.setStoredInSite( site );
paint.addRequires( stair );
WeldingTorch torch = new WeldingTorchImpl();
torch.setStoredInSite( site );
torch.setOid( "tor2" );
paint.addRequires( torch );
return paint;
}
/**
* Layout algorithm that computes a convex hull surrounding
* aggregate items and saves it in the "_polygon" field.
*/
static class AggregateLayout extends Layout {
private int m_margin = 5; // convex hull pixel margin
private double[] m_pts; // buffer for computing convex hulls
public AggregateLayout(String aggrGroup) {
super(aggrGroup);
}
/**
*/
public void run(double frac) {
AggregateTable aggr = (AggregateTable)m_vis.getGroup(m_group);
synchronized (aggr) {
// do we have any to process?
int num = aggr.getTupleCount();
if ( num == 0 ) return;
// update buffers
int maxsz = 0;
for ( Iterator aggrs = aggr.tuples(); aggrs.hasNext(); )
maxsz = Math.max(maxsz, 4*2*
((AggregateItem)aggrs.next()).getAggregateSize());
if ( m_pts == null || maxsz > m_pts.length ) {
m_pts = new double[maxsz];
}
// compute and assign convex hull for each aggregate
Iterator aggrs = m_vis.visibleItems(m_group);
while ( aggrs.hasNext() ) {
AggregateItem aitem = (AggregateItem)aggrs.next();
int idx = 0;
if ( aitem.getAggregateSize() == 0 ) continue;
VisualItem item = null;
Iterator iter = aitem.items();
while ( iter.hasNext() ) {
item = (VisualItem)iter.next();
if ( item.isVisible() ) {
addPoint(m_pts, idx, item, m_margin);
idx += 2*4;
}
}
// if no aggregates are visible, do nothing
if ( idx == 0 ) continue;
// compute convex hull
double[] nhull = GraphicsLib.convexHull( m_pts, idx );
// prepare viz attribute array
float[] fhull = (float[])aitem.get(VisualItem.POLYGON);
if ( fhull == null || fhull.length < nhull.length )
fhull = new float[nhull.length];
else if ( fhull.length > nhull.length )
fhull[nhull.length] = Float.NaN;
// copy hull values
for ( int j=0; j<nhull.length; j++ )
fhull[j] = (float)nhull[j];
aitem.set(VisualItem.POLYGON, fhull);
aitem.setValidated(false); // force invalidation
}
}
}
private static void addPoint(double[] pts, int idx,
VisualItem item, int growth)
{
Rectangle2D b = item.getBounds();
double minX = (b.getMinX())-growth, minY = (b.getMinY())-growth;
double maxX = (b.getMaxX())+growth, maxY = (b.getMaxY())+growth;
pts[idx] = minX; pts[idx+1] = minY;
pts[idx+2] = minX; pts[idx+3] = maxY;
pts[idx+4] = maxX; pts[idx+5] = minY;
pts[idx+6] = maxX; pts[idx+7] = maxY;
}
} // end of class AggregateLayout
}