aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/Font3D.java
diff options
context:
space:
mode:
authorJulien Gouesse <[email protected]>2015-11-28 15:11:48 +0100
committerJulien Gouesse <[email protected]>2015-11-28 15:11:48 +0100
commitdbc98deea1884e44da2c74d6ea807253cdefa693 (patch)
tree29c3ee7dea82d7dd773d81f33f645dde67e43a17 /src/javax/media/j3d/Font3D.java
parent2c99f1329dc55bd496bce91b9aba956ecba3c67e (diff)
Relocate package prefix to org.jogamp.java3d
Diffstat (limited to 'src/javax/media/j3d/Font3D.java')
-rw-r--r--src/javax/media/j3d/Font3D.java1183
1 files changed, 0 insertions, 1183 deletions
diff --git a/src/javax/media/j3d/Font3D.java b/src/javax/media/j3d/Font3D.java
deleted file mode 100644
index 0d7c631..0000000
--- a/src/javax/media/j3d/Font3D.java
+++ /dev/null
@@ -1,1183 +0,0 @@
-/*
- * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * This code 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
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- */
-
-package javax.media.j3d;
-
-import java.awt.Font;
-import java.awt.Shape;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphMetrics;
-import java.awt.font.GlyphVector;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.ServiceLoader;
-
-import javax.vecmath.Point3d;
-import javax.vecmath.Point3f;
-import javax.vecmath.Vector3f;
-
-/**
- * The Font3D object is used to store extruded 2D glyphs. These
- * 3D glyphs can then be used to construct Text3D NodeComponent
- * objects.
- * <P>
- * A 3D Font consists of a Java 2D font, a tesellation tolerance,
- * and an extrusion path. The extrusion
- * path creates depth by describing how the edge of a glyph varies
- * in the Z axis.
- * <P>
- * The construction of a Text3D object requires a Font3D object.
- * The Font3D object describes the style of the text, such as its
- * depth. The text also needs other classes, such as java.awt.Font and
- * FontExtrusion. The Font object describes the font name (Helvetica,
- * Courier, etc.), the font style (bold, Italic, etc.), and point
- * size. The FontExtrusion object extends Font3D by describing
- * the extrusion path for the Font3D object (how the edge of the
- * font glyph varies in the Z axis).
- *<P>
- * To ensure correct rendering, the 2D Font object should be created
- * with the default AffineTransform. The point size of the 2D font will
- * be used as a rough measure of how fine a tesselation to use when
- * creating the Font3D object: the larger the point size, in
- * general, the finer the tesselation.
- * <P>
- * Custom 3D fonts as well as methods to store 3D fonts
- * to disk will be addressed in a future release.
- *
- * @see java.awt.Font
- * @see FontExtrusion
- * @see Text3D
- */
-public class Font3D extends NodeComponent {
-
- Font font;
- double tessellationTolerance;
- FontExtrusion fontExtrusion;
- FontRenderContext frc;
- // Used by triangulateGlyphs method to split contour data into islands.
- final static float EPS = 0.000001f;
-
-// Map glyph code to GeometryArrayRetained
-Hashtable<Character, GeometryArrayRetained> geomHash = new Hashtable<Character, GeometryArrayRetained>(20);
-
- /**
- * Constructs a Font3D object from the specified Font and
- * FontExtrusion objects, using the default value for the
- * tessellation tolerance. The default value is as follows:
- *
- * <ul>
- * tessellation tolerance : 0.01<br>
- * </ul>
- * <P>
- * The FontExtrusion object contains the extrusion path to use on
- * the 2D Font glyphs. To ensure correct rendering the font must
- * be created with the default AffineTransform. Passing null for
- * the FontExtrusion parameter results in no extrusion being done.
- *
- * @param font the Java 2D font used to create the 3D font object
- * @param extrudePath the extrusion path used to describe how
- * the edge of the font varies along the Z axis
- */
- public Font3D(Font font, FontExtrusion extrudePath) {
- this(font, 0.01, extrudePath);
- }
-
- /**
- * Constructs a Font3D object from the specified Font and
- * FontExtrusion objects, using the specified tessellation
- * tolerance.
- * The FontExtrusion object contains the extrusion path to use on
- * the 2D Font glyphs. To ensure correct rendering, the font must
- * be created with the default AffineTransform. Passing null for
- * the FontExtrusion parameter results in no extrusion being done.
- *
- * @param font the Java 2D font used to create the 3D font object.
- * @param tessellationTolerance the tessellation tolerance value
- * used in tessellating the glyphs of the 2D Font.
- * This corresponds to the <code>flatness</code> parameter in
- * the <code>java.awt.Shape.getPathIterator</code> method.
- * @param extrudePath the extrusion path used to describe how
- * the edge of the font varies along the Z axis.
- *
- * @since Java 3D 1.2
- */
- public Font3D(Font font,
- double tessellationTolerance,
- FontExtrusion extrudePath) {
-
- this.font = font;
- this.tessellationTolerance = tessellationTolerance;
- this.fontExtrusion = extrudePath;
- this.frc = new FontRenderContext(new AffineTransform(),
- true, true);
- }
-
- /**
- * Returns the Java 2D Font used to create this Font3D object.
- * @return Font object used by this Font3D
- */
- public Font getFont() {
- return this.font;
- }
-
-
- /**
- * Returns the tessellation tolerance with which this Font3D was
- * created.
- * @return the tessellation tolerance used by this Font3D
- *
- * @since Java 3D 1.2
- */
- public double getTessellationTolerance() {
- return tessellationTolerance;
- }
-
-
- /**
- * Copies the FontExtrusion object used to create this Font3D object
- * into the specified parameter.
- *
- * @param extrudePath object that will receive the
- * FontExtrusion information for this Font3D object
- */
- public void getFontExtrusion(FontExtrusion extrudePath) {
- extrudePath = this.fontExtrusion;
- }
-
- /**
- * Returns the 3D bounding box of the specified glyph code.
- *
- * @param glyphCode the glyphCode from the original 2D Font
- * @param bounds the 3D glyph's bounds
- */
- public void getBoundingBox(int glyphCode, BoundingBox bounds){
- int[] gCodes = {glyphCode};
- GlyphVector gVec = font.createGlyphVector(frc, gCodes);
- Rectangle2D.Float bounds2d = (Rectangle2D.Float)
- (((GlyphMetrics)(gVec.getGlyphMetrics(0))).getBounds2D());
-
- Point3d lower = new Point3d(bounds2d.x, bounds2d.y, 0.0);
- Point3d upper;
- if (fontExtrusion != null) {
- upper = new Point3d(bounds2d.x + bounds2d.width,
- bounds2d.y + bounds2d.height,
- fontExtrusion.length);
- } else {
- upper = new Point3d(bounds2d.x + bounds2d.width,
- bounds2d.y + bounds2d.height,
- 0.0);
- }
- bounds.setLower(lower);
- bounds.setUpper(upper);
- }
-
-/**
- * An append-only array-based integer list
- */
-private static class IntVector {
- int[] data;
- int size;
-
- IntVector() {
- data = new int[10];
- size = 0;
- }
-
- void add(int i) {
- // need to expand backing
- if (size == data.length)
- data = Arrays.copyOf(data, 2 * size);
-
- data[size] = i;
- size++;
- }
-}
-
- // BY MIK OF CLASSX
- /**
- * Returns a GeometryArray of a glyph in this Font3D.
- *
- * @param c character from which to generate a tessellated glyph.
- *
- * @return a GeometryArray
- *
- * @since Java 3D 1.4
- */
- public GeometryArray getGlyphGeometry(char c) {
- char code[] = { c };
- GlyphVector gv = font.createGlyphVector(frc, code);
-
- // triangulate the glyph
- GeometryArrayRetained glyph_gar = triangulateGlyphs(gv, code[0]);
-
- // Assume that triangulateGlyphs returns a triangle array with only coords & normals
- // (and without by-ref, interleaved, etc.)
- assert glyph_gar instanceof TriangleArrayRetained :
- "Font3D: GeometryArray is not an instance of TrangleArray";
- assert glyph_gar.getVertexFormat() == (GeometryArray.COORDINATES | GeometryArray.NORMALS) :
- "Font3D: Illegal GeometryArray format -- only coordinates and normals expected";
-
- // create a correctly sized TriangleArray
- TriangleArray ga = new TriangleArray(glyph_gar.getVertexCount(),glyph_gar.getVertexFormat());
-
- // temp storage for coords, normals
- float tmp[] = new float[3];
-
- int vertexCount = ga.getVertexCount();
- for(int i=0; i<vertexCount; i++) {
- // copy the glyph geometry to the TriangleArray
- glyph_gar.getCoordinate(i,tmp);
- ga.setCoordinate(i,tmp);
-
- glyph_gar.getNormal(i,tmp);
- ga.setNormal(i,tmp);
- }
-
- return ga;
- }
-
-
- // Triangulate glyph with 'unicode' if not already done.
- GeometryArrayRetained triangulateGlyphs(GlyphVector gv, char c) {
- Character ch = new Character(c);
- GeometryArrayRetained geo = geomHash.get(ch);
-
- if (geo == null) {
- // Font Y-axis is downwards, so send affine transform to flip it.
- Rectangle2D bnd = gv.getVisualBounds();
- AffineTransform aTran = new AffineTransform();
- double tx = bnd.getX() + 0.5 * bnd.getWidth();
- double ty = bnd.getY() + 0.5 * bnd.getHeight();
- aTran.setToTranslation(-tx, -ty);
- aTran.scale(1.0, -1.0);
- aTran.translate(tx, -ty);
- Shape shape = gv.getOutline();
- PathIterator pIt = shape.getPathIterator(aTran, tessellationTolerance);
- int flag= -1, numPoints = 0, i, j, k, num=0, vertCnt;
- UnorderList coords = new UnorderList(100, Point3f.class);
- float tmpCoords[] = new float[6];
- float lastX= .0f, lastY= .0f;
- float firstPntx = Float.MAX_VALUE, firstPnty = Float.MAX_VALUE;
- IntVector contours = new IntVector();
- float maxY = -Float.MAX_VALUE;
- int maxYIndex = 0, beginIdx = 0, endIdx = 0, start = 0;
-
- boolean setMaxY = false;
-
-
- while (!pIt.isDone()) {
- Point3f vertex = new Point3f();
- flag = pIt.currentSegment(tmpCoords);
- if (flag == PathIterator.SEG_CLOSE){
- if (num > 0) {
- if (setMaxY) {
- // Get Previous point
- beginIdx = start;
- endIdx = numPoints-1;
- }
- contours.add(num);
- num = 0;
- }
- } else if (flag == PathIterator.SEG_MOVETO){
- vertex.x = tmpCoords[0];
- vertex.y = tmpCoords[1];
- lastX = vertex.x;
- lastY = vertex.y;
-
- if ((lastX == firstPntx) && (lastY == firstPnty)) {
- pIt.next();
- continue;
- }
- setMaxY = false;
- coords.add(vertex);
- firstPntx = lastX;
- firstPnty = lastY;
- if (num> 0){
- contours.add(num);
- num = 0;
- }
- num++;
- numPoints++;
- // skip checking of first point,
- // since the last point will repeat this.
- start = numPoints ;
- } else if (flag == PathIterator.SEG_LINETO){
- vertex.x = tmpCoords[0];
- vertex.y = tmpCoords[1];
- //Check here for duplicate points. Code
- //later in this function can not handle
- //duplicate points.
-
- if ((vertex.x == lastX) && (vertex.y == lastY)) {
- pIt.next();
- continue;
- }
- if (vertex.y > maxY) {
- maxY = vertex.y;
- maxYIndex = numPoints;
- setMaxY = true;
- }
- lastX = vertex.x;
- lastY = vertex.y;
- coords.add(vertex);
- num++;
- numPoints++;
- }
- pIt.next();
- }
-
- // No data(e.g space, control characters)
- // Two point can't form a valid contour
- if (numPoints == 0){
- return null;
- }
-
-
-
- // Determine font winding order use for side triangles
- Point3f p1 = new Point3f(), p2 = new Point3f(), p3 = new Point3f();
- boolean flip_side_orient = true;
- Point3f vertices[] = (Point3f []) coords.toArray(false);
-
- if (endIdx - beginIdx > 0) {
- // must be true unless it is a single line
- // define as "MoveTo p1 LineTo p2 Close" which is
- // not a valid font definition.
-
- if (maxYIndex == beginIdx) {
- p1.set(vertices[endIdx]);
- } else {
- p1.set(vertices[maxYIndex-1]);
- }
- p2.set(vertices[maxYIndex]);
- if (maxYIndex == endIdx) {
- p3.set(vertices[beginIdx]);
- } else {
- p3.set(vertices[maxYIndex+1]);
- }
-
- if (p3.x != p2.x) {
- if (p1.x != p2.x) {
- // Use the one with smallest slope
- if (Math.abs((p2.y - p1.y)/(p2.x - p1.x)) >
- Math.abs((p3.y - p2.y)/(p3.x - p2.x))) {
- flip_side_orient = (p3.x > p2.x);
- } else {
- flip_side_orient = (p2.x > p1.x);
- }
- } else {
- flip_side_orient = (p3.x > p2.x);
- }
- } else {
- // p1.x != p2.x, otherwise all three
- // point form a straight vertical line with
- // the middle point the highest. This is not a
- // valid font definition.
- flip_side_orient = (p2.x > p1.x);
- }
- }
-
- // Build a Tree of Islands
- int startIdx = 0;
- IslandsNode islandsTree = new IslandsNode(-1, -1);
-
- for (int cIdx = 0; cIdx < contours.size; cIdx++) {
- endIdx = startIdx + contours.data[cIdx];
- islandsTree.insert(new IslandsNode(startIdx, endIdx), vertices);
- startIdx = endIdx;
- }
-
- coords = null; // Free memory
- contours = null;
-
- // Compute islandCounts[][] and outVerts[][]
- UnorderList islandsList = new UnorderList(10, IslandsNode.class);
- islandsTree.collectOddLevelNode(islandsList, 0);
- IslandsNode nodes[] = (IslandsNode []) islandsList.toArray(false);
- int islandCounts[][] = new int[islandsList.arraySize()][];
- Point3f outVerts[][] = new Point3f[islandCounts.length][];
- int nchild, sum;
- IslandsNode node;
-
- for (i=0; i < islandCounts.length; i++) {
- node = nodes[i];
- nchild = node.numChild();
- islandCounts[i] = new int[nchild + 1];
- islandCounts[i][0] = node.numVertices();
- sum = 0;
- sum += islandCounts[i][0];
- for (j=0; j < nchild; j++) {
- islandCounts[i][j+1] = node.getChild(j).numVertices();
- sum += islandCounts[i][j+1];
- }
- outVerts[i] = new Point3f[sum];
- startIdx = 0;
- for (k=node.startIdx; k < node.endIdx; k++) {
- outVerts[i][startIdx++] = vertices[k];
- }
-
- for (j=0; j < nchild; j++) {
- endIdx = node.getChild(j).endIdx;
- for (k=node.getChild(j).startIdx; k < endIdx; k++) {
- outVerts[i][startIdx++] = vertices[k];
- }
- }
- }
-
-
-
- islandsTree = null; // Free memory
- islandsList = null;
- vertices = null;
-
- int[] contourCounts = new int[1];
- ArrayList<GeometryArray> triangData = new ArrayList<GeometryArray>();
-
- Point3f q1 = new Point3f(), q2 = new Point3f(), q3 = new Point3f();
- Vector3f n1 = new Vector3f(), n2 = new Vector3f();
- numPoints = 0;
- for (i = 0; i < islandCounts.length; i++) {
- numPoints += outVerts[i].length;
- }
-
- final GeometryService gs = newGeometryService();
- int vertOffset =
- gs.triangulateIslands(islandCounts, outVerts, contourCounts, triangData);
-
- // Multiply by 2 since we create 2 faces of the font
- // Second term is for side-faces along depth of the font
- if (fontExtrusion == null)
- vertCnt = vertOffset;
- else{
- if (fontExtrusion.shape == null)
- vertCnt = vertOffset * 2 + numPoints *6;
- else{
- vertCnt = vertOffset * 2 + numPoints * 6 *
- (fontExtrusion.pnts.length -1);
- }
- }
-
- // XXXX: Should use IndexedTriangleArray to avoid
- // duplication of vertices. To create triangles for
- // side faces, every vertex is duplicated currently.
- TriangleArray triAry = new TriangleArray(vertCnt,
- GeometryArray.COORDINATES |
- GeometryArray.NORMALS);
-
- boolean flip_orient[] = new boolean[islandCounts.length];
- boolean findOrient;
- // last known non-degenerate normal
- Vector3f goodNormal = new Vector3f();
-
- int currCoordIndex = 0;
- for (j=0;j < islandCounts.length;j++) {
- GeometryArray ga = triangData.get(j);
- vertOffset = ga.getVertexCount();
-
- findOrient = false;
-
- //Create the triangle array
- for (i= 0; i < vertOffset; i+= 3, currCoordIndex += 3){
- //Get 3 points. Since triangle is known to be flat, normal
- // must be same for all 3 points.
- ga.getCoordinate(i, p1);
- ga.getNormal(i, n1);
- ga.getCoordinate(i+1, p2);
- ga.getCoordinate(i+2, p3);
-
- if (!findOrient) {
- //Check here if triangles are wound incorrectly and need
- //to be flipped.
- if (!getNormal(p1,p2, p3, n2)) {
- continue;
- }
-
- if (n2.z >= EPS) {
- flip_orient[j] = false;
- } else if (n2.z <= -EPS) {
- flip_orient[j] = true;
- } else {
- continue;
- }
- findOrient = true;
- }
- if (flip_orient[j]){
- //New Triangulator preserves contour orientation. If contour
- //input is wound incorrectly, swap 2nd and 3rd points to
- //sure all triangles are wound correctly for j3d.
- q1.x = p2.x; q1.y = p2.y; q1.z = p2.z;
- p2.x = p3.x; p2.y = p3.y; p2.z = p3.z;
- p3.x = q1.x; p3.y = q1.y; p3.z = q1.z;
- n1.x = -n1.x; n1.y = -n1.y; n1.z = -n1.z;
- }
-
-
- if (fontExtrusion != null) {
- n2.x = -n1.x;n2.y = -n1.y;n2.z = -n1.z;
-
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, n2);
- triAry.setCoordinate(currCoordIndex+1, p3);
- triAry.setNormal(currCoordIndex+1, n2);
- triAry.setCoordinate(currCoordIndex+2, p2);
- triAry.setNormal(currCoordIndex+2, n2);
-
- q1.x = p1.x; q1.y = p1.y; q1.z = p1.z + fontExtrusion.length;
- q2.x = p2.x; q2.y = p2.y; q2.z = p2.z + fontExtrusion.length;
- q3.x = p3.x; q3.y = p3.y; q3.z = p3.z + fontExtrusion.length;
-
- triAry.setCoordinate(currCoordIndex+vertOffset, q1);
- triAry.setNormal(currCoordIndex+vertOffset, n1);
- triAry.setCoordinate(currCoordIndex+1+vertOffset, q2);
- triAry.setNormal(currCoordIndex+1+vertOffset, n1);
- triAry.setCoordinate(currCoordIndex+2+vertOffset, q3);
- triAry.setNormal(currCoordIndex+2+vertOffset, n1);
- } else {
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, n1);
- triAry.setCoordinate(currCoordIndex+1, p2);
- triAry.setNormal(currCoordIndex+1, n1);
- triAry.setCoordinate(currCoordIndex+2, p3);
- triAry.setNormal(currCoordIndex+2, n1);
- }
-
- }
- if (fontExtrusion != null) {
- currCoordIndex += vertOffset;
- }
- }
-
- //Now add side triangles in both cases.
-
- // Since we duplicated triangles with different Z, make sure
- // currCoordIndex points to correct location.
- if (fontExtrusion != null){
- if (fontExtrusion.shape == null){
- boolean smooth;
- // we'll put a crease if the angle between the normals is
- // greater than 44 degrees
- float threshold = (float) Math.cos(44.0*Math.PI/180.0);
- float cosine;
- // need the previous normals to check for smoothing
- Vector3f pn1 = null, pn2 = null;
- // need the next normals to check for smoothing
- Vector3f n3 = new Vector3f(), n4 = new Vector3f();
- // store the normals for each point because they are
- // the same for both triangles
- Vector3f p1Normal = new Vector3f();
- Vector3f p2Normal = new Vector3f();
- Vector3f p3Normal = new Vector3f();
- Vector3f q1Normal = new Vector3f();
- Vector3f q2Normal = new Vector3f();
- Vector3f q3Normal = new Vector3f();
-
- for (i=0;i < islandCounts.length;i++){
- for (j=0, k=0, num =0;j < islandCounts[i].length;j++){
- num += islandCounts[i][j];
- p1.x = outVerts[i][num - 1].x;
- p1.y = outVerts[i][num - 1].y;
- p1.z = 0.0f;
- q1.x = p1.x; q1.y = p1.y; q1.z = p1.z+fontExtrusion.length;
- p2.z = 0.0f;
- q2.z = p2.z+fontExtrusion.length;
- for (int m=0; m < num;m++) {
- p2.x = outVerts[i][m].x;
- p2.y = outVerts[i][m].y;
- q2.x = p2.x;
- q2.y = p2.y;
- if (getNormal(p1, q1, p2, n1)) {
-
- if (!flip_side_orient) {
- n1.negate();
- }
- goodNormal.set(n1);
- break;
- }
- }
-
- for (;k < num;k++){
- p2.x = outVerts[i][k].x;p2.y = outVerts[i][k].y;p2.z = 0.0f;
- q2.x = p2.x; q2.y = p2.y; q2.z = p2.z+fontExtrusion.length;
-
- if (!getNormal(p1, q1, p2, n1)) {
- n1.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n1.negate();
- }
- goodNormal.set(n1);
- }
-
- if (!getNormal(p2, q1, q2, n2)) {
- n2.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n2.negate();
- }
- goodNormal.set(n2);
- }
- // if there is a previous normal, see if we need to smooth
- // this normal or make a crease
-
- if (pn1 != null) {
- cosine = n1.dot(pn2);
- smooth = cosine > threshold;
- if (smooth) {
- p1Normal.x = (pn1.x + pn2.x + n1.x);
- p1Normal.y = (pn1.y + pn2.y + n1.y);
- p1Normal.z = (pn1.z + pn2.z + n1.z);
- normalize(p1Normal);
-
- q1Normal.x = (pn2.x + n1.x + n2.x);
- q1Normal.y = (pn2.y + n1.y + n2.y);
- q1Normal.z = (pn2.z + n1.z + n2.z);
- normalize(q1Normal);
- } // if smooth
- else {
- p1Normal.x = n1.x; p1Normal.y = n1.y; p1Normal.z = n1.z;
- q1Normal.x = n1.x+n2.x;
- q1Normal.y = n1.y+n2.y;
- q1Normal.z = n1.z+ n2.z;
- normalize(q1Normal);
- } // else
- } // if pn1 != null
- else {
- pn1 = new Vector3f();
- pn2 = new Vector3f();
- p1Normal.x = n1.x;
- p1Normal.y = n1.y;
- p1Normal.z = n1.z;
-
- q1Normal.x = (n1.x + n2.x);
- q1Normal.y = (n1.y + n2.y);
- q1Normal.z = (n1.z + n2.z);
- normalize(q1Normal);
- } // else
-
- // if there is a next, check if we should smooth normal
-
- if (k+1 < num) {
- p3.x = outVerts[i][k+1].x; p3.y = outVerts[i][k+1].y;
- p3.z = 0.0f;
- q3.x = p3.x; q3.y = p3.y; q3.z = p3.z + fontExtrusion.length;
-
- if (!getNormal(p2, q2, p3, n3)) {
- n3.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n3.negate();
- }
- goodNormal.set(n3);
- }
-
- if (!getNormal(p3, q2, q3, n4)) {
- n4.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n4.negate();
- }
- goodNormal.set(n4);
- }
-
- cosine = n2.dot(n3);
- smooth = cosine > threshold;
-
- if (smooth) {
- p2Normal.x = (n1.x + n2.x + n3.x);
- p2Normal.y = (n1.y + n2.y + n3.y);
- p2Normal.z = (n1.z + n2.z + n3.z);
- normalize(p2Normal);
-
- q2Normal.x = (n2.x + n3.x + n4.x);
- q2Normal.y = (n2.y + n3.y + n4.y);
- q2Normal.z = (n2.z + n3.z + n4.z);
- normalize(q2Normal);
- } else { // if smooth
- p2Normal.x = n1.x + n2.x;
- p2Normal.y = n1.y + n2.y;
- p2Normal.z = n1.z + n2.z;
- normalize(p2Normal);
- q2Normal.x = n2.x; q2Normal.y = n2.y; q2Normal.z = n2.z;
- } // else
- } else { // if k+1 < num
- p2Normal.x = (n1.x + n2.x);
- p2Normal.y = (n1.y + n2.y);
- p2Normal.z = (n1.z + n2.z);
- normalize(p2Normal);
-
- q2Normal.x = n2.x;
- q2Normal.y = n2.y;
- q2Normal.z = n2.z;
- } // else
-
- // add pts for the 2 tris
- // p1, q1, p2 and p2, q1, q2
-
- if (flip_side_orient) {
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, p1Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, q1Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, p2Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, p2Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, q1Normal);
- currCoordIndex++;
- } else {
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, q1Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, p1Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, p2Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, q1Normal);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, p2Normal);
- currCoordIndex++;
- }
- triAry.setCoordinate(currCoordIndex, q2);
- triAry.setNormal(currCoordIndex, q2Normal);
- currCoordIndex++;
- pn1.x = n1.x; pn1.y = n1.y; pn1.z = n1.z;
- pn2.x = n2.x; pn2.y = n2.y; pn2.z = n2.z;
- p1.x = p2.x; p1.y = p2.y; p1.z = p2.z;
- q1.x = q2.x; q1.y = q2.y; q1.z = q2.z;
-
- }// for k
-
- // set the previous normals to null when we are done
- pn1 = null;
- pn2 = null;
- }// for j
- }//for i
- } else { // if shape
- int m, offset=0;
- Point3f P2 = new Point3f(), Q2 = new Point3f(), P1=new Point3f();
- Vector3f nn = new Vector3f(), nn1= new Vector3f(),
- nn2= new Vector3f(), nn3= new Vector3f();
- Vector3f nna = new Vector3f(), nnb=new Vector3f();
- float length;
- boolean validNormal = false;
-
- // fontExtrusion.shape is specified, and is NOT straight line
- for (i=0;i < islandCounts.length;i++){
- for (j=0, k= 0, offset = num =0;j < islandCounts[i].length;j++){
- num += islandCounts[i][j];
-
- p1.x = outVerts[i][num - 1].x;
- p1.y = outVerts[i][num - 1].y;
- p1.z = 0.0f;
- q1.x = p1.x; q1.y = p1.y; q1.z = p1.z+fontExtrusion.length;
- p3.z = 0.0f;
- for (m=num-2; m >= 0; m--) {
- p3.x = outVerts[i][m].x;
- p3.y = outVerts[i][m].y;
-
- if (getNormal(p3, q1, p1, nn1)) {
- if (!flip_side_orient) {
- nn1.negate();
- }
- goodNormal.set(nn1);
- break;
- }
- }
- for (;k < num;k++){
- p2.x = outVerts[i][k].x;p2.y = outVerts[i][k].y;p2.z = 0.0f;
- q2.x = p2.x; q2.y = p2.y; q2.z = p2.z+fontExtrusion.length;
- getNormal(p1, q1, p2, nn2);
-
- p3.x = outVerts[i][(k+1)==num ? offset:(k+1)].x;
- p3.y = outVerts[i][(k+1)==num ? offset:(k+1)].y;
- p3.z = 0.0f;
- if (!getNormal(p3,p2,q2, nn3)) {
- nn3.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- nn3.negate();
- }
- goodNormal.set(nn3);
- }
-
- // Calculate normals at the point by averaging normals
- // of two faces on each side of the point.
- nna.x = (nn1.x+nn2.x);
- nna.y = (nn1.y+nn2.y);
- nna.z = (nn1.z+nn2.z);
- normalize(nna);
-
- nnb.x = (nn3.x+nn2.x);
- nnb.y = (nn3.y+nn2.y);
- nnb.z = (nn3.z+nn2.z);
- normalize(nnb);
-
- P1.x = p1.x;P1.y = p1.y;P1.z = p1.z;
- P2.x = p2.x;P2.y = p2.y; P2.z = p2.z;
- Q2.x = q2.x;Q2.y = q2.y; Q2.z = q2.z;
- for (m=1;m < fontExtrusion.pnts.length;m++){
- q1.z = q2.z = fontExtrusion.pnts[m].x;
- q1.x = P1.x + nna.x * fontExtrusion.pnts[m].y;
- q1.y = P1.y + nna.y * fontExtrusion.pnts[m].y;
- q2.x = P2.x + nnb.x * fontExtrusion.pnts[m].y;
- q2.y = P2.y + nnb.y * fontExtrusion.pnts[m].y;
-
- if (!getNormal(p1, q1, p2, n1)) {
- n1.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n1.negate();
- }
- goodNormal.set(n1);
- }
-
- if (flip_side_orient) {
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
- } else {
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
- }
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- if (!getNormal(p2, q1, q2, n1)) {
- n1.set(goodNormal);
- } else {
- if (!flip_side_orient) {
- n1.negate();
- }
- goodNormal.set(n1);
- }
-
- if (flip_side_orient) {
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
- } else {
- triAry.setCoordinate(currCoordIndex, q1);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- triAry.setCoordinate(currCoordIndex, p2);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
- }
- triAry.setCoordinate(currCoordIndex, q2);
- triAry.setNormal(currCoordIndex, n1);
- currCoordIndex++;
-
- p1.x = q1.x;p1.y = q1.y;p1.z = q1.z;
- p2.x = q2.x;p2.y = q2.y;p2.z = q2.z;
- }// for m
- p1.x = P2.x; p1.y = P2.y; p1.z = P2.z;
- q1.x = Q2.x; q1.y = Q2.y; q1.z = Q2.z;
- nn1.x = nn2.x;nn1.y = nn2.y;nn1.z = nn2.z;
- }// for k
- offset = num;
- }// for j
- }//for i
- }// if shape
- }// if fontExtrusion
- geo = (GeometryArrayRetained) triAry.retained;
- geomHash.put(ch, geo);
- }
-
- return geo;
- }
-
- private GeometryService newGeometryService() {
- final ServiceLoader<GeometryService> gsLoader =
- ServiceLoader.load(GeometryService.class);
-
- final Iterator<GeometryService> iter = gsLoader.iterator();
- if (iter.hasNext()) return iter.next();
-
- throw new IllegalStateException("No GeometryService implementation found. "
- + "Please add j3d-core-utils to the classpath.");
- }
-
- static boolean getNormal(Point3f p1, Point3f p2, Point3f p3, Vector3f normal) {
- Vector3f v1 = new Vector3f();
- Vector3f v2 = new Vector3f();
-
- // Must compute normal
- v1.sub(p2, p1);
- v2.sub(p2, p3);
- normal.cross(v1, v2);
- normal.negate();
-
- float length = normal.length();
-
- if (length > 0) {
- length = 1 / length;
- normal.x *= length;
- normal.y *= length;
- normal.z *= length;
- return true;
- }
- return false;
- }
-
-
- // check if 2 contours are inside/outside/intersect one another
- // INPUT:
- // vertCnt1, vertCnt2 - number of vertices in 2 contours
- // begin1, begin2 - starting indices into vertices for 2 contours
- // vertices - actual vertex data
- // OUTPUT:
- // status == 1 - intersecting contours
- // 2 - first contour inside the second
- // 3 - second contour inside the first
- // 0 - disjoint contours(2 islands)
-
- static int check2Contours(int begin1, int end1, int begin2, int end2,
- Point3f[] vertices) {
- int i;
- boolean inside2, inside1;
-
- inside2 = pointInPolygon2D(vertices[begin1].x, vertices[begin1].y,
- begin2, end2, vertices);
-
- for (i=begin1+1; i < end1;i++) {
- if (pointInPolygon2D(vertices[i].x, vertices[i].y,
- begin2, end2, vertices) != inside2) {
- return 1; //intersecting contours
- }
- }
-
- // Since we are using point in polygon test and not
- // line in polygon test. There are cases we miss the interesting
- // if we are not checking the reverse for all points. This happen
- // when two points form a line pass through a polygon but the two
- // points are outside of it.
-
- inside1 = pointInPolygon2D(vertices[begin2].x, vertices[begin2].y,
- begin1, end1, vertices);
-
- for (i=begin2+1; i < end2;i++) {
- if (pointInPolygon2D(vertices[i].x, vertices[i].y,
- begin1, end1, vertices) != inside1) {
- return 1; //intersecting contours
- }
- }
-
- if (!inside2) {
- if (!inside1) {
- return 0; // disjoint countours
- }
- // inside2 = false and inside1 = true
- return 3; // second contour inside first
- }
-
- // must be inside2 = true and inside1 = false
- // Note that it is not possible inside2 = inside1 = true
- // unless two contour overlap to each others.
- //
- return 2; // first contour inside second
- }
-
- // Test if 2D point (x,y) lies inside polygon represented by verts.
- // z-value of polygon vertices is ignored. Sent only to avoid data-copy.
- // Uses ray-shooting algorithm to compute intersections along +X axis.
- // This algorithm works for all polygons(concave, self-intersecting) and
- // is best solution here due to large number of polygon vertices.
- // Point is INSIDE if number of intersections is odd, OUTSIDE if number
- // of intersections is even.
- static boolean pointInPolygon2D(float x, float y, int begIdx, int endIdx,
- Point3f[] verts){
-
- int i, num_intersections = 0;
- float xi;
-
- for (i=begIdx;i < endIdx-1;i++) {
- if ((verts[i].y >= y && verts[i+1].y >= y) ||
- (verts[i].y < y && verts[i+1].y < y))
- continue;
-
- xi = verts[i].x + (verts[i].x - verts[i+1].x)*(y - verts[i].y)/
- (verts[i].y - verts[i+1].y);
-
- if (x < xi) num_intersections++;
- }
-
- // Check for segment from last vertex to first vertex.
-
- if (!((verts[i].y >= y && verts[begIdx].y >= y) ||
- (verts[i].y < y && verts[begIdx].y < y))) {
- xi = verts[i].x + (verts[i].x - verts[begIdx].x)*(y - verts[i].y)/
- (verts[i].y - verts[begIdx].y);
-
- if (x < xi) num_intersections++;
- }
-
- return ((num_intersections % 2) != 0);
- }
-
-
- static final boolean normalize(Vector3f v) {
- float len = v.length();
-
- if (len > 0) {
- len = 1.0f/len;
- v.x *= len;
- v.y *= len;
- v.z *= len;
- return true;
- }
- return false;
- }
-
-
- // A Tree of islands form based on contour, each parent's contour
- // enclosed all the child. We built this since Triangular fail to
- // handle the case of multiple concentrated contours. i.e. if
- // 4 contours A > B > C > D. Triangular will fail recongized
- // two island, one form by A & B and the other by C & D.
- // Using this tree we can separate out every 2 levels and pass
- // in to triangular to workaround its limitation.
- static private class IslandsNode {
-
-private ArrayList<IslandsNode> islandsList = null;
- int startIdx, endIdx;
-
- IslandsNode(int startIdx, int endIdx) {
- this.startIdx = startIdx;
- this.endIdx = endIdx;
- islandsList = null;
- }
-
-void addChild(IslandsNode node) {
- if (islandsList == null)
- islandsList = new ArrayList<IslandsNode>(5);
- islandsList.add(node);
-}
-
- void removeChild(IslandsNode node) {
- islandsList.remove(islandsList.indexOf(node));
- }
-
-IslandsNode getChild(int idx) {
- return islandsList.get(idx);
-}
-
- int numChild() {
- return (islandsList == null ? 0 : islandsList.size());
- }
-
- int numVertices() {
- return endIdx - startIdx;
- }
-
- void insert(IslandsNode newNode, Point3f[] vertices) {
- boolean createNewLevel = false;
-
- if (islandsList != null) {
- IslandsNode childNode;
- int status;
-
- for (int i=numChild()-1; i>=0; i--) {
- childNode = getChild(i);
- status = check2Contours(newNode.startIdx, newNode.endIdx,
- childNode.startIdx, childNode.endIdx,
- vertices);
- switch (status) {
- case 2: // newNode inside childNode, go down recursively
- childNode.insert(newNode, vertices);
- return;
- case 3:// childNode inside newNode,
- // continue to search other childNode also
- // inside this one and group them together.
- newNode.addChild(childNode);
- createNewLevel = true;
- break;
- default: // intersecting or disjoint
-
- }
- }
- }
-
- if (createNewLevel) {
- // Remove child in newNode from this
- for (int i=newNode.numChild()-1; i>=0; i--) {
- removeChild(newNode.getChild(i));
- }
- // Add the newNode to parent
- }
- addChild(newNode);
- }
-
- // Return a list of node with odd number of level
- void collectOddLevelNode(UnorderList list, int level) {
- if ((level % 2) == 1) {
- list.add(this);
- }
- if (islandsList != null) {
- level++;
- for (int i=numChild()-1; i>=0; i--) {
- getChild(i).collectOddLevelNode(list, level);
- }
- }
- }
- }
-}