Commit 1f468729 authored by Krzysztof Nowinski's avatar Krzysztof Nowinski

2D text glyph reinvented

parent ef83b1ca
......@@ -46,6 +46,8 @@ import javax.swing.event.ChangeListener;
import javax.vecmath.Color3f;
import pl.edu.icm.visnow.geometries.utils.transform.LocalToWindow;
import static org.apache.commons.math3.util.FastMath.*;
import static pl.edu.icm.visnow.geometries.parameters.FontParams.Decoration.*;
import static pl.edu.icm.visnow.geometries.parameters.FontParams.Position.*;
/**
*
......@@ -54,15 +56,107 @@ import static org.apache.commons.math3.util.FastMath.*;
public class FontParams
{
public enum Position
{
RADIAL ( 0, 0, 0, 0, 0, 0),
N ( 0, 1, 0, -1, 1, -1),
NE ( 1, 1, 1, -1, 1, -1),
E ( 1, 0, 1, 0, 1, -1),
SE ( 1, -1, 1, 1, 1, 1),
S ( 0, -1, 0, 1, 1, 1),
SW (-1, -1, -1, 1, -1, -1),
W (-1, 0, -1, 0, -1, -1),
NW (-1, 1, -1, -1, -1, -1),
AT_POINT( 0, 0, -1, -1, 0, 0) ;
private final int xShift;
private final int yShift;
private final int xFrameShift;
private final int yFrameShift;
private final int xEdgeShift;
private final int yEdgeShift;
private Position(int xShift, int yShift, int xFrameShift, int yFrameShift, int xEdgeShift, int yEdgeShift)
{
this.xShift = xShift;
this.yShift = yShift;
this.xFrameShift = xFrameShift;
this.yFrameShift = yFrameShift;
this.xEdgeShift = xEdgeShift;
this.yEdgeShift = yEdgeShift;
}
public int getxShift() {
return xShift;
}
public int getyShift() {
return yShift;
}
public int getxFrameShift() {
return xFrameShift;
}
public int getyFrameShift() {
return yFrameShift;
}
public int getxEdgeShift()
{
return xEdgeShift;
}
public int getyEdgeShift()
{
return yEdgeShift;
}
public static Position getPosition(float x, float y)
{
double phi = Math.atan2(y, x) / Math.PI;
if (phi < 0)
phi += 2;
int dir = (int)(4 * phi + .5);
if (dir >= 8)
dir = 0;
switch (dir) {
case 0:
return E;
case 1:
return NE;
case 2:
return N;
case 3:
return NW;
case 4:
return W;
case 5:
return SW;
case 6:
return S;
default:
return SE;
}
}
}
public enum Decoration {FRAME, EDGE, NONE}
private boolean threeDimensional = false;
private float size = .02f; //size of text glyps relative to the window size (2D) or field diameter (3d)
private float precision = 3; //precision of font model used for 3d fonts
private float font3DSize = 1;
private String fontName = "sans-serif";
private int fontSize = 12; //font size in pixels for 2d labels
private int fontSize = 15; //font size in pixels for 2d labels
private int fontType = Font.PLAIN;
private Color color = Color.WHITE;
private float colorCorrection = 1;
private Position position = RADIAL;
private Decoration decoration = FRAME;
private float opacity = .5f;
private float shift = 1f;
private Color bgColor = Color.BLACK;
public FontParams() {
}
......@@ -76,18 +170,16 @@ public class FontParams
this.color = color;
}
public void createFontMetrics(LocalToWindow localToWindow, int w, int h)
{
fontSize = max(5, (int) (h * size));
fontSize = max(12, (int) (h * size));
float z = localToWindow.transformPt(new double[]{0, 0, 0}, new int[2]);
float[] xl = localToWindow.reverseTransformPt(w / 2, (h - fontSize) / 2, z);
float[] xu = localToWindow.reverseTransformPt(w / 2, (h + fontSize) / 2, z);
font3DSize = (float) (sqrt((xu[0] - xl[0]) * (xu[0] - xl[0]) +
(xu[1] - xl[1]) * (xu[1] - xl[1]) +
(xu[2] - xl[2]) * (xu[2] - xl[2])));
(xu[1] - xl[1]) * (xu[1] - xl[1]) +
(xu[2] - xl[2]) * (xu[2] - xl[2])));
}
/**
......@@ -116,6 +208,27 @@ public class FontParams
fireStateChanged();
}
/**
* Get the value of bgColor
*
* @return the value of transparent background color
*/
public Color getBgColor()
{
return bgColor;
}
/**
* Set the value of bgColor
*
* @param bgColor new value of bgColor
*/
public void setBgColor(Color bgColor)
{
this.bgColor = bgColor;
fireStateChanged();
}
/**
* Set the value of fontType
*
......@@ -257,6 +370,93 @@ public class FontParams
fireStateChanged();
}
/**
* Get the value of position
*
* @return the value of position of text relative to anchor point
*/
public Position getPosition()
{
return position;
}
/**
* Set the value of position
*
* @param position new value of position
*/
public void setPosition(Position position)
{
this.position = position;
fireStateChanged();
}
/**
* Get the value of decoration
*
* @return the value of decoration
*/
public Decoration getDecoration()
{
return decoration;
}
/**
* Set the value of decoration
*
* @param decoration new value of decoration
*/
public void setDecoration(Decoration decoration)
{
this.decoration = decoration;
fireStateChanged();
}
/**
* Get the value of opacity
*
* @return the value of opacity
*/
public float getOpacity()
{
return opacity;
}
/**
* Set the value of opacity
*
* @param opacity new value of opacity
*/
public void setOpacity(float opacity)
{
this.opacity = opacity;
fireStateChanged();
}
/**
* Get the value of shift
*
* @return the value of shift
*/
public float getShift()
{
return shift;
}
/**
* Set the value of shift
*
* @param shift new value of shift
*/
public void setShift(float shift)
{
this.shift = shift;
fireStateChanged();
}
/**
* A flag indicating if state change will be forwarded to listeners.
*/
......
......@@ -42,9 +42,9 @@ package pl.edu.icm.visnow.geometries.textUtils;
import java.awt.*;
import javax.media.j3d.J3DGraphics2D;
import pl.edu.icm.visnow.geometries.parameters.FontParams;
import static pl.edu.icm.visnow.geometries.textUtils.TextGlyph2DParams.Decoration.*;
import pl.edu.icm.visnow.geometries.textUtils.TextGlyph2DParams.Position;
import static pl.edu.icm.visnow.geometries.textUtils.TextGlyph2DParams.Position.RADIAL;
import pl.edu.icm.visnow.geometries.parameters.FontParams.Position;
import static pl.edu.icm.visnow.geometries.parameters.FontParams.Position.*;
import static pl.edu.icm.visnow.geometries.parameters.FontParams.Decoration.*;
/**
*
......@@ -108,24 +108,28 @@ public class CXYZString
/**
*
* @param vGraphics
* @param params
* @param fm
* @param width
* @param height
*/
public void draw(J3DGraphics2D vGraphics, int w, int h, TextGlyph2DParams params, Font ft, FontMetrics fm, int width, int height)
public void draw(J3DGraphics2D vGraphics, FontParams params, FontMetrics fm, int width, int height)
{
if (text == null || text.length == 0)
return;
String[][]texts = new String[text.length][];
int effectiveTextsLength = 0;
for (int i = 0; i < texts.length; i++) {
texts[i] = text[i].split("_;_");
for (int j = 0; j < texts[i].length; j++)
if (!texts[i][j].isEmpty())
texts[i] = text[i].split("\\\\n");
for (String text1 : texts[i])
if (!text1.isEmpty())
effectiveTextsLength += 1;
}
String[] effectiveTexts = new String[effectiveTextsLength];
for (int i = 0, k = 0; i < texts.length; i++)
for (int j = 0; j < texts[i].length; j++)
if (!texts[i][j].isEmpty()) {
effectiveTexts[k] = texts[i][j];
for (String text1 : texts[i])
if (!text1.isEmpty()) {
effectiveTexts[k] = text1;
k += 1;
}
float depth = 1 - sCoords[2] / 2;
......@@ -138,10 +142,10 @@ public class CXYZString
textW = k;
}
textW += 8;
int textH = effectiveTexts.length * (ft.getSize() + 1) + 8;
int textH = effectiveTexts.length * (params.getFontSize() + 1) + 8;
float[] fc = new float[3];
float[] bc = new float[3];
params.getFgColor().getRGBColorComponents(fc);
params.getColor().getRGBColorComponents(fc);
params.getBgColor().getRGBColorComponents(bc);
int anchorX = (int)sCoords[0], anchorY = (int)sCoords[1];
int glyphX, glyphY;
......@@ -154,35 +158,13 @@ public class CXYZString
stemEndY = height - (int)(height / 2 - (anchorY - height / 2) * (1. + shift / 10));
}
else {
stemEndX = (int)(anchorX + effectivePosition.getxShift() * shift * ft.getSize());
stemEndY = (int)(anchorY - effectivePosition.getyShift() * shift * ft.getSize());
}
glyphX = stemEndX;
glyphY = stemEndY + textH / 2;
switch (effectivePosition) {
case NW:
case W:
case SW:
glyphX -= textW;
break;
case N:
case S:
glyphX -= textW / 2;
break;
default:
}
switch (effectivePosition) {
case SE:
case S:
case SW:
glyphY += textH / 2;
break;
case NE:
case N:
case NW:
glyphY -= textH / 2;
break;
stemEndX = (int)(anchorX + effectivePosition.getxShift() * shift * params.getFontSize());
stemEndY = (int)(anchorY - effectivePosition.getyShift() * shift * params.getFontSize());
}
int glyphShiftX = params.getDecoration() == FRAME ? effectivePosition.getxFrameShift() - 1 : effectivePosition.getxEdgeShift() - 1;
int glyphShiftY = params.getDecoration() == FRAME ? effectivePosition.getyFrameShift() + 1 : effectivePosition.getyEdgeShift() + 1;
glyphX = stemEndX + textW * glyphShiftX / 2;
glyphY = stemEndY + textH * glyphShiftY/ 2;
vGraphics.setColor(new Color(bc[0], bc[1], bc[2], params.getOpacity()));
vGraphics.fillRect(glyphX, glyphY - textH, textW, textH);
vGraphics.setColor(new Color(depth * fc[0] + (1 - depth) * bc[0],
......@@ -192,9 +174,6 @@ public class CXYZString
case FRAME:
vGraphics.drawRect(glyphX, glyphY - textH, textW, textH);
break;
case BASELINE:
vGraphics.drawLine(glyphX, glyphY, glyphX + textW, glyphY);
break;
case EDGE:
vGraphics.drawLine(glyphX, glyphY, glyphX + textW, glyphY);
vGraphics.drawLine(glyphX, glyphY, glyphX, glyphY - textH);
......@@ -204,7 +183,8 @@ public class CXYZString
vGraphics.drawLine(anchorX, anchorY, stemEndX, stemEndY);
for (int i = 0; i < effectiveTexts.length; i++)
vGraphics.drawString(effectiveTexts[i], glyphX + 4, glyphY - 4);
vGraphics.drawString(effectiveTexts[i], glyphX + 4, glyphY - 4 - (params.getFontSize() + 1) * (effectiveTexts.length - 1 - i));
}
}
//<editor-fold defaultstate="collapsed" desc=" COPYRIGHT AND LICENSE ">
/* VisNow
Copyright (C) 2006-2013 University of Warsaw, ICM
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
University of Warsaw, Interdisciplinary Centre for Mathematical and
Computational Modelling, Pawinskiego 5a, 02-106 Warsaw, Poland.
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
//</editor-fold>
package pl.edu.icm.visnow.geometries.textUtils;
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import static pl.edu.icm.visnow.geometries.textUtils.TextGlyph2DParams.Decoration.*;
import static pl.edu.icm.visnow.geometries.textUtils.TextGlyph2DParams.Position.*;
/**
** @author Krzysztof S. Nowinski, University of Warsaw ICM
*/
public class TextGlyph2DParams
{
public enum Position
{
AT_POINT( 0, 0, -1, -1),
RADIAL ( 0, 0, 0, 0),
N ( 0, 1, -1, -1),
NE ( 1, 1, -1, -1),
E ( 1, 0, -1, 0),
SE ( 1, -1, -1, 1),
S ( 0, -1, -1, 1),
SW (-1, -1, 1, 1),
W (-1, 0, 1, 0),
NW (-1, 1, 1, -1);
private final int xShift;
private final int yShift;
private final int xFrameShift;
private final int yFrameShift;
private Position(int xShift, int yShift, int xFrameShift, int yFrameShift) {
this.xShift = xShift;
this.yShift = yShift;
this.xFrameShift = xFrameShift;
this.yFrameShift = yFrameShift;
}
public int getxShift() {
return xShift;
}
public int getyShift() {
return yShift;
}
public int getxFrameShift() {
return xFrameShift;
}
public int getyFrameShift() {
return yFrameShift;
}
public static Position getPosition(float x, float y)
{
double phi = Math.atan2(y, x) / Math.PI;
if (phi < 0)
phi += 2;
int dir = (int)(4 * phi + .5);
if (dir >= 8)
dir = 0;
switch (dir) {
case 0:
return E;
case 1:
return NE;
case 2:
return N;
case 3:
return NW;
case 4:
return W;
case 5:
return SW;
case 6:
return S;
default:
return SE;
}
}
}
public enum Decoration {NONE, BASELINE, EDGE, FRAME}
private Position position = RADIAL;
private Decoration decoration = FRAME;
private float opacity = .5f;
private float shift = .5f;
private Color fgColor = Color.WHITE;
private Color bgColor = Color.BLACK;
private boolean active = true;
/**
* Set the value of active
*
* @param active new value of active
*/
public void setActive(boolean active)
{
this.active = active;
}
/**
* Get the value of position
*
* @return the value of position of text relative to anchor point
*/
public Position getPosition()
{
return position;
}
/**
* Set the value of position
*
* @param position new value of position
*/
public void setPosition(Position position)
{
this.position = position;
fireStateChanged();
}
/**
* Get the value of decoration
*
* @return the value of decoration
*/
public Decoration getDecoration()
{
return decoration;
}
/**
* Set the value of decoration
*
* @param decoration new value of decoration
*/
public void setDecoration(Decoration decoration)
{
this.decoration = decoration;
fireStateChanged();
}
/**
* Get the value of opacity
*
* @return the value of opacity
*/
public float getOpacity()
{
return opacity;
}
/**
* Set the value of opacity
*
* @param opacity new value of opacity
*/
public void setOpacity(float opacity)
{
this.opacity = opacity;
fireStateChanged();
}
/**
* Get the value of shift
*
* @return the value of shift
*/
public float getShift()
{
return shift;
}
/**
* Set the value of shift
*
* @param shift new value of shift
*/
public void setShift(float shift)
{
this.shift = shift;
fireStateChanged();
}
/**
* Get the value of fgColor
*
* @return the value of text and frame color
*/
public Color getFgColor()
{
return fgColor;
}
/**
* Set the value of fgColor
*
* @param fgColor new value of text color
*/
public void setFgColor(Color fgColor)
{
this.fgColor = fgColor;
fireStateChanged();
}
/**
* Get the value of bgColor
*
* @return the value of transparent background color
*/
public Color getBgColor()
{
return bgColor;
}
/**
* Set the value of bgColor
*
* @param bgColor new value of bgColor
*/
public void setBgColor(Color bgColor)
{
this.bgColor = bgColor;
fireStateChanged();
}
/**
* Utility field holding list of ChangeListeners.
*/
private transient ArrayList<ChangeListener> listeners = new ArrayList<ChangeListener>();
public void addChangeListener(ChangeListener l)
{
listeners.add(l);
}
public void removeChangeListener(ChangeListener l)
{
listeners.remove(l);
}
/**
* Notifies all registered listeners about the event.
*
* @param object Parameter #1 of the <CODE>ChangeEvent<CODE> constructor.
*/
private void fireStateChanged()
{
if (!active)
return;
ChangeEvent e = new ChangeEvent(this);
for (ChangeListener l : listeners)
l.stateChanged(e);