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

org.graphstream.ui.view.Viewer Maven / Gradle / Ivy

/*
 * 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.
 */

/**
 * @since 2009-07-26
 * 
 * @author Alex Bowen 
 * @author Guilhelm Savin 
 * @author lucaslugao 
 * @author kitskub 
 * @author Hicham Brahimi 
 */
package org.graphstream.ui.view;

import java.util.Map;
import java.util.TreeMap;

import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.stream.ProxyPipe;
import org.graphstream.stream.Source;
import org.graphstream.stream.thread.ThreadProxyPipe;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.GraphicGraph;
import org.graphstream.ui.layout.Layout;
import org.graphstream.ui.layout.LayoutRunner;
import org.graphstream.ui.layout.Layouts;
import org.graphstream.ui.view.camera.Camera;

/**
 * Set of views on a graphic graph.
 * 
 * 

* The viewer class is in charge of maintaining : *

    *
  • A "graphic graph" (a special graph that internally stores the graph under * the form of style sets of "graphic" elements, suitable to draw the graph, but * not to adapted to used it as a general graph),
  • *
  • The eventual proxy pipe from which the events come from (but graph events * can come from any kind of source),
  • *
  • A default view, and eventually more views on the graphic graph.
  • *
  • A flag that allows to repaint the view only if the graphic graph changed. *
  • *
*

* *

* The graphic graph can be created by the viewer or given at construction (to * share it with another viewer). *

* *

* Once created, the viewer runs in a loop inside the UI thread. You cannot * call methods on it directly if you are not in this thread. The only * operation that you can use in other threads is the constructor, the * {@link #addView(View)}, {@link #removeView(String)} and the {@link #close()} * methods. Other methods are not protected from concurrent accesses. *

* *

* Some constructors allow a {@link ProxyPipe} as argument. If given, the * graphic graph is made listener of this pipe and the pipe is "pumped" during * the view loop. This allows to run algorithms on a graph in the main thread * (or any other thread) while letting the viewer run in the ui thread. *

* *

* Be very careful: due to the nature of graph events in GraphStream, the viewer * is not aware of events that occured on the graph before its creation. * There is a special mechanism that replay the graph if you use a proxy pipe or * if you pass the graph directly. However, when you create the viewer by * yourself and only pass a {@link Source}, the viewer will not display * the events that occured on the source before it is connected to it. *

*/ public abstract class Viewer { // Attributes /** * How does the viewer synchronise its internal graphic graph with the graph * displayed. The graph we display can be in the Swing thread (as will be the * viewer, therefore in the same thread as the viewer), in another thread, or on * a distant machine. */ public enum ThreadingModel { GRAPH_IN_GUI_THREAD, GRAPH_IN_ANOTHER_THREAD, GRAPH_ON_NETWORK }; /** * Name of the default view. */ public abstract String getDefaultID(); // Attribute /** * If true the graph we display is in another thread, the synchronisation * between the graph and the graphic graph must therefore use thread proxies. */ protected boolean graphInAnotherThread = true; /** * The graph observed by the views. */ protected GraphicGraph graph; /** * If we have to pump events by ourself. */ protected ProxyPipe pumpPipe; /** * If we take graph events from a source in this thread. */ protected Source sourceInSameThread; /** * The set of views. */ protected final Map views = new TreeMap(); /** * What to do when a view frame is closed. */ protected CloseFramePolicy closeFramePolicy = CloseFramePolicy.EXIT; // Attribute /** * Optional layout algorithm running in another thread. */ protected LayoutRunner optLayout = null; /** * If there is a layout in another thread, this is the pipe coming from it. */ protected ProxyPipe layoutPipeIn = null; /** * What to do when a view frame is closed. */ public static enum CloseFramePolicy { CLOSE_VIEWER, HIDE_ONLY, EXIT }; /** * Create a new unique identifier for a graph. * * @return The new identifier. */ public String newGGId() { return String.format("GraphicGraph_%d", (int) (Math.random() * 10000)); } /** * Initialise the viewer. * * @param graph * The graphic graph. * @param ppipe * The source of events from another thread or machine (null if * source != null). * @param source * The source of events from this thread (null if ppipe != null). */ public abstract void init(GraphicGraph graph, ProxyPipe ppipe, Source source); /** * Close definitively this viewer and all its views. */ public abstract void close(); // Access /** * What to do when a frame is closed. */ public CloseFramePolicy getCloseFramePolicy() { return closeFramePolicy; } /** * New proxy pipe on events coming from the viewer through a thread. * * @return The new proxy pipe. */ public ProxyPipe newThreadProxyOnGraphicGraph() { ThreadProxyPipe tpp = new ThreadProxyPipe(); tpp.init(graph); return tpp; } /** * New viewer pipe on the events coming from the viewer through a thread. * * @return The new viewer pipe. */ public ViewerPipe newViewerPipe() { ThreadProxyPipe tpp = new ThreadProxyPipe(); tpp.init(graph, false); enableXYZfeedback(true); return new ViewerPipe(String.format("viewer_%d", (int) (Math.random() * 10000)), tpp); } /** * The underlying graphic graph. Caution : Use the returned graph only in the UI * thread !! */ public GraphicGraph getGraphicGraph() { return graph; } /** * The view that correspond to the given identifier. * * @param id * The view identifier. * @return A view or null if not found. */ public View getView(String id) { synchronized (views) { return views.get(id); } } /** * The default view. This is a shortcut to a call to {@link #getView(String)} * with {@link #DEFAULT_VIEW_ID} as parameter. * * @return The default view or null if no default view has been installed. */ public View getDefaultView() { return getView(getDefaultID()); } // Command /** * Create a new instance of the default graph renderer. */ public abstract GraphRenderer newDefaultGraphRenderer(); /** * Build the default graph view and insert it. The view identifier is * {@link #DEFAULT_VIEW_ID}. You can request the view to be open in its own * frame. * * @param openInAFrame * It true, the view is placed in a frame, else the view is only * created and you must embed it yourself in your application. */ public View addDefaultView(boolean openInAFrame) { synchronized (views) { GraphRenderer renderer = newDefaultGraphRenderer(); View view = renderer.createDefaultView(this, getDefaultID()); addView(view); if (openInAFrame) view.openInAFrame(true); return view; } } /** * Add a view using its identifier. If there was already a view with this * identifier, it is closed and returned (if different of the one added). * * @param view * The view to add. * @return The old view that was at the given identifier, if any, else null. */ public View addView(View view) { synchronized (views) { View old = views.put(view.getIdView(), view); if (old != null && old != view) old.close(graph); return old; } } /** * Add a new default view with a specific renderer. If a view with the same id * exists, it is removed and closed. By default the view is open in a frame. * * @param id * The new view identifier. * @param renderer * The renderer to use. * @return The created view. */ public View addView(String id, GraphRenderer renderer) { return addView(id, renderer, true); } /** * Same as {@link #addView(String, GraphRenderer)} but allows to specify that * the view uses a frame or not. * * @param id * The new view identifier. * @param renderer * The renderer to use. * @param openInAFrame * If true the view is open in a frame, else the returned view is a * JPanel that can be inserted in a GUI. * @return The created view. */ public View addView(String id, GraphRenderer renderer, boolean openInAFrame) { synchronized (views) { View view = renderer.createDefaultView(this, id); addView(view); if (openInAFrame) view.openInAFrame(true); return view; } } /** * Remove a view. The view is not closed. * * @param id * The view identifier. */ public void removeView(String id) { synchronized (views) { views.remove(id); } } /** * Compute the overall bounds of the graphic graph according to the nodes and * sprites positions. We can only compute the graph bounds from the nodes and * sprites centres since the node and graph bounds may in certain circumstances * be computed according to the graph bounds. The bounds are stored in the graph * metrics. */ public void computeGraphMetrics() { graph.computeBounds(); synchronized (views) { Point3 lo = graph.getMinPos(); Point3 hi = graph.getMaxPos(); for (final View view : views.values()) { Camera camera = view.getCamera(); if (camera != null) { camera.setBounds(lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); } } } } /** * What to do when the frame containing one or more views is closed. * * @param policy * The close frame policy. */ public void setCloseFramePolicy(CloseFramePolicy policy) { synchronized (views) { closeFramePolicy = policy; } } // Optional layout algorithm /** * Enable or disable the "xyz" attribute change when a node is moved in the * views. By default the "xyz" attribute is changed. * * By default, each time a node of the graphic graph is moved, its "xyz" * attribute is reset to follow the node position. This is useful only if * someone listen at the graphic graph or use the graphic graph directly. But * this operation is quite costly. Therefore by default if this viewer runs in * its own thread, and the main graph is in another thread, xyz attribute change * will be disabled until a listener is added. * * When the viewer is created to be used only in the ui thread, this feature is * always on. */ public void enableXYZfeedback(boolean on) { synchronized (views) { graph.feedbackXYZ(on); } } /** * Launch an automatic layout process that will position nodes in the * background. */ public void enableAutoLayout() { enableAutoLayout(Layouts.newLayoutAlgorithm()); } /** * Launch an automatic layout process that will position nodes in the * background. * * @param layoutAlgorithm * The algorithm to use (see Layouts.newLayoutAlgorithm() for the * default algorithm). */ public void enableAutoLayout(Layout layoutAlgorithm) { synchronized (views) { if (optLayout == null) { // optLayout = new LayoutRunner(graph, layoutAlgorithm, true, // true); optLayout = new LayoutRunner(graph, layoutAlgorithm, true, false); graph.replay(); layoutPipeIn = optLayout.newLayoutPipe(); layoutPipeIn.addAttributeSink(graph); } } } /** * Disable the running automatic layout process, if any. */ public void disableAutoLayout() { synchronized (views) { if (optLayout != null) { ((ThreadProxyPipe) layoutPipeIn).unregisterFromSource(); layoutPipeIn.removeSink(graph); layoutPipeIn = null; optLayout.release(); optLayout = null; } } } /** Dirty replay of the graph. */ public void replayGraph(Graph graph) { // Replay all graph attributes. graph.attributeKeys().forEach(key -> { this.graph.setAttribute(key, graph.getAttribute(key)); }); // Replay all nodes and their attributes. graph.nodes().forEach(node -> { Node n = this.graph.addNode(node.getId()); node.attributeKeys().forEach(key -> { n.setAttribute(key, node.getAttribute(key)); }); }); // Replay all edges and their attributes. graph.edges().forEach(edge -> { Edge e = this.graph.addEdge(edge.getId(), edge.getSourceNode().getId(), edge.getTargetNode().getId(), edge.isDirected()); edge.attributeKeys().forEach(key -> { e.setAttribute(key, edge.getAttribute(key)); }); }); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy