001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.ui.graphicGraph; 033 034import java.util.ArrayList; 035import java.util.HashMap; 036 037import org.graphstream.graph.Edge; 038import org.graphstream.graph.Node; 039import org.graphstream.stream.SourceBase.ElementType; 040import org.graphstream.ui.graphicGraph.stylesheet.Selector; 041 042/** 043 * Graphical edge. 044 * 045 * <p> 046 * The graphic edge defines its source and target node as well as a direction, a 047 * string label and a style from the style sheet. 048 * </p> 049 * 050 * @see GraphicGraph 051 */ 052public class GraphicEdge extends GraphicElement implements Edge { 053 // Attributes 054 055 /** 056 * The first node. 057 */ 058 public GraphicNode from; 059 060 /** 061 * The second node. 062 */ 063 public GraphicNode to; 064 065 /** 066 * Is the edge directed ?. 067 */ 068 public boolean directed; 069 070 /** 071 * In case of a multi-graph this is the index of the edge between to and 072 * from. 073 */ 074 public int multi; 075 076 /** 077 * If non null, this gives the number of edges between the two same nodes. 078 */ 079 public EdgeGroup group; 080 081 /** 082 * Control points for curved edges or polylines. This contains the control 083 * points of an edge. If the edge is in 2D each sequence of two cells gives 084 * the x and y coordinates of a control point. Else each sequence of three 085 * cells gives the x, y and z coordinates. Therefore the number of control 086 * points can be obtained by dividing by 2 or 3 the length of this array. 087 * For example for cubic Bezier curves in 2D this array contains four cells. 088 * The control points are ordered from node0 to node1. 089 */ 090 public double[] ctrl; 091 092 // Constructors 093 094 /** 095 * New graphic edge. 096 * 097 * @param id 098 * The edge unique identifier. 099 * @param from 100 * The source node. 101 * @param to 102 * The target node. 103 * @param dir 104 * True if the edge is directed in the direction from-to. 105 * @param attributes 106 * A set of initial attributes. 107 */ 108 public GraphicEdge(String id, GraphicNode from, GraphicNode to, 109 boolean dir, HashMap<String, Object> attributes) { 110 super(id, from.mygraph); 111 112 this.from = from; 113 this.to = to; 114 this.directed = dir; 115 116 if (this.attributes == null) 117 this.attributes = new HashMap<String, Object>(); 118 119 if (attributes != null) 120 addAttributes(attributes); 121 } 122 123 @Override 124 public Selector.Type getSelectorType() { 125 return Selector.Type.EDGE; 126 } 127 128 /** 129 * Obtain the node that is not "n" attached to this edge. 130 * 131 * @param n 132 * One of the node of this edge. 133 * @return The other node of this edge. 134 */ 135 public GraphicNode otherNode(GraphicNode n) { 136 return (GraphicNode) getOpposite(n); 137 } 138 139 @Override 140 public double getX() { 141 return from.x + ((to.x - from.x) / 2); 142 } 143 144 @Override 145 public double getY() { 146 return from.y + ((to.y - from.y) / 2); 147 } 148 149 @Override 150 public double getZ() { 151 return from.z + ((to.z - from.z) / 2); 152 } 153 154 /** 155 * Control points for curved edges or polylines. This contains the control 156 * points of an edge. If the edge is in 2D each sequence of two cells gives 157 * the x and y coordinates of a control point. Else each sequence of three 158 * cells gives the x, y and z coordinates. Therefore the number of control 159 * points can be obtained by dividing by 2 or 3 the length of this array. 160 * For example for cubic Bezier curves in 2D this array contains four cells. 161 * The control points are ordered from node0 to node1. The units are 162 * "graph units". 163 * 164 * @return The control points coordinates or null if this edge is a straight 165 * line. 166 */ 167 public double[] getControlPoints() { 168 return ctrl; 169 } 170 171 /** 172 * True if the the edge defines control points to draw a curve or polyline. 173 * This does not mean the edge style asks to paint the edge as a curve, only 174 * that control points are defined. 175 * 176 * @return True if control points are available. 177 */ 178 public boolean isCurve() { 179 return ctrl != null; 180 } 181 182 /** 183 * Change the control points array for this edge. 184 * 185 * @param points 186 * The new set of points. See the {@link #getControlPoints()} 187 * method for an explanation on the organisation of this array. 188 * @see #getControlPoints() 189 */ 190 public void setControlPoints(double points[]) { 191 ctrl = points; 192 } 193 194 /** 195 * This edge is the i-th between the two same nodes. 196 * 197 * @return The edge index between the two nodes if there are several such 198 * edges. 199 */ 200 public int getMultiIndex() { 201 return multi; 202 } 203 204 @Override 205 public void move(double x, double y, double z) { 206 // NOP on edges !!! 207 } 208 209 @Override 210 protected void attributeChanged(AttributeChangeEvent event, 211 String attribute, Object oldValue, Object newValue) { 212 super.attributeChanged(event, attribute, oldValue, newValue); 213 214 if (attribute.startsWith("ui.sprite.")) { 215 mygraph.spriteAttribute(event, this, attribute, newValue); 216 } 217 218 mygraph.listeners.sendAttributeChangedEvent(getId(), ElementType.EDGE, 219 attribute, event, oldValue, newValue); 220 } 221 222 /** 223 * Count the number of identical edges between the two nodes of this edge 224 * and create or update the edge group. The edge group contains all the 225 * edges between two same nodes and allows to render faster multiple edges 226 * in a multigraph. 227 * 228 * @param edgeList 229 * The actual set of edges between two nodes (see the 230 * connectivity in the graphic graph). 231 */ 232 protected void countSameEdges(ArrayList<GraphicEdge> edgeList) { 233 for (GraphicEdge other : edgeList) { 234 if (other != this) { 235 if ((other.from == from && other.to == to) 236 || (other.to == from && other.from == to)) { 237 group = other.group; 238 239 if (group == null) 240 group = new EdgeGroup(other, this); 241 else 242 group.increment(this); 243 244 break; 245 } 246 } 247 } 248 } 249 250 @Override 251 public void removed() { 252 if (group != null) { 253 group.decrement(this); 254 255 if (group.getCount() == 1) 256 group = null; 257 } 258 } 259 260 // Edge interface 261 262 @SuppressWarnings("all") 263 public <T extends Node> T getNode0() { 264 return (T) from; 265 } 266 267 @SuppressWarnings("all") 268 public <T extends Node> T getNode1() { 269 return (T) to; 270 } 271 272 /** 273 * If there are several edges between two nodes, this edge pertains to a 274 * group. Else this method returns null. 275 * 276 * @return The group of edges between two same nodes, null if the edge is 277 * alone between the two nodes. 278 */ 279 public EdgeGroup getGroup() { 280 return group; 281 } 282 283 @SuppressWarnings("all") 284 public <T extends Node> T getOpposite(Node node) { 285 if (node == from) 286 return (T) to; 287 288 return (T) from; 289 } 290 291 @SuppressWarnings("all") 292 public <T extends Node> T getSourceNode() { 293 return (T) from; 294 } 295 296 @SuppressWarnings("all") 297 public <T extends Node> T getTargetNode() { 298 return (T) to; 299 } 300 301 public boolean isDirected() { 302 return directed; 303 } 304 305 public boolean isLoop() { 306 return (from == to); 307 } 308 309 public void setDirected(boolean on) { 310 directed = on; // / XXX 311 } 312 313 public void switchDirection() { 314 GraphicNode tmp; // XXX 315 tmp = from; 316 from = to; 317 to = tmp; 318 } 319 320 // Nested classes 321 322 /** 323 * An edge group contains the set of edges between two given nodes. This 324 * allows to quickly know how many 'multi' edges there is between two nodes 325 * in a multigraph and to associate invariant indices to edges (the 326 * {@link GraphicEdge#multi} attribute) inside the multi-representation. 327 */ 328 public class EdgeGroup { 329 /** 330 * The set of multiple edges. 331 */ 332 public ArrayList<GraphicEdge> edges; 333 334 /** 335 * Create a new edge group, starting with two edges. 336 * 337 * @param first 338 * The initial edge. 339 * @param second 340 * The second edge. 341 */ 342 public EdgeGroup(GraphicEdge first, GraphicEdge second) { 343 edges = new ArrayList<GraphicEdge>(); 344 first.group = this; 345 second.group = this; 346 edges.add(first); 347 edges.add(second); 348 first.multi = 0; 349 second.multi = 1; 350 } 351 352 /** 353 * I-th edge of the group. 354 * 355 * @param i 356 * The edge index. 357 * @return The i-th edge. 358 */ 359 public GraphicEdge getEdge(int i) { 360 return edges.get(i); 361 } 362 363 /** 364 * Number of edges in this group. 365 * 366 * @return The edge count. 367 */ 368 public int getCount() { 369 return edges.size(); 370 } 371 372 /** 373 * Add an edge in the group. 374 * 375 * @param edge 376 * The edge to add. 377 */ 378 public void increment(GraphicEdge edge) { 379 edge.multi = getCount(); 380 edges.add(edge); 381 } 382 383 /** 384 * Remove an edge from the group. 385 * 386 * @param edge 387 * The edge to remove. 388 */ 389 public void decrement(GraphicEdge edge) { 390 edges.remove(edges.indexOf(edge)); 391 392 for (int i = 0; i < edges.size(); i++) 393 edges.get(i).multi = i; 394 } 395 } 396 397}