Hia, I’m back! ;D
now I would like to share with you one of my little projects. basically, I wanted to use a text in 3d-space, and not bitmaps. I’ll keep it short for now, because it’s already 01:36 where I am right now…
The font class in java has some interesting properties for this goal, it allows us to get a general shape for any string. So all I needed to figure out is to triangulate (build triangles) a ‘shape’ object.
here’s how I did it: (click for an larger image)
http://cal007300.student.utwente.nl/vincent/explainsmall.jpg
1) we start with a regular shape. you can get the shape of a text by invoking the createGlyphVector() method on the Font object. this will generate Glyphs, which we can iterate over and get Glyph Outlines from using getGlyphOutline(i) on every Glyph. These Shapes we will try to triangulize. Notice the Shape.getPathIterator receives() a ‘flatness’ parameter which will return an iterator which will only return LINETO objects (as opposed to quadratic and cubic CURVES). This makes it easy for us…
2) Because of the triangulation method we are going to use (Delauney) we need to interpolate the ‘long’ edges.
3) After interpolating, we have the points required to feed to the Delauney triangulation routine
4) This is the result after triangulating, lots of triangles, connecting everything…
5) So we decide which triangles to keep and which to throw away. I check for every triangle-centerpoint if it is within the shape.
6) And this is the result.
I’ve got the Delauney-triangulazing code from this site: http://www.cs.cornell.edu/Info/People/chew/Delaunay.html
The main code which i created was this:
package javafont;
import java.awt.Font;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.PathIterator;
import java.util.HashSet;
import java.util.Set;
import org.lwjgl.util.vector.Vector2f;
import delaunay.DelaunayTriangulation;
import delaunay.Pnt;
import delaunay.Simplex;
public class ShapeTriangulator {
public static final double TRISIZE = 10000;
public static Set<Vector2f[]> convertSimplexSetToVector2fSet(Set<Simplex<Pnt>> oldSet) {
Set<Vector2f[]> finalSet = new HashSet<Vector2f[]>();
for (Simplex<Pnt> triangle: oldSet) {
Pnt[] pntArray = triangle.toArray(new Pnt[3]);
Vector2f[] vecArray = new Vector2f[3];
//System.out.println(pntArray[0] + "," + pntArray[1] + "," + pntArray[2]);
vecArray[0] = new Vector2f((float) pntArray[0].coord(0), (float) pntArray[0].coord(1));
vecArray[1] = new Vector2f((float) pntArray[1].coord(0), (float) pntArray[1].coord(1));
vecArray[2] = new Vector2f((float) pntArray[2].coord(0), (float) pntArray[2].coord(1));
finalSet.add(vecArray);
}
return finalSet;
}
public static Set<Simplex<Pnt>> triangulateText(Font font, String text, double flatness, double interpolateFlatness) {
GlyphVector vector = font.createGlyphVector(new FontRenderContext(null, false, false), text);
HashSet<Simplex<Pnt>> finalTriangleSet = new HashSet<Simplex<Pnt>>();
for(int i=0; i<vector.getNumGlyphs(); i++) {
//Point2D characterLocation = vector.getGlyphPosition(i);
Shape characterShape = vector.getGlyphOutline(i);
Set<Simplex<Pnt>> characterTriangleSet = triangulateShape(characterShape, flatness, interpolateFlatness);
finalTriangleSet.addAll(characterTriangleSet);
}
return finalTriangleSet;
}
public static Set<Simplex<Pnt>> triangulateShape(Shape shape, double flatness, double interpolateFlatness) {
return triangulateShape(shape, flatness, interpolateFlatness, 0, 0);
}
public static Set<Simplex<Pnt>> triangulateShape(Shape shape, double flatness, double interpolateFlatness, double relX, double relY) {
PathIterator shapePathIterator = shape.getPathIterator(null, flatness);
/*
* Build the triangle which encompasses the shape (-0.5,+1) - (+0.5,+1) - (0,-1)
*/
Simplex<Pnt> tri = new Simplex<Pnt>(
new Pnt(-TRISIZE/2.0,TRISIZE),
new Pnt(+TRISIZE/2.0,TRISIZE),
new Pnt(0,-TRISIZE)
);
// initialize the triangulation with this triangle
DelaunayTriangulation triangulation = new DelaunayTriangulation(tri);
/*
* Add all points of the shape to the triangulation
*/
double x = 0, y = 0, px, py;
while (!shapePathIterator.isDone()) {
px = x;
py = y;
double[] args = new double[6];
int mode = shapePathIterator.currentSegment(args);
switch (mode) {
case PathIterator.SEG_MOVETO:
x = args[0];
y = args[1];
break;
case PathIterator.SEG_LINETO:
x = args[0];
y = args[1];
if(px == x && py == y)
break;
Pnt p1 = new Pnt(px+relX,py+relY);
Pnt p2 = new Pnt(x+relX,y+relY);
triangulation.delaunayPlace(p1);
Pnt lengthVector = new Pnt(x-px, y-py);
// sqrt( x^2 + y^2 )
double length =Math.sqrt((lengthVector.coord(0) * lengthVector.coord(0)) +
(lengthVector.coord(1) * lengthVector.coord(1)));
double num = length / interpolateFlatness;
double nx = lengthVector.coord(0) / num;
double ny = lengthVector.coord(1) / num;
if(num>1) {
double start = (num - Math.floor(num)) / 2.0;
double cx = p1.coord(0);
double cy = p1.coord(1);
double ll = length;
ll = ll -start;
while(ll > interpolateFlatness) {
triangulation.delaunayPlace(new Pnt(cx,cy));
cx = cx + nx;
cy = cy + ny;
ll = ll -interpolateFlatness;
}
}
triangulation.delaunayPlace(p2);
break;
case PathIterator.SEG_QUADTO:
break;
case PathIterator.SEG_CUBICTO:
break;
case PathIterator.SEG_CLOSE:
break;
}
shapePathIterator.next();
}
// now add all triangles which are in the shape to the set
HashSet<Simplex<Pnt>> finalTriangleSet = new HashSet<Simplex<Pnt>>();
for (Simplex<Pnt> triangle: triangulation) {
double midX = 0.0f,midY = 0.0f;
for (Set<Pnt> edge: triangle.facets()) {
Pnt[] endpoint = edge.toArray(new Pnt[2]);
midX = midX+endpoint[0].coord(0);
midY = midY+endpoint[0].coord(1);
midX = midX+endpoint[1].coord(0);
midY = midY+endpoint[1].coord(1);
}
midX /= 6.0;
midY /= 6.0;
if(shape.contains(midX, midY)) {
finalTriangleSet.add(triangle);
}
}
return finalTriangleSet;
}
}
and this is how you could use it:
String text = "World Domination";
Font myFont = new Font("Verdana", Font.PLAIN, 10);
triangleSet =
ShapeTriangulator.convertSimplexSetToVector2fSet(
ShapeTriangulator.triangulateText(myFont, text, 0.1, 0.5)
);
here you can download the full source code plus some demos: http://cal007300.student.utwente.nl/vincent/javafont2.rar (255kb)
I hope you enjoy this code, as an excersize ( ) I’ll leave to you some interesting ideas:
- Extrusion (making it fully 3d, instead of 2d)
- optimiziation
Regards,
- Vince