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 org.graphstream.graph.Edge;
035import org.graphstream.graph.Graph;
036import org.graphstream.graph.Node;
037import org.graphstream.ui.geom.Point2;
038import org.graphstream.ui.geom.Point3;
039import org.graphstream.ui.geom.Vector2;
040import org.graphstream.ui.geom.Vector3;
041
042/**
043 * Lots of small often used measuring algorithms on graphs.
044 * 
045 * <p>
046 * Use this class with a static import.
047 * </p>
048 */
049public class GraphPosLengthUtils {
050        /**
051         * Retrieve a node position from its attributes ("x", "y", "z", or "xy", or
052         * "xyz").
053         * 
054         * @param id
055         *            The node identifier.
056         * @return A newly allocated array of three floats containing the (x,y,z)
057         *         position of the node, or null if the node is not part of the
058         *         graph.
059         */
060        public static double[] nodePosition(Graph graph, String id) {
061                Node node = graph.getNode(id);
062
063                if (node != null)
064                        return nodePosition(node);
065
066                return null;
067        }
068
069        /**
070         * Retrieve a node position from its attributes ("x", "y", "z", or "xy", or
071         * "xyz").
072         * 
073         * @param id
074         *            The node identifier.
075         * @return A newly allocated point containing the (x,y,z)
076         *         position of the node, or null if the node is not part of the
077         *         graph.
078         */
079        public static Point3 nodePointPosition(Graph graph, String id) {
080                Node node = graph.getNode(id);
081
082                if (node != null)
083                        return nodePointPosition(node);
084
085                return null;
086        }
087
088        /**
089         * Like {@link #nodePosition(Graph,String)} but use an existing node as
090         * argument.
091         * 
092         * @param node
093         *            The node to consider.
094         * @return A newly allocated array of three floats containing the (x,y,z)
095         *         position of the node.
096         */
097        public static double[] nodePosition(Node node) {
098                double xyz[] = new double[3];
099
100                nodePosition(node, xyz);
101
102                return xyz;
103        }
104
105        /**
106         * Like {@link #nodePointPosition(Graph,String)} but use an existing node as
107         * argument.
108         * 
109         * @param node
110         *            The node to consider.
111         * @return A newly allocated point containing the (x,y,z)
112         *         position of the node.
113         */
114        public static Point3 nodePointPosition(Node node) {
115                Point3 pos = new Point3();
116
117                nodePosition(node, pos);
118
119                return pos;
120        }
121
122        /**
123         * Like {@link #nodePosition(Graph,String)}, but instead of returning a
124         * newly allocated array, fill up the array given as parameter. This array
125         * must have at least three cells.
126         * 
127         * @param id
128         *            The node identifier.
129         * @param xyz
130         *            An array of at least three cells.
131         * @throws RuntimeException
132         *             If the node with the given identifier does not exist.
133         */
134        public static void nodePosition(Graph graph, String id, double xyz[]) {
135                Node node = graph.getNode(id);
136
137                if (node != null)
138                        nodePosition(node, xyz);
139
140                throw new RuntimeException("node '" + id + "' does not exist");
141        }
142        
143        /**
144         * Like {@link #nodePointPosition(Graph,String)}, but instead of returning a
145         * newly allocated array, fill up the array given as parameter. This array
146         * must have at least three cells.
147         * 
148         * @param id
149         *            The node identifier.
150         * @param pos
151         *            A point that will receive the node position.
152         * @throws RuntimeException
153         *             If the node with the given identifier does not exist.
154         */
155        public static void nodePosition(Graph graph, String id, Point3 pos) {
156                Node node = graph.getNode(id);
157
158                if (node != null)
159                        nodePosition(node, pos);
160
161                throw new RuntimeException("node '" + id + "' does not exist");
162        }
163
164        /**
165         * Like {@link #nodePosition(Graph,String,double[])} but use an existing node
166         * as argument.
167         * 
168         * @param node
169         *            The node to consider.
170         * @param xyz
171         *            An array of at least three cells.
172         */
173        public static void nodePosition(Node node, double xyz[]) {
174                if (xyz.length < 3)
175                        return;
176
177                if (node.hasAttribute("xyz") || node.hasAttribute("xy")) {
178                        Object o = node.getAttribute("xyz");
179
180                        if (o == null)
181                                o = node.getAttribute("xy");
182
183                        if (o != null) {
184                                positionFromObject(o, xyz);
185                        }
186                        
187                } else if (node.hasAttribute("x")) {
188                        xyz[0] = (double) node.getNumber("x");
189
190                        if (node.hasAttribute("y"))
191                                xyz[1] = (double) node.getNumber("y");
192
193                        if (node.hasAttribute("z"))
194                                xyz[2] = (double) node.getNumber("z");
195                }
196        }
197        
198        /**
199         * Like {@link #nodePosition(Graph,String,Point3)} but use an existing node
200         * as argument.
201         * 
202         * @param node
203         *            The node to consider.
204         * @param pos
205         *            A point that will receive the node position.
206         */
207        public static void nodePosition(Node node, Point3 pos) {
208                if (node.hasAttribute("xyz") || node.hasAttribute("xy")) {
209                        Object o = node.getAttribute("xyz");
210
211                        if (o == null)
212                                o = node.getAttribute("xy");
213
214                        if (o != null) {
215                                positionFromObject(o, pos);
216                        }
217                } else if (node.hasAttribute("x")) {
218                        pos.x = (double) node.getNumber("x");
219
220                        if (node.hasAttribute("y"))
221                                pos.y = (double) node.getNumber("y");
222
223                        if (node.hasAttribute("z"))
224                                pos.z = (double) node.getNumber("z");
225                }
226
227//              if (node.hasAttribute("xyz") || node.hasAttribute("xy")) {
228//                      Object o = node.getAttribute("xyz");
229//
230//                      if (o == null)
231//                              o = node.getAttribute("xy");
232//
233//                      if (o != null && o instanceof Object[]) {
234//                              Object oo[] = (Object[]) o;
235//
236//                              if (oo.length > 0 && oo[0] instanceof Number) {
237//                                      pos.x = ((Number) oo[0]).doubleValue();
238//
239//                                      if (oo.length > 1)
240//                                              pos.y = ((Number) oo[1]).doubleValue();
241//                                      if (oo.length > 2)
242//                                              pos.z = ((Number) oo[2]).doubleValue();
243//                              }
244//                      }
245//              } else if (node.hasAttribute("x")) {
246//                      pos.x = (double) node.getNumber("x");
247//
248//                      if (node.hasAttribute("y"))
249//                              pos.y = (double) node.getNumber("y");
250//
251//                      if (node.hasAttribute("z"))
252//                              pos.z = (double) node.getNumber("z");
253//              }
254        }
255        
256        /**
257         * Try to convert an object to a position. The object can be an array of 
258         * numbers, an array of base numeric types or their object counterparts.
259         * @param o The object to try to convert.
260         * @param xyz The result.
261         */
262        public static void positionFromObject(Object o, double xyz[]) {
263                if(o instanceof Object[]) {
264                        Object oo[] = (Object[]) o;
265
266                        if (oo.length > 0 && oo[0] instanceof Number) {
267                                xyz[0] = ((Number) oo[0]).doubleValue();
268                                if (oo.length > 1) xyz[1] = ((Number) oo[1]).doubleValue();
269                                if (oo.length > 2) xyz[2] = ((Number) oo[2]).doubleValue();
270                        }
271                } else if(o instanceof Double[]) {
272                        Double oo[] = (Double[]) o;
273                        if(oo.length > 0) xyz[0] = oo[0];
274                        if(oo.length > 1) xyz[1] = oo[1];
275                        if(oo.length > 2) xyz[2] = oo[2];
276                } else if(o instanceof Float[]) {
277                        Float oo[] = (Float[]) o;
278                        if(oo.length > 0) xyz[0] = oo[0];
279                        if(oo.length > 1) xyz[1] = oo[1];
280                        if(oo.length > 2) xyz[2] = oo[2];
281                } else if(o instanceof Integer[]) {
282                        Integer oo[] = (Integer[]) o;
283                        if(oo.length > 0) xyz[0] = oo[0];
284                        if(oo.length > 1) xyz[1] = oo[1];
285                        if(oo.length > 2) xyz[2] = oo[2];
286                } else if(o instanceof double[]) {
287                        double oo[] = (double[]) o;
288                        if(oo.length > 0) xyz[0] = oo[0];
289                        if(oo.length > 1) xyz[1] = oo[1];
290                        if(oo.length > 2) xyz[2] = oo[2];
291                } else if(o instanceof float[]) {
292                        float oo[] = (float[]) o;
293                        if(oo.length > 0) xyz[0] = oo[0];
294                        if(oo.length > 1) xyz[1] = oo[1];
295                        if(oo.length > 2) xyz[2] = oo[2];
296                } else if(o instanceof int[]) {
297                        int oo[] = (int[]) o;
298                        if(oo.length > 0) xyz[0] = oo[0];
299                        if(oo.length > 1) xyz[1] = oo[1];
300                        if(oo.length > 2) xyz[2] = oo[2];
301                } else if(o instanceof Number[]) {
302                        Number oo[] = (Number[]) o;
303                        if(oo.length > 0) xyz[0] = oo[0].doubleValue();
304                        if(oo.length > 1) xyz[1] = oo[1].doubleValue();
305                        if(oo.length > 2) xyz[2] = oo[2].doubleValue();
306                } else if(o instanceof Point3) {
307                        Point3 oo = (Point3) o;
308                        xyz[0] = oo.x;
309                        xyz[1] = oo.y;
310                        xyz[2] = oo.z;
311                } else if(o instanceof Vector3) {
312                        Vector3 oo = (Vector3) o;
313                        xyz[0] = oo.data[0];
314                        xyz[1] = oo.data[1];
315                        xyz[2] = oo.data[2];
316                } else if(o instanceof Point2) {
317                        Point2 oo = (Point2) o;
318                        xyz[0] = oo.x;
319                        xyz[1] = oo.y;
320                        xyz[2] = 0;
321                } else if(o instanceof Vector2) {
322                        Vector2 oo = (Vector2) o;
323                        xyz[0] = oo.data[0];
324                        xyz[1] = oo.data[1];
325                        xyz[2] = 0;
326                } else {
327                        System.err.printf("Do not know how to handle xyz attribute %s%n", o.getClass().getName());
328                }
329        }
330        
331        /**
332         * Try to convert an object to a position. The object can be an array of 
333         * numbers, an array of base numeric types or their object counterparts.
334         * @param o The object to try to convert.
335         * @param pos The result.
336         */
337        public static void positionFromObject(Object o, Point3 pos) {
338                if(o instanceof Object[]) {
339                        Object oo[] = (Object[]) o;
340
341                        if (oo.length > 0 && oo[0] instanceof Number) {
342                                pos.x = ((Number) oo[0]).doubleValue();
343                                if (oo.length > 1) pos.y = ((Number) oo[1]).doubleValue();
344                                if (oo.length > 2) pos.z = ((Number) oo[2]).doubleValue();
345                        }
346                } else if(o instanceof Double[]) {
347                        Double oo[] = (Double[]) o;
348                        if(oo.length > 0) pos.x = oo[0];
349                        if(oo.length > 1) pos.y = oo[1];
350                        if(oo.length > 2) pos.z = oo[2];
351                } else if(o instanceof Float[]) {
352                        Float oo[] = (Float[]) o;
353                        if(oo.length > 0) pos.x = oo[0];
354                        if(oo.length > 1) pos.y = oo[1];
355                        if(oo.length > 2) pos.z = oo[2];
356                } else if(o instanceof Integer[]) {
357                        Integer oo[] = (Integer[]) o;
358                        if(oo.length > 0) pos.x = oo[0];
359                        if(oo.length > 1) pos.y = oo[1];
360                        if(oo.length > 2) pos.z = oo[2];
361                } else if(o instanceof double[]) {
362                        double oo[] = (double[]) o;
363                        if(oo.length > 0) pos.x = oo[0];
364                        if(oo.length > 1) pos.y = oo[1];
365                        if(oo.length > 2) pos.z = oo[2];
366                } else if(o instanceof float[]) {
367                        float oo[] = (float[]) o;
368                        if(oo.length > 0) pos.x = oo[0];
369                        if(oo.length > 1) pos.y = oo[1];
370                        if(oo.length > 2) pos.z = oo[2];
371                } else if(o instanceof int[]) {
372                        int oo[] = (int[]) o;
373                        if(oo.length > 0) pos.x = oo[0];
374                        if(oo.length > 1) pos.y = oo[1];
375                        if(oo.length > 2) pos.z = oo[2];
376                } else if(o instanceof Number[]) {
377                        Number oo[] = (Number[]) o;
378                        if(oo.length > 0) pos.x = oo[0].doubleValue();
379                        if(oo.length > 1) pos.y = oo[1].doubleValue();
380                        if(oo.length > 2) pos.z = oo[2].doubleValue();
381                } else if(o instanceof Point3) {
382                        Point3 oo = (Point3) o;
383                        pos.x = oo.x;
384                        pos.y = oo.y;
385                        pos.z = oo.z;
386                } else if(o instanceof Vector3) {
387                        Vector3 oo = (Vector3) o;
388                        pos.x = oo.data[0];
389                        pos.y = oo.data[1];
390                        pos.z = oo.data[2];
391                } else if(o instanceof Point2) {
392                        Point2 oo = (Point2) o;
393                        pos.x = oo.x;
394                        pos.y = oo.y;
395                        pos.z = 0;
396                } else if(o instanceof Vector2) {
397                        Vector2 oo = (Vector2) o;
398                        pos.x = oo.data[0];
399                        pos.y = oo.data[1];
400                        pos.z = 0;
401                } else {
402                        System.err.printf("Do not know how to handle xyz attribute %s%n", o.getClass().getName());
403                }
404        }
405
406        /**
407         * Compute the edge length of the given edge according to its two nodes
408         * positions.
409         * 
410         * @param id
411         *            The identifier of the edge.
412         * @return The edge length or -1 if the nodes of the edge have no positions.
413         * @throws RuntimeException
414         *             If the edge cannot be found.
415         */
416        public static double edgeLength(Graph graph, String id) {
417                Edge edge = graph.getEdge(id);
418
419                if (edge != null)
420                        return edgeLength(edge);
421
422                throw new RuntimeException("edge '" + id + "' cannot be found");
423        }
424
425        /**
426         * Like {@link #edgeLength(Graph,String)} but use an existing edge as
427         * argument.
428         * 
429         * @param edge
430         * @return The edge length or -1 if the nodes of the edge have no positions.
431         */
432        public static double edgeLength(Edge edge) {
433                double xyz0[] = nodePosition(edge.getNode0());
434                double xyz1[] = nodePosition(edge.getNode1());
435
436                if (xyz0 == null || xyz1 == null)
437                        return -1;
438
439                xyz0[0] = xyz1[0] - xyz0[0];
440                xyz0[1] = xyz1[1] - xyz0[1];
441                xyz0[2] = xyz1[2] - xyz0[2];
442
443                return Math.sqrt(xyz0[0] * xyz0[0] + xyz0[1] * xyz0[1]
444                                + xyz0[2] * xyz0[2]);
445        }
446}