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.shapes; 033 034import java.awt.Color; 035import java.awt.Graphics2D; 036import java.awt.geom.Path2D; 037 038import org.graphstream.ui.graphicGraph.GraphicEdge; 039import org.graphstream.ui.graphicGraph.GraphicNode; 040import org.graphstream.ui.graphicGraph.StyleGroup; 041import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.StrokeMode; 042import org.graphstream.ui.swingViewer.util.GraphMetrics; 043 044public class Arrow extends Shape { 045 protected Color fillColor = Color.BLACK; 046 047 protected Color strokeColor = Color.BLACK; 048 049 protected int lengthGu = 0; 050 051 protected int widthGu = 0; 052 053 protected double x, y; 054 055 protected Path2D.Float path = new Path2D.Float(); 056 057 public void setArrowLengthGu(int lengthGu) { 058 this.lengthGu = lengthGu; 059 } 060 061 public void setArrowWidthGu(int widthGu) { 062 this.widthGu = widthGu; 063 } 064 065 public void setFillColor(Color color) { 066 fillColor = color; 067 } 068 069 public void setStrokeColor(Color color) { 070 strokeColor = color; 071 } 072 073 @Override 074 public void renderFill(Graphics2D g, GraphMetrics metrics) { 075 g.setColor(fillColor); 076 g.fill(path); 077 } 078 079 @Override 080 public void renderStroke(Graphics2D g, GraphMetrics metrics) { 081 g.setColor(strokeColor); 082 g.draw(path); 083 } 084 085 // Utility 086 087 protected void setPositionAndShape(GraphicEdge edge, GraphMetrics metrics) { 088 // Compute the direction vector and some lengths. 089 090 x = edge.to.x; 091 y = edge.to.y; 092 double vx = x - edge.from.x; 093 double vy = y - edge.from.y; 094 double off = evalTargetRadius(edge, metrics); 095 096 // Normalise the vectors. 097 098 double d = (double) Math.sqrt(vx * vx + vy * vy); 099 100 vx /= d; 101 vy /= d; 102 103 // Choose an arrow "length". 104 105 x -= vx * off; 106 y -= vy * off; 107 108 setShapeAt(x, y, vx, vy); 109 } 110 111 /** 112 * Compute the shape of the arrow. 113 * 114 * @param x 115 * Point at which the edge crosses the node shape. 116 * @param y 117 * Point at which the edge crosses the node shape. 118 * @param dx 119 * The arrow vector (and length). 120 * @param dy 121 * The arrow vector (and length). 122 */ 123 protected void setShapeAt(double x, double y, double dx, double dy) { 124 // Compute the edge vector (1) and the perpendicular vector (2). 125 126 double dx2 = dy; 127 double dy2 = -dx; 128 129 // Normalise the vectors. 130 131 double d2 = (double) Math.sqrt(dx2 * dx2 + dy2 * dy2); 132 133 dx2 /= d2; 134 dy2 /= d2; 135 136 // Choose an arrow "width". 137 138 dx2 *= widthGu; 139 dy2 *= widthGu; 140 141 // Create a polygon. 142 143 path.reset(); 144 path.moveTo(x, y); 145 path.lineTo(x - dx + dx2, y - dy + dy2); 146 path.lineTo(x - dx - dx2, y - dy - dy2); 147 path.closePath(); 148 } 149 150 /** 151 * Evaluate the position of the arrow to avoid putting it above or under the 152 * target node. 153 * 154 * @param edge 155 * The edge. 156 * @param metrics 157 * The metrics. 158 * @return The length from the node centre along the edge to position the 159 * arrow. 160 */ 161 protected double evalTargetRadius(GraphicEdge edge, GraphMetrics metrics) { 162 GraphicNode target = edge.to; 163 StyleGroup group = target.getStyle(); 164 double w = metrics.lengthToGu(group.getSize(), 0); 165 double h = group.getSize().size() > 1 ? metrics.lengthToGu( 166 group.getSize(), 1) : w; 167 168 if (w == h) { 169 double b = group.getStrokeMode() != StrokeMode.NONE ? metrics 170 .lengthToGu(group.getStrokeWidth()) : 0; 171 return ((w / 2) + b); 172 } else { 173 return evalEllipseRadius(edge, w, h); 174 } 175 } 176 177 /** 178 * Compute the length of a vector along the edge from the ellipse centre to 179 * the intersection between the edge and the ellipse. 180 * 181 * @param edge 182 * The edge representing the vector. 183 * @param w 184 * The ellipse first radius (width/2). 185 * @param h 186 * The ellipse second radius (height/2). 187 * @return The length of the radius along the edge vector. 188 */ 189 protected double evalEllipseRadius(GraphicEdge edge, double w, double h) { 190 // Vector of the entering edge. 191 192 double dx; 193 double dy; 194 195 dx = edge.to.x - edge.from.x; 196 dy = edge.to.y - edge.from.y; 197 198 // The entering edge must be deformed by the ellipse ratio to find the 199 // correct angle. 200 201 dy *= (w / h); // I searched a lot to find this line was missing ! Tsu ! 202 // This comment is in memory of this long search. 203 204 // Find the angle of the entering vector with (1,0). 205 206 double d = (double) Math.sqrt(dx * dx + dy * dy); 207 double a = dx / d; 208 209 // Compute the coordinates at which the entering vector and the ellipse 210 // cross. 211 212 a = (double) Math.acos(a); 213 dx = (double) Math.cos(a) * w; 214 dy = (double) Math.sin(a) * h; 215 216 // The distance from the ellipse centre to the crossing point of the 217 // ellipse and 218 // vector. Yo ! 219 220 return Math.sqrt(dx * dx + dy * dy); 221 } 222}