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.algorithm.generator;
033
034import org.graphstream.graph.Node;
035import org.graphstream.stream.Pipe;
036
037/**
038 * Random Euclidean graph generator.
039 * 
040 * <p>
041 * This generator creates random graphs of any size. Links of such graphs are
042 * created according to a threshold. If the Euclidean distance between two nodes
043 * is less than a given threshold, then a link is created between those 2 nodes.
044 * <p>
045 * 
046 * <h2>Usage</h2>
047 * 
048 * <p>
049 * Calling {@link #begin()} put one unique node in the graph, then
050 * {@link #nextEvents()} will add a new node each time it is called and connect
051 * this node to its neighbors according to the threshold planar Euclidean
052 * distance.
053 * </p>
054 * 
055 * <p>
056 * This generator has the ability to add randomly chosen numerical values on
057 * arbitrary attributes on edges or nodes of the graph, and to randomly choose a
058 * direction for edges.
059 * </p>
060 * 
061 * <p>
062 * A list of attributes can be given for nodes and edges. In this case each new
063 * node or edge added will have this attribute and the value will be a randomly
064 * chosen number. The range in which these numbers are chosen can be specified.
065 * </p>
066 * 
067 * <p>
068 * By default, edges are not oriented. It is possible to ask orientation, in
069 * which case the direction is chosen randomly.
070 * </p>
071 * 
072 * <p>
073 * By default, the graph is generated in the plane (2 dimensions) . Cartesian
074 * coordinates on nodes will be generated at random. So, each node will
075 * automatically be given two attributes: "x" and "y". If a dimension is
076 * specified, then |dimension| attributes are generated, and the 2-norm distance
077 * (<a href="http://en.wikipedia.org/wiki/Euclidean_distance">Euclidean
078 * distance</a>) is considered in that dimension between the nodes.
079 * </p>
080 * 
081 * <p>
082 * If the dimension is 2, then attributes "x" and "y" are defined for each node.
083 * If dimension is 3, then attributes "x", "y" and "z" are used. For other
084 * values of dimension, |dimension| attributes are defined ("xi" with "i" \in
085 * |dimension|) .
086 * </p>
087 * 
088 * <h2>Complexity</h2>
089 * 
090 * For the construction of a n nodes graph, the complexity is about O(n^2).
091 * 
092 * <h2>Example</h2>
093 * 
094 * <pre>
095 * Graph graph = new SingleGraph("random euclidean");
096 * Generator gen = new RandomEuclideanGenerator();
097 * gen.addSink(graph);
098 * gen.begin();
099 * for(int i=0; i<1000; i++) {
100 *              gen.nextEvents();
101 * }
102 * gen.end();
103 * graph.display(false);
104 * </pre>
105 * 
106 * @since June 25 2007
107 * @complexity For the construction of a n nodes graph, the complexity is about
108 *             O(n^2).
109 */
110public class RandomEuclideanGenerator extends BaseGenerator implements Pipe {
111        /**
112         * Used to generate node names.
113         */
114        protected int nodeNames = 0;
115
116        /**
117         * The dimension of the space.
118         */
119        protected int dimension = 2;
120
121        /**
122         * The threshold that defines whether or not a link is created between to
123         * nodes. Since the coordinate system is defined between 0 and 1, the
124         * threshold has to be set between these two bounds.
125         */
126        protected double threshold = 0.1;
127        
128        /**
129         * New random Euclidean graph generator. By default no attributes are added
130         * to nodes and edges. Dimension of the space is two.
131         */
132        public RandomEuclideanGenerator() {
133                super();
134                initDimension(2);
135                setUseInternalGraph(true);
136        }
137
138        /**
139         * New random Euclidean graph generator. By default no attributes are added
140         * to nodes and edges. You may also specify a dimension for the space.
141         * 
142         * @param dimension
143         *            The dimension of the space for the graph. By default it is
144         *            two.
145         */
146        public RandomEuclideanGenerator(int dimension) {
147                super();
148                initDimension(dimension);
149                setUseInternalGraph(true);
150        }
151
152        /**
153         * New random Euclidean graph generator. By default no attributes are added
154         * to nodes and edges. It is possible to make edge randomly directed. You
155         * may also specify a dimension for the space.
156         * 
157         * @param dimension
158         *            The dimension of the space for the graph. By default it is
159         *            two.
160         * @param directed
161         *            If true the edges are directed.
162         * @param randomlyDirectedEdges
163         *            If true edge, are directed and the direction is chosen at
164         *            randomly.
165         */
166        public RandomEuclideanGenerator(int dimension, boolean directed,
167                        boolean randomlyDirectedEdges) {
168                super(directed, randomlyDirectedEdges);
169                initDimension(dimension);
170                setUseInternalGraph(true);
171        }
172
173        /**
174         * New random Euclidean graph generator.
175         * 
176         * @param dimension
177         *            The dimension of the space for the graph. By default it is
178         *            two.
179         * @param directed
180         *            If true the edges are directed.
181         * @param randomlyDirectedEdges
182         *            It true, edges are directed and the direction is chosen at
183         *            random.
184         * @param nodeAttribute
185         *            put an attribute by that name on each node with a random
186         *            numeric value.
187         * @param edgeAttribute
188         *            put an attribute by that name on each edge with a random
189         *            numeric value.
190         */
191        public RandomEuclideanGenerator(int dimension, boolean directed,
192                        boolean randomlyDirectedEdges, String nodeAttribute,
193                        String edgeAttribute) {
194                super(directed, randomlyDirectedEdges, nodeAttribute, edgeAttribute);
195                initDimension(dimension);
196                setUseInternalGraph(true);
197        }
198
199        private void initDimension(int dimension) {
200                this.dimension = dimension;
201                super.setNodeAttributesRange(0f, 1f);
202                if (dimension > 0) {
203                        if (dimension == 2) {
204                                super.addNodeAttribute("x");
205                                super.addNodeAttribute("y");
206                        } else if (dimension == 3) {
207                                super.addNodeAttribute("x");
208                                super.addNodeAttribute("y");
209                                super.addNodeAttribute("z");
210                        } else {
211                                for (int i = 0; i < dimension; i++)
212                                        super.addNodeAttribute("x" + i);
213                        }
214                } else
215                        System.err.println("dimension has to be higher that zero");
216
217        }
218
219        /**
220         * Start the generator. A single node is added.
221         * 
222         * @see org.graphstream.algorithm.generator.Generator#begin()
223         */
224        public void begin() {
225                String id = Integer.toString(nodeNames++);
226
227                addNode(id);
228        }
229
230        /**
231         * Step of the generator. Add a new node and connect it with some others.
232         * 
233         * @see org.graphstream.algorithm.generator.Generator#nextEvents()
234         */
235        public boolean nextEvents() {
236                String id = Integer.toString(nodeNames++);
237
238                addNode(id);
239
240                for (Node n : internalGraph.getEachNode()) {
241                        if (!id.equals(n.getId()) && distance(id, n.getId()) < threshold)
242                                addEdge(id + "-" + n.getId(), id, n.getId());
243                }
244
245                return true;
246        }
247
248        /*
249         * (non-Javadoc)
250         * 
251         * @see org.graphstream.algorithm.generator.Generator#end()
252         */
253        @Override
254        public void end() {
255                super.end();
256        }
257
258        /**
259         * Distance between two nodes.
260         * 
261         * @param n1
262         *            first node
263         * @param n2
264         *            second node
265         * @return distance between n1 and n2
266         */
267        private double distance(String n1, String n2) {
268                double d = 0.0;
269
270                if (dimension == 2) {
271                        double x1 = internalGraph.getNode(n1).getNumber("x");
272                        double y1 = internalGraph.getNode(n1).getNumber("y");
273                        double x2 = internalGraph.getNode(n2).getNumber("x");
274                        double y2 = internalGraph.getNode(n2).getNumber("y");
275
276                        d = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
277                } else if (dimension == 3) {
278                        double x1 = internalGraph.getNode(n1).getNumber("x");
279                        double y1 = internalGraph.getNode(n1).getNumber("y");
280                        double x2 = internalGraph.getNode(n2).getNumber("x");
281                        double y2 = internalGraph.getNode(n2).getNumber("y");
282                        double z1 = internalGraph.getNode(n1).getNumber("z");
283                        double z2 = internalGraph.getNode(n2).getNumber("z");
284
285                        d = Math.pow(z1 - z2, 2) + Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
286                } else {
287                        for (int i = 0; i < dimension; i++) {
288                                double xi1 = internalGraph.getNode(n1).getNumber("x" + i);
289                                double xi2 = internalGraph.getNode(n2).getNumber("x" + i);
290
291                                d += Math.pow(xi1 - xi2, 2);
292                        }
293                }
294
295                return Math.sqrt(d);
296        }
297
298        /**
299         * Set the threshold that defines whether or not a link is created between
300         * to notes. Since the coordinate system is defined between 0 and 1, the
301         * threshold has to be set between these two bounds.
302         * 
303         * @param threshold
304         *            The defined threshold.
305         */
306        public void setThreshold(double threshold) {
307                if (threshold <= 1f && threshold >= 0f)
308                        this.threshold = threshold;
309        }
310
311        protected void nodeAttributeHandling(String nodeId, String key, Object val) {
312                if (key != null && key.matches("x|y|z") && val instanceof Float) {
313                        int i = ((int) key.charAt(0)) - (int) 'x';
314
315                        if (i < dimension)
316                                internalGraph.getNode(nodeId).addAttribute(key, val);
317                }
318        }
319
320        public void edgeAttributeAdded(String sourceId, long timeId, String edgeId,
321                        String attribute, Object value) {
322        }
323
324        public void edgeAttributeChanged(String sourceId, long timeId,
325                        String edgeId, String attribute, Object oldValue, Object newValue) {
326        }
327
328        public void edgeAttributeRemoved(String sourceId, long timeId,
329                        String edgeId, String attribute) {
330        }
331
332        public void graphAttributeAdded(String sourceId, long timeId,
333                        String attribute, Object value) {
334        }
335
336        public void graphAttributeChanged(String sourceId, long timeId,
337                        String attribute, Object oldValue, Object newValue) {
338        }
339
340        public void graphAttributeRemoved(String sourceId, long timeId,
341                        String attribute) {
342        }
343
344        public void nodeAttributeAdded(String sourceId, long timeId, String nodeId,
345                        String attribute, Object value) {
346                nodeAttributeHandling(nodeId, attribute, value);
347        }
348
349        public void nodeAttributeChanged(String sourceId, long timeId,
350                        String nodeId, String attribute, Object oldValue, Object newValue) {
351                nodeAttributeHandling(nodeId, attribute, newValue);
352        }
353
354        public void nodeAttributeRemoved(String sourceId, long timeId,
355                        String nodeId, String attribute) {
356        }
357
358        public void edgeAdded(String sourceId, long timeId, String edgeId,
359                        String fromNodeId, String toNodeId, boolean directed) {
360        }
361
362        public void edgeRemoved(String sourceId, long timeId, String edgeId) {
363        }
364
365        public void graphCleared(String sourceId, long timeId) {
366        }
367
368        public void nodeAdded(String sourceId, long timeId, String nodeId) {
369        }
370
371        public void nodeRemoved(String sourceId, long timeId, String nodeId) {
372        }
373
374        public void stepBegins(String sourceId, long timeId, double step) {
375        }
376}