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.Iterator;
035
036import org.graphstream.graph.Node;
037import org.graphstream.stream.SourceBase.ElementType;
038import org.graphstream.ui.graphicGraph.stylesheet.Selector;
039import org.graphstream.ui.graphicGraph.stylesheet.Style;
040import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
041import org.graphstream.ui.graphicGraph.stylesheet.Values;
042
043/**
044 * A small gentle sprite.
045 */
046public class GraphicSprite extends GraphicElement {
047        // Attributes
048
049        /**
050         * The node this sprite is attached to.
051         */
052        protected GraphicNode node;
053
054        /**
055         * The edge this sprite is attached to.
056         */
057        protected GraphicEdge edge;
058
059        /**
060         * Sprite position.
061         */
062        public Values position = new Values(StyleConstants.Units.GU, 0, 0, 0);
063
064        // Constructors
065
066        /**
067         * New sprite.
068         * 
069         * @param id
070         *            The sprite unique identifier.
071         * @param graph
072         *            The graph containing this sprite.
073         */
074        public GraphicSprite(String id, GraphicGraph graph) {
075                super(id, graph);
076
077                // Get the position of a random node.
078
079                if (graph.getNodeCount() > 0) {
080                        Iterator<? extends Node> nodes = graph.getNodeIterator();
081
082                        GraphicNode node = (GraphicNode) nodes.next();
083
084                        position.setValue(0, node.x);
085                        position.setValue(1, node.y);
086                        position.setValue(2, node.z);
087                }
088
089                String myPrefix = String.format("ui.sprite.%s", id);
090
091                if (mygraph.getAttribute(myPrefix) == null)
092                        mygraph.addAttribute(myPrefix, position);
093        }
094
095        // Access
096
097        /**
098         * The node this sprite is attached to or null if not attached to an edge.
099         * 
100         * @return A graphic node.
101         */
102        public GraphicNode getNodeAttachment() {
103                return node;
104        }
105
106        /**
107         * The edge this sprite is attached to or null if not attached to an edge.
108         * 
109         * @return A graphic edge.
110         */
111        public GraphicEdge getEdgeAttachment() {
112                return edge;
113        }
114
115        /**
116         * Return the graphic object this sprite is attached to or null if not
117         * attached.
118         * 
119         * @return A graphic object or null if no attachment.
120         */
121        public GraphicElement getAttachment() {
122                GraphicNode n = getNodeAttachment();
123
124                if (n != null)
125                        return n;
126
127                return getEdgeAttachment();
128        }
129
130        /**
131         * True if the sprite is attached to a node or edge.
132         */
133        public boolean isAttached() {
134                return (edge != null || node != null);
135        }
136
137        /**
138         * True if the sprite is attached to a node.
139         */
140        public boolean isAttachedToNode() {
141                return node != null;
142        }
143
144        /**
145         * True if the node is attached to an edge.
146         */
147        public boolean isAttachedToEdge() {
148                return edge != null;
149        }
150
151        @Override
152        public Selector.Type getSelectorType() {
153                return Selector.Type.SPRITE;
154        }
155
156        @Override
157        public double getX() {
158                return position.get(0);
159        }
160
161        @Override
162        public double getY() {
163                return position.get(1);
164        }
165
166        @Override
167        public double getZ() {
168                return position.get(2);
169        }
170
171        public Style.Units getUnits() {
172                return position.getUnits();
173        }
174
175        // Commands
176
177        @Override
178        public void move(double x, double y, double z) {
179                setPosition(x, y, z, Style.Units.GU);
180        }
181
182        /**
183         * Attach this sprite to the given node.
184         * 
185         * @param node
186         *            A graphic node.
187         */
188        public void attachToNode(GraphicNode node) {
189                this.edge = null;
190                this.node = node;
191
192                String prefix = String.format("ui.sprite.%s", getId());
193
194                if (this.node.getAttribute(prefix) == null)
195                        this.node.addAttribute(prefix);
196
197                mygraph.graphChanged = true;
198        }
199
200        /**
201         * Attach this sprite to the given edge.
202         * 
203         * @param edge
204         *            A graphic edge.
205         */
206        public void attachToEdge(GraphicEdge edge) {
207                this.node = null;
208                this.edge = edge;
209
210                String prefix = String.format("ui.sprite.%s", getId());
211
212                if (this.edge.getAttribute(prefix) == null)
213                        this.edge.addAttribute(prefix);
214
215                mygraph.graphChanged = true;
216        }
217
218        /**
219         * Detach this sprite from the edge or node it was attached to.
220         */
221        public void detach() {
222                String prefix = String.format("ui.sprite.%s", getId());
223
224                if (this.node != null)
225                        this.node.removeAttribute(prefix);
226                else if (this.edge != null)
227                        this.edge.removeAttribute(prefix);
228
229                this.edge = null;
230                this.node = null;
231                mygraph.graphChanged = true;
232        }
233
234        /**
235         * Reposition this sprite.
236         * 
237         * @param value
238         *            The coordinate.
239         */
240        public void setPosition(double value) {
241                setPosition(value, 0, 0, getUnits());
242        }
243
244        /**
245         * Reposition this sprite.
246         * 
247         * @param x
248         *            First coordinate.
249         * @param y
250         *            Second coordinate.
251         * @param z
252         *            Third coordinate.
253         * @param units
254         *            The units to use for lengths and radii, null means
255         *            "unchanged".
256         */
257        public void setPosition(double x, double y, double z, Style.Units units) {
258                /*
259                 * if( node != null ) { y = checkAngle( y ); z = checkAngle( z ); } else
260                 */if (edge != null) {
261                        if (x < 0)
262                                x = 0;
263                        else if (x > 1)
264                                x = 1;
265                }
266
267                boolean changed = false;
268
269                if (getX() != x) {
270                        changed = true;
271                        position.setValue(0, x);
272                }
273                if (getY() != y) {
274                        changed = true;
275                        position.setValue(1, y);
276                }
277                if (getZ() != z) {
278                        changed = true;
279                        position.setValue(2, z);
280                }
281                if (getUnits() != units) {
282                        changed = true;
283                        position.setUnits(units);
284                }
285
286                if (changed) {
287                        mygraph.graphChanged = true;
288                        mygraph.boundsChanged = true;
289
290                        String prefix = String.format("ui.sprite.%s", getId());
291
292                        mygraph.setAttribute(prefix, position);
293                }
294        }
295
296        public void setPosition(Values values) {
297                double x = 0;
298                double y = 0;
299                double z = 0;
300
301                if (values.getValueCount() > 0)
302                        x = values.get(0);
303                if (values.getValueCount() > 1)
304                        y = values.get(1);
305                if (values.getValueCount() > 2)
306                        z = values.get(2);
307
308                // System.err.printf(
309                // "setting %s position x=%f y=%f z=%f units=%s (value in=%s)%n",
310                // getId(), x, y, z, values.units, values );
311                if (x == 1 && y == 1 && z == 1)
312                        throw new RuntimeException("WTF !!!");
313                setPosition(x, y, z, values.units);
314        }
315
316        protected double checkAngle(double angle) {
317                if (angle > Math.PI * 2)
318                        angle = angle % (Math.PI * 2);
319                else if (angle < 0)
320                        angle = (Math.PI * 2) - (angle % (Math.PI * 2));
321
322                return angle;
323        }
324
325        @Override
326        protected void attributeChanged(AttributeChangeEvent event,
327                        String attribute, Object oldValue, Object newValue) {
328                super.attributeChanged(event, attribute, oldValue, newValue);
329
330                // if( attribute.equals( "ui.clicked" ) ) // Filter the clicks to avoid
331                // loops XXX BAD !!! XXX
332                // return;
333
334                String completeAttr = String.format("ui.sprite.%s.%s", getId(),
335                                attribute);
336                // System.err.printf( "GSprite add attribute %s %s (old=%s) (new=%s)%n",
337                // event, attribute, oldValue, newValue );
338
339                mygraph.listeners.sendAttributeChangedEvent(mygraph.getId(),
340                                ElementType.GRAPH, completeAttr, event, oldValue, newValue);
341        }
342
343        @Override
344        protected void removed() {
345        }
346}