All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graphstream.ui.swing.SwingGraphRenderer Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of GraphStream .
 * 
 * GraphStream is a library whose purpose is to handle static or dynamic
 * graph, create them from scratch, file or any source and display them.
 * 
 * This program is free software distributed under the terms of two licenses, the
 * CeCILL-C license that fits European law, and the GNU Lesser General Public
 * License. You can  use, modify and/ or redistribute the software under the terms
 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
 * URL  or under the terms of the GNU LGPL as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * This program 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 program.  If not, see .
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
 */

 /**
  * @author Antoine Dutot 
  * @author Guilhelm Savin 
  * @author Hicham Brahimi 
  */
  
package org.graphstream.ui.swing;

import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import org.graphstream.graph.Element;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.GraphicEdge;
import org.graphstream.ui.graphicGraph.GraphicElement;
import org.graphstream.ui.graphicGraph.GraphicElement.SwingElementRenderer;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.graphicGraph.GraphicNode;
import org.graphstream.ui.graphicGraph.StyleGroup;
import org.graphstream.ui.graphicGraph.StyleGroupListener;
import org.graphstream.ui.graphicGraph.StyleGroupSet;
import org.graphstream.ui.graphicGraph.stylesheet.Selector;
import org.graphstream.ui.swing.renderer.GraphBackgroundRenderer;
import org.graphstream.ui.swing.renderer.JComponentRenderer;
import org.graphstream.ui.swing.renderer.SelectionRenderer;
import org.graphstream.ui.swing.renderer.StyleRenderer;
import org.graphstream.ui.swing.util.FPSLogger;
import org.graphstream.ui.swing.util.Selection;
import org.graphstream.ui.swing_viewer.DefaultView;
import org.graphstream.ui.swing_viewer.util.Graphics2DOutput;
import org.graphstream.ui.view.GraphRenderer;
import org.graphstream.ui.view.LayerRenderer;
import org.graphstream.ui.view.View;
import org.graphstream.ui.view.Viewer;
import org.graphstream.ui.view.camera.Camera;
import org.graphstream.ui.view.camera.DefaultCamera2D;
import org.graphstream.ui.view.util.GraphMetrics;
import org.graphstream.ui.view.util.InteractiveElement;

/**	
 * 2D renderer.
 * 
 * The role of this class is to equip each style group with a specific renderer and
 * to call these renderer to redraw the graph when needed. The renderers then equip
 * each node, edge and sprite with a skeleton that gives is main geometry, then
 * selects a shape according to the group style. The shape will be "applied" to
 * each element to draw in the group. The shape will be moved and scaled according
 * to the skeleton.
 * 
 * A render pass begins by using the camera instance to set up the projection (allows
 * to pass from graph units to pixels, make a rotation a zoom or a translation) and
 * render each style group once for the shadows, and once for the real rendering
 * in Z order.
 * 
 * This class also handles a "selection" object that represents the current selection
 * and renders it.
 * 
 * The renderer uses a backend so that it can adapt to multiple rendering
 * targets (here Swing and OpenGL). As the shapes are finally responsible
 * for drawing the graph, the backend is also responsible for the shape
 * creation.
 */
public class SwingGraphRenderer implements GraphRenderer, StyleGroupListener {
	public final static String DEFAULT_RENDERER = "j2d_def_rndr";
	
	protected DefaultCamera2D camera = null;
	
	protected GraphicGraph graph = null;
	
	protected Selection selection = new Selection();
	
	protected LayerRenderer backRenderer = null;

	protected LayerRenderer foreRenderer = null;
	
	protected Backend backend = null ;
	
	protected FPSLogger fpsLogger = null ;
	
// Construction

	@Override
	public void open(GraphicGraph graph, Container drawingSurface) {
		if( this.graph == null ) {
			this.graph   = graph;
			this.backend = new BackendJ2D();		// choose it according to some setting
		  	this.camera  = new DefaultCamera2D(graph);
		  	graph.getStyleGroups().addListener(this);
		  	backend.open(drawingSurface);
	  	}
		else {
	  		throw new RuntimeException("renderer already open, use close() first");
	  	}
	}
	
	@Override
	public void close() {
		if(graph != null) {
			if(fpsLogger != null) {
				fpsLogger.close();
				fpsLogger = null;
			}
   
			removeRenderers();
			backend.close();
			graph.getStyleGroups().removeListener(this);
			graph   = null;
			backend = null;
			camera  = null;
		}
	}
	
// Access
	
	@Override
	public Camera getCamera() {
		return camera;
	}
		  
	public Container renderingSurface() {
		return backend.drawingSurface();
	}
	
	/** Get (and assign if needed) a style renderer to the graphic graph. The renderer will be reused then. */
    protected GraphBackgroundRenderer getStyleRenderer(GraphicGraph graph) {
  		if(graph.getStyle().getRenderer("dr") == null)
  			graph.getStyle().addRenderer("dr", new GraphBackgroundRenderer(graph, graph.getStyle()));
  		
  		return (GraphBackgroundRenderer)graph.getStyle().getRenderer("dr");
    }

// Access -- Renderer bindings
    
    /** Get (and assign if needed) a style renderer to a style group. The renderer will be reused then. */
    protected StyleRenderer getStyleRenderer(StyleGroup style) {
    	if( style.getRenderer("dr") == null) {
  			style.addRenderer("dr", StyleRenderer.apply(style, this));
  		}
    
  		return (StyleRenderer)style.getRenderer("dr");
    }
    
    /** Get (and assign if needed) the style renderer associated with the style group of the element. */
    protected StyleRenderer getStyleRenderer(GraphicElement element) {
  		return getStyleRenderer(element.getStyle());
    }
    
    /** Remove all the registered renderers from the graphic graph. */
    protected void removeRenderers() {
        graph.getStyle().removeRenderer("dr");
        graph.nodes().forEach(node -> ((GraphicNode)node).getStyle().removeRenderer("dr")); 
        graph.edges().forEach(edge -> ((GraphicEdge)edge).getStyle().removeRenderer("dr"));
        graph.sprites().forEach(sprite -> sprite.getStyle().removeRenderer("dr"));
    }
    
// Command

    public void beginSelectionAt(double x, double y) {
  		selection.setActive(true);
  		selection.begins(x, y);
  		Logger.getLogger(this.getClass().getSimpleName()).fine("Selection begins at "+x+" "+y);
  	}

    public void selectionGrowsAt(double x, double y) {
  		selection.grows(x, y);
  	}

    public void endSelectionAt(double x, double y) {
  		selection.grows(x, y);
  		selection.setActive(false);
  	}

    public void moveElementAtPx(GraphicElement element, double x, double y) {
  		Point3 p = camera.transformPxToGu(x, y);
  		element.move(p.x, p.y, element.getZ());
  	}
 
// Commands -- Rendering
    
    @Override
    public void render(Graphics2D g, int x, int y, int width, int height) {
    	if(graph != null) {
  	        startFrame();
  	        
  		    // Verify this view is not closed, the Swing repaint mechanism may trigger 1 or 2
  		    // calls to this after being closed.
  		    if(backend == null)
  		        backend = new BackendJ2D(); // TODO choose it according to some setting ...
  		    
  		    backend.prepareNewFrame(g);
  		    camera.setBackend(backend);
  		        
  			StyleGroupSet sgs = graph.getStyleGroups();
  			
  			setupGraphics();
  			graph.computeBounds();
  			camera.setBounds(graph);
  			camera.setViewport(x, y, width, height);
  			getStyleRenderer(graph).render(backend, camera, width, height);
  			renderBackLayer();

  			camera.pushView(graph);
  			sgs.shadows().forEach( s -> getStyleRenderer(s).renderShadow(backend, camera));
  			
  			
  			sgs.getZIndex().forEach( groups -> {
  				groups.forEach( group -> {
  					if(group.getType() != Selector.Type.GRAPH) {
  						getStyleRenderer(group).render(backend, camera);
		  	  		}
  				});
  			});
 
  			camera.popView();
  			renderForeLayer();
  
  			if( selection.getRenderer() == null ) 
  				selection.setRenderer(new SelectionRenderer( selection, graph ));
  			selection.getRenderer().render(backend, camera, width, height );
  			
  	    	endFrame();
  	    }
    }
    
    protected void startFrame() {
  	    if((fpsLogger == null) && graph.hasLabel("ui.log")) {
  	    	fpsLogger = new FPSLogger(graph.getLabel("ui.log").toString());
  	    }
  	    
  	    if(! (fpsLogger == null))
  	    	fpsLogger.beginFrame();
  	}
  	
  	protected void endFrame() {
  	    if(! (fpsLogger == null))
  	        fpsLogger.endFrame();
  	}
  	
  	protected void renderBackLayer() {
  		if(backRenderer != null) 
  			renderLayer(backRenderer);
  	}
	
	protected void renderForeLayer() {
		if(foreRenderer != null)
			renderLayer(foreRenderer);
	}
	
	/** Render a back or from layer. */ 
	protected void renderLayer(LayerRenderer renderer) {
		GraphMetrics metrics = camera.getMetrics();
		
		renderer.render(backend.graphics2D(), graph, metrics.ratioPx2Gu,
			(int)metrics.viewport[2],
			(int)metrics.viewport[3],
			metrics.loVisible.x,
			metrics.loVisible.y,
			metrics.hiVisible.x,
			metrics.hiVisible.y);
	}
    
	/** Setup the graphic pipeline before drawing. */
	protected void setupGraphics() {
       backend.setAntialias(graph.hasAttribute("ui.antialias"));
       backend.setQuality(graph.hasAttribute("ui.quality"));
	}
	
	public void screenshot(String filename, int width, int height) {
		if(filename.toLowerCase().endsWith("png")) {
			BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			render(img.createGraphics(), 0, 0, width, height);
			File file = new File(filename);
			try {
				ImageIO.write(img, "png", file);
			} catch (IOException e) { e.printStackTrace(); }
	   	} 
		else if(filename.toLowerCase().endsWith("bmp")) {
			// Who, in the world, is still using BMP ???
			BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			render(img.createGraphics(), 0, 0, width, height);
			File file = new File(filename);
			try {
				ImageIO.write(img, "bmp", file);
			} catch (IOException e) { e.printStackTrace(); }
		}
		else if(filename.toLowerCase().endsWith("jpg") || filename.toLowerCase().endsWith("jpeg")) {
			BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			render(img.createGraphics(), 0, 0, width, height);
			File file = new File(filename);
			try {
				ImageIO.write(img, "jpg", file);
			} catch (IOException e) {e.printStackTrace();}
		} 
		else if(filename.toLowerCase().endsWith("svg")) {
		    try {
				String plugin = "org.graphstream.ui.batik.BatikGraphics2D";
				Class c = Class.forName(plugin);
				Object o = (Object)c.newInstance();
				if(o instanceof Graphics2DOutput) {
					Graphics2DOutput out = (Graphics2DOutput)o;
					Graphics2D g2 = out.getGraphics();
					render(g2, 0, 0, width, height);
					out.outputTo(filename);
				} 
				else {
					Logger.getLogger(this.getClass().getSimpleName()).warning("Plugin "+plugin+" is not an instance of Graphics2DOutput ("+o.getClass().getName()+").");
				}
			} catch (Exception e){ e.printStackTrace();}
		} 
		else {
		    Logger.getLogger(this.getClass().getSimpleName()).warning("Unknown screenshot filename extension "+filename+", saving to jpeg.");
		    BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			render(img.createGraphics(), 0, 0, width, height);
			File file = new File(filename+".jpg");
			try {
				ImageIO.write(img, "jpg", file);
			} catch (IOException e) { e.printStackTrace(); }
		}
	}
	
	public void setBackLayerRenderer(LayerRenderer renderer) { backRenderer = renderer; }

	public void setForeLayoutRenderer(LayerRenderer renderer) { foreRenderer = renderer; }
	
	// Commands -- Style group listener
    public void elementStyleChanged(Element element, StyleGroup oldStyle, StyleGroup style) {
    	// XXX The element renderer should be the listener, not this. ... XXX

    	if(oldStyle != null) {
    		SwingElementRenderer renderer = oldStyle.getRenderer(SwingGraphRenderer.DEFAULT_RENDERER);

	    	if((renderer != null ) && renderer instanceof JComponentRenderer)
	    		((JComponentRenderer)renderer).unequipElement((GraphicElement)element);
    	}
    }
    
    @Override
    public View createDefaultView(Viewer viewer, String id) {
    	return new DefaultView(viewer, id, this);
    }

	@Override
	public GraphicElement findGraphicElementAt(EnumSet types, double x, double y) {
		return camera.findGraphicElementAt(graph, types, x, y);
	}

	@Override
	public Collection allGraphicElementsIn(EnumSet types, double x1, double y1,
			double x2, double y2) {
		return camera.allGraphicElementsIn(graph,types,x1, y1, x2, y2);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy