diff options
author | Julien Gouesse <[email protected]> | 2015-11-28 15:11:48 +0100 |
---|---|---|
committer | Julien Gouesse <[email protected]> | 2015-11-28 15:11:48 +0100 |
commit | dbc98deea1884e44da2c74d6ea807253cdefa693 (patch) | |
tree | 29c3ee7dea82d7dd773d81f33f645dde67e43a17 /src/javax/media/j3d/Font3D.java | |
parent | 2c99f1329dc55bd496bce91b9aba956ecba3c67e (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.java | 1183 |
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); - } - } - } - } -} |