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.swingViewer.basicRenderer;
033
034import java.awt.Color;
035import java.awt.Font;
036import java.awt.Graphics2D;
037import java.awt.geom.AffineTransform;
038import java.awt.geom.Point2D;
039
040import org.graphstream.graph.Element;
041import org.graphstream.ui.geom.Point3;
042import org.graphstream.ui.graphicGraph.GraphicElement;
043import org.graphstream.ui.graphicGraph.GraphicSprite;
044import org.graphstream.ui.graphicGraph.StyleGroup;
045import org.graphstream.ui.graphicGraph.StyleGroup.ElementEvents;
046import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
047import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Units;
048import org.graphstream.ui.swingViewer.util.Camera;
049import org.graphstream.ui.swingViewer.util.FontCache;
050import org.graphstream.ui.swingViewer.util.DefaultCamera;
051
052public abstract class ElementRenderer {
053        // Attribute
054
055        /**
056         * Allow to know if an event began or ended.
057         */
058        protected boolean hadEvents = false;
059
060        protected Font textFont;
061
062        protected Color textColor;
063
064        protected int textSize;
065
066        // Constructor
067
068        /**
069         * New swing element renderer for the given style group.
070         */
071        public ElementRenderer() {
072        }
073
074        // Command
075
076        /**
077         * Render all the (visible) elements of the group.
078         */
079        public void render(StyleGroup group, Graphics2D g, Camera camera) {
080                setupRenderingPass(group, g, camera);
081                pushStyle(group, g, camera);
082
083                for (Element e : group.bulkElements()) {
084                        GraphicElement ge = (GraphicElement) e;
085
086                        if (camera.isVisible(ge))
087                                renderElement(group, g, camera, ge);
088                        else
089                                elementInvisible(group, g, camera, ge);
090                }
091
092                if (group.hasDynamicElements()) {
093                        for (Element e : group.dynamicElements()) {
094                                GraphicElement ge = (GraphicElement) e;
095
096                                if (camera.isVisible(ge)) {
097                                        if (!group.elementHasEvents(ge)) {
098                                                pushDynStyle(group, g, camera, ge);
099                                                renderElement(group, g, camera, ge);
100                                        }
101                                } else {
102                                        elementInvisible(group, g, camera, ge);
103                                }
104                        }
105                }
106
107                if (group.hasEventElements()) {
108                        for (ElementEvents event : group.elementsEvents()) {
109                                GraphicElement ge = (GraphicElement) event.getElement();
110
111                                if (camera.isVisible(ge)) {
112                                        event.activate();
113                                        pushStyle(group, g, camera);
114                                        renderElement(group, g, camera, ge);
115                                        event.deactivate();
116                                } else {
117                                        elementInvisible(group, g, camera, ge);
118                                }
119                        }
120
121                        hadEvents = true;
122                } else {
123                        hadEvents = false;
124                }
125        }
126
127        /**
128         * Called before the whole rendering pass for all elements.
129         * 
130         * @param g
131         *            The Swing graphics.
132         * @param camera
133         *            The camera.
134         */
135        protected abstract void setupRenderingPass(StyleGroup group, Graphics2D g,
136                        Camera camera);
137
138        /**
139         * Called before the rendering of bulk and event elements.
140         * 
141         * @param g
142         *            The Swing graphics.
143         * @param camera
144         *            The camera.
145         */
146        protected abstract void pushStyle(StyleGroup group, Graphics2D g,
147                        Camera camera);
148
149        /**
150         * Called before the rendering of elements on dynamic styles. This must only
151         * change the style properties that can change dynamically.
152         * 
153         * @param g
154         *            The Swing graphics.
155         * @param camera
156         *            The camera.
157         * @param element
158         *            The graphic element concerned by the dynamic style change.
159         */
160        protected abstract void pushDynStyle(StyleGroup group, Graphics2D g,
161                        Camera camera, GraphicElement element);
162
163        /**
164         * Render a single element knowing the style is already prepared. Elements
165         * that are not visible are not drawn.
166         * 
167         * @param g
168         *            The Swing graphics.
169         * @param camera
170         *            The camera.
171         * @param element
172         *            The element to render.
173         */
174        protected abstract void renderElement(StyleGroup group, Graphics2D g,
175                        Camera camera, GraphicElement element);
176
177        /**
178         * Called during rendering in place of
179         * {@link #renderElement(StyleGroup, Graphics2D, Camera, GraphicElement)}
180         * to signal that the given element is not inside the view. The
181         * renderElement() method will be called as soon as the element becomes
182         * visible anew.
183         * 
184         * @param g
185         *            The Swing graphics.
186         * @param camera
187         *            The camera.
188         * @param element
189         *            The element to render.
190         */
191        protected abstract void elementInvisible(StyleGroup group, Graphics2D g,
192                        Camera camera, GraphicElement element);
193
194        // Utility
195
196        protected void configureText(StyleGroup group, Camera camera) {
197                String fontName = group.getTextFont();
198                StyleConstants.TextStyle textStyle = group.getTextStyle();
199
200                textSize = (int) group.getTextSize().value;
201                textColor = group.getTextColor(0);
202                textFont = FontCache.defaultFontCache().getFont(fontName, textStyle,
203                                textSize);
204        }
205
206        protected void renderText(StyleGroup group, Graphics2D g, Camera camera,
207                        GraphicElement element) {
208                String label = element.getLabel();
209                
210                if (label != null && group.getTextMode() != StyleConstants.TextMode.HIDDEN
211                                && group.getTextVisibilityMode() != StyleConstants.TextVisibilityMode.HIDDEN) {
212
213                        Point3 p = null;
214                        GraphicSprite s = null;
215                        Point2D.Double pos = null;
216
217                        if (element instanceof GraphicSprite) {
218                                s   = (GraphicSprite) element;
219                                pos = ((DefaultCamera) camera).getSpritePosition(s,
220                                        new Point2D.Double(), StyleConstants.Units.GU);
221                        }
222
223                        if (pos != null && s.getUnits() == Units.PX) {
224                                double w = camera.getMetrics().lengthToPx(group.getSize(),
225                                                0);
226                                p = camera.transformGuToPx(pos.x, pos.y, 0);
227                                p.x += w/2;
228                        } else if (s != null && s.getUnits() == Units.PERCENTS) {
229                                double w = camera.getMetrics().lengthToPx(group.getSize(),
230                                                0);
231                                p = camera.transformGuToPx(camera.getMetrics().viewport[2] * pos.x,
232                                        camera.getMetrics().viewport[3] *  pos.y, 0);
233                                p.x += (w/2);
234                        } else {
235                                double w = camera.getMetrics().lengthToGu(group.getSize(),
236                                                0);
237                                p = camera.transformGuToPx(element.getX() + (w / 2), element
238                                                .getY(), 0);
239                        }
240
241                        AffineTransform Tx = g.getTransform();
242                        Color c = g.getColor();
243
244                        g.setColor(textColor);
245                        g.setFont(textFont);
246                        g.setTransform(new AffineTransform());
247                        g.drawString(label, (float) p.x, (float) (p.y + textSize / 3)); // approximation
248                        // to gain time.
249                        g.setTransform(Tx);
250                        g.setColor(c);
251                }
252        }
253
254        protected Color interpolateColor(StyleGroup group, GraphicElement element) {
255                Color color = group.getFillColor(0);
256
257                int n = group.getFillColorCount();
258
259                if (n > 1) {
260                        if (element.hasNumber("ui.color") && n > 1) {
261                                double value = element.getNumber("ui.color");
262
263                                if (value < 0)
264                                        value = 0;
265                                else if (value > 1)
266                                        value = 1;
267
268                                if (value == 1) {
269                                        color = group.getFillColor(n - 1); // Simplification,
270                                        // faster.
271                                } else if (value != 0) // If value == 0, color is already set
272                                // above.
273                                {
274                                        double div = 1f / (n - 1);
275                                        int col = (int) (value / div);
276
277                                        div = (value - (div * col)) / div;
278                                        // div = value / div - col;
279
280                                        Color color0 = group.getFillColor(col);
281                                        Color color1 = group.getFillColor(col + 1);
282                                        double red = ((color0.getRed() * (1 - div)) + (color1
283                                                        .getRed() * div)) / 255f;
284                                        double green = ((color0.getGreen() * (1 - div)) + (color1
285                                                        .getGreen() * div)) / 255f;
286                                        double blue = ((color0.getBlue() * (1 - div)) + (color1
287                                                        .getBlue() * div)) / 255f;
288                                        double alpha = ((color0.getAlpha() * (1 - div)) + (color1
289                                                        .getAlpha() * div)) / 255f;
290
291                                        color = new Color((float) red, (float) green, (float) blue,
292                                                        (float) alpha);
293                                }
294                        } else if (element.hasAttribute("ui.color", Color.class)) {
295                                color = element.getAttribute("ui.color");
296                        }
297                } else if (element.hasAttribute("ui.color", Color.class)) {
298                        color = element.getAttribute("ui.color");
299                }
300
301                return color;
302        }
303}