2
Copyright 2008-2011 Gephi
3
Authors : Yudi Xue <yudi.xue@usask.ca>, Mathieu Bastian
4
Website : http://www.gephi.org
6
This file is part of Gephi.
8
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
10
Copyright 2011 Gephi Consortium. All rights reserved.
12
The contents of this file are subject to the terms of either the GNU
13
General Public License Version 3 only ("GPL") or the Common
14
Development and Distribution License("CDDL") (collectively, the
15
"License"). You may not use this file except in compliance with the
16
License. You can obtain a copy of the License at
17
http://gephi.org/about/legal/license-notice/
18
or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
19
specific language governing permissions and limitations under the
20
License. When distributing the software, include this License Header
21
Notice in each file and include the License files at
22
/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
23
License Header, with the fields enclosed by brackets [] replaced by
24
your own identifying information:
25
"Portions Copyrighted [year] [name of copyright owner]"
27
If you wish your version of this file to be governed by only the CDDL
28
or only the GPL Version 3, indicate your decision by adding
29
"[Contributor] elects to include this software in this distribution
30
under the [CDDL or GPL Version 3] license." If you do not indicate a
31
single choice of license, a recipient has the option to distribute
32
your version of this file under either the CDDL, the GPL Version 3 or
33
to extend the choice of license to its licensees as provided above.
34
However, if you add GPL Version 3 code and therefore, elected the GPL
35
Version 3 license, then the option applies only if the new code is
36
made subject to such option by the copyright holder.
40
Portions Copyrighted 2011 Gephi Consortium.
42
package org.gephi.preview.plugin.renderers;
44
import com.itextpdf.text.pdf.BaseFont;
45
import com.itextpdf.text.pdf.PdfContentByte;
46
import com.itextpdf.text.pdf.PdfGState;
47
import java.awt.BasicStroke;
48
import java.awt.Color;
50
import java.awt.FontMetrics;
51
import java.awt.Graphics2D;
52
import java.awt.Shape;
53
import java.awt.font.FontRenderContext;
54
import java.awt.font.GlyphVector;
55
import org.gephi.graph.api.Edge;
56
import org.gephi.preview.api.Item;
57
import org.gephi.preview.api.PDFTarget;
58
import org.gephi.preview.api.PreviewModel;
59
import org.gephi.preview.api.PreviewProperties;
60
import org.gephi.preview.api.PreviewProperty;
61
import org.gephi.preview.api.ProcessingTarget;
62
import org.gephi.preview.api.RenderTarget;
63
import org.gephi.preview.api.SVGTarget;
65
import org.gephi.preview.plugin.items.EdgeItem;
66
import org.gephi.preview.plugin.items.EdgeLabelItem;
68
import org.gephi.preview.plugin.items.NodeItem;
69
import org.gephi.preview.spi.Renderer;
70
import org.gephi.preview.types.DependantColor;
71
import org.gephi.preview.types.DependantOriginalColor;
72
import org.gephi.preview.types.EdgeColor;
73
import org.openide.util.NbBundle;
74
import org.openide.util.lookup.ServiceProvider;
75
import org.w3c.dom.Element;
76
import org.w3c.dom.Text;
77
import processing.core.PGraphics;
78
import processing.core.PGraphicsJava2D;
79
import processing.core.PVector;
83
* @author Yudi Xue, Mathieu Bastian
85
@ServiceProvider(service = Renderer.class, position = 500)
86
public class EdgeLabelRenderer implements Renderer {
89
public static final String EDGE_COLOR = "edge.label.edgeColor";
90
public static final String LABEL_X = "edge.label.x";
91
public static final String LABEL_Y = "edge.label.y";
93
protected final boolean defaultShowLabels = true;
94
protected final Font defaultFont = new Font("Arial", Font.PLAIN, 10);
95
protected final boolean defaultShorten = false;
96
protected final DependantOriginalColor defaultColor = new DependantOriginalColor(DependantOriginalColor.Mode.ORIGINAL);
97
protected final int defaultMaxChar = 30;
98
protected final float defaultOutlineSize = 2;
99
protected final DependantColor defaultOutlineColor = new DependantColor(Color.WHITE);
100
protected final float defaultOutlineOpacity = 40;
104
public void preProcess(PreviewModel previewModel) {
105
PreviewProperties properties = previewModel.getProperties();
106
if (properties.getBooleanValue(PreviewProperty.EDGE_LABEL_SHORTEN)) {
108
Item[] EdgeLabelsItems = previewModel.getItems(Item.EDGE_LABEL);
110
int maxChars = properties.getIntValue(PreviewProperty.EDGE_LABEL_MAX_CHAR);
111
for (Item item : EdgeLabelsItems) {
112
String label = item.getData(EdgeLabelItem.LABEL);
113
if (label.length() >= maxChars + 3) {
114
label = label.substring(0, maxChars) + "...";
115
item.setData(EdgeLabelItem.LABEL, label);
120
//Put parent color, and calculate position
121
for (Item item : previewModel.getItems(Item.EDGE_LABEL)) {
122
Edge edge = (Edge) item.getSource();
123
Item edgeItem = previewModel.getItem(Item.EDGE, edge);
125
EdgeColor edgeColor = (EdgeColor) properties.getValue(PreviewProperty.EDGE_COLOR);
126
NodeItem sourceItem = (NodeItem) edgeItem.getData(EdgeRenderer.SOURCE);
127
NodeItem targetItem = (NodeItem) edgeItem.getData(EdgeRenderer.TARGET);
128
Color color = edgeColor.getColor((Color) item.getData(EdgeItem.COLOR),
129
(Color) sourceItem.getData(NodeItem.COLOR),
130
(Color) targetItem.getData(NodeItem.COLOR));
131
item.setData(EDGE_COLOR, color);
132
if (edge.isSelfLoop()) {
134
Float x = sourceItem.getData(NodeItem.X);
135
Float y = sourceItem.getData(NodeItem.Y);
136
Float size = sourceItem.getData(NodeItem.SIZE);
138
PVector v1 = new PVector(x, y);
139
v1.add(size, -size, 0);
141
PVector v2 = new PVector(x, y);
142
v2.add(size, size, 0);
144
PVector middle = bezierPoint(x, y, v1.x, v1.y, v2.x, v2.y, x, y, 0.5f);
145
item.setData(LABEL_X, middle.x);
146
item.setData(LABEL_Y, middle.y);
148
} else if (properties.getBooleanValue(PreviewProperty.EDGE_CURVED)) {
149
//Middle of the curve
150
Float x1 = sourceItem.getData(NodeItem.X);
151
Float x2 = targetItem.getData(NodeItem.X);
152
Float y1 = sourceItem.getData(NodeItem.Y);
153
Float y2 = targetItem.getData(NodeItem.Y);
156
PVector direction = new PVector(x2, y2);
157
direction.sub(new PVector(x1, y1));
158
float length = direction.mag();
159
direction.normalize();
161
float factor = properties.getFloatValue(EdgeRenderer.BEZIER_CURVENESS) * length;
163
// normal vector to the edge
164
PVector n = new PVector(direction.y, -direction.x);
167
// first control point
168
PVector v1 = new PVector(direction.x, direction.y);
170
v1.add(new PVector(x1, y1));
173
// second control point
174
PVector v2 = new PVector(direction.x, direction.y);
176
v2.add(new PVector(x2, y2));
179
PVector middle = bezierPoint(x1, y1, v1.x, v1.y, v2.x, v2.y, x2, y2, 0.5f);
180
item.setData(LABEL_X, middle.x);
181
item.setData(LABEL_Y, middle.y);
183
Float x = ((Float) sourceItem.getData(NodeItem.X) + (Float) targetItem.getData(NodeItem.X)) / 2f;
184
Float y = ((Float) sourceItem.getData(NodeItem.Y) + (Float) targetItem.getData(NodeItem.Y)) / 2f;
185
item.setData(LABEL_X, x);
186
item.setData(LABEL_Y, y);
191
font = properties.getFontValue(PreviewProperty.EDGE_LABEL_FONT);
194
public void render(Item item, RenderTarget target, PreviewProperties properties) {
195
Edge edge = (Edge) item.getSource();
197
Color edgeColor = item.getData(EDGE_COLOR);
198
Color color = item.getData(EdgeLabelItem.COLOR);
199
DependantOriginalColor propColor = properties.getValue(PreviewProperty.EDGE_LABEL_COLOR);
200
color = propColor.getColor(edgeColor, color);
201
String label = item.getData(EdgeLabelItem.LABEL);
202
Float x = item.getData(LABEL_X);
203
Float y = item.getData(LABEL_Y);
206
DependantColor outlineDependantColor = properties.getValue(PreviewProperty.EDGE_LABEL_OUTLINE_COLOR);
207
Float outlineSize = properties.getFloatValue(PreviewProperty.EDGE_LABEL_OUTLINE_SIZE);
208
outlineSize = outlineSize * (font.getSize() / 32f);
209
int outlineAlpha = (int) ((properties.getFloatValue(PreviewProperty.EDGE_LABEL_OUTLINE_OPACITY) / 100f) * 255f);
210
if (outlineAlpha > 255) {
213
Color outlineColor = outlineDependantColor.getColor(edgeColor);
214
outlineColor = new Color(outlineColor.getRed(), outlineColor.getGreen(), outlineColor.getBlue(), outlineAlpha);
216
if (target instanceof ProcessingTarget) {
217
renderProcessing((ProcessingTarget) target, label, x, y, color, outlineSize, outlineColor);
218
} else if (target instanceof SVGTarget) {
219
renderSVG((SVGTarget) target, edge, label, x, y, color, outlineSize, outlineColor);
220
} else if (target instanceof PDFTarget) {
221
renderPDF(((PDFTarget) target), label, x, y, color, outlineSize, outlineColor);
225
public void renderProcessing(ProcessingTarget target, String label, float x, float y, Color color, float outlineSize, Color outlineColor) {
226
PGraphics graphics = target.getGraphics();
227
Graphics2D g2 = ((PGraphicsJava2D) graphics).g2;
228
graphics.textAlign(PGraphics.CENTER, PGraphics.CENTER);
232
FontMetrics fm = g2.getFontMetrics();
233
float posX = x - fm.stringWidth(label) / 2f;
234
float posY = y + fm.getAscent() / 2f;
236
if (outlineSize > 0) {
237
FontRenderContext frc = g2.getFontRenderContext();
238
GlyphVector gv = font.createGlyphVector(frc, label);
239
Shape glyph = gv.getOutline(posX, posY);
240
g2.setColor(outlineColor);
241
g2.setStroke(new BasicStroke(outlineSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
246
g2.drawString(label, posX, posY);
249
public void renderSVG(SVGTarget target, Edge edge, String label, float x, float y, Color color, float outlineSize, Color outlineColor) {
250
Text labelText = target.createTextNode(label);
252
if (outlineSize > 0) {
253
Text labelTextOutline = target.createTextNode(label);
254
Element outlineElem = target.createElement("text");
255
outlineElem.setAttribute("class", edge.getEdgeData().getId());
256
outlineElem.setAttribute("x", String.valueOf(x));
257
outlineElem.setAttribute("y", String.valueOf(y));
258
outlineElem.setAttribute("style", "text-anchor: middle; dominant-baseline: central;");
259
outlineElem.setAttribute("fill", target.toHexString(color));
260
outlineElem.setAttribute("font-family", font.getFamily());
261
outlineElem.setAttribute("font-size", font.getSize() + "");
262
outlineElem.setAttribute("stroke", target.toHexString(outlineColor));
263
outlineElem.setAttribute("stroke-width", (outlineSize * target.getScaleRatio()) + "px");
264
outlineElem.setAttribute("stroke-linecap", "round");
265
outlineElem.setAttribute("stroke-linejoin", "round");
266
outlineElem.setAttribute("stroke-opacity", String.valueOf(outlineColor.getAlpha() / 255f));
267
outlineElem.appendChild(labelTextOutline);
268
target.getTopElement(SVGTarget.TOP_NODE_LABELS_OUTLINE).appendChild(outlineElem);
271
Element labelElem = target.createElement("text");
272
labelElem.setAttribute("class", edge.getEdgeData().getId());
273
labelElem.setAttribute("x", x + "");
274
labelElem.setAttribute("y", y + "");
275
labelElem.setAttribute("style", "text-anchor: middle; dominant-baseline: central;");
276
labelElem.setAttribute("fill", target.toHexString(color));
277
labelElem.setAttribute("font-family", font.getFamily());
278
labelElem.setAttribute("font-size", font.getSize() + "");
279
labelElem.appendChild(labelText);
280
target.getTopElement(SVGTarget.TOP_EDGE_LABELS).appendChild(labelElem);
283
public void renderPDF(PDFTarget target, String label, float x, float y, Color color, float outlineSize, Color outlineColor) {
284
PdfContentByte cb = target.getContentByte();
285
cb.setRGBColorFill(color.getRed(), color.getGreen(), color.getBlue());
286
BaseFont bf = target.getBaseFont(font);
287
float textHeight = getTextHeight(bf, font.getSize(), label);
288
if (outlineSize > 0) {
289
cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
290
cb.setRGBColorStroke(outlineColor.getRed(), outlineColor.getGreen(), outlineColor.getBlue());
291
cb.setLineWidth(outlineSize);
292
cb.setLineJoin(PdfContentByte.LINE_JOIN_ROUND);
293
cb.setLineCap(PdfContentByte.LINE_CAP_ROUND);
294
if (outlineColor.getAlpha() < 255) {
296
float alpha = outlineColor.getAlpha() / 255f;
297
PdfGState gState = new PdfGState();
298
gState.setStrokeOpacity(alpha);
299
cb.setGState(gState);
302
cb.setFontAndSize(bf, font.getSize());
303
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, label, x, -y - (textHeight / 2f), 0f);
305
if (outlineColor.getAlpha() < 255) {
309
cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
311
cb.setFontAndSize(bf, font.getSize());
312
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, label, x, -y - (textHeight / 2f), 0f);
316
private float getTextHeight(BaseFont baseFont, float fontSize, String text) {
317
float ascend = baseFont.getAscentPoint(text, fontSize);
318
float descend = baseFont.getDescentPoint(text, fontSize);
319
return ascend + descend;
322
public PreviewProperty[] getProperties() {
323
return new PreviewProperty[]{
324
PreviewProperty.createProperty(this, PreviewProperty.SHOW_EDGE_LABELS, Boolean.class,
325
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.display.displayName"),
326
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.display.description"),
327
PreviewProperty.CATEGORY_EDGE_LABELS).setValue(defaultShowLabels),
328
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_FONT, Font.class,
329
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.font.displayName"),
330
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.font.description"),
331
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultFont),
332
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_COLOR, DependantOriginalColor.class,
333
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.color.displayName"),
334
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.color.description"),
335
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultColor),
336
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_SHORTEN, Boolean.class,
337
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.shorten.displayName"),
338
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.shorten.description"),
339
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultShorten),
340
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_MAX_CHAR, Integer.class,
341
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.maxchar.displayName"),
342
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.maxchar.description"),
343
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultMaxChar),
344
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_OUTLINE_SIZE, Float.class,
345
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineSize.displayName"),
346
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineSize.description"),
347
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultOutlineSize),
348
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_OUTLINE_COLOR, DependantColor.class,
349
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineColor.displayName"),
350
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineColor.description"),
351
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultOutlineColor),
352
PreviewProperty.createProperty(this, PreviewProperty.EDGE_LABEL_OUTLINE_OPACITY, Float.class,
353
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineOpacity.displayName"),
354
NbBundle.getMessage(EdgeLabelRenderer.class, "EdgeLabelRenderer.property.outlineOpacity.description"),
355
PreviewProperty.CATEGORY_EDGE_LABELS, PreviewProperty.SHOW_EDGE_LABELS).setValue(defaultOutlineOpacity),};
358
public boolean isRendererForitem(Item item, PreviewProperties properties) {
359
return item instanceof EdgeLabelItem && properties.getBooleanValue(PreviewProperty.SHOW_EDGE_LABELS)
360
&& !properties.getBooleanValue(PreviewProperty.MOVING);
363
protected PVector bezierPoint(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float c) {
364
PVector ab = linearInterpolation(x1, y1, x2, y2, c);
365
PVector bc = linearInterpolation(x2, y2, x3, y3, c);
366
PVector cd = linearInterpolation(x3, y3, x4, y4, c);
367
PVector abbc = linearInterpolation(ab.x, ab.y, bc.x, bc.y, c);
368
PVector bccd = linearInterpolation(bc.x, bc.y, cd.x, cd.y, c);
369
return linearInterpolation(abbc.x, abbc.y, bccd.x, bccd.y, c);
372
protected PVector linearInterpolation(float x1, float y1, float x2, float y2, float c) {
373
PVector r = new PVector(x1 + (x2 - x1) * c, y1 + (y2 - y1) * c);