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}