/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.visualization.opengl.text;
import org.gephi.visualization.impl.TextDataImpl;
import com.sun.opengl.util.j2d.TextRenderer;
import java.awt.Font;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.gephi.data.attributes.api.AttributeColumn;
import org.gephi.data.attributes.api.AttributeController;
import org.gephi.data.attributes.api.AttributeModel;
import org.gephi.data.attributes.type.TimeInterval;
import org.gephi.data.properties.PropertiesColumn;
import org.gephi.dynamic.api.DynamicController;
import org.gephi.dynamic.api.DynamicModel;
import org.gephi.dynamic.api.DynamicModelEvent;
import org.gephi.dynamic.api.DynamicModelListener;
import org.gephi.graph.api.EdgeData;
import org.gephi.graph.api.NodeData;
import org.gephi.graph.api.Renderable;
import org.gephi.visualization.VizArchitecture;
import org.gephi.visualization.VizController;
import org.gephi.visualization.apiimpl.GraphDrawable;
import org.gephi.visualization.apiimpl.ModelImpl;
import org.gephi.visualization.apiimpl.VizConfig;
import org.openide.util.Lookup;
/**
*
* @author Mathieu Bastian
*/
public class TextManager implements VizArchitecture {
//Architecture
private VizConfig vizConfig;
private GraphDrawable drawable;
private DynamicController dynamicController;
//Configuration
private SizeMode[] sizeModes;
private ColorMode[] colorModes;
//Processing
private TextUtils textUtils;
private Renderer nodeRenderer;
private Renderer edgeRenderer;
private TextDataBuilderImpl builder;
//Variables
private TextModel model;
private boolean nodeRefresh = true;
private boolean edgeRefresh = true;
private TimeInterval currentTimeInterval;
//Preferences
private boolean renderer3d;
private boolean mipmap;
private boolean fractionalMetrics;
private boolean antialised;
public TextManager() {
textUtils = new TextUtils(this);
builder = new TextDataBuilderImpl();
//SizeMode init
sizeModes = new SizeMode[3];
sizeModes[0] = new FixedSizeMode();
sizeModes[1] = new ScaledSizeMode();
sizeModes[2] = new ProportionalSizeMode();
//ColorMode init
colorModes = new ColorMode[2];
colorModes[0] = new UniqueColorMode();
colorModes[1] = new ObjectColorMode();
}
public void initArchitecture() {
model = VizController.getInstance().getVizModel().getTextModel();
vizConfig = VizController.getInstance().getVizConfig();
drawable = VizController.getInstance().getDrawable();
initRenderer();
//Init sizemodes
for (SizeMode s : sizeModes) {
s.init();
}
//Model listening
model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (!nodeRenderer.getFont().equals(model.getNodeFont())) {
nodeRenderer.setFont(model.getNodeFont());
}
if (!edgeRenderer.getFont().equals(model.getEdgeFont())) {
edgeRenderer.setFont(model.getEdgeFont());
}
nodeRefresh = true;
edgeRefresh = true;
}
});
//Model change
VizController.getInstance().getVizModel().addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("init")) {
TextManager.this.model = VizController.getInstance().getVizModel().getTextModel();
//Initialize columns if needed
if (model.getNodeTextColumns() == null || model.getNodeTextColumns().length == 0) {
AttributeController attributeController = Lookup.getDefault().lookup(AttributeController.class);
if (attributeController != null && attributeController.getModel() != null) {
AttributeModel attributeModel = attributeController.getModel();
AttributeColumn[] nodeCols = new AttributeColumn[]{attributeModel.getNodeTable().getColumn(PropertiesColumn.NODE_LABEL.getIndex())};
AttributeColumn[] edgeCols = new AttributeColumn[]{attributeModel.getEdgeTable().getColumn(PropertiesColumn.EDGE_LABEL.getIndex())};
model.setTextColumns(nodeCols, edgeCols);
}
}
DynamicModel dynamicModel = dynamicController.getModel();
if(dynamicModel!=null) {
currentTimeInterval = dynamicModel.getVisibleInterval();
builder.setDefaultEstimator(dynamicModel.getEstimator());
builder.setNumberEstimator(dynamicModel.getNumberEstimator());
} else {
currentTimeInterval = null;
}
}
}
});
//Settings
antialised = vizConfig.isLabelAntialiased();
mipmap = vizConfig.isLabelMipMap();
fractionalMetrics = vizConfig.isLabelFractionalMetrics();
renderer3d = false;
//Dynamic change
dynamicController = Lookup.getDefault().lookup(DynamicController.class);
dynamicController.addModelListener(new DynamicModelListener() {
public void dynamicModelChanged(DynamicModelEvent event) {
if(event.getEventType().equals(DynamicModelEvent.EventType.VISIBLE_INTERVAL)) {
currentTimeInterval = (TimeInterval) event.getData();
}
}
});
}
private void initRenderer() {
if (renderer3d) {
nodeRenderer = new Renderer3D();
edgeRenderer = new Renderer3D();
} else {
nodeRenderer = new Renderer2D();
edgeRenderer = new Renderer2D();
}
nodeRenderer.initRenderer(model.getNodeFont());
edgeRenderer.initRenderer(model.getEdgeFont());
}
public void defaultNodeColor() {
model.colorMode.defaultNodeColor(nodeRenderer);
}
public void defaultEdgeColor() {
model.colorMode.defaultEdgeColor(edgeRenderer);
}
public boolean isSelectedOnly() {
return model.selectedOnly;
}
public TextModel getModel() {
return model;
}
public void setModel(TextModel model) {
this.model = model;
}
public SizeMode[] getSizeModes() {
return sizeModes;
}
public ColorMode[] getColorModes() {
return colorModes;
}
public Renderer getNodeRenderer() {
return nodeRenderer;
}
public Renderer getEdgeRenderer() {
return edgeRenderer;
}
public void setRenderer3d(boolean renderer3d) {
this.renderer3d = renderer3d;
initRenderer();
}
//-------------------------------------------------------------------------------------------------
public static interface Renderer {
public void initRenderer(Font font);
public void reinitRenderer();
public void disposeRenderer();
public void beginRendering();
public void endRendering();
public void drawTextNode(ModelImpl model);
public void drawTextEdge(ModelImpl model);
public Font getFont();
public void setFont(Font font);
public void setColor(float r, float g, float b, float a);
public TextRenderer getJOGLRenderer();
}
private class Renderer3D implements Renderer {
private TextRenderer renderer;
public void initRenderer(Font font) {
renderer = new TextRenderer(font, antialised, fractionalMetrics, null, mipmap);
}
public void reinitRenderer() {
renderer = new TextRenderer(renderer.getFont(), antialised, fractionalMetrics, null, mipmap);
}
public void disposeRenderer() {
renderer.flush();
renderer.dispose();
}
public Font getFont() {
return renderer.getFont();
}
public void setFont(Font font) {
initRenderer(font);
}
public void beginRendering() {
renderer.begin3DRendering();
}
public void endRendering() {
renderer.end3DRendering();
}
public void drawTextNode(ModelImpl objectModel) {
Renderable renderable = objectModel.getObj();
TextDataImpl textData = (TextDataImpl) renderable.getTextData();
if (textData != null) {
model.colorMode.textColor(this, textData, objectModel);
model.sizeMode.setSizeFactor3d(model.nodeSizeFactor, textData, objectModel);
if (nodeRefresh) {
builder.buildNodeText((NodeData) renderable, textData, model, currentTimeInterval);
}
String txt = textData.getLine().getText();
Rectangle2D r = renderer.getBounds(txt);
textData.getLine().setBounds(r);
float posX = renderable.x() + (float) r.getWidth() / -2 * textData.getSizeFactor();
float posY = renderable.y() + (float) r.getHeight() / -2 * textData.getSizeFactor();
float posZ = renderable.getRadius();
renderer.draw3D(txt, posX, posY, posZ, textData.getSizeFactor());
}
}
public void drawTextEdge(ModelImpl objectModel) {
Renderable renderable = objectModel.getObj();
TextDataImpl textData = (TextDataImpl) renderable.getTextData();
if (textData != null) {
model.colorMode.textColor(this, textData, objectModel);
model.sizeMode.setSizeFactor3d(model.edgeSizeFactor, textData, objectModel);
if (edgeRefresh) {
builder.buildEdgeText((EdgeData) renderable, textData, model, currentTimeInterval);
}
String txt = textData.getLine().getText();
Rectangle2D r = renderer.getBounds(txt);
textData.getLine().setBounds(r);
float posX = renderable.x() + (float) r.getWidth() / -2 * textData.getSizeFactor();
float posY = renderable.y() + (float) r.getHeight() / -2 * textData.getSizeFactor();
float posZ = renderable.getRadius();
renderer.draw3D(txt, posX, posY, posZ, textData.getSizeFactor());
}
}
public void setColor(float r, float g, float b, float a) {
renderer.setColor(r, g, b, a);
}
public TextRenderer getJOGLRenderer() {
return renderer;
}
}
private class Renderer2D implements Renderer {
private TextRenderer renderer;
private static final float PIXEL_LIMIT = 3.5f;
public void initRenderer(Font font) {
renderer = new TextRenderer(font, antialised, fractionalMetrics, null, mipmap);
}
public void reinitRenderer() {
renderer = new TextRenderer(renderer.getFont(), antialised, fractionalMetrics, null, mipmap);
}
public void disposeRenderer() {
renderer.flush();
renderer.dispose();
}
public Font getFont() {
return renderer.getFont();
}
public void setFont(Font font) {
initRenderer(font);
}
public void beginRendering() {
renderer.beginRendering(drawable.getViewportWidth(), drawable.getViewportHeight());
}
public void endRendering() {
renderer.endRendering();
}
public void drawTextNode(ModelImpl objectModel) {
Renderable renderable = objectModel.getObj();
TextDataImpl textData = (TextDataImpl) renderable.getTextData();
if (textData != null) {
model.colorMode.textColor(this, textData, objectModel);
model.sizeMode.setSizeFactor2d(model.nodeSizeFactor, textData, objectModel);
if (nodeRefresh) {
builder.buildNodeText((NodeData) renderable, textData, model, currentTimeInterval);
}
if (textData.getSizeFactor() * renderer.getCharWidth('a') < PIXEL_LIMIT) {
return;
}
String txt = textData.getLine().getText();
Rectangle2D r = renderer.getBounds(txt);
float posX = renderable.getModel().getViewportX() + (float) r.getWidth() / -2 * textData.getSizeFactor();
float posY = renderable.getModel().getViewportY() + (float) r.getHeight() / -2 * textData.getSizeFactor();
r.setRect(0, 0, r.getWidth() / Math.abs(drawable.getDraggingMarkerX()), r.getHeight() / Math.abs(drawable.getDraggingMarkerY()));
textData.getLine().setBounds(r);
renderer.draw3D(txt, posX, posY, 0, textData.getSizeFactor());
}
}
public void drawTextEdge(ModelImpl objectModel) {
Renderable renderable = objectModel.getObj();
TextDataImpl textData = (TextDataImpl) renderable.getTextData();
if (textData != null) {
model.colorMode.textColor(this, textData, objectModel);
model.sizeMode.setSizeFactor2d(model.edgeSizeFactor, textData, objectModel);
if (edgeRefresh) {
builder.buildEdgeText((EdgeData) renderable, textData, model, currentTimeInterval);
}
if (textData.getSizeFactor() * renderer.getCharWidth('a') < PIXEL_LIMIT) {
return;
}
String txt = textData.getLine().getText();
Rectangle2D r = renderer.getBounds(txt);
float posX = renderable.getModel().getViewportX() + (float) r.getWidth() / -2 * textData.getSizeFactor();
float posY = renderable.getModel().getViewportY() + (float) r.getHeight() / -2 * textData.getSizeFactor();
r.setRect(0, 0, r.getWidth() / drawable.getDraggingMarkerX(), r.getHeight() / drawable.getDraggingMarkerY());
textData.getLine().setBounds(r);
renderer.draw3D(txt, posX, posY, 0, textData.getSizeFactor());
}
}
public void setColor(float r, float g, float b, float a) {
renderer.setColor(r, g, b, a);
}
public TextRenderer getJOGLRenderer() {
return renderer;
}
}
}