diff options
Diffstat (limited to 'src/jogl/classes/jogamp/graph')
43 files changed, 5834 insertions, 0 deletions
diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/RegionRendererImpl01.java b/src/jogl/classes/jogamp/graph/curve/opengl/RegionRendererImpl01.java new file mode 100755 index 000000000..c1f293fff --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/RegionRendererImpl01.java @@ -0,0 +1,206 @@ +/**
+ * Copyright 2010 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.graph.curve.opengl;
+
+import java.nio.FloatBuffer;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLException;
+import javax.media.opengl.GLUniformData;
+import javax.media.opengl.fixedfunc.GLMatrixFunc;
+
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.opengl.util.glsl.ShaderCode;
+import com.jogamp.opengl.util.glsl.ShaderProgram;
+import com.jogamp.opengl.util.glsl.ShaderState;
+
+
+public class RegionRendererImpl01 extends RegionRenderer {
+ /**Sharpness is equivalent to the value of t value of texture coord
+ * on the off-curve vertex. The high value of sharpness will
+ * result in high curvature.
+ */
+ private GLUniformData mgl_sharpness = new GLUniformData("p1y", 0.5f);
+ GLUniformData mgl_alpha = new GLUniformData("g_alpha", 1.0f);
+ private GLUniformData mgl_color = new GLUniformData("g_color", 3, FloatBuffer.allocate(3));
+ private GLUniformData mgl_strength = new GLUniformData("a_strength", 3.0f);
+
+ public RegionRendererImpl01(Vertex.Factory<? extends Vertex> factory, int type) {
+ super(factory, type);
+ }
+
+ protected boolean initImpl(GL2ES2 gl) {
+ boolean VBOsupported = gl.isFunctionAvailable("glGenBuffers") &&
+ gl.isFunctionAvailable("glBindBuffer") &&
+ gl.isFunctionAvailable("glBufferData") &&
+ gl.isFunctionAvailable("glDrawElements") &&
+ gl.isFunctionAvailable("glVertexAttribPointer") &&
+ gl.isFunctionAvailable("glDeleteBuffers");
+
+ if(DEBUG) {
+ System.err.println("RegionRenderer: VBO Supported = " + VBOsupported);
+ }
+
+ if(!VBOsupported){
+ return false;
+ }
+
+ gl.glEnable(GL2ES2.GL_BLEND);
+ gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA);
+
+ ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, RegionRendererImpl01.class,
+ "shader", "shader/bin", "curverenderer01");
+ ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, RegionRendererImpl01.class,
+ "shader", "shader/bin", "curverenderer01");
+
+ ShaderProgram sp = new ShaderProgram();
+ sp.add(rsVp);
+ sp.add(rsFp);
+
+ if(!sp.link(gl, System.err)) {
+ throw new GLException("RegionRenderer: Couldn't link program: "+sp);
+ }
+
+ st = new ShaderState();
+ st.attachShaderProgram(gl, sp);
+ gl.glBindAttribLocation(sp.id(), 0, "v_position");
+ gl.glBindAttribLocation(sp.id(), 1, "texCoord");
+
+ st.glUseProgram(gl, true);
+
+ pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
+ pmvMatrix.glLoadIdentity();
+ pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmvMatrix.glLoadIdentity();
+
+ pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
+ pmvMatrix.glLoadIdentity();
+ resetModelview(null);
+
+ mgl_PMVMatrix = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf());
+ if(!st.glUniform(gl, mgl_PMVMatrix)) {
+ if(DEBUG){
+ System.err.println("Error setting PMVMatrix in shader: "+st);
+ }
+ return false;
+ }
+
+ if(!st.glUniform(gl, mgl_sharpness)) {
+ if(DEBUG){
+ System.err.println("Error setting sharpness in shader: "+st);
+ }
+ return false;
+ }
+
+ if(!st.glUniform(gl, mgl_alpha)) {
+ if(DEBUG){
+ System.err.println("Error setting global alpha in shader: "+st);
+ }
+ return false;
+ }
+
+ if(!st.glUniform(gl, mgl_color)) {
+ if(DEBUG){
+ System.err.println("Error setting global color in shader: "+st);
+ }
+ return false;
+ }
+
+ if(!st.glUniform(gl, mgl_strength)) {
+ System.err.println("Error setting antialias strength in shader: "+st);
+ }
+
+ if(DEBUG) {
+ System.err.println("RegionRendererImpl01 initialized: " + Thread.currentThread()+" "+st);
+ }
+ return true;
+ }
+
+ @Override
+ protected void disposeImpl(GL2ES2 gl) {
+ }
+
+
+ @Override
+ public float getAlpha() {
+ return mgl_alpha.floatValue();
+ }
+
+ @Override
+ public void setAlpha(GL2ES2 gl, float alpha_t) {
+ mgl_alpha.setData(alpha_t);
+ if(null != gl && st.inUse()) {
+ st.glUniform(gl, mgl_alpha);
+ }
+ }
+
+ @Override
+ public void setColor(GL2ES2 gl, float r, float g, float b){
+ FloatBuffer fb = (FloatBuffer) mgl_color.getBuffer();
+ fb.put(0, r);
+ fb.put(1, r);
+ fb.put(2, r);
+ if(null != gl && st.inUse()) {
+ st.glUniform(gl, mgl_color);
+ }
+ }
+
+
+ @Override
+ public void renderOutlineShape(GL2ES2 gl, OutlineShape outlineShape, float[] position, int texSize) {
+ if(!isInitialized()){
+ throw new GLException("RegionRendererImpl01: not initialized!");
+ }
+ int hashCode = getHashCode(outlineShape);
+ Region region = regions.get(hashCode);
+
+ if(null == region) {
+ region = createRegion(gl, outlineShape, mgl_sharpness.floatValue());
+ regions.put(hashCode, region);
+ }
+ region.render(pmvMatrix, vp_width, vp_height, texSize);
+ }
+
+ @Override
+ public void renderOutlineShapes(GL2ES2 gl, OutlineShape[] outlineShapes, float[] position, int texSize) {
+ if(!isInitialized()){
+ throw new GLException("RegionRendererImpl01: not initialized!");
+ }
+
+ int hashCode = getHashCode(outlineShapes);
+ Region region = regions.get(hashCode);
+
+ if(null == region) {
+ region = createRegion(gl, outlineShapes, mgl_sharpness.floatValue());
+ regions.put(hashCode, region);
+ }
+ region.render(pmvMatrix, vp_width, vp_height, texSize);
+ }
+}
diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/TextRendererImpl01.java b/src/jogl/classes/jogamp/graph/curve/opengl/TextRendererImpl01.java new file mode 100644 index 000000000..cebe7a19e --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/TextRendererImpl01.java @@ -0,0 +1,188 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.opengl; + +import java.nio.FloatBuffer; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import jogamp.graph.curve.text.GlyphString; + +import com.jogamp.graph.curve.opengl.TextRenderer; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.geom.Vertex; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; + +public class TextRendererImpl01 extends TextRenderer { + /**Sharpness is equivalent to the value of t value of texture coord + * on the off-curve vertex. The high value of sharpness will + * result in high curvature. + */ + private GLUniformData mgl_sharpness = new GLUniformData("p1y", 0.5f); + GLUniformData mgl_alpha = new GLUniformData("g_alpha", 1.0f); + private GLUniformData mgl_color = new GLUniformData("g_color", 3, FloatBuffer.allocate(3)); + private GLUniformData mgl_strength = new GLUniformData("a_strength", 1.8f); + + public TextRendererImpl01(Vertex.Factory<? extends Vertex> factory, int type) { + super(factory, type); + } + + @Override + protected boolean initImpl(GL2ES2 gl){ + boolean VBOsupported = gl.isFunctionAvailable("glGenBuffers") && + gl.isFunctionAvailable("glBindBuffer") && + gl.isFunctionAvailable("glBufferData") && + gl.isFunctionAvailable("glDrawElements") && + gl.isFunctionAvailable("glVertexAttribPointer") && + gl.isFunctionAvailable("glDeleteBuffers"); + + if(DEBUG) { + System.err.println("TextRendererImpl01: VBO Supported = " + VBOsupported); + } + + if(!VBOsupported){ + return false; + } + + gl.glEnable(GL2ES2.GL_BLEND); + gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); + + ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, TextRendererImpl01.class, + "shader", "shader/bin", "curverenderer01"); + ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, TextRendererImpl01.class, + "shader", "shader/bin", "curverenderer01"); + + ShaderProgram sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + + if(!sp.link(gl, System.err)) { + throw new GLException("TextRendererImpl01: Couldn't link program: "+sp); + } + + st.attachShaderProgram(gl, sp); + gl.glBindAttribLocation(sp.id(), 0, "v_position"); + gl.glBindAttribLocation(sp.id(), 1, "texCoord"); + + st.glUseProgram(gl, true); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + resetModelview(null); + + mgl_PMVMatrix = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); + if(!st.glUniform(gl, mgl_PMVMatrix)) { + if(DEBUG){ + System.err.println("Error setting PMVMatrix in shader: "+st); + } + return false; + } + + if(!st.glUniform(gl, mgl_sharpness)) { + if(DEBUG){ + System.err.println("Error setting sharpness in shader: "+st); + } + return false; + } + + if(!st.glUniform(gl, mgl_alpha)) { + if(DEBUG){ + System.err.println("Error setting global alpha in shader: "+st); + } + return false; + } + + if(!st.glUniform(gl, mgl_color)) { + if(DEBUG){ + System.err.println("Error setting global color in shader: "+st); + } + return false; + } + + if(!st.glUniform(gl, mgl_strength)) { + System.err.println("Error setting antialias strength in shader: "+st); + } + + if(DEBUG) { + System.err.println("TextRendererImpl01 initialized: " + Thread.currentThread()+" "+st); + } + return true; + } + + @Override + protected void disposeImpl(GL2ES2 gl) { + } + + @Override + public float getAlpha() { + return mgl_alpha.floatValue(); + } + + @Override + public void setAlpha(GL2ES2 gl, float alpha_t) { + mgl_alpha.setData(alpha_t); + if(null != gl && st.inUse()) { + st.glUniform(gl, mgl_alpha); + } + } + + @Override + public void setColor(GL2ES2 gl, float r, float g, float b){ + FloatBuffer fb = (FloatBuffer) mgl_color.getBuffer(); + fb.put(0, r); + fb.put(1, r); + fb.put(2, r); + if(null != gl && st.inUse()) { + st.glUniform(gl, mgl_color); + } + } + + @Override + public void renderString3D(GL2ES2 gl, Font font, String str, float[] position, int fontSize, int texSize) { + if(!isInitialized()){ + throw new GLException("TextRendererImpl01: not initialized!"); + } + GlyphString glyphString = getCachedGlyphString(font, str, fontSize); + if(null == glyphString) { + glyphString = createString(gl, font, fontSize, str, mgl_sharpness.floatValue()); + addCachedGlyphString(font, str, fontSize, glyphString); + } + + glyphString.renderString3D(pmvMatrix, vp_width, vp_height, texSize); + } + +} diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PES2.java new file mode 100644 index 000000000..c7c370f6d --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PES2.java @@ -0,0 +1,385 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.opengl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; + +import javax.media.opengl.GL2ES2; +// FIXME: Subsume GL2GL3.GL_DRAW_FRAMEBUFFER -> GL2ES2.GL_DRAW_FRAMEBUFFER ! +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.common.nio.Buffers; + +import com.jogamp.graph.geom.AABBox; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.Vertex; + +import com.jogamp.graph.curve.Region; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class VBORegion2PES2 implements Region{ + private int numVertices = 0; + private IntBuffer vboIds; + + private IntBuffer t_vboIds; + + private ArrayList<Triangle> triangles = new ArrayList<Triangle>(); + private ArrayList<Vertex> vertices = new ArrayList<Vertex>(); + private GLContext context; + + private int numBuffers = 3; + + private boolean flipped = false; + + private boolean dirty = false; + + private AABBox box = null; + private int[] texture = { 0 } ; + private int[] fbo = { 0 } ; + private int[] rbo_depth = { 0 } ; + private boolean texInitialized = false; + + private int tex_width_c = 0; + private int tex_height_c = 0; + + private ShaderState st; + + public VBORegion2PES2(GLContext context, ShaderState st){ + this.context =context; + this.st = st; + } + + public void update(){ + box = new AABBox(); + + GL2ES2 gl = context.getGL().getGL2ES2(); + ShortBuffer indicies = Buffers.newDirectShortBuffer(triangles.size() * 3); + + for(Triangle t:triangles){ + if(t.getVertices()[0].getId() == Integer.MAX_VALUE){ + t.getVertices()[0].setId(numVertices++); + t.getVertices()[1].setId(numVertices++); + t.getVertices()[2].setId(numVertices++); + + vertices.add(t.getVertices()[0]); + vertices.add(t.getVertices()[1]); + vertices.add(t.getVertices()[2]); + + indicies.put((short) t.getVertices()[0].getId()); + indicies.put((short) t.getVertices()[1].getId()); + indicies.put((short) t.getVertices()[2].getId()); + } + else{ + Vertex v1 = t.getVertices()[0]; + Vertex v2 = t.getVertices()[1]; + Vertex v3 = t.getVertices()[2]; + + indicies.put((short) v1.getId()); + indicies.put((short) v2.getId()); + indicies.put((short) v3.getId()); + } + } + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 3); + for(Vertex v:vertices){ + verticesBuffer.put(v.getX()); + if(flipped){ + verticesBuffer.put(-1*v.getY()); + } + else{ + verticesBuffer.put(v.getY()); + } + verticesBuffer.put(v.getZ()); + if(flipped){ + box.resize(v.getX(), -1*v.getY(), v.getZ()); + } + else{ + box.resize(v.getX(), v.getY(), v.getZ()); + } + } + verticesBuffer.rewind(); + + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 2); + for(Vertex v:vertices){ + float[] tex = v.getTexCoord(); + texCoordBuffer.put(tex[0]); + texCoordBuffer.put(tex[1]); + } + texCoordBuffer.rewind(); + + vboIds = IntBuffer.allocate(numBuffers); + gl.glGenBuffers(numBuffers, vboIds); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); // vertices + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); //texture + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); //triangles + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, triangles.size()* 3 * Buffers.SIZEOF_SHORT, indicies, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + + dirty = false; + } + + public void render(PMVMatrix matrix, int vp_width, int vp_height, int width){ + if(null == matrix || vp_width <=0 || vp_height <= 0 || width <= 0){ + renderRegion(); + } + else { + if(width != tex_width_c){ + texInitialized = false; + tex_width_c = width; + } + if(!texInitialized){ + initFBOTexture(matrix,vp_width, vp_height); + texInitialized = true; + } +// System.out.println("Scale: " + matrix.glGetMatrixf().get(1+4*3) +" " + matrix.glGetMatrixf().get(2+4*3)); + renderTexture(matrix, vp_width, vp_height); + } + } + + private void renderTexture(PMVMatrix matrix, int width, int hight){ + GL2ES2 gl = context.getGL().getGL2ES2(); + gl.glViewport(0, 0, width, hight); + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, matrix.glGetPMvMatrixf()))){ + System.out.println("Cnt set tex based mat"); + } + gl.glEnable(GL2ES2.GL_TEXTURE_2D); + gl.glActiveTexture(GL2ES2.GL_TEXTURE0); + gl.glBindTexture(GL2ES2.GL_TEXTURE_2D, texture[0]); + + st.glUniform(gl, new GLUniformData("texture", texture[0])); + int loc = gl.glGetUniformLocation(st.shaderProgram().id(), "texture"); + gl.glUniform1i(loc, 0); + + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, t_vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_ATTR_IDX); + gl.glVertexAttribPointer(VERTEX_ATTR_IDX, 3, GL2ES2.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, t_vboIds.get(1)); + gl.glEnableVertexAttribArray(TEXCOORD_ATTR_IDX); + gl.glVertexAttribPointer(TEXCOORD_ATTR_IDX, 2, GL2ES2.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, t_vboIds.get(2)); + gl.glDrawElements(GL2ES2.GL_TRIANGLES, 2 * 3, GL2ES2.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + } + + private void setupBoundingBuffers(){ + GL2ES2 gl = context.getGL().getGL2ES2(); + + ShortBuffer indicies = Buffers.newDirectShortBuffer(6); + indicies.put((short) 0); indicies.put((short) 1); indicies.put((short) 3); + indicies.put((short) 1); indicies.put((short) 2); indicies.put((short) 3); + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(4 * 3); + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(4 * 2); + + verticesBuffer.put(box.getLow()[0]); + verticesBuffer.put(box.getLow()[1]); + verticesBuffer.put(box.getLow()[2]); + texCoordBuffer.put(5); + texCoordBuffer.put(5); + + verticesBuffer.put(box.getLow()[0]); + verticesBuffer.put(box.getHigh()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(5); + texCoordBuffer.put(6); + + verticesBuffer.put(box.getHigh()[0]); + verticesBuffer.put(box.getHigh()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(6); + texCoordBuffer.put(6); + + verticesBuffer.put(box.getHigh()[0]); + verticesBuffer.put(box.getLow()[1]); + verticesBuffer.put(box.getLow()[2]); + + texCoordBuffer.put(6); + texCoordBuffer.put(5); + + verticesBuffer.rewind(); + texCoordBuffer.rewind(); + + t_vboIds = IntBuffer.allocate(3); + gl.glGenBuffers(numBuffers, t_vboIds); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, t_vboIds.get(0)); // vertices + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, 4 * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, t_vboIds.get(1)); //texture + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, 4 * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, t_vboIds.get(2)); //triangles + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 4 * 3 * Buffers.SIZEOF_SHORT, indicies, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + private void initFBOTexture(PMVMatrix m, int width, int hight){ + tex_height_c = (int)(tex_width_c*box.getHeight()/box.getWidth()); + // tex_height_c = tex_width_c; + System.out.println("FBO Size: "+tex_height_c+"x"+tex_width_c); + System.out.println("FBO Scale: " + m.glGetMatrixf().get(0) +" " + m.glGetMatrixf().get(5)); + GL2ES2 gl = context.getGL().getGL2ES2(); + + if(fbo[0] > 0) { + gl.glDeleteFramebuffers(1, fbo, 0); + fbo[0] = 0; + } + if(texture[0]>0) { + gl.glDeleteTextures(1, texture, 0); + texture[0] = 0; + } + + gl.glGenFramebuffers(1, fbo, 0); + gl.glGenTextures(1, texture, 0); + gl.glGenRenderbuffers(1,rbo_depth, 0); + System.out.println("FBO: fbo " + fbo[0] + ", tex " + texture[0] + ", depth " + rbo_depth[0]); + + gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, fbo[0]); + gl.glBindTexture(GL2ES2.GL_TEXTURE_2D, texture[0]); + gl.glTexImage2D(GL2ES2.GL_TEXTURE_2D, 0, GL2ES2.GL_RGBA, tex_width_c, + tex_height_c, 0, GL2ES2.GL_RGBA, GL2ES2.GL_UNSIGNED_BYTE, null); + + gl.glTexParameterf(GL2ES2.GL_TEXTURE_2D, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2ES2.GL_LINEAR); + gl.glTexParameterf(GL2ES2.GL_TEXTURE_2D, GL2ES2.GL_TEXTURE_MAG_FILTER, GL2ES2.GL_LINEAR); + gl.glTexParameterf(GL2ES2.GL_TEXTURE_2D, GL2ES2.GL_TEXTURE_WRAP_S, GL2ES2.GL_CLAMP_TO_EDGE); + gl.glTexParameterf(GL2ES2.GL_TEXTURE_2D, GL2ES2.GL_TEXTURE_WRAP_T, GL2ES2.GL_CLAMP_TO_EDGE); + + gl.glFramebufferTexture2D(GL2GL3.GL_DRAW_FRAMEBUFFER, GL2ES2.GL_COLOR_ATTACHMENT0, + GL2ES2.GL_TEXTURE_2D, texture[0], 0); + + // Set up the depth buffer + gl.glBindRenderbuffer(GL2ES2.GL_RENDERBUFFER, rbo_depth[0]); + gl.glRenderbufferStorage(GL2ES2.GL_RENDERBUFFER, GL2ES2.GL_DEPTH_COMPONENT, tex_width_c, tex_height_c); + gl.glFramebufferRenderbuffer(GL2ES2.GL_FRAMEBUFFER, GL2ES2.GL_DEPTH_COMPONENT, GL2ES2.GL_RENDERBUFFER, rbo_depth[0]); + + int status = gl.glCheckFramebufferStatus(GL2ES2.GL_FRAMEBUFFER); + if(status != GL2ES2.GL_FRAMEBUFFER_COMPLETE){ + System.err.println("Cant Create R2T pass!"); + } + + //render texture + PMVMatrix tex_matrix = new PMVMatrix(); + gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, fbo[0]); + gl.glViewport(0, 0, tex_width_c, tex_height_c); + tex_matrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + tex_matrix.glLoadIdentity(); + tex_matrix.glOrthof(box.getLow()[0], box.getHigh()[0], box.getLow()[1], box.getHigh()[1], -1, 1); + + if(!st.glUniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, tex_matrix.glGetPMvMatrixf()))){ + System.out.println("Cnt set tex based mat"); + } + + gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_DEPTH_BUFFER_BIT); + renderRegion(); + + gl.glBindFramebuffer(GL2ES2.GL_FRAMEBUFFER, 0); + gl.glBindTexture(GL2ES2.GL_TEXTURE_2D, 0); + + setupBoundingBuffers(); + } + + private void renderRegion(){ + GL2ES2 gl = context.getGL().getGL2ES2(); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_ATTR_IDX); + gl.glVertexAttribPointer(VERTEX_ATTR_IDX, 3, GL2ES2.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); + gl.glEnableVertexAttribArray(TEXCOORD_ATTR_IDX); + gl.glVertexAttribPointer(TEXCOORD_ATTR_IDX, 2, GL2ES2.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); + gl.glDrawElements(GL2ES2.GL_TRIANGLES, triangles.size() * 3, GL2ES2.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + } + + public void addTriangles(ArrayList<Triangle> tris) { + triangles.addAll(tris); + dirty = true; + } + + public int getNumVertices(){ + return numVertices; + } + + public void addVertices(ArrayList<Vertex> verts){ + vertices.addAll(verts); + numVertices = vertices.size(); + dirty = true; + } + + public boolean isDirty(){ + return dirty; + } + + public void destroy() { + GL2ES2 gl = context.getGL().getGL2ES2(); + gl.glDeleteBuffers(numBuffers, vboIds); + gl.glDeleteFramebuffers(1, fbo, 0); + fbo[0] = 0; + gl.glDeleteTextures(1, texture, 0); + texture[0] = 0; + gl.glDeleteRenderbuffers(1, rbo_depth, 0); + rbo_depth[0] = 0; + } + + public boolean isFlipped() { + return flipped; + } + + public void setFlipped(boolean flipped) { + this.flipped = flipped; + } +} diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java new file mode 100644 index 000000000..701549d46 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegionSPES2.java @@ -0,0 +1,185 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.opengl; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLContext; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.opengl.util.PMVMatrix; + +public class VBORegionSPES2 implements Region{ + private int numVertices = 0; + private IntBuffer vboIds; + + private ArrayList<Triangle> triangles = new ArrayList<Triangle>(); + private ArrayList<Vertex> vertices = new ArrayList<Vertex>(); + + private GLContext context; + + private int numBuffers = 3; + + private boolean flipped = false; + private boolean dirty = false; + + public VBORegionSPES2(GLContext context){ + this.context =context; + } + + public void update(){ + GL2ES2 gl = context.getGL().getGL2ES2(); + ShortBuffer indicies = Buffers.newDirectShortBuffer(triangles.size() * 3); + + for(Triangle t:triangles){ + final Vertex[] t_vertices = t.getVertices(); + + if(t_vertices[0].getId() == Integer.MAX_VALUE){ + t_vertices[0].setId(numVertices++); + t_vertices[1].setId(numVertices++); + t_vertices[2].setId(numVertices++); + + vertices.add(t.getVertices()[0]); + vertices.add(t.getVertices()[1]); + vertices.add(t.getVertices()[2]); + + indicies.put((short) t.getVertices()[0].getId()); + indicies.put((short) t.getVertices()[1].getId()); + indicies.put((short) t.getVertices()[2].getId()); + } + else{ + Vertex v1 = t_vertices[0]; + Vertex v2 = t_vertices[1]; + Vertex v3 = t_vertices[2]; + + indicies.put((short) v1.getId()); + indicies.put((short) v2.getId()); + indicies.put((short) v3.getId()); + } + } + indicies.rewind(); + + FloatBuffer verticesBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 3); + for(Vertex v:vertices){ + verticesBuffer.put(v.getX()); + if(flipped){ + verticesBuffer.put(-1*v.getY()); + } + else{ + verticesBuffer.put(v.getY()); + } + verticesBuffer.put(v.getZ()); + } + verticesBuffer.rewind(); + + FloatBuffer texCoordBuffer = Buffers.newDirectFloatBuffer(vertices.size() * 2); + for(Vertex v:vertices){ + float[] tex = v.getTexCoord(); + texCoordBuffer.put(tex[0]); + texCoordBuffer.put(tex[1]); + } + texCoordBuffer.rewind(); + + vboIds = IntBuffer.allocate(numBuffers); + gl.glGenBuffers(numBuffers, vboIds); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); // vertices + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 3 * Buffers.SIZEOF_FLOAT, verticesBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); //texture + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, numVertices * 2 * Buffers.SIZEOF_FLOAT, texCoordBuffer, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); //triangles + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, triangles.size()* 3 * Buffers.SIZEOF_SHORT, indicies, GL2ES2.GL_STATIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + + dirty = false; + } + + private void render() { + GL2ES2 gl = context.getGL().getGL2ES2(); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(0)); + gl.glEnableVertexAttribArray(VERTEX_ATTR_IDX); + gl.glVertexAttribPointer(VERTEX_ATTR_IDX, 3, GL2ES2.GL_FLOAT, false, 3 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, vboIds.get(1)); + gl.glEnableVertexAttribArray(TEXCOORD_ATTR_IDX); + gl.glVertexAttribPointer(TEXCOORD_ATTR_IDX, 2, GL2ES2.GL_FLOAT, false, 2 * Buffers.SIZEOF_FLOAT, 0); + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2)); + gl.glDrawElements(GL2ES2.GL_TRIANGLES, triangles.size() * 3, GL2ES2.GL_UNSIGNED_SHORT, 0); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, 0); + } + + public void render(PMVMatrix matrix, int vp_width, int vp_height, int width){ + render(); + } + + public void addTriangles(ArrayList<Triangle> tris) { + triangles.addAll(tris); + dirty = true; + } + + public int getNumVertices(){ + return numVertices; + } + + public void addVertices(ArrayList<Vertex> verts){ + vertices.addAll(verts); + numVertices = vertices.size(); + dirty = true; + } + + public boolean isDirty(){ + return dirty; + } + + public void destroy() { + GL2ES2 gl = context.getGL().getGL2ES2(); + gl.glDeleteBuffers(numBuffers, vboIds); + } + + public boolean isFlipped() { + return flipped; + } + + public void setFlipped(boolean flipped) { + this.flipped = flipped; + } +} diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.fp b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.fp new file mode 100644 index 000000000..2b3a0ce1d --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.fp @@ -0,0 +1,99 @@ +//#version 100 + +uniform float p1y; +uniform float g_alpha; +uniform vec3 g_color; +uniform float a_strength; + +varying vec2 v_texCoord; + +vec3 b_color = vec3(0.0, 0.0, 0.0); + +uniform sampler2D texture; +vec4 weights = vec4(0.075, 0.06, 0.045, 0.025); + +void main (void) +{ + vec2 rtex = vec2(abs(v_texCoord.x),abs(v_texCoord.y)); + vec3 c = g_color; + + float alpha = 0.0; + + if((v_texCoord.x == 0.0) && (v_texCoord.y == 0.0)){ + alpha = g_alpha; + } + else if((v_texCoord.x >= 5.0)){ + vec2 dfx = dFdx(v_texCoord); + vec2 dfy = dFdy(v_texCoord); + + vec2 size = 1.0/textureSize(texture,0); //version 130 + rtex -= 5.0; + vec4 t = texture2D(texture, rtex)* 0.18; + + t += texture2D(texture, rtex + size*(vec2(1, 0)))*weights.x; + t += texture2D(texture, rtex - size*(vec2(1, 0)))*weights.x; + t += texture2D(texture, rtex + size*(vec2(0, 1)))*weights.x; + t += texture2D(texture, rtex - size*(vec2(0, 1)))*weights.x; + + t += texture2D(texture, rtex + 2.0*size*(vec2(1, 0))) *weights.y; + t += texture2D(texture, rtex - 2.0*size*(vec2(1, 0)))*weights.y; + t += texture2D(texture, rtex + 2.0*size*(vec2(0, 1)))*weights.y; + t += texture2D(texture, rtex - 2.0*size*(vec2(0, 1)))*weights.y; + + t += texture2D(texture, rtex + 3.0*size*(vec2(1, 0))) *weights.z; + t += texture2D(texture, rtex - 3.0*size*(vec2(1, 0)))*weights.z; + t += texture2D(texture, rtex + 3.0*size*(vec2(0, 1)))*weights.z; + t += texture2D(texture, rtex - 3.0*size*(vec2(0, 1)))*weights.z; + + t += texture2D(texture, rtex + 4.0*size*(vec2(1, 0))) *weights.w; + t += texture2D(texture, rtex - 4.0*size*(vec2(1, 0)))*weights.w; + t += texture2D(texture, rtex + 4.0*size*(vec2(0, 1)))*weights.w; + t += texture2D(texture, rtex - 4.0*size*(vec2(0, 1)))*weights.w; + + if(t.w == 0.0){ + discard; + } + + c = t.xyz; + alpha = g_alpha* t.w; + } + /////////////////////////////////////////////////////////// + else if ((v_texCoord.x > 0.0) && (rtex.y > 0.0 || rtex.x == 1.0)){ + vec2 dtx = dFdx(rtex); + vec2 dty = dFdy(rtex); + + rtex.y -= 0.1; + if(rtex.y < 0.0) { + if(v_texCoord.y < 0.0) + discard; + else{ + rtex.y = 0.0; + } + } + + vec2 f = vec2((dtx.y - 2.0*p1y*dtx.x + 4.0*p1y*rtex.x*dtx.x), (dty.y - 2.0*p1y*dty.x + 4.0*p1y*rtex.x*dty.x)); + + float position = rtex.y - ((2.0 * rtex.x * p1y) * (1.0 - rtex.x)); + float d = position/(length(f)); + + float a = (0.5 - d * sign(v_texCoord.y)); + + + if (a >= 1.0) { + alpha = g_alpha; + // c = vec3(1.0,1.0,1.0); + } + else if (a <= 0.0) { + alpha = 0.0;//discard; + // c = vec3(0.0,0.0,0.0); + + } + else { + alpha = g_alpha*a; + // c = vec3(a,a,a); + mix(b_color,g_color, a); + } + } + + gl_FragColor = vec4(c, alpha); +} diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.vp b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.vp new file mode 100644 index 000000000..bc9ecb41e --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/opengl/shader/curverenderer01.vp @@ -0,0 +1,13 @@ +//#version 100 + +uniform mat4 mgl_PMVMatrix[2]; +attribute vec4 v_position; +attribute vec2 texCoord; + +varying vec2 v_texCoord; + +void main(void) +{ + gl_Position = mgl_PMVMatrix[0] * mgl_PMVMatrix[1] * v_position; + v_texCoord = texCoord.st; +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java b/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java new file mode 100644 index 000000000..5dae296e5 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java @@ -0,0 +1,81 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Outline; +import com.jogamp.graph.geom.Vertex; + +public class GraphOutline { + final private Outline outline; + final private ArrayList<GraphVertex> controlpoints = new ArrayList<GraphVertex>(3); + + public GraphOutline(){ + this.outline = new Outline(); + } + + /**Create a control polyline of control vertices + * the curve pieces can be identified by onCurve flag + * of each cp the control polyline is open by default + */ + public GraphOutline(Outline ol){ + this.outline = ol; + ArrayList<Vertex> vertices = this.outline.getVertices(); + for(Vertex v:vertices){ + this.controlpoints.add(new GraphVertex(v)); + } + } + + public Outline getOutline() { + return outline; + } + + /*public void setOutline(Outline<T> outline) { + this.outline = outline; + }*/ + + + public ArrayList<GraphVertex> getGraphPoint() { + return controlpoints; + } + + public ArrayList<Vertex> getPoints() { + return outline.getVertices(); + } + + /*public void setControlpoints(ArrayList<GraphPoint<T>> controlpoints) { + this.controlpoints = controlpoints; + }*/ + + public void addVertex(GraphVertex v) { + controlpoints.add(v); + outline.addVertex(v.getPoint()); + } + +} diff --git a/src/jogl/classes/jogamp/graph/curve/tess/GraphVertex.java b/src/jogl/classes/jogamp/graph/curve/tess/GraphVertex.java new file mode 100644 index 000000000..b9f95a0e7 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/tess/GraphVertex.java @@ -0,0 +1,120 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Vertex; + +public class GraphVertex { + private Vertex point; + private ArrayList<HEdge> edges = null; + private boolean boundaryContained = false; + + public GraphVertex(Vertex point) { + this.point = point; + } + + public Vertex getPoint() { + return point; + } + + public float getX(){ + return point.getX(); + } + + public float getY(){ + return point.getY(); + } + + public float getZ(){ + return point.getZ(); + } + public float[] getCoord() { + return point.getCoord(); + } + + public void setPoint(Vertex point) { + this.point = point; + } + + public ArrayList<HEdge> getEdges() { + return edges; + } + + public void setEdges(ArrayList<HEdge> edges) { + this.edges = edges; + } + + public void addEdge(HEdge edge){ + if(edges == null){ + edges = new ArrayList<HEdge>(); + } + edges.add(edge); + } + public void removeEdge(HEdge edge){ + if(edges == null) + return; + edges.remove(edge); + if(edges.size() == 0){ + edges = null; + } + } + public HEdge findNextEdge(GraphVertex nextVert){ + for(HEdge e:edges){ + if(e.getNext().getGraphPoint() == nextVert){ + return e; + } + } + return null; + } + public HEdge findBoundEdge(){ + for(HEdge e:edges){ + if((e.getType() == HEdge.BOUNDARY) || (e.getType() == HEdge.HOLE)){ + return e; + } + } + return null; + } + public HEdge findPrevEdge(GraphVertex prevVert){ + for(HEdge e:edges){ + if(e.getPrev().getGraphPoint() == prevVert){ + return e; + } + } + return null; + } + + public boolean isBoundaryContained() { + return boundaryContained; + } + + public void setBoundaryContained(boolean boundaryContained) { + this.boundaryContained = boundaryContained; + } +} diff --git a/src/jogl/classes/jogamp/graph/curve/tess/HEdge.java b/src/jogl/classes/jogamp/graph/curve/tess/HEdge.java new file mode 100644 index 000000000..d1bcc6e17 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/tess/HEdge.java @@ -0,0 +1,130 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Triangle; + + +public class HEdge { + public static int BOUNDARY = 3; + public static int INNER = 1; + public static int HOLE = 2; + + private GraphVertex vert; + private HEdge prev = null; + private HEdge next = null; + private HEdge sibling = null; + private int type = BOUNDARY; + private Triangle triangle = null; + + public HEdge(GraphVertex vert, int type) { + this.vert = vert; + this.type = type; + } + + public HEdge(GraphVertex vert, HEdge prev, HEdge next, HEdge sibling, int type) { + this.vert = vert; + this.prev = prev; + this.next = next; + this.sibling = sibling; + this.type = type; + } + + public HEdge(GraphVertex vert, HEdge prev, HEdge next, HEdge sibling, int type, Triangle triangle) { + this.vert = vert; + this.prev = prev; + this.next = next; + this.sibling = sibling; + this.type = type; + this.triangle = triangle; + } + + public GraphVertex getGraphPoint() { + return vert; + } + + public void setVert(GraphVertex vert) { + this.vert = vert; + } + + public HEdge getPrev() { + return prev; + } + + public void setPrev(HEdge prev) { + this.prev = prev; + } + + public HEdge getNext() { + return next; + } + + public void setNext(HEdge next) { + this.next = next; + } + + public HEdge getSibling() { + return sibling; + } + + public void setSibling(HEdge sibling) { + this.sibling = sibling; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Triangle getTriangle() { + return triangle; + } + + public void setTriangle(Triangle triangle) { + this.triangle = triangle; + } + + public static <T extends Vertex> void connect(HEdge first, HEdge next){ + first.setNext(next); + next.setPrev(first); + } + + public static <T extends Vertex> void makeSiblings(HEdge first, HEdge second){ + first.setSibling(second); + second.setSibling(first); + } + + public boolean vertexOnCurveVertex(){ + return vert.getPoint().isOnCurve(); + } + +} diff --git a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java new file mode 100644 index 000000000..fd7736a20 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java @@ -0,0 +1,373 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.tess; + +import java.util.ArrayList; + + +import com.jogamp.graph.geom.AABBox; +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.math.VectorUtil; + +public class Loop { + private HEdge root = null; + private AABBox box = new AABBox(); + private GraphOutline initialOutline = null; + + public Loop(GraphOutline polyline, int direction){ + initialOutline = polyline; + this.root = initFromPolyline(initialOutline, direction); + } + + public HEdge getHEdge(){ + return root; + } + + public Triangle cut(boolean delaunay){ + if(isSimplex()){ + Triangle t = new Triangle(root.getGraphPoint().getPoint(), root.getNext().getGraphPoint().getPoint(), + root.getNext().getNext().getGraphPoint().getPoint()); + t.setVerticesBoundary(checkVerticesBoundary(root)); + return t; + } + HEdge prev = root.getPrev(); + HEdge next1 = root.getNext(); + + HEdge next2 = findClosestValidNeighbor(next1.getNext(), delaunay); + if(next2 == null){ + root = root.getNext(); + return null; + } + + GraphVertex v1 = root.getGraphPoint(); + GraphVertex v2 = next1.getGraphPoint(); + GraphVertex v3 = next2.getGraphPoint(); + + HEdge v3Edge = new HEdge(v3, HEdge.INNER); + + HEdge.connect(v3Edge, root); + HEdge.connect(next1, v3Edge); + + HEdge v3EdgeSib = v3Edge.getSibling(); + if(v3EdgeSib == null){ + v3EdgeSib = new HEdge(v3Edge.getNext().getGraphPoint(), HEdge.INNER); + HEdge.makeSiblings(v3Edge, v3EdgeSib); + } + + HEdge.connect(prev, v3EdgeSib); + HEdge.connect(v3EdgeSib, next2); + + Triangle t = createTriangle(v1.getPoint(), v2.getPoint(), v3.getPoint(), root); + this.root = next2; + return t; + } + + public boolean isSimplex(){ + return (root.getNext().getNext().getNext() == root); + } + + /**Create a connected list of half edges (loop) + * from the boundary profile + * @param direction requested winding of edges (CCW or CW) + */ + private HEdge initFromPolyline(GraphOutline outline, int direction){ + ArrayList<GraphVertex> vertices = outline.getGraphPoint(); + + if(vertices.size()<3) { + throw new IllegalArgumentException("outline's vertices < 3: " + vertices.size()); + } + boolean isCCW = VectorUtil.ccw(vertices.get(0).getPoint(), vertices.get(1).getPoint(), + vertices.get(2).getPoint()); + boolean invert = isCCW && (direction == VectorUtil.CW); + + HEdge firstEdge = null; + HEdge lastEdge = null; + int index =0; + int max = vertices.size(); + + int edgeType = HEdge.BOUNDARY; + if(invert){ + index = vertices.size() -1; + max = -1; + edgeType = HEdge.HOLE; + } + + while(index != max){ + GraphVertex v1 = vertices.get(index); + box.resize(v1.getX(), v1.getY(), v1.getZ()); + + HEdge edge = new HEdge(v1, edgeType); + + v1.addEdge(edge); + if(lastEdge != null){ + lastEdge.setNext(edge); + edge.setPrev(lastEdge); + } + else{ + firstEdge = edge; + } + + if(!invert){ + if(index == vertices.size()-1){ + edge.setNext(firstEdge); + firstEdge.setPrev(edge); + } + } + else if (index == 0){ + edge.setNext(firstEdge); + firstEdge.setPrev(edge); + } + + lastEdge = edge; + + if(!invert){ + index++; + } + else{ + index--; + } + } + return firstEdge; + } + + public void addConstraintCurve(GraphOutline polyline) { + // GraphOutline outline = new GraphOutline(polyline); + /**needed to generate vertex references.*/ + initFromPolyline(polyline, VectorUtil.CW); + + GraphVertex v3 = locateClosestVertex(polyline); + HEdge v3Edge = v3.findBoundEdge(); + HEdge v3EdgeP = v3Edge.getPrev(); + HEdge crossEdge = new HEdge(root.getGraphPoint(), HEdge.INNER); + + HEdge.connect(root.getPrev(), crossEdge); + HEdge.connect(crossEdge, v3Edge); + + HEdge crossEdgeSib = crossEdge.getSibling(); + if(crossEdgeSib == null) { + crossEdgeSib = new HEdge(crossEdge.getNext().getGraphPoint(), HEdge.INNER); + HEdge.makeSiblings(crossEdge, crossEdgeSib); + } + + HEdge.connect(v3EdgeP, crossEdgeSib); + HEdge.connect(crossEdgeSib, root); + } + + /** Locates the vertex and update the loops root + * to have (root + vertex) as closest pair + * @param polyline the control polyline + * to search for closestvertices + * @return the vertex that is closest to the newly set root Hedge. + */ + private GraphVertex locateClosestVertex(GraphOutline polyline) { + HEdge closestE = null; + GraphVertex closestV = null; + + float minDistance = Float.MAX_VALUE; + boolean inValid = false; + ArrayList<GraphVertex> initVertices = initialOutline.getGraphPoint(); + ArrayList<GraphVertex> vertices = polyline.getGraphPoint(); + + for(int i=0; i< initVertices.size()-1; i++){ + GraphVertex v = initVertices.get(i); + GraphVertex nextV = initVertices.get(i+1); + for(GraphVertex cand:vertices){ + float distance = VectorUtil.computeLength(v.getCoord(), cand.getCoord()); + if(distance < minDistance){ + for (GraphVertex vert:vertices){ + if(vert == v || vert == nextV || vert == cand) + continue; + inValid = VectorUtil.inCircle(v.getPoint(), nextV.getPoint(), + cand.getPoint(), vert.getPoint()); + if(inValid){ + break; + } + } + if(!inValid){ + closestV = cand; + minDistance = distance; + closestE = v.findBoundEdge(); + } + } + + } + } + + if(closestE != null){ + root = closestE; + } + + return closestV; + } + + private HEdge findClosestValidNeighbor(HEdge edge, boolean delaunay) { + HEdge next = root.getNext(); + + if(!VectorUtil.ccw(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(), + edge.getGraphPoint().getPoint())){ + return null; + } + + HEdge candEdge = edge; + boolean inValid = false; + + if(delaunay){ + Vertex cand = candEdge.getGraphPoint().getPoint(); + HEdge e = candEdge.getNext(); + while (e != candEdge){ + if(e.getGraphPoint() == root.getGraphPoint() + || e.getGraphPoint() == next.getGraphPoint() + || e.getGraphPoint().getPoint() == cand){ + e = e.getNext(); + continue; + } + inValid = VectorUtil.inCircle(root.getGraphPoint().getPoint(), next.getGraphPoint().getPoint(), + cand, e.getGraphPoint().getPoint()); + if(inValid){ + break; + } + e = e.getNext(); + } + } + if(!inValid){ + return candEdge; + } + return null; + } + + /** Create a triangle from the param vertices only if + * the triangle is valid. IE not outside region. + * @param v1 vertex 1 + * @param v2 vertex 2 + * @param v3 vertex 3 + * @param root and edge of this triangle + * @return the triangle iff it satisfies, null otherwise + */ + private Triangle createTriangle(Vertex v1, Vertex v2, Vertex v3, HEdge rootT){ + Triangle t = new Triangle(v1, v2, v3); + t.setVerticesBoundary(checkVerticesBoundary(rootT)); + return t; + } + + private boolean[] checkVerticesBoundary(HEdge rootT) { + boolean[] boundary = new boolean[3]; + HEdge e1 = rootT; + HEdge e2 = rootT.getNext(); + HEdge e3 = rootT.getNext().getNext(); + + if(e1.getGraphPoint().isBoundaryContained()){ + boundary[0] = true; + } + if(e2.getGraphPoint().isBoundaryContained()){ + boundary[1] = true; + } + if(e3.getGraphPoint().isBoundaryContained()){ + boundary[2] = true; + } + return boundary; + } + + + /** Check if vertex inside the Loop + * @param vertex the Vertex + * @return true if the vertex is inside, false otherwise + */ + public boolean checkInside(Vertex vertex) { + if(!box.contains(vertex.getX(), vertex.getY(), vertex.getZ())){ + return false; + } + + float[] center = box.getCenter(); + + int hits = 0; + HEdge current = root; + HEdge next = root.getNext(); + while(next!= root){ + if(current.getType() == HEdge.INNER || next.getType() == HEdge.INNER){ + current = next; + next = current.getNext(); + continue; + } + Vertex vert1 = current.getGraphPoint().getPoint(); + Vertex vert2 = next.getGraphPoint().getPoint(); + + /** The ray is P0+s*D0, where P0 is the ray origin, D0 is a direction vector and s >= 0. + * The segment is P1+t*D1, where P1 and P1+D1 are the endpoints, and 0 <= t <= 1. + * perp(x,y) = (y,-x). + * if Dot(perp(D1),D0) is not zero, + * s = Dot(perp(D1),P1-P0)/Dot(perp(D1),D0) + * t = Dot(perp(D0),P1-P0)/Dot(perp(D1),D0) + */ + + float[] d0 = new float[]{center[0] - vertex.getX(), center[1]-vertex.getY(), + center[2]-vertex.getZ()}; + float[] d1 = {vert2.getX() - vert1.getX(), vert2.getY() - vert1.getY(), + vert2.getZ() - vert1.getZ()}; + + float[] prep_d1 = {d1[1],-1*d1[0], d1[2]}; + float[] prep_d0 = {d0[1],-1*d0[0], d0[2]}; + + float[] p0p1 = new float[]{vert1.getX() - vertex.getX(), vert1.getY() - vertex.getY(), + vert1.getZ() - vertex.getZ()}; + + float dotD1D0 = VectorUtil.dot(prep_d1, d0); + if(dotD1D0 == 0){ + /** ray parallel to segment */ + current = next; + next = current.getNext(); + continue; + } + + float s = VectorUtil.dot(prep_d1,p0p1)/dotD1D0; + float t = VectorUtil.dot(prep_d0,p0p1)/dotD1D0; + + if(s >= 0 && t >= 0 && t<= 1){ + hits++; + } + current = next; + next = current.getNext(); + } + + if(hits % 2 != 0){ + /** check if hit count is even */ + return true; + } + return false; + } + + public int computeLoopSize(){ + int size = 0; + HEdge e = root; + do{ + size++; + e = e.getNext(); + }while(e != root); + return size; + } +} diff --git a/src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java b/src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java new file mode 100644 index 000000000..36ba57244 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java @@ -0,0 +1,161 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.text; + +import java.util.ArrayList; + +import jogamp.graph.geom.plane.PathIterator; + +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Triangle; + +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.math.Quaternion; + +public class GlyphShape { + + private Quaternion quat= null; + private int numVertices = 0; + private OutlineShape shape = null; + + /** Create a new Glyph shape + * based on Parametric curve control polyline + */ + public GlyphShape(Vertex.Factory<? extends Vertex> factory){ + shape = new OutlineShape(factory); + } + + /** Create a GlyphShape from a font Path Iterator + * @param pathIterator the path iterator + * + * @see PathIterator + */ + public GlyphShape(Vertex.Factory<? extends Vertex> factory, PathIterator pathIterator){ + this(factory); + + if(null != pathIterator){ + while(!pathIterator.isDone()){ + float[] coords = new float[6]; + int segmentType = pathIterator.currentSegment(coords); + addOutlineVerticesFromGlyphVector(coords, segmentType); + + pathIterator.next(); + } + } + shape.transformOutlines(OutlineShape.QUADRATIC_NURBS); + } + + public final Vertex.Factory<? extends Vertex> vertexFactory() { return shape.vertexFactory(); } + + private void addVertexToLastOutline(Vertex vertex){ + shape.addVertex(vertex); + } + + private void addOutlineVerticesFromGlyphVector(float[] coords, int segmentType){ + if(segmentType == PathIterator.SEG_MOVETO){ + if(!shape.getLastOutline().isEmpty()){ + shape.addEmptyOutline(); + } + Vertex vert = vertexFactory().create(coords[0],coords[1]); + vert.setOnCurve(true); + addVertexToLastOutline(vert); + + numVertices++; + } + else if(segmentType == PathIterator.SEG_LINETO){ + Vertex vert1 = vertexFactory().create(coords[0],coords[1]); + vert1.setOnCurve(true); + addVertexToLastOutline(vert1); + + numVertices++; + } + else if(segmentType == PathIterator.SEG_QUADTO){ + Vertex vert1 = vertexFactory().create(coords[0],coords[1]); + vert1.setOnCurve(false); + addVertexToLastOutline(vert1); + + Vertex vert2 = vertexFactory().create(coords[2],coords[3]); + vert2.setOnCurve(true); + addVertexToLastOutline(vert2); + + numVertices+=2; + } + else if(segmentType == PathIterator.SEG_CUBICTO){ + Vertex vert1 = vertexFactory().create(coords[0],coords[1]); + vert1.setOnCurve(false); + addVertexToLastOutline(vert1); + + Vertex vert2 = vertexFactory().create(coords[2],coords[3]); + vert2.setOnCurve(false); + addVertexToLastOutline(vert2); + + Vertex vert3 = vertexFactory().create(coords[4],coords[5]); + vert3.setOnCurve(true); + addVertexToLastOutline(vert3); + + numVertices+=3; + } + else if(segmentType == PathIterator.SEG_CLOSE){ + shape.closeLastOutline(); + } + } + + public int getNumVertices() { + return numVertices; + } + + /** Get the rotational Quaternion attached to this Shape + * @return the Quaternion Object + */ + public Quaternion getQuat() { + return quat; + } + + /** Set the Quaternion that shall defien the rotation + * of this shape. + * @param quat + */ + public void setQuat(Quaternion quat) { + this.quat = quat; + } + + /** Triangluate the glyph shape + * @param sharpness sharpness of the curved regions default = 0.5 + * @return ArrayList of triangles which define this shape + */ + public ArrayList<Triangle> triangulate(float sharpness){ + return shape.triangulate(sharpness); + } + + /** Get the list of Vertices of this Object + * @return arrayList of Vertices + */ + public ArrayList<Vertex> getVertices(){ + return shape.getVertices(); + } +} diff --git a/src/jogl/classes/jogamp/graph/curve/text/GlyphString.java b/src/jogl/classes/jogamp/graph/curve/text/GlyphString.java new file mode 100644 index 000000000..808e3a415 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/curve/text/GlyphString.java @@ -0,0 +1,163 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.curve.text; + +import java.util.ArrayList; + +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Triangle; +import com.jogamp.graph.geom.opengl.SVertex; + +import javax.media.opengl.GLContext; + +import jogamp.graph.geom.plane.AffineTransform; +import jogamp.graph.geom.plane.Path2D; +import jogamp.graph.geom.plane.PathIterator; + + +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.RegionFactory; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class GlyphString { + private final Vertex.Factory<? extends Vertex> pointFactory; + private ArrayList<GlyphShape> glyphs = new ArrayList<GlyphShape>(); + private String str = ""; + private String fontname = ""; + private Region region; + + private SVertex origin = new SVertex(); + + /** Create a new GlyphString object + * @param fontname the name of the font that this String is + * associated with + * @param str the string object + */ + public GlyphString(Vertex.Factory<? extends Vertex> factory, String fontname, String str){ + pointFactory = factory; + this.fontname = fontname; + this.str = str; + } + + public final Vertex.Factory<? extends Vertex> pointFactory() { return pointFactory; } + + public void addGlyphShape(GlyphShape glyph){ + glyphs.add(glyph); + } + public String getString(){ + return str; + } + + /** Creates the Curve based Glyphs from a Font + * @param paths a list of FontPath2D objects that define the outline + * @param affineTransform a global affine transformation applied to the paths. + */ + public void createfromFontPath(Path2D[] paths, AffineTransform affineTransform){ + final int numGlyps = paths.length; + for (int index=0;index<numGlyps;index++){ + if(paths[index] == null){ + continue; + } + PathIterator iterator = paths[index].iterator(affineTransform); + GlyphShape glyphShape = new GlyphShape(pointFactory, iterator); + + if(glyphShape.getNumVertices() < 3) { + continue; + } + addGlyphShape(glyphShape); + } + } + + private ArrayList<Triangle> initializeTriangles(float sharpness){ + ArrayList<Triangle> triangles = new ArrayList<Triangle>(); + for(GlyphShape glyph:glyphs){ + ArrayList<Triangle> tris = glyph.triangulate(sharpness); + triangles.addAll(tris); + } + return triangles; + } + + /** Generate a OGL Region to represent this Object. + * @param context the GLContext which the region is defined by. + * @param shaprness the curvature sharpness of the object. + * @param st shader state + */ + public void generateRegion(GLContext context, float shaprness, ShaderState st, int type){ + region = RegionFactory.create(context, st, type); + region.setFlipped(true); + + ArrayList<Triangle> tris = initializeTriangles(shaprness); + region.addTriangles(tris); + + int numVertices = region.getNumVertices(); + for(GlyphShape glyph:glyphs){ + ArrayList<Vertex> gVertices = glyph.getVertices(); + for(Vertex vert:gVertices){ + vert.setId(numVertices++); + } + region.addVertices(gVertices); + } + + /** initialize the region */ + region.update(); + } + + /** Generate a Hashcode for this object + * @return a string defining the hashcode + */ + public String getTextHashCode(){ + return "" + fontname.hashCode() + str.hashCode(); + } + + /** Render the Object based using the associated Region + * previously generated. + */ + public void renderString3D() { + region.render(null, 0, 0, 0); + } + /** Render the Object based using the associated Region + * previously generated. + */ + public void renderString3D(PMVMatrix matrix, int vp_width, int vp_height, int size) { + region.render(matrix, vp_width, vp_height, size); + } + + /** Get the Origion of this GlyphString + * @return + */ + public Vertex getOrigin() { + return origin; + } + + /** Destroy the associated OGL objects + */ + public void destroy(){ + region.destroy(); + } +} diff --git a/src/jogl/classes/jogamp/graph/font/FontConstructor.java b/src/jogl/classes/jogamp/graph/font/FontConstructor.java new file mode 100644 index 000000000..a382d292e --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/FontConstructor.java @@ -0,0 +1,34 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font; + +import com.jogamp.graph.font.Font; + +public interface FontConstructor { + Font create(String name); +} diff --git a/src/jogl/classes/jogamp/graph/font/FontInt.java b/src/jogl/classes/jogamp/graph/font/FontInt.java new file mode 100644 index 000000000..4d9390da2 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/FontInt.java @@ -0,0 +1,50 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font; + +import jogamp.graph.geom.plane.AffineTransform; +import jogamp.graph.geom.plane.Path2D; + +import com.jogamp.graph.font.Font; + +public interface FontInt extends Font { + + public interface Glyph extends Font.Glyph { + // reserved special glyph IDs + // http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#ba57949e + public static final int ID_UNKNOWN = 0; + public static final int ID_CR = 2; + public static final int ID_SPACE = 3; + + public Path2D getPath(); // unscaled path + public Path2D getPath(float pixelSize); + } + + public void getOutline(String string, float pixelSize, + AffineTransform transform, Path2D[] result); +} diff --git a/src/jogl/classes/jogamp/graph/font/JavaFontLoader.java b/src/jogl/classes/jogamp/graph/font/JavaFontLoader.java new file mode 100644 index 000000000..33505e797 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/JavaFontLoader.java @@ -0,0 +1,129 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font; + +import com.jogamp.common.util.IntObjectHashMap; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.FontSet; +import com.jogamp.graph.font.FontFactory; + +public class JavaFontLoader implements FontSet { + + final static FontSet fontLoader = new JavaFontLoader(); + + public static FontSet get() { + return fontLoader; + } + + final static String availableFontFileNames[] = + { + /* 00 */ "LucidaBrightRegular.ttf", + /* 01 */ "LucidaBrightItalic.ttf", + /* 02 */ "LucidaBrightDemiBold.ttf", + /* 03 */ "LucidaBrightDemiItalic.ttf", + /* 04 */ "LucidaSansRegular.ttf", + /* 05 */ "LucidaSansDemiBold.ttf", + /* 06 */ "LucidaTypewriterRegular.ttf", + /* 07 */ "LucidaTypewriterBold.ttf", + }; + + final String javaFontPath; + + private JavaFontLoader() { + javaFontPath = System.getProperty("java.home") + "/lib/fonts/"; + } + + // FIXME: Add cache size to limit memory usage + static final IntObjectHashMap fontMap = new IntObjectHashMap(); + + static boolean is(int bits, int bit) { + return 0 != ( bits & bit ) ; + } + + public Font getDefault() { + return get(FAMILY_REGULAR, 0) ; // Sans Serif Regular + } + + public Font get(int family, int style) { + Font font = (Font)fontMap.get( ( family << 8 ) | style ); + if (font != null) { + return font; + } + + // 1st process Sans Serif (2 fonts) + if( is(style, STYLE_SERIF) ) { + if( is(style, STYLE_BOLD) ) { + font = abspath(availableFontFileNames[5], family, style); + } else { + font = abspath(availableFontFileNames[4], family, style); + } + fontMap.put( ( family << 8 ) | style, font ); + return font; + } + + // Serif Fonts .. + switch (family) { + case FAMILY_LIGHT: + case FAMILY_MEDIUM: + case FAMILY_CONDENSED: + case FAMILY_REGULAR: + if( is(style, STYLE_BOLD) ) { + if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[3], family, style); + } else { + font = abspath(availableFontFileNames[2], family, style); + } + } else if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[1], family, style); + } else { + font = abspath(availableFontFileNames[0], family, style); + } + break; + + case FAMILY_MONOSPACED: + if( is(style, STYLE_BOLD) ) { + font = abspath(availableFontFileNames[7], family, style); + } else { + font = abspath(availableFontFileNames[6], family, style); + } + break; + } + + return font; + } + + Font abspath(String fname, int family, int style) { + final Font f = FontFactory.getFontConstr().create(javaFontPath+fname); + if(null != f) { + fontMap.put( ( family << 8 ) | style, f ); + } + return f; + + } + +} diff --git a/src/jogl/classes/jogamp/graph/font/UbuntuFontLoader.java b/src/jogl/classes/jogamp/graph/font/UbuntuFontLoader.java new file mode 100644 index 000000000..e09ea85e5 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/UbuntuFontLoader.java @@ -0,0 +1,132 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font; + +import com.jogamp.common.util.IntObjectHashMap; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.FontSet; +import com.jogamp.graph.font.FontFactory; +import com.jogamp.opengl.util.Locator; + +public class UbuntuFontLoader implements FontSet { + + final static FontSet fontLoader = new UbuntuFontLoader(); + + public static FontSet get() { + return fontLoader; + } + + final static String availableFontFileNames[] = + { + /* 00 */ "Ubuntu-R.ttf", // regular + /* 01 */ "Ubuntu-RI.ttf", // regular italic + /* 02 */ "Ubuntu-B.ttf", // bold + /* 03 */ "Ubuntu-BI.ttf", // bold italic + /* 04 */ "Ubuntu-L.ttf", // light + /* 05 */ "Ubuntu-LI.ttf", // light italic + /* 06 */ "Ubuntu-M.ttf", // medium + /* 07 */ "Ubuntu-MI.ttf", // medium italic + + }; + + final static String relPath = "fonts/ubuntu/" ; + + private UbuntuFontLoader() { + } + + // FIXME: Add cache size to limit memory usage + static final IntObjectHashMap fontMap = new IntObjectHashMap(); + + static boolean is(int bits, int bit) { + return 0 != ( bits & bit ) ; + } + + public Font getDefault() { + return get(FAMILY_REGULAR, 0) ; // Sans Serif Regular + } + + public Font get(int family, int style) + { + Font font = (Font)fontMap.get( ( family << 8 ) | style ); + if (font != null) { + return font; + } + + switch (family) { + case FAMILY_MONOSPACED: + case FAMILY_CONDENSED: + case FAMILY_REGULAR: + if( is(style, STYLE_BOLD) ) { + if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[3], family, style); + } else { + font = abspath(availableFontFileNames[2], family, style); + } + } else if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[1], family, style); + } else { + font = abspath(availableFontFileNames[0], family, style); + } + break; + + case FAMILY_LIGHT: + if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[5], family, style); + } else { + font = abspath(availableFontFileNames[4], family, style); + } + break; + + case FAMILY_MEDIUM: + if( is(style, STYLE_ITALIC) ) { + font = abspath(availableFontFileNames[6], family, style); + } else { + font = abspath(availableFontFileNames[7], family, style); + } + break; + } + + return font; + } + + Font abspath(String fname) { + return FontFactory.getFontConstr().create( + Locator.getResource(UbuntuFontLoader.class, relPath+fname).getPath() ); + } + + Font abspath(String fname, int family, int style) { + final Font f = FontFactory.getFontConstr().create( + Locator.getResource(UbuntuFontLoader.class, relPath+fname).getPath() ); + if(null != f) { + fontMap.put( ( family << 8 ) | style, f ); + } + return f; + } + + +} diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/CONTRIBUTING.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/CONTRIBUTING.txt new file mode 100644 index 000000000..15bdc0c0b --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/CONTRIBUTING.txt @@ -0,0 +1,21 @@ +The Ubuntu Font Family is very long-term endeavour, and the first time +that a professionally-designed font has been funded specifically with +the intent of being an on-going community expanded project: + + http://font.ubuntu.com/ + +Development of the Ubuntu Font Family is undertaken on Launchpad: + + http://launchpad.net/ubuntu-font-family/ + +and this is where milestones, bug management and releases are handled. + +Contributions are welcomed. Your work will be used on millions of +computers every single day! Following the initial bootstrapping of +Latin, Cyrillic, Greek, Arabic and Hebrew expansion will be undertaken +by font designers from the font design and Ubuntu communities. + +To ensure that the Ubuntu Font Family can be re-licensed to future +widely-used libre font licences, copyright assignment is being required: + + https://launchpad.net/~uff-contributors diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/FONTLOG.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/FONTLOG.txt new file mode 100644 index 000000000..cf0e4c111 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/FONTLOG.txt @@ -0,0 +1,211 @@ +This is the FONTLOG file for the Ubuntu Font Family and attempts to follow +the recommendations at: http://scripts.sil.org/OFL-FAQ_web#43cecb44 + + +Overview + +The new Ubuntu Font Family was started to enable the personality of +Ubuntu to be seen and felt in every menu, button and dialog. +The typeface is sans-serif, uses OpenType features and is manually +hinted for clarity on desktop and mobile computing screens. + +The scope of the Ubuntu Font Family includes all the languages used by +the various Ubuntu users around the world in tune with Ubuntu's +philosophy which states that every user should be able to use their +software in the language of their choice. So the Ubuntu Font Family +project will be extended to cover many more written languages. + + +History + +The Ubuntu Font Family has been creating during 2010. As of December 2010 +coverage is provided for Latin, Cyrillic and Greek across Regular, Italic, +Bold and Bold-Italic. + + +ChangeLog + +2010-03-08 (Paul Sladen) Ubuntu Font Family version 0.71.2 + + * (Production) Adjust Medium WeightClass to 500 (Md, MdIt) (LP: #730912) + +2010-03-07 (Paul Sladen) Ubuntu Font Family version 0.71.1 + + * (Design) Add Capitalised version of glyphs and kern. (Lt, LtIt, + Md, MdIt) DM (LP: #677446) + * (Design) Re-space and tighen Regular and Italic by amount specified + by Mark Shuttleworth (minus 4 FUnits). (Rg, It) (LP: #677149) + * (Design) Design: Latin (U+0192) made straight more like l/c f with + tail (LP: #670768) + * (Design) (U+01B3) should have hook on right, as the lowercase + (U+01B4) (LP: #681026) + * (Design) Tail of Light Italic germandbls, longs and lowercase 'f' + to match Italic/BoldItalic (LP: #623925) + * (Production) Update <case> feature (Lt, LtIt, Md, MdIt). DM + (LP: #676538, #676539) + * (Production) Remove Bulgarian locl feature for Italics. (LP: #708578) + * (Production) Update Description information with new string: + "The Ubuntu Font Family are libre fonts funded by Canonical Ltd + on behalf of the Ubuntu project. The font design work and + technical implementation is being undertaken by Dalton Maag. The + typeface is sans-serif, uses OpenType features and is manually + hinted for clarity on desktop and mobile computing screens. The + scope of the Ubuntu Font Family includes all the languages used + by the various Ubuntu users around the world in tune with + Ubuntu's philosophy which states that every user should be able + to use their software in the language of their choice. The + project is ongoing, and we expect the family will be extended to + cover many written languages in the coming years." + (Rg, It, Bd, BdIt, Lt, LtIt, Md, MdIt) (LP: #690590) + * (Production) Pixel per em indicator added at U+F000 (Lt, LtIt, Md, + MdIt) (LP: #615787) + * (Production) Version number indicator added at U+EFFD (Lt, LtIt, Md, + MdIt) (LP: #640623) + * (Production) fstype bit set to 0 - Editable (Lt, LtIt, Md, MdIt) + (LP: #648406) + * (Production) Localisation of name table has been removed because + of problems with Mac OS/X interpretation of localisation. DM + (LP: #730785) + * (Hinting) Regular '?' dot non-circular (has incorrect control + value). (LP: #654336) + * (Hinting) Too much space after latin capital 'G' in 13pt + regular. Now reduced. (LP: #683437) + * (Hinting) Balance Indian Rupee at 18,19pt (LP: #662177) + * (Hinting) Make Regular '£' less ambiguous at 13-15 ppm (LP: #685562) + * (Hinting) Regular capital 'W' made symmetrical at 31 ppem (LP: #686168) + +2010-12-14 (Paul Sladen) Ubuntu Font Family version 0.70.1 + + Packaging, rebuilt from '2010-12-08 UbuntuFontsSourceFiles_070.zip': + * (Midstream) Fstype bit != 0 (LP: #648406) + * (Midstream) Add unit test to validate fstype bits (LP: #648406) + * (Midstream) Add unit test to validate licence + +2010-12-14 (Paul Sladen) Ubuntu Font Family version 0.70 + + Release notes 0.70: + * (Design) Add Capitalised version of glyphs and kern. (Rg, It, Bd, + BdIt) DM (LP: #676538, #677446) + * (Design) Give acute and grave a slight upright move to more match + the Hungarian double acute angle. (Rg, It, Bd, BdIt) (LP: #656647) + * (Design) Shift Bold Italic accent glyphs to be consistent with the + Italic. (BdIt only) DM (LP: #677449) + * (Design) Check spacing and kerning of dcaron, lcaron and + tcaron. (Rg, It, Bd, BdIt) (LP: #664722) + * (Design) Add positive kerning to () {} [] to open out the + combinations so they are less like a closed box. (Rg, It, Bd, + BdIt) (LP: #671228) + * (Design) Change design of acute.asc and check highest points (Bd + and BdIt only) DM + * (Production) Update <case> feature. DM (LP: #676538, #676539) + * (Production) Remove Romanian locl feature. (Rg, It, Bd, BdIt) + (LP: #635615) + * (Production) Update Copyright information with new + strings. "Copyright 2010 Canonical Ltd. Licensed under the Ubuntu + Font Licence 1.0" Trademark string "Ubuntu and Canonical are + registered trademarks of Canonical Ltd." (Rg, It, Bd, BdIt) DM + (LP: #677450) + * (Design) Check aligning of hyphen, math signs em, en, check braces + and other brackets. 16/11 (LP: #676465) + * (Production) Pixel per em indicator added at U+F000 (Rg, It, Bd, + BdIt) (LP: #615787) + * (Production) Version number indicator added at U+EFFD (Rg, It, Bd, + BdIt) (LP: #640623) + * (Production) fstype bit set to 0 - Editable (Rg, It, Bd, BdIt) + (LP: #648406) + +2010-10-05 (Paul Sladen) Ubuntu Font Family version 0.69 + + [Dalton Maag] + * Italic, + - Hinting on lowercase Italic l amended 19ppm (LP: #632451) + - Hinting on lowercase Italic u amended 12ppm (LP: #626376) + + * Regular, Italic, Bold, BoldItalic + - New Rupee Sign added @ U+20B9 (LP: #645987) + - Ubuntu Roundel added @ U+E0FF (LP: #651606) + + [Paul Sladen] + * All + - Removed "!ubu" GSUB.calt ligature for U+E0FF (LP: #651606) + + +Acknowledgements + +If you make modifications be sure to add your name (N), email (E), +web-address (if you have one) (W) and description (D). This list is in +alphabetical order. + +N: Amélie Bonet +W: http://ameliebonet.com/ +D: Type design with Dalton Maag, particularly Ubuntu Mono + +N: Ron Carpenter +N: Vincent Connare +N: Lukas Paltram +W: http://www.daltonmaag.com/ +D: Type design and engineering with Dalton Maag + +N: Dave Crossland +W: http://understandingfonts.com/ +D: Documentation and libre licensing guidance + +N: Iain Farrell +W: http://www.flickr.com/photos/iain +D: Ubuntu Font Family delivery for the Ubuntu UX team + +N: Shiraaz Gabru +W: http://www.daltonmaag.com/ +D: Ubuntu Font Family project management at Dalton Maag + +N: Marcus Haslam +W: http://design.canonical.com/author/marcus-haslam/ +D: Creative inspiration + +N: Ben Laenen +D: Inspiration behind the pixels-per-em (PPEM) readout debugging glyph at U+F000 + (for this font the concept was re-implemented from scratch by Dalton-Maag) + +N: Bruno Maag +W: http://www.daltonmaag.com/ +D: Stylistic direction of the Ubuntu Font Family, as head of Dalton Maag + +N: Ivanka Majic +W: http://www.ivankamajic.com/ +D: Guiding the UX team and Cyrillic feedback + +N: David Marshall +N: Malcolm Wooden +W: http://www.daltonmaag.com/ +D: Font Engineering and technical direction + +N: Rodrigo Rivas +D: Indian Rupee Sign glyph + +N: Mark Shuttleworth +W: http://www.markshuttleworth.com/ +D: Executive quality-control and funding + +N: Paul Sladen +W: http://www.paul.sladen.org/ +D: Bug triaging, packaging + +N: Nicolas Spalinger +W: http://planet.open-fonts.org +D: Continuous guidance on libre/open font licensing, best practises in source + tree layout, release and packaging (pkg-fonts Debian team) + +N: Kenneth Wimer +D: Initial PPA packaging + +* Canonical Ltd is the primary commercial sponsor of the Ubuntu and + Kubuntu operating systems +* Dalton Maag are a custom type foundry headed by Bruno Maag + +For further documentation, information on contributors, source code +downloads and those involved with the Ubuntu Font Family, visit: + + http://font.ubuntu.com/ diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE-FAQ.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE-FAQ.txt new file mode 100644 index 000000000..776a25edf --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE-FAQ.txt @@ -0,0 +1,177 @@ + Ubuntu Font Family Licensing FAQ + + Stylistic Foundations + + The Ubuntu Font Family is the first time that a libre typeface has been + designed professionally and explicitly with the intent of developing a + public and long-term community-based development process. + + When developing an open project, it is generally necessary to have firm + foundations: a font needs to maintain harmony within itself even across + many type designers and writing systems. For the [1]Ubuntu Font Family, + the process has been guided with the type foundry Dalton Maag setting + the project up with firm stylistic foundation covering several + left-to-right scripts: Latin, Greek and Cyrillic; and right-to-left + scripts: Arabic and Hebrew (due in 2011). + + With this starting point the community will, under the supervision of + [2]Canonical and [3]Dalton Maag, be able to build on the existing font + sources to expand their character coverage. Ultimately everybody will + be able to use the Ubuntu Font Family in their own written languages + across the whole of Unicode (and this will take some time!). + + Licensing + + The licence chosen by any free software project is one of the + foundational decisions that sets out how derivatives and contributions + can occur, and in turn what kind of community will form around the + project. + + Using a licence that is compatible with other popular licences is a + powerful constraint because of the [4]network effects: the freedom to + share improvements between projects allows free software to reach + high-quality over time. Licence-proliferation leads to many + incompatible licences, undermining the network effect, the freedom to + share and ultimately making the libre movement that Ubuntu is a part of + less effective. For all kinds of software, writing a new licence is not + to be taken lightly and is a choice that needs to be thoroughly + justified if this path is taken. + + Today it is not clear to Canonical what the best licence for a font + project like the Ubuntu Font Family is: one that starts life designed + by professionals and continues with the full range of community + development, from highly commercial work in new directions to curious + beginners' experimental contributions. The fast and steady pace of the + Ubuntu release cycle means that an interim libre licence has been + necessary to enable the consideration of the font family as part of + Ubuntu 10.10 operating system release. + + Before taking any decision on licensing, Canonical as sponsor and + backer of the project has reviewed the many existing licenses used for + libre/open fonts and engaged the stewards of the most popular licenses + in detailed discussions. The current interim licence is the first step + in progressing the state-of-the-art in licensing for libre/open font + development. + + The public discussion must now involve everyone in the (comparatively + new) area of the libre/open font community; including font users, + software freedom advocates, open source supporters and existing libre + font developers. Most importantly, the minds and wishes of professional + type designers considering entering the free software business + community must be taken on board. + + Conversations and discussion has taken place, privately, with + individuals from the following groups (generally speaking personally on + behalf of themselves, rather than their affiliations): + * [5]SIL International + * [6]Open Font Library + * [7]Software Freedom Law Center + * [8]Google Font API + + Document embedding + + One issue highlighted early on in the survey of existing font licences + is that of document embedding. Almost all font licences, both free and + unfree, permit embedding a font into a document to a certain degree. + Embedding a font with other works that make up a document creates a + "combined work" and copyleft would normally require the whole document + to be distributed under the terms of the font licence. As beautiful as + the font might be, such a licence makes a font too restrictive for + useful general purpose digital publishing. + + The situation is not entirely unique to fonts and is encountered also + with tools such as GNU Bison: a vanilla GNU GPL licence would require + anything generated with Bison to be made available under the terms of + the GPL as well. To avoid this, Bison is [9]published with an + additional permission to the GPL which allows the output of Bison to be + made available under any licence. + + The conflict between licensing of fonts and licensing of documents, is + addressed in two popular libre font licences, the SIL OFL and GNU GPL: + * [10]SIL Open Font Licence: When OFL fonts are embedded in a + document, the OFL's terms do not apply to that document. (See + [11]OFL-FAQ for details. + * [12]GPL Font Exception: The situation is resolved by granting an + additional permission to allow documents to not be covered by the + GPL. (The exception is being reviewed). + + The Ubuntu Font Family must also resolve this conflict, ensuring that + if the font is embedded and then extracted it is once again clearly + under the terms of its libre licence. + + Long-term licensing + + Those individuals involved, especially from Ubuntu and Canonical, are + interested in finding a long-term libre licence that finds broad favour + across the whole libre/open font community. The deliberation during the + past months has been on how to licence the Ubuntu Font Family in the + short-term, while knowingly encouraging everyone to pursue a long-term + goal. + * [13]Copyright assignment will be required so that the Ubuntu Font + Family's licensing can be progressively expanded to one (or more) + licences, as best practice continues to evolve within the + libre/open font community. + * Canonical will support and fund legal work on libre font licensing. + It is recognised that the cost and time commitments required are + likely to be significant. We invite other capable parties to join + in supporting this activity. + + The GPL version 3 (GPLv3) will be used for Ubuntu Font Family build + scripts and the CC-BY-SA for associated documentation and non-font + content: all items which do not end up embedded in general works and + documents. + +Ubuntu Font Licence + + For the short-term only, the initial licence is the [14]Ubuntu Font + License (UFL). This is loosely inspired from the work on the SIL + OFL 1.1, and seeks to clarify the issues that arose during discussions + and legal review, from the perspective of the backers, Canonical Ltd. + Those already using established licensing models such as the GPL, OFL + or Creative Commons licensing should have no worries about continuing + to use them. The Ubuntu Font Licence (UFL) and the SIL Open Font + Licence (SIL OFL) are not identical and should not be confused with + each other. Please read the terms precisely. The UFL is only intended + as an interim license, and the overriding aim is to support the + creation of a more suitable and generic libre font licence. As soon as + such a licence is developed, the Ubuntu Font Family will migrate to + it—made possible by copyright assignment in the interium. Between the + OFL 1.1, and the UFL 1.0, the following changes are made to produce the + Ubuntu Font Licence: + * Clarification: + + 1. Document embedding (see [15]embedding section above). + 2. Apply at point of distribution, instead of receipt + 3. Author vs. copyright holder disambiguation (type designers are + authors, with the copyright holder normally being the funder) + 4. Define "Propagate" (for internationalisation, similar to the GPLv3) + 5. Define "Substantially Changed" + 6. Trademarks are explicitly not transferred + 7. Refine renaming requirement + + Streamlining: + 8. Remove "not to be sold separately" clause + 9. Remove "Reserved Font Name(s)" declaration + + A visual demonstration of how these points were implemented can be + found in the accompanying coloured diff between SIL OFL 1.1 and the + Ubuntu Font Licence 1.0: [16]ofl-1.1-ufl-1.0.diff.html + +References + + 1. http://font.ubuntu.com/ + 2. http://www.canonical.com/ + 3. http://www.daltonmaag.com/ + 4. http://en.wikipedia.org/wiki/Network_effect + 5. http://scripts.sil.org/ + 6. http://openfontlibrary.org/ + 7. http://www.softwarefreedom.org/ + 8. http://code.google.com/webfonts + 9. http://www.gnu.org/licenses/gpl-faq.html#CanIUseGPLToolsForNF + 10. http://scripts.sil.org/OFL_web + 11. http://scripts.sil.org/OFL-FAQ_web + 12. http://www.gnu.org/licenses/gpl-faq.html#FontException + 13. https://launchpad.net/~uff-contributors + 14. http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt + 15. http://font.ubuntu.com/ufl/FAQ.html#embedding + 16. http://font.ubuntu.com/ufl/ofl-1.1-ufl-1.0.diff.html diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE.txt new file mode 100644 index 000000000..ae78a8f94 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/LICENCE.txt @@ -0,0 +1,96 @@ +------------------------------- +UBUNTU FONT LICENCE Version 1.0 +------------------------------- + +PREAMBLE +This licence allows the licensed fonts to be used, studied, modified and +redistributed freely. The fonts, including any derivative works, can be +bundled, embedded, and redistributed provided the terms of this licence +are met. The fonts and derivatives, however, cannot be released under +any other licence. The requirement for fonts to remain under this +licence does not require any document created using the fonts or their +derivatives to be published under this licence, as long as the primary +purpose of the document is not to be a vehicle for the distribution of +the fonts. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this licence and clearly marked as such. This may +include source files, build scripts and documentation. + +"Original Version" refers to the collection of Font Software components +as received under this licence. + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to +a new environment. + +"Copyright Holder(s)" refers to all individuals and companies who have a +copyright ownership of the Font Software. + +"Substantially Changed" refers to Modified Versions which can be easily +identified as dissimilar to the Font Software by users of the Font +Software comparing the Original Version with the Modified Version. + +To "Propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification and with or without charging +a redistribution fee), making available to the public, and in some +countries other activities as well. + +PERMISSION & CONDITIONS +This licence does not grant any rights under trademark law and all such +rights are reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to propagate the Font Software, subject to +the below conditions: + +1) Each copy of the Font Software must contain the above copyright +notice and this licence. These can be included either as stand-alone +text files, human-readable headers or in the appropriate machine- +readable metadata fields within text or binary files as long as those +fields can be easily viewed by the user. + +2) The font name complies with the following: +(a) The Original Version must retain its name, unmodified. +(b) Modified Versions which are Substantially Changed must be renamed to +avoid use of the name of the Original Version or similar names entirely. +(c) Modified Versions which are not Substantially Changed must be +renamed to both (i) retain the name of the Original Version and (ii) add +additional naming elements to distinguish the Modified Version from the +Original Version. The name of such Modified Versions must be the name of +the Original Version, with "derivative X" where X represents the name of +the new work, appended to that name. + +3) The name(s) of the Copyright Holder(s) and any contributor to the +Font Software shall not be used to promote, endorse or advertise any +Modified Version, except (i) as required by this licence, (ii) to +acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with +their explicit written permission. + +4) The Font Software, modified or unmodified, in part or in whole, must +be distributed entirely under this licence, and must not be distributed +under any other licence. The requirement for fonts to remain under this +licence does not affect any document created using the Font Software, +except any version of the Font Software extracted from a document +created using the Font Software may only be distributed under this +licence. + +TERMINATION +This licence becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/README.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/README.txt new file mode 100644 index 000000000..292d4ade6 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/README.txt @@ -0,0 +1,15 @@ + ---------------------- + Ubuntu Font Family + ====================== + +The Ubuntu Font Family are a set of matching new libre/open fonts in +development during 2010--2011. The development is being funded by +Canonical Ltd on behalf the wider Free Software community and the +Ubuntu project. The technical font design work and implementation is +being undertaken by Dalton Maag. + +Both the final font Truetype/OpenType files and the design files used +to produce the font family are distributed under an open licence and +you are expressly encouraged to experiment, modify, share and improve. + + http://font.ubuntu.com/ diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/TRADEMARKS.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/TRADEMARKS.txt new file mode 100644 index 000000000..d34265bc8 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/TRADEMARKS.txt @@ -0,0 +1,4 @@ +Ubuntu and Canonical are registered trademarks of Canonical Ltd. + +The licence accompanying these works does not grant any rights +under trademark law and all such rights are reserved. diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-B.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-B.ttf Binary files differnew file mode 100644 index 000000000..7639344e7 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-B.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-BI.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-BI.ttf Binary files differnew file mode 100644 index 000000000..337b8a88b --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-BI.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-L.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-L.ttf Binary files differnew file mode 100644 index 000000000..c3b0fa46d --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-L.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-LI.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-LI.ttf Binary files differnew file mode 100644 index 000000000..d65e8eab3 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-LI.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-M.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-M.ttf Binary files differnew file mode 100644 index 000000000..387ef03fc --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-M.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-MI.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-MI.ttf Binary files differnew file mode 100644 index 000000000..5b92fcb5d --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-MI.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-R.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-R.ttf Binary files differnew file mode 100644 index 000000000..a46446440 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-R.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-RI.ttf b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-RI.ttf Binary files differnew file mode 100644 index 000000000..0e0955918 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/Ubuntu-RI.ttf diff --git a/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/copyright.txt b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/copyright.txt new file mode 100644 index 000000000..3a45d712e --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/fonts/ubuntu/copyright.txt @@ -0,0 +1,5 @@ +Copyright 2010 Canonical Ltd. + +This Font Software is licensed under the Ubuntu Font Licence, Version +1.0. https://launchpad.net/ubuntu-font-licence + diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java new file mode 100644 index 000000000..0d018a314 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java @@ -0,0 +1,268 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font.typecast; + +import jogamp.graph.font.FontInt; +import jogamp.graph.geom.plane.AffineTransform; +import jogamp.graph.geom.plane.Path2D; +import net.java.dev.typecast.ot.OTFont; +import net.java.dev.typecast.ot.OTFontCollection; +import net.java.dev.typecast.ot.table.CmapFormat; +import net.java.dev.typecast.ot.table.CmapIndexEntry; +import net.java.dev.typecast.ot.table.CmapTable; +import net.java.dev.typecast.ot.table.HdmxTable; +import net.java.dev.typecast.ot.table.ID; + +import com.jogamp.common.util.IntObjectHashMap; +import com.jogamp.graph.geom.AABBox; + +class TypecastFont implements FontInt { + static final boolean DEBUG = false; + + final OTFontCollection fontset; + final OTFont font; + TypecastHMetrics metrics; + final CmapFormat cmapFormat; + int cmapentries; + + // FIXME: Add cache size to limit memory usage ?? + IntObjectHashMap char2Glyph; + + public TypecastFont(OTFontCollection fontset) { + this.fontset = fontset; + this.font = fontset.getFont(0); + + // FIXME: Generic attempt to find the best CmapTable, + // which is assumed to be the one with the most entries (stupid 'eh?) + CmapTable cmapTable = font.getCmapTable(); + CmapFormat[] _cmapFormatP = { null, null, null, null }; + int platform = -1; + int platformLength = -1; + int encoding = -1; + for(int i=0; i<cmapTable.getNumTables(); i++) { + CmapIndexEntry cmapIdxEntry = cmapTable.getCmapIndexEntry(i); + int pidx = cmapIdxEntry.getPlatformId(); + CmapFormat cf = cmapIdxEntry.getFormat(); + if(DEBUG) { + System.err.println("CmapFormat["+i+"]: platform " + pidx + + ", encoding "+cmapIdxEntry.getEncodingId() + ": "+cf); + } + if( _cmapFormatP[pidx] == null || + _cmapFormatP[pidx].getLength() < cf.getLength() ) { + _cmapFormatP[pidx] = cf; + if( cf.getLength() > platformLength ) { + platformLength = cf.getLength() ; + platform = pidx; + encoding = cmapIdxEntry.getEncodingId(); + } + } + } + if(0 <= platform) { + cmapFormat = _cmapFormatP[platform]; + if(DEBUG) { + System.err.println("Selected CmapFormat: platform " + platform + + ", encoding "+encoding + ": "+cmapFormat); + } + } else { + CmapFormat _cmapFormat = null; + /*if(null == _cmapFormat) { + platform = ID.platformMacintosh; + encoding = ID.encodingASCII; + _cmapFormat = cmapTable.getCmapFormat(platform, encoding); + } */ + if(null == _cmapFormat) { + // default unicode + platform = ID.platformMicrosoft; + encoding = ID.encodingUnicode; + _cmapFormat = cmapTable.getCmapFormat((short)platform, (short)encoding); + } + if(null == _cmapFormat) { + // maybe a symbol font ? + platform = ID.platformMicrosoft; + encoding = ID.encodingSymbol; + _cmapFormat = cmapTable.getCmapFormat((short)platform, (short)encoding); + } + if(null == _cmapFormat) { + throw new RuntimeException("Cannot find a suitable cmap table for font "+font); + } + cmapFormat = _cmapFormat; + if(DEBUG) { + System.err.println("Selected CmapFormat (2): platform " + platform + ", encoding "+encoding + ": "+cmapFormat); + } + } + + cmapentries = 0; + for (int i = 0; i < cmapFormat.getRangeCount(); ++i) { + CmapFormat.Range range = cmapFormat.getRange(i); + cmapentries += range.getEndCode() - range.getStartCode() + 1; // end included + } + if(DEBUG) { + System.err.println("num glyphs: "+font.getNumGlyphs()); + System.err.println("num cmap entries: "+cmapentries); + System.err.println("num cmap ranges: "+cmapFormat.getRangeCount()); + + for (int i = 0; i < cmapFormat.getRangeCount(); ++i) { + CmapFormat.Range range = cmapFormat.getRange(i); + for (int j = range.getStartCode(); j <= range.getEndCode(); ++j) { + final int code = cmapFormat.mapCharCode(j); + if(code < 15) { + System.err.println(" char: " + (int)j + " ( " + (char)j +" ) -> " + code); + } + } + } + } + char2Glyph = new IntObjectHashMap(cmapentries + cmapentries/4); + } + + public String getName() { + return fontset.getFileName(); + } + + public Metrics getMetrics() { + if (metrics == null) { + metrics = new TypecastHMetrics(this); + } + return metrics; + } + + public Glyph getGlyph(char symbol) { + TypecastGlyph result = (TypecastGlyph) char2Glyph.get(symbol); + if (null == result) { + // final short code = (short) char2Code.get(symbol); + short code = (short) cmapFormat.mapCharCode(symbol); + if(0 == code && 0 != symbol) { + // reserved special glyph IDs by convention + switch(symbol) { + case ' ': code = Glyph.ID_SPACE; break; + case '\n': code = Glyph.ID_CR; break; + default: code = Glyph.ID_UNKNOWN; + } + } + + net.java.dev.typecast.ot.OTGlyph glyph = font.getGlyph(code); + if(null == glyph) { + glyph = font.getGlyph(Glyph.ID_UNKNOWN); + } + if(null == glyph) { + throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+code); + } + Path2D path = TypecastRenderer.buildPath(glyph); + result = new TypecastGlyph(this, symbol, code, glyph.getBBox(), glyph.getAdvanceWidth(), path); + if(DEBUG) { + System.err.println("New glyph: " + (int)symbol + " ( " + (char)symbol +" ) -> " + code + ", contours " + glyph.getPointCount() + ": " + path); + } + final HdmxTable hdmx = font.getHdmxTable(); + if (null!= result && null != hdmx) { + /*if(DEBUG) { + System.err.println("hdmx "+hdmx); + }*/ + for (int i=0; i<hdmx.getNumberOfRecords(); i++) + { + final HdmxTable.DeviceRecord dr = hdmx.getRecord(i); + result.addAdvance(dr.getWidth(code), dr.getPixelSize()); + if(DEBUG) { + System.err.println("hdmx advance : pixelsize = "+dr.getWidth(code)+" : "+ dr.getPixelSize()); + } + } + } + char2Glyph.put(symbol, result); + } + return result; + } + + public void getOutline(String string, float pixelSize, AffineTransform transform, Path2D[] result) { + TypecastRenderer.getOutline(this, string, pixelSize, transform, result); + } + + public float getStringWidth(String string, float pixelSize) { + float width = 0; + final int len = string.length(); + for (int i=0; i< len; i++) + { + char character = string.charAt(i); + if (character == '\n') { + width = 0; + } else { + Glyph glyph = getGlyph(character); + width += glyph.getAdvance(pixelSize, false); + } + } + + return (int)(width + 0.5f); + } + + public float getStringHeight(String string, float pixelSize) { + int height = 0; + + for (int i=0; i<string.length(); i++) + { + char character = string.charAt(i); + if (character != ' ') + { + Glyph glyph = getGlyph(character); + AABBox bbox = glyph.getBBox(pixelSize); + height = (int)Math.ceil(Math.max(bbox.getHeight(), height)); + } + } + return height; + } + + public AABBox getStringBounds(CharSequence string, float pixelSize) { + if (string == null) { + return new AABBox(); + } + final Metrics metrics = getMetrics(); + final float lineGap = metrics.getLineGap(pixelSize); + final float ascent = metrics.getAscent(pixelSize); + final float descent = metrics.getDescent(pixelSize); + final float advanceY = lineGap - descent + ascent; + float totalHeight = 0; + float totalWidth = 0; + float curLineWidth = 0; + for (int i=0; i<string.length(); i++) { + char character = string.charAt(i); + if (character == '\n') { + totalWidth = Math.max(curLineWidth, totalWidth); + curLineWidth = 0; + totalHeight -= advanceY; + continue; + } + Glyph glyph = getGlyph(character); + curLineWidth += glyph.getAdvance(pixelSize, true); + } + if (curLineWidth > 0) { + totalHeight -= advanceY; + totalWidth = Math.max(curLineWidth, totalWidth); + } + return new AABBox(0, 0, 0, totalWidth, totalHeight,0); + } + + final public int getNumGlyphs() { + return font.getNumGlyphs(); + } +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java new file mode 100644 index 000000000..5fb9d32f7 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java @@ -0,0 +1,53 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font.typecast; + +import java.io.File; +import java.io.IOException; + +import jogamp.graph.font.FontConstructor; + +import net.java.dev.typecast.ot.OTFontCollection; + +import com.jogamp.graph.font.Font; + + +public class TypecastFontConstructor implements FontConstructor { + + public Font create(String path) { + OTFontCollection fontset; + try { + fontset = OTFontCollection.create(new File(path)); + return new TypecastFont(fontset); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java new file mode 100644 index 000000000..88d865f9c --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java @@ -0,0 +1,232 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font.typecast; + +import java.util.HashMap; + +import jogamp.graph.font.FontInt; +import jogamp.graph.geom.plane.AffineTransform; +import jogamp.graph.geom.plane.Path2D; + +import com.jogamp.graph.font.Font; +import com.jogamp.graph.geom.AABBox; + +public class TypecastGlyph implements FontInt.Glyph { + public class Advance + { + final Font font; + final float advance; + HashMap<Float, Float> size2advance = new HashMap<Float, Float>(); + + public Advance(Font font, float advance) + { + this.font = font; + this.advance = advance; + } + + public void reset() { + size2advance.clear(); + } + + public float getScale(float pixelSize) + { + return this.font.getMetrics().getScale(pixelSize); + } + + public void add(float advance, float size) + { + size2advance.put(size, advance); + } + + public float get(float size, boolean useFrationalMetrics) + { + Float fo = size2advance.get(size); + if(null == fo) { + float value = (this.advance * getScale(size)); + if (useFrationalMetrics == false) { + //value = (float)Math.ceil(value); + // value = (int)value; + value = (int) ( value + 0.5f ) ; // TODO: check + } + size2advance.put(size, value); + return value; + } + return fo.floatValue(); + } + + public String toString() + { + return "\nAdvance:"+ + "\n advance: "+this.advance+ + "\n advances: \n"+size2advance; + } + } + + public class Metrics + { + AABBox bbox; + Advance advance; + + public Metrics(Font font, AABBox bbox, float advance) + { + this.bbox = bbox; + this.advance = new Advance(font, advance); + } + + public void reset() { + advance.reset(); + } + + public float getScale(float pixelSize) + { + return this.advance.getScale(pixelSize); + } + + public AABBox getBBox() + { + return this.bbox; + } + + public void addAdvance(float advance, float size) + { + this.advance.add(advance, size); + } + + public float getAdvance(float size, boolean useFrationalMetrics) + { + return this.advance.get(size, useFrationalMetrics); + } + + public String toString() + { + return "\nMetrics:"+ + "\n bbox: "+this.bbox+ + this.advance; + } + } + + public static final short INVALID_ID = (short)((1 << 16) - 1); + public static final short MAX_ID = (short)((1 << 16) - 2); + + private final Font font; + + char symbol; + short id; + int advance; + Metrics metrics; + + protected Path2D path; // in EM units + protected Path2D pathSized; + protected float numberSized; + + protected TypecastGlyph(Font font, char symbol) { + this.font = font; + this.symbol = symbol; + } + + protected TypecastGlyph(Font font, + char symbol, short id, AABBox bbox, int advance, Path2D path) { + this.font = font; + this.symbol = symbol; + this.advance = advance; + + init(id, bbox, advance); + + this.path = path; + this.pathSized = null; + this.numberSized = 0.0f; + } + + void init(short id, AABBox bbox, int advance) { + this.id = id; + this.advance = advance; + this.metrics = new Metrics(this.font, bbox, this.advance); + } + + public void reset(Path2D path) { + this.path = path; + this.metrics.reset(); + } + + public Font getFont() { + return this.font; + } + + public char getSymbol() { + return this.symbol; + } + + AABBox getBBoxUnsized() { + return this.metrics.getBBox(); + } + + public AABBox getBBox() { + return this.metrics.getBBox(); + } + + public Metrics getMetrics() { + return this.metrics; + } + + public short getID() { + return this.id; + } + + public float getScale(float pixelSize) { + return this.metrics.getScale(pixelSize); + } + + public AABBox getBBox(float pixelSize) { + final float size = getScale(pixelSize); + AABBox newBox = getBBox().clone(); + newBox.scale(size); + return newBox; + } + + protected void addAdvance(float advance, float size) { + this.metrics.addAdvance(advance, size); + } + + public float getAdvance(float pixelSize, boolean useFrationalMetrics) { + return this.metrics.getAdvance(pixelSize, useFrationalMetrics); + } + + public Path2D getPath() { + return this.path; + } + + public Path2D getPath(float pixelSize) { + final float size = getScale(pixelSize); + + if (this.numberSized != size) { + this.numberSized = size; + this.pathSized = AffineTransform.getScaleInstance(null, size, size).createTransformedShape(getPath()); + } + return this.pathSized; + } +} diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java new file mode 100644 index 000000000..cd8595498 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java @@ -0,0 +1,83 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font.typecast; + +import net.java.dev.typecast.ot.table.HeadTable; +import net.java.dev.typecast.ot.table.HheaTable; +import com.jogamp.graph.font.Font.Metrics; +import com.jogamp.graph.geom.AABBox; + +class TypecastHMetrics implements Metrics { + private final TypecastFont fontImpl; + + // HeadTable + private final HeadTable headTable; + private final float unitsPerEM_Inv; + private final AABBox bbox; + // HheaTable + private final HheaTable hheaTable; + // VheaTable (for horizontal fonts) + // private final VheaTable vheaTable; + + public TypecastHMetrics(TypecastFont fontImpl) { + this.fontImpl = fontImpl; + headTable = this.fontImpl.font.getHeadTable(); + hheaTable = this.fontImpl.font.getHheaTable(); + // vheaTable = this.fontImpl.font.getVheaTable(); + unitsPerEM_Inv = 1.0f / ( (float) headTable.getUnitsPerEm() ); + + int maxWidth = headTable.getXMax() - headTable.getXMin(); + int maxHeight = headTable.getYMax() - headTable.getYMin(); + float lowx= headTable.getXMin(); + float lowy = -(headTable.getYMin()+maxHeight); + float highx = lowx + maxWidth; + float highy = lowy + maxHeight; + bbox = new AABBox(lowx, lowy, 0, highx, highy, 0); // invert + } + + public final float getAscent(float pixelSize) { + return getScale(pixelSize) * -hheaTable.getAscender(); // invert + } + public final float getDescent(float pixelSize) { + return getScale(pixelSize) * -hheaTable.getDescender(); // invert + } + public final float getLineGap(float pixelSize) { + return getScale(pixelSize) * -hheaTable.getLineGap(); // invert + } + public final float getMaxExtend(float pixelSize) { + return getScale(pixelSize) * hheaTable.getXMaxExtent(); + } + public final float getScale(float pixelSize) { + return pixelSize * unitsPerEM_Inv; + } + public final AABBox getBBox(float pixelSize) { + AABBox res = new AABBox(bbox.getLow(), bbox.getHigh()); + res.scale(getScale(pixelSize)); + return res; + } +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java new file mode 100644 index 000000000..410f5b73a --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java @@ -0,0 +1,163 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.graph.font.typecast; + +import jogamp.graph.geom.plane.AffineTransform; +import jogamp.graph.geom.plane.Path2D; + +import com.jogamp.graph.font.Font; +import net.java.dev.typecast.ot.Point; +import net.java.dev.typecast.ot.OTGlyph; + +/** + * Factory to build a {@link com.jogamp.graph.geom.Path2D Path2D} from + * {@link net.java.dev.typecast.ot.OTGlyph Glyph}s. + */ +public class TypecastRenderer { + + public static void getOutline(TypecastFont font, + String string, float pixelSize, AffineTransform transform, Path2D[] p) + { + if (string == null) { + return; + } + Font.Metrics metrics = font.getMetrics(); + float advanceTotal = 0; + float lineGap = metrics.getLineGap(pixelSize) ; + float ascent = metrics.getAscent(pixelSize) ; + float descent = metrics.getDescent(pixelSize) ; + if (transform == null) { + transform = new AffineTransform(); + } + AffineTransform t = new AffineTransform(); + + float advanceY = lineGap - descent + ascent; + float y = 0; + for (int i=0; i<string.length(); i++) + { + p[i] = new Path2D(); + p[i].reset(); + t.setTransform(transform); + char character = string.charAt(i); + if (character == '\n') { + y -= advanceY; + advanceTotal = 0; + continue; + } else if (character == ' ') { + advanceTotal += font.font.getHmtxTable().getAdvanceWidth(TypecastGlyph.ID_SPACE) * metrics.getScale(pixelSize); + continue; + } + TypecastGlyph glyph = (TypecastGlyph) font.getGlyph(character); + Path2D gp = glyph.getPath(); + float scale = metrics.getScale(pixelSize); + t.translate(advanceTotal, y); + t.scale(scale, scale); + p[i].append(gp.iterator(t), false); + advanceTotal += glyph.getAdvance(pixelSize, true); + } + } + + /** + * Build a {@link com.jogamp.graph.geom.Path2D Path2D} from a + * {@link net.java.dev.typecast.ot.OTGlyph Glyph}. This glyph path can then + * be transformed and rendered. + */ + public static Path2D buildPath(OTGlyph glyph) { + + if (glyph == null) { + return null; + } + + Path2D glyphPath = new Path2D(); + + // Iterate through all of the points in the glyph. Each time we find a + // contour end point, add the point range to the path. + int firstIndex = 0; + int count = 0; + for (int i = 0; i < glyph.getPointCount(); i++) { + count++; + if (glyph.getPoint(i).endOfContour) { + addContourToPath(glyphPath, glyph, firstIndex, count); + firstIndex = i + 1; + count = 0; + } + } + return glyphPath; + } + + private static void addContourToPath(Path2D gp, OTGlyph glyph, int startIndex, int count) { + int offset = 0; + while (offset < count) { + Point point = glyph.getPoint(startIndex + offset%count); + Point point_plus1 = glyph.getPoint(startIndex + (offset+1)%count); + Point point_plus2 = glyph.getPoint(startIndex + (offset+2)%count); + if(offset == 0) + { + gp.moveTo(point.x, -point.y); + } + + if (point.onCurve) { + if (point_plus1.onCurve) { + // s = new Line2D.Float(point.x, -point.y, point_plus1.x, -point_plus1.y); + gp.lineTo( point_plus1.x, -point_plus1.y ); + offset++; + } else { + if (point_plus2.onCurve) { + // s = new QuadCurve2D.Float( point.x, -point.y, point_plus1.x, -point_plus1.y, point_plus2.x, -point_plus2.y); + gp.quadTo(point_plus1.x, -point_plus1.y, point_plus2.x, -point_plus2.y); + offset+=2; + } else { + // s = new QuadCurve2D.Float(point.x,-point.y,point_plus1.x,-point_plus1.y, + // midValue(point_plus1.x, point_plus2.x), -midValue(point_plus1.y, point_plus2.y)); + gp.quadTo(point_plus1.x, -point_plus1.y, midValue(point_plus1.x, point_plus2.x), -midValue(point_plus1.y, point_plus2.y)); + offset+=2; + } + } + } else { + if (point_plus1.onCurve) { + // s = new QuadCurve2D.Float(midValue(point_minus1.x, point.x), -midValue(point_minus1.y, point.y), + // point.x, -point.y, point_plus1.x, -point_plus1.y); + //gp.curve3(point_plus1.x, -point_plus1.y, point.x, -point.y); + gp.quadTo(point.x, -point.y, point_plus1.x, -point_plus1.y); + offset++; + + } else { + // s = new QuadCurve2D.Float(midValue(point_minus1.x, point.x), -midValue(point_minus1.y, point.y), point.x, -point.y, + // midValue(point.x, point_plus1.x), -midValue(point.y, point_plus1.y)); + //gp.curve3(midValue(point.x, point_plus1.x), -midValue(point.y, point_plus1.y), point.x, -point.y); + gp.quadTo(point.x, -point.y, midValue(point.x, point_plus1.x), -midValue(point.y, point_plus1.y)); + offset++; + } + } + } + } + + private static int midValue(int a, int b) { + return a + (b - a)/2; + } +} diff --git a/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java b/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java new file mode 100644 index 000000000..2ba9f8d06 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/geom/plane/AffineTransform.java @@ -0,0 +1,580 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.geom.plane; + +import java.io.IOException; +import java.io.Serializable; + +import jogamp.graph.math.MathFloat; +import org.apache.harmony.misc.HashCode; + +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Vertex.Factory; + +public class AffineTransform implements Cloneable, Serializable { + + private static final long serialVersionUID = 1330973210523860834L; + + static final String determinantIsZero = "Determinant is zero"; + + public static final int TYPE_IDENTITY = 0; + public static final int TYPE_TRANSLATION = 1; + public static final int TYPE_UNIFORM_SCALE = 2; + public static final int TYPE_GENERAL_SCALE = 4; + public static final int TYPE_QUADRANT_ROTATION = 8; + public static final int TYPE_GENERAL_ROTATION = 16; + public static final int TYPE_GENERAL_TRANSFORM = 32; + public static final int TYPE_FLIP = 64; + public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE; + public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION; + + /** + * The <code>TYPE_UNKNOWN</code> is an initial type value + */ + static final int TYPE_UNKNOWN = -1; + + /** + * The min value equivalent to zero. If absolute value less then ZERO it considered as zero. + */ + static final float ZERO = (float) 1E-10; + + private final Vertex.Factory<? extends Vertex> pointFactory; + + /** + * The values of transformation matrix + */ + float m00; + float m10; + float m01; + float m11; + float m02; + float m12; + + /** + * The transformation <code>type</code> + */ + transient int type; + + public AffineTransform() { + pointFactory = null; + type = TYPE_IDENTITY; + m00 = m11 = 1.0f; + m10 = m01 = m02 = m12 = 0.0f; + } + + public AffineTransform(Factory<? extends Vertex> factory) { + pointFactory = factory; + type = TYPE_IDENTITY; + m00 = m11 = 1.0f; + m10 = m01 = m02 = m12 = 0.0f; + } + + public AffineTransform(AffineTransform t) { + this.pointFactory = t.pointFactory; + this.type = t.type; + this.m00 = t.m00; + this.m10 = t.m10; + this.m01 = t.m01; + this.m11 = t.m11; + this.m02 = t.m02; + this.m12 = t.m12; + } + + public AffineTransform(Vertex.Factory<? extends Vertex> factory, float m00, float m10, float m01, float m11, float m02, float m12) { + pointFactory = factory; + this.type = TYPE_UNKNOWN; + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + } + + public AffineTransform(Vertex.Factory<? extends Vertex> factory, float[] matrix) { + pointFactory = factory; + this.type = TYPE_UNKNOWN; + m00 = matrix[0]; + m10 = matrix[1]; + m01 = matrix[2]; + m11 = matrix[3]; + if (matrix.length > 4) { + m02 = matrix[4]; + m12 = matrix[5]; + } + } + + /* + * Method returns type of affine transformation. + * + * Transform matrix is + * m00 m01 m02 + * m10 m11 m12 + * + * According analytic geometry new basis vectors are (m00, m01) and (m10, m11), + * translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1). + * Type transformations classification: + * TYPE_IDENTITY - new basis equals original one and zero translation + * TYPE_TRANSLATION - translation vector isn't zero + * TYPE_UNIFORM_SCALE - vectors length of new basis equals + * TYPE_GENERAL_SCALE - vectors length of new basis doesn't equal + * TYPE_FLIP - new basis vector orientation differ from original one + * TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360 degrees + * TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle + * TYPE_GENERAL_TRANSFORM - transformation can't be inversed + */ + public int getType() { + if (type != TYPE_UNKNOWN) { + return type; + } + + int type = 0; + + if (m00 * m01 + m10 * m11 != 0.0) { + type |= TYPE_GENERAL_TRANSFORM; + return type; + } + + if (m02 != 0.0 || m12 != 0.0) { + type |= TYPE_TRANSLATION; + } else + if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) { + type = TYPE_IDENTITY; + return type; + } + + if (m00 * m11 - m01 * m10 < 0.0) { + type |= TYPE_FLIP; + } + + float dx = m00 * m00 + m10 * m10; + float dy = m01 * m01 + m11 * m11; + if (dx != dy) { + type |= TYPE_GENERAL_SCALE; + } else + if (dx != 1.0) { + type |= TYPE_UNIFORM_SCALE; + } + + if ((m00 == 0.0 && m11 == 0.0) || + (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) + { + type |= TYPE_QUADRANT_ROTATION; + } else + if (m01 != 0.0 || m10 != 0.0) { + type |= TYPE_GENERAL_ROTATION; + } + + return type; + } + + public float getScaleX() { + return m00; + } + + public float getScaleY() { + return m11; + } + + public float getShearX() { + return m01; + } + + public float getShearY() { + return m10; + } + + public float getTranslateX() { + return m02; + } + + public float getTranslateY() { + return m12; + } + + public boolean isIdentity() { + return getType() == TYPE_IDENTITY; + } + + public void getMatrix(float[] matrix) { + matrix[0] = m00; + matrix[1] = m10; + matrix[2] = m01; + matrix[3] = m11; + if (matrix.length > 4) { + matrix[4] = m02; + matrix[5] = m12; + } + } + + public float getDeterminant() { + return m00 * m11 - m01 * m10; + } + + public void setTransform(float m00, float m10, float m01, float m11, float m02, float m12) { + this.type = TYPE_UNKNOWN; + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + } + + public void setTransform(AffineTransform t) { + type = t.type; + setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12); + } + + public void setToIdentity() { + type = TYPE_IDENTITY; + m00 = m11 = 1.0f; + m10 = m01 = m02 = m12 = 0.0f; + } + + public void setToTranslation(float mx, float my) { + m00 = m11 = 1.0f; + m01 = m10 = 0.0f; + m02 = mx; + m12 = my; + if (mx == 0.0f && my == 0.0f) { + type = TYPE_IDENTITY; + } else { + type = TYPE_TRANSLATION; + } + } + + public void setToScale(float scx, float scy) { + m00 = scx; + m11 = scy; + m10 = m01 = m02 = m12 = 0.0f; + if (scx != 1.0f || scy != 1.0f) { + type = TYPE_UNKNOWN; + } else { + type = TYPE_IDENTITY; + } + } + + public void setToShear(float shx, float shy) { + m00 = m11 = 1.0f; + m02 = m12 = 0.0f; + m01 = shx; + m10 = shy; + if (shx != 0.0f || shy != 0.0f) { + type = TYPE_UNKNOWN; + } else { + type = TYPE_IDENTITY; + } + } + + public void setToRotation(float angle) { + float sin = MathFloat.sin(angle); + float cos = MathFloat.cos(angle); + if (MathFloat.abs(cos) < ZERO) { + cos = 0.0f; + sin = sin > 0.0f ? 1.0f : -1.0f; + } else + if (MathFloat.abs(sin) < ZERO) { + sin = 0.0f; + cos = cos > 0.0f ? 1.0f : -1.0f; + } + m00 = m11 = cos; + m01 = -sin; + m10 = sin; + m02 = m12 = 0.0f; + type = TYPE_UNKNOWN; + } + + public void setToRotation(float angle, float px, float py) { + setToRotation(angle); + m02 = px * (1.0f - m00) + py * m10; + m12 = py * (1.0f - m00) - px * m10; + type = TYPE_UNKNOWN; + } + + public static <T extends Vertex> AffineTransform getTranslateInstance(Vertex.Factory<? extends Vertex> factory, float mx, float my) { + AffineTransform t = new AffineTransform(factory); + t.setToTranslation(mx, my); + return t; + } + + public static <T extends Vertex> AffineTransform getScaleInstance(Vertex.Factory<? extends Vertex> factory, float scx, float scY) { + AffineTransform t = new AffineTransform(factory); + t.setToScale(scx, scY); + return t; + } + + public static <T extends Vertex> AffineTransform getShearInstance(Vertex.Factory<? extends Vertex> factory, float shx, float shy) { + AffineTransform t = new AffineTransform(factory); + t.setToShear(shx, shy); + return t; + } + + public static <T extends Vertex> AffineTransform getRotateInstance(Vertex.Factory<? extends Vertex> factory, float angle) { + AffineTransform t = new AffineTransform(factory); + t.setToRotation(angle); + return t; + } + + public static <T extends Vertex> AffineTransform getRotateInstance(Vertex.Factory<? extends Vertex> factory, float angle, float x, float y) { + AffineTransform t = new AffineTransform(factory); + t.setToRotation(angle, x, y); + return t; + } + + public void translate(float mx, float my) { + concatenate(AffineTransform.getTranslateInstance(pointFactory, mx, my)); + } + + public void scale(float scx, float scy) { + concatenate(AffineTransform.getScaleInstance(pointFactory, scx, scy)); + } + + public void shear(float shx, float shy) { + concatenate(AffineTransform.getShearInstance(pointFactory, shx, shy)); + } + + public void rotate(float angle) { + concatenate(AffineTransform.getRotateInstance(pointFactory, angle)); + } + + public void rotate(float angle, float px, float py) { + concatenate(AffineTransform.getRotateInstance(pointFactory, angle, px, py)); + } + + /** + * Multiply matrix of two AffineTransform objects. + * The first argument's {@link Vertex.Factory} is being used. + * + * @param t1 - the AffineTransform object is a multiplicand + * @param t2 - the AffineTransform object is a multiplier + * @return an AffineTransform object that is a result of t1 multiplied by matrix t2. + */ + AffineTransform multiply(AffineTransform t1, AffineTransform t2) { + return new AffineTransform(t1.pointFactory, + t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00 + t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01 + t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10 + t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11 + t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02 + t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12 + } + + public void concatenate(AffineTransform t) { + setTransform(multiply(t, this)); + } + + public void preConcatenate(AffineTransform t) { + setTransform(multiply(this, t)); + } + + public AffineTransform createInverse() throws NoninvertibleTransformException { + float det = getDeterminant(); + if (MathFloat.abs(det) < ZERO) { + throw new NoninvertibleTransformException(determinantIsZero); + } + return new AffineTransform( + this.pointFactory, + m11 / det, // m00 + -m10 / det, // m10 + -m01 / det, // m01 + m00 / det, // m11 + (m01 * m12 - m11 * m02) / det, // m02 + (m10 * m02 - m00 * m12) / det // m12 + ); + } + + public Vertex transform(Vertex src, Vertex dst) { + if (dst == null) { + dst = pointFactory.create(); + } + + float x = src.getX(); + float y = src.getY(); + + dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); + return dst; + } + + public void transform(Vertex[] src, int srcOff, Vertex[] dst, int dstOff, int length) { + while (--length >= 0) { + Vertex srcPoint = src[srcOff++]; + float x = srcPoint.getX(); + float y = srcPoint.getY(); + Vertex dstPoint = dst[dstOff]; + if (dstPoint == null) { + throw new IllegalArgumentException("dst["+dstOff+"] is null"); + } + dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); + dst[dstOff++] = dstPoint; + } + } + + public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) { + int step = 2; + if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { + srcOff = srcOff + length * 2 - 2; + dstOff = dstOff + length * 2 - 2; + step = -2; + } + while (--length >= 0) { + float x = src[srcOff + 0]; + float y = src[srcOff + 1]; + dst[dstOff + 0] = x * m00 + y * m01 + m02; + dst[dstOff + 1] = x * m10 + y * m11 + m12; + srcOff += step; + dstOff += step; + } + } + + public Vertex deltaTransform(Vertex src, Vertex dst) { + if (dst == null) { + dst = pointFactory.create(); + } + + float x = src.getX(); + float y = src.getY(); + + dst.setCoord(x * m00 + y * m01, x * m10 + y * m11); + return dst; + } + + public void deltaTransform(float[] src, int srcOff, float[] dst, int dstOff, int length) { + while (--length >= 0) { + float x = src[srcOff++]; + float y = src[srcOff++]; + dst[dstOff++] = x * m00 + y * m01; + dst[dstOff++] = x * m10 + y * m11; + } + } + + public Vertex inverseTransform(Vertex src, Vertex dst) throws NoninvertibleTransformException { + float det = getDeterminant(); + if (MathFloat.abs(det) < ZERO) { + throw new NoninvertibleTransformException(determinantIsZero); + } + if (dst == null) { + dst = pointFactory.create(); + } + + float x = src.getX() - m02; + float y = src.getY() - m12; + + dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det); + return dst; + } + + public void inverseTransform(float[] src, int srcOff, float[] dst, int dstOff, int length) + throws NoninvertibleTransformException + { + float det = getDeterminant(); + if (MathFloat.abs(det) < ZERO) { + throw new NoninvertibleTransformException(determinantIsZero); + } + + while (--length >= 0) { + float x = src[srcOff++] - m02; + float y = src[srcOff++] - m12; + dst[dstOff++] = (x * m11 - y * m01) / det; + dst[dstOff++] = (y * m00 - x * m10) / det; + } + } + + public Path2D createTransformedShape(Path2D src) { + if (src == null) { + return null; + } + if (src instanceof Path2D) { + return ((Path2D)src).createTransformedShape(this); + } + PathIterator path = src.iterator(this); + Path2D dst = new Path2D(path.getWindingRule()); + dst.append(path, false); + return dst; + } + + @Override + public String toString() { + return + getClass().getName() + + "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + + @Override + public int hashCode() { + HashCode hash = new HashCode(); + hash.append(m00); + hash.append(m01); + hash.append(m02); + hash.append(m10); + hash.append(m11); + hash.append(m12); + return hash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof AffineTransform) { + AffineTransform t = (AffineTransform)obj; + return + m00 == t.m00 && m01 == t.m01 && + m02 == t.m02 && m10 == t.m10 && + m11 == t.m11 && m12 == t.m12; + } + return false; + } + + + /** + * Write AffineTrasform object to the output steam. + * @param stream - the output stream + * @throws IOException - if there are I/O errors while writing to the output strem + */ + private void writeObject(java.io.ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + } + + + /** + * Read AffineTransform object from the input stream + * @param stream - the input steam + * @throws IOException - if there are I/O errors while reading from the input strem + * @throws ClassNotFoundException - if class could not be found + */ + private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + type = TYPE_UNKNOWN; + } + +} + diff --git a/src/jogl/classes/jogamp/graph/geom/plane/IllegalPathStateException.java b/src/jogl/classes/jogamp/graph/geom/plane/IllegalPathStateException.java new file mode 100644 index 000000000..55211b3f9 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/geom/plane/IllegalPathStateException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.geom.plane; + +public class IllegalPathStateException extends RuntimeException { + + private static final long serialVersionUID = -5158084205220481094L; + + public IllegalPathStateException() { + } + + public IllegalPathStateException(String s) { + super(s); + } + +} + diff --git a/src/jogl/classes/jogamp/graph/geom/plane/NoninvertibleTransformException.java b/src/jogl/classes/jogamp/graph/geom/plane/NoninvertibleTransformException.java new file mode 100644 index 000000000..398a03fca --- /dev/null +++ b/src/jogl/classes/jogamp/graph/geom/plane/NoninvertibleTransformException.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.geom.plane; + +public class NoninvertibleTransformException extends java.lang.Exception { + + private static final long serialVersionUID = 6137225240503990466L; + + public NoninvertibleTransformException(String s) { + super(s); + } + +} + diff --git a/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java b/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java new file mode 100644 index 000000000..431891361 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/geom/plane/Path2D.java @@ -0,0 +1,428 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.geom.plane; + +import java.util.NoSuchElementException; + +import com.jogamp.graph.geom.AABBox; +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.opengl.SVertex; + +import jogamp.graph.math.plane.Crossing; + +public final class Path2D implements Cloneable { + + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + static final String invalidWindingRuleValue = "Invalid winding rule value"; + static final String iteratorOutOfBounds = "Iterator out of bounds"; + + /** + * The buffers size + */ + private static final int BUFFER_SIZE = 10; + + /** + * The buffers capacity + */ + private static final int BUFFER_CAPACITY = 10; + + /** + * The point's types buffer + */ + byte[] types; + + /** + * The points buffer + */ + float[] points; + + /** + * The point's type buffer size + */ + int typeSize; + + /** + * The points buffer size + */ + int pointSize; + + /** + * The path rule + */ + int rule; + + /** + * The space amount in points buffer for different segmenet's types + */ + static int pointShift[] = { + 2, // MOVETO + 2, // LINETO + 4, // QUADTO + 6, // CUBICTO + 0}; // CLOSE + + /* + * GeneralPath path iterator + */ + class Iterator implements PathIterator { + + /** + * The current cursor position in types buffer + */ + int typeIndex; + + /** + * The current cursor position in points buffer + */ + int pointIndex; + + /** + * The source GeneralPath object + */ + Path2D p; + + /** + * The path iterator transformation + */ + AffineTransform t; + + /** + * Constructs a new GeneralPath.Iterator for given general path + * @param path - the source GeneralPath object + */ + Iterator(Path2D path) { + this(path, null); + } + + /** + * Constructs a new GeneralPath.Iterator for given general path and transformation + * @param path - the source GeneralPath object + * @param at - the AffineTransform object to apply rectangle path + */ + Iterator(Path2D path, AffineTransform at) { + this.p = path; + this.t = at; + } + + public int getWindingRule() { + return p.getWindingRule(); + } + + public boolean isDone() { + return typeIndex >= p.typeSize; + } + + public void next() { + typeIndex++; + } + + public int currentSegment(float[] coords) { + if (isDone()) { + throw new NoSuchElementException(iteratorOutOfBounds); + } + int type = p.types[typeIndex]; + int count = Path2D.pointShift[type]; + System.arraycopy(p.points, pointIndex, coords, 0, count); + if (t != null) { + t.transform(coords, 0, coords, 0, count / 2); + } + pointIndex += count; + return type; + } + + } + + public Path2D() { + this(WIND_NON_ZERO, BUFFER_SIZE); + } + + public Path2D(int rule) { + this(rule, BUFFER_SIZE); + } + + public Path2D(int rule, int initialCapacity) { + setWindingRule(rule); + types = new byte[initialCapacity]; + points = new float[initialCapacity * 2]; + } + + public Path2D(Path2D path) { + this(WIND_NON_ZERO, BUFFER_SIZE); + PathIterator p = path.iterator(null); + setWindingRule(p.getWindingRule()); + append(p, false); + } + + public void setWindingRule(int rule) { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { + throw new NoSuchElementException(invalidWindingRuleValue); + } + this.rule = rule; + } + + public int getWindingRule() { + return rule; + } + + /** + * Checks points and types buffer size to add pointCount points. If necessary realloc buffers to enlarge size. + * @param pointCount - the point count to be added in buffer + */ + void checkBuf(int pointCount, boolean checkMove) { + if (checkMove && typeSize == 0) { + throw new IllegalPathStateException("First segment should be SEG_MOVETO type"); + } + if (typeSize == types.length) { + byte tmp[] = new byte[typeSize + BUFFER_CAPACITY]; + System.arraycopy(types, 0, tmp, 0, typeSize); + types = tmp; + } + if (pointSize + pointCount > points.length) { + float tmp[] = new float[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)]; + System.arraycopy(points, 0, tmp, 0, pointSize); + points = tmp; + } + } + + public void moveTo(float x, float y) { + if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) { + points[pointSize - 2] = x; + points[pointSize - 1] = y; + } else { + checkBuf(2, false); + types[typeSize++] = PathIterator.SEG_MOVETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + } + + public void lineTo(float x, float y) { + checkBuf(2, true); + types[typeSize++] = PathIterator.SEG_LINETO; + points[pointSize++] = x; + points[pointSize++] = y; + } + + public void quadTo(float x1, float y1, float x2, float y2) { + checkBuf(4, true); + types[typeSize++] = PathIterator.SEG_QUADTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + } + + public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) { + checkBuf(6, true); + types[typeSize++] = PathIterator.SEG_CUBICTO; + points[pointSize++] = x1; + points[pointSize++] = y1; + points[pointSize++] = x2; + points[pointSize++] = y2; + points[pointSize++] = x3; + points[pointSize++] = y3; + } + + final public int size() { + return typeSize; + } + + final public boolean isClosed() { + return typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_CLOSE ; + } + + public void closePath() { + if (!isClosed()) { + checkBuf(0, true); + types[typeSize++] = PathIterator.SEG_CLOSE; + } + } + + public String toString() { + return "[size "+size()+", closed "+isClosed()+"]"; + } + + public void append(Path2D path, boolean connect) { + PathIterator p = path.iterator(null); + append(p, connect); + } + + public void append(PathIterator path, boolean connect) { + while (!path.isDone()) { + float coords[] = new float[6]; + switch (path.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (!connect || typeSize == 0) { + moveTo(coords[0], coords[1]); + break; + } + if (types[typeSize - 1] != PathIterator.SEG_CLOSE && + points[pointSize - 2] == coords[0] && + points[pointSize - 1] == coords[1]) + { + break; + } + // NO BREAK; + case PathIterator.SEG_LINETO: + lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(coords[0], coords[1], coords[2], coords[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); + break; + case PathIterator.SEG_CLOSE: + closePath(); + break; + } + path.next(); + connect = false; + } + } + + public SVertex getCurrentPoint() { + if (typeSize == 0) { + return null; + } + int j = pointSize - 2; + if (types[typeSize - 1] == PathIterator.SEG_CLOSE) { + + for (int i = typeSize - 2; i > 0; i--) { + int type = types[i]; + if (type == PathIterator.SEG_MOVETO) { + break; + } + j -= pointShift[type]; + } + } + return new SVertex(points[j], points[j + 1]); + } + + public void reset() { + typeSize = 0; + pointSize = 0; + } + + public void transform(AffineTransform t) { + t.transform(points, 0, points, 0, pointSize / 2); + } + + public Path2D createTransformedShape(AffineTransform t) { + Path2D p = (Path2D)clone(); + if (t != null) { + p.transform(t); + } + return p; + } + + public final synchronized AABBox getBounds2D() { + float rx1, ry1, rx2, ry2; + if (pointSize == 0) { + rx1 = ry1 = rx2 = ry2 = 0.0f; + } else { + int i = pointSize - 1; + ry1 = ry2 = points[i--]; + rx1 = rx2 = points[i--]; + while (i > 0) { + float y = points[i--]; + float x = points[i--]; + if (x < rx1) { + rx1 = x; + } else + if (x > rx2) { + rx2 = x; + } + if (y < ry1) { + ry1 = y; + } else + if (y > ry2) { + ry2 = y; + } + } + } + return new AABBox(rx1, ry1, 0f, rx2, ry2, 0f); + } + + /** + * Checks cross count according to path rule to define is it point inside shape or not. + * @param cross - the point cross count + * @return true if point is inside path, or false otherwise + */ + boolean isInside(int cross) { + if (rule == WIND_NON_ZERO) { + return Crossing.isInsideNonZero(cross); + } + return Crossing.isInsideEvenOdd(cross); + } + + public boolean contains(float px, float py) { + return isInside(Crossing.crossShape(this, px, py)); + } + + public boolean contains(float rx, float ry, float rw, float rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross != Crossing.CROSSING && isInside(cross); + } + + public boolean intersects(float rx, float ry, float rw, float rh) { + int cross = Crossing.intersectShape(this, rx, ry, rw, rh); + return cross == Crossing.CROSSING || isInside(cross); + } + + public boolean contains(Vertex p) { + return contains(p.getX(), p.getY()); + } + + public boolean contains(AABBox r) { + return contains(r); + } + + public boolean intersects(AABBox r) { + return intersects(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight()); + } + + public PathIterator iterator() { + return new Iterator(this); + } + + public PathIterator iterator(AffineTransform t) { + return new Iterator(this, t); + } + + /* public PathIterator getPathIterator(AffineTransform t, float flatness) { + return new FlatteningPathIterator(getPathIterator(t), flatness); + } */ + + @Override + public Object clone() { + try { + Path2D p = (Path2D) super.clone(); + p.types = types.clone(); + p.points = points.clone(); + return p; + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } +} + diff --git a/src/jogl/classes/jogamp/graph/geom/plane/PathIterator.java b/src/jogl/classes/jogamp/graph/geom/plane/PathIterator.java new file mode 100644 index 000000000..8868a8c58 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/geom/plane/PathIterator.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.geom.plane; + +public interface PathIterator { + + public static final int WIND_EVEN_ODD = 0; + public static final int WIND_NON_ZERO = 1; + + public static final int SEG_MOVETO = 0; + public static final int SEG_LINETO = 1; + public static final int SEG_QUADTO = 2; + public static final int SEG_CUBICTO = 3; + public static final int SEG_CLOSE = 4; + + public int getWindingRule(); + + public boolean isDone(); + + public void next(); + + public int currentSegment(float[] coords); + +} + diff --git a/src/jogl/classes/jogamp/graph/math/MathFloat.java b/src/jogl/classes/jogamp/graph/math/MathFloat.java new file mode 100644 index 000000000..0b8d69eba --- /dev/null +++ b/src/jogl/classes/jogamp/graph/math/MathFloat.java @@ -0,0 +1,45 @@ +/**
+ * Copyright 2011 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.graph.math;
+
+public class MathFloat {
+
+ public static final float E = 2.7182818284590452354f;
+
+ public static final float PI = 3.14159265358979323846f;
+
+ public static float abs(float a) { return (float) java.lang.Math.abs(a); }
+ public static float pow(float a, float b) { return (float) java.lang.Math.pow(a, b); }
+
+ public static float sin(float a) { return (float) java.lang.Math.sin(a); }
+ public static float cos(float a) { return (float) java.lang.Math.cos(a); }
+ public static float acos(float a) { return (float) java.lang.Math.acos(a); }
+
+ public static float sqrt(float a) { return (float) java.lang.Math.sqrt(a); }
+
+}
diff --git a/src/jogl/classes/jogamp/graph/math/plane/Crossing.java b/src/jogl/classes/jogamp/graph/math/plane/Crossing.java new file mode 100644 index 000000000..8f8638632 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/math/plane/Crossing.java @@ -0,0 +1,897 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Denis M. Kishenko + */ +package jogamp.graph.math.plane; + +import jogamp.graph.geom.plane.Path2D; +import jogamp.graph.geom.plane.PathIterator; +import jogamp.graph.math.MathFloat; + + +public class Crossing { + + /** + * Allowable tolerance for bounds comparison + */ + static final float DELTA = (float) 1E-5; + + /** + * If roots have distance less then <code>ROOT_DELTA</code> they are double + */ + static final float ROOT_DELTA = (float) 1E-10; + + /** + * Rectangle cross segment + */ + public static final int CROSSING = 255; + + /** + * Unknown crossing result + */ + static final int UNKNOWN = 254; + + /** + * Solves quadratic equation + * @param eqn - the coefficients of the equation + * @param res - the roots of the equation + * @return a number of roots + */ + public static int solveQuad(float eqn[], float res[]) { + float a = eqn[2]; + float b = eqn[1]; + float c = eqn[0]; + int rc = 0; + if (a == 0.0) { + if (b == 0.0) { + return -1; + } + res[rc++] = -c / b; + } else { + float d = b * b - 4.0f * a * c; + // d < 0.0 + if (d < 0.0) { + return 0; + } + d = MathFloat.sqrt(d); + res[rc++] = (- b + d) / (a * 2.0f); + // d != 0.0 + if (d != 0.0) { + res[rc++] = (- b - d) / (a * 2.0f); + } + } + return fixRoots(res, rc); + } + + /** + * Solves cubic equation + * @param eqn - the coefficients of the equation + * @param res - the roots of the equation + * @return a number of roots + */ + public static int solveCubic(float eqn[], float res[]) { + float d = eqn[3]; + if (d == 0) { + return solveQuad(eqn, res); + } + float a = eqn[2] / d; + float b = eqn[1] / d; + float c = eqn[0] / d; + int rc = 0; + + float Q = (a * a - 3.0f * b) / 9.0f; + float R = (2.0f * a * a * a - 9.0f * a * b + 27.0f * c) / 54.0f; + float Q3 = Q * Q * Q; + float R2 = R * R; + float n = - a / 3.0f; + + if (R2 < Q3) { + float t = MathFloat.acos(R / MathFloat.sqrt(Q3)) / 3.0f; + float p = 2.0f * MathFloat.PI / 3.0f; + float m = -2.0f * MathFloat.sqrt(Q); + res[rc++] = m * MathFloat.cos(t) + n; + res[rc++] = m * MathFloat.cos(t + p) + n; + res[rc++] = m * MathFloat.cos(t - p) + n; + } else { +// Debug.println("R2 >= Q3 (" + R2 + "/" + Q3 + ")"); + float A = MathFloat.pow(MathFloat.abs(R) + MathFloat.sqrt(R2 - Q3), 1.0f / 3.0f); + if (R > 0.0) { + A = -A; + } +// if (A == 0.0) { + if (-ROOT_DELTA < A && A < ROOT_DELTA) { + res[rc++] = n; + } else { + float B = Q / A; + res[rc++] = A + B + n; +// if (R2 == Q3) { + float delta = R2 - Q3; + if (-ROOT_DELTA < delta && delta < ROOT_DELTA) { + res[rc++] = - (A + B) / 2.0f + n; + } + } + + } + return fixRoots(res, rc); + } + + /** + * Excludes float roots. Roots are float if they lies enough close with each other. + * @param res - the roots + * @param rc - the roots count + * @return new roots count + */ + static int fixRoots(float res[], int rc) { + int tc = 0; + for(int i = 0; i < rc; i++) { + out: { + for(int j = i + 1; j < rc; j++) { + if (isZero(res[i] - res[j])) { + break out; + } + } + res[tc++] = res[i]; + } + } + return tc; + } + + /** + * QuadCurve class provides basic functionality to find curve crossing and calculating bounds + */ + public static class QuadCurve { + + float ax, ay, bx, by; + float Ax, Ay, Bx, By; + + public QuadCurve(float x1, float y1, float cx, float cy, float x2, float y2) { + ax = x2 - x1; + ay = y2 - y1; + bx = cx - x1; + by = cy - y1; + + Bx = bx + bx; // Bx = 2.0 * bx + Ax = ax - Bx; // Ax = ax - 2.0 * bx + + By = by + by; // By = 2.0 * by + Ay = ay - By; // Ay = ay - 2.0 * by + } + + int cross(float res[], int rc, float py1, float py2) { + int cross = 0; + + for (int i = 0; i < rc; i++) { + float t = res[i]; + + // CURVE-OUTSIDE + if (t < -DELTA || t > 1 + DELTA) { + continue; + } + // CURVE-START + if (t < DELTA) { + if (py1 < 0.0 && (bx != 0.0 ? bx : ax - bx) < 0.0) { + cross--; + } + continue; + } + // CURVE-END + if (t > 1 - DELTA) { + if (py1 < ay && (ax != bx ? ax - bx : bx) > 0.0) { + cross++; + } + continue; + } + // CURVE-INSIDE + float ry = t * (t * Ay + By); + // ry = t * t * Ay + t * By + if (ry > py2) { + float rxt = t * Ax + bx; + // rxt = 2.0 * t * Ax + Bx = 2.0 * t * Ax + 2.0 * bx + if (rxt > -DELTA && rxt < DELTA) { + continue; + } + cross += rxt > 0.0 ? 1 : -1; + } + } // for + + return cross; + } + + int solvePoint(float res[], float px) { + float eqn[] = {-px, Bx, Ax}; + return solveQuad(eqn, res); + } + + int solveExtrem(float res[]) { + int rc = 0; + if (Ax != 0.0) { + res[rc++] = - Bx / (Ax + Ax); + } + if (Ay != 0.0) { + res[rc++] = - By / (Ay + Ay); + } + return rc; + } + + int addBound(float bound[], int bc, float res[], int rc, float minX, float maxX, boolean changeId, int id) { + for(int i = 0; i < rc; i++) { + float t = res[i]; + if (t > -DELTA && t < 1 + DELTA) { + float rx = t * (t * Ax + Bx); + if (minX <= rx && rx <= maxX) { + bound[bc++] = t; + bound[bc++] = rx; + bound[bc++] = t * (t * Ay + By); + bound[bc++] = id; + if (changeId) { + id++; + } + } + } + } + return bc; + } + + } + + /** + * CubicCurve class provides basic functionality to find curve crossing and calculating bounds + */ + public static class CubicCurve { + + float ax, ay, bx, by, cx, cy; + float Ax, Ay, Bx, By, Cx, Cy; + float Ax3, Bx2; + + public CubicCurve(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2) { + ax = x2 - x1; + ay = y2 - y1; + bx = cx1 - x1; + by = cy1 - y1; + cx = cx2 - x1; + cy = cy2 - y1; + + Cx = bx + bx + bx; // Cx = 3.0 * bx + Bx = cx + cx + cx - Cx - Cx; // Bx = 3.0 * cx - 6.0 * bx + Ax = ax - Bx - Cx; // Ax = ax - 3.0 * cx + 3.0 * bx + + Cy = by + by + by; // Cy = 3.0 * by + By = cy + cy + cy - Cy - Cy; // By = 3.0 * cy - 6.0 * by + Ay = ay - By - Cy; // Ay = ay - 3.0 * cy + 3.0 * by + + Ax3 = Ax + Ax + Ax; + Bx2 = Bx + Bx; + } + + int cross(float res[], int rc, float py1, float py2) { + int cross = 0; + for (int i = 0; i < rc; i++) { + float t = res[i]; + + // CURVE-OUTSIDE + if (t < -DELTA || t > 1 + DELTA) { + continue; + } + // CURVE-START + if (t < DELTA) { + if (py1 < 0.0 && (bx != 0.0 ? bx : (cx != bx ? cx - bx : ax - cx)) < 0.0) { + cross--; + } + continue; + } + // CURVE-END + if (t > 1 - DELTA) { + if (py1 < ay && (ax != cx ? ax - cx : (cx != bx ? cx - bx : bx)) > 0.0) { + cross++; + } + continue; + } + // CURVE-INSIDE + float ry = t * (t * (t * Ay + By) + Cy); + // ry = t * t * t * Ay + t * t * By + t * Cy + if (ry > py2) { + float rxt = t * (t * Ax3 + Bx2) + Cx; + // rxt = 3.0 * t * t * Ax + 2.0 * t * Bx + Cx + if (rxt > -DELTA && rxt < DELTA) { + rxt = t * (Ax3 + Ax3) + Bx2; + // rxt = 6.0 * t * Ax + 2.0 * Bx + if (rxt < -DELTA || rxt > DELTA) { + // Inflection point + continue; + } + rxt = ax; + } + cross += rxt > 0.0 ? 1 : -1; + } + } //for + + return cross; + } + + int solvePoint(float res[], float px) { + float eqn[] = {-px, Cx, Bx, Ax}; + return solveCubic(eqn, res); + } + + int solveExtremX(float res[]) { + float eqn[] = {Cx, Bx2, Ax3}; + return solveQuad(eqn, res); + } + + int solveExtremY(float res[]) { + float eqn[] = {Cy, By + By, Ay + Ay + Ay}; + return solveQuad(eqn, res); + } + + int addBound(float bound[], int bc, float res[], int rc, float minX, float maxX, boolean changeId, int id) { + for(int i = 0; i < rc; i++) { + float t = res[i]; + if (t > -DELTA && t < 1 + DELTA) { + float rx = t * (t * (t * Ax + Bx) + Cx); + if (minX <= rx && rx <= maxX) { + bound[bc++] = t; + bound[bc++] = rx; + bound[bc++] = t * (t * (t * Ay + By) + Cy); + bound[bc++] = id; + if (changeId) { + id++; + } + } + } + } + return bc; + } + + } + + /** + * Returns how many times ray from point (x,y) cross line. + */ + public static int crossLine(float x1, float y1, float x2, float y2, float x, float y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < x2) || + (x > x1 && x > x2) || + (y > y1 && y > y2) || + (x1 == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < y2) { + } else { + // INSIDE + if ((y2 - y1) * (x - x1) / (x2 - x1) <= y - y1) { + // INSIDE-UP + return 0; + } + } + + // START + if (x == x1) { + return x1 < x2 ? 0 : -1; + } + + // END + if (x == x2) { + return x1 < x2 ? 1 : 0; + } + + // INSIDE-DOWN + return x1 < x2 ? 1 : -1; + } + + /** + * Returns how many times ray from point (x,y) cross quard curve + */ + public static int crossQuad(float x1, float y1, float cx, float cy, float x2, float y2, float x, float y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < cx && x < x2) || + (x > x1 && x > cx && x > x2) || + (y > y1 && y > cy && y > y2) || + (x1 == cx && cx == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < cy && y < y2 && x != x1 && x != x2) { + if (x1 < x2) { + return x1 < x && x < x2 ? 1 : 0; + } + return x2 < x && x < x1 ? -1 : 0; + } + + // INSIDE + QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); + float px = x - x1; + float py = y - y1; + float res[] = new float[3]; + int rc = c.solvePoint(res, px); + + return c.cross(res, rc, py, py); + } + + /** + * Returns how many times ray from point (x,y) cross cubic curve + */ + public static int crossCubic(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float x, float y) { + + // LEFT/RIGHT/UP/EMPTY + if ((x < x1 && x < cx1 && x < cx2 && x < x2) || + (x > x1 && x > cx1 && x > cx2 && x > x2) || + (y > y1 && y > cy1 && y > cy2 && y > y2) || + (x1 == cx1 && cx1 == cx2 && cx2 == x2)) + { + return 0; + } + + // DOWN + if (y < y1 && y < cy1 && y < cy2 && y < y2 && x != x1 && x != x2) { + if (x1 < x2) { + return x1 < x && x < x2 ? 1 : 0; + } + return x2 < x && x < x1 ? -1 : 0; + } + + // INSIDE + CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); + float px = x - x1; + float py = y - y1; + float res[] = new float[3]; + int rc = c.solvePoint(res, px); + return c.cross(res, rc, py, py); + } + + /** + * Returns how many times ray from point (x,y) cross path + */ + public static int crossPath(PathIterator p, float x, float y) { + int cross = 0; + float mx, my, cx, cy; + mx = my = cx = cy = 0.0f; + float coords[] = new float[6]; + + while (!p.isDone()) { + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (cx != mx || cy != my) { + cross += crossLine(cx, cy, mx, my, x, y); + } + mx = cx = coords[0]; + my = cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + cross += crossLine(cx, cy, cx = coords[0], cy = coords[1], x, y); + break; + case PathIterator.SEG_QUADTO: + cross += crossQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], x, y); + break; + case PathIterator.SEG_CUBICTO: + cross += crossCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], x, y); + break; + case PathIterator.SEG_CLOSE: + if (cy != my || cx != mx) { + cross += crossLine(cx, cy, cx = mx, cy = my, x, y); + } + break; + } + + // checks if the point (x,y) is the vertex of shape with PathIterator p + if (x == cx && y == cy) { + cross = 0; + cy = my; + break; + } + p.next(); + } + if (cy != my) { + cross += crossLine(cx, cy, mx, my, x, y); + } + return cross; + } + + /** + * Returns how many times ray from point (x,y) cross shape + */ + public static int crossShape(Path2D s, float x, float y) { + if (!s.getBounds2D().contains(x, y)) { + return 0; + } + return crossPath(s.iterator(null), x, y); + } + + /** + * Returns true if value enough small + */ + public static boolean isZero(float val) { + return -DELTA < val && val < DELTA; + } + + /** + * Sort bound array + */ + static void sortBound(float bound[], int bc) { + for(int i = 0; i < bc - 4; i += 4) { + int k = i; + for(int j = i + 4; j < bc; j += 4) { + if (bound[k] > bound[j]) { + k = j; + } + } + if (k != i) { + float tmp = bound[i]; + bound[i] = bound[k]; + bound[k] = tmp; + tmp = bound[i + 1]; + bound[i + 1] = bound[k + 1]; + bound[k + 1] = tmp; + tmp = bound[i + 2]; + bound[i + 2] = bound[k + 2]; + bound[k + 2] = tmp; + tmp = bound[i + 3]; + bound[i + 3] = bound[k + 3]; + bound[k + 3] = tmp; + } + } + } + + /** + * Returns are bounds intersect or not intersect rectangle + */ + static int crossBound(float bound[], int bc, float py1, float py2) { + + // LEFT/RIGHT + if (bc == 0) { + return 0; + } + + // Check Y coordinate + int up = 0; + int down = 0; + for(int i = 2; i < bc; i += 4) { + if (bound[i] < py1) { + up++; + continue; + } + if (bound[i] > py2) { + down++; + continue; + } + return CROSSING; + } + + // UP + if (down == 0) { + return 0; + } + + if (up != 0) { + // bc >= 2 + sortBound(bound, bc); + boolean sign = bound[2] > py2; + for(int i = 6; i < bc; i += 4) { + boolean sign2 = bound[i] > py2; + if (sign != sign2 && bound[i + 1] != bound[i - 3]) { + return CROSSING; + } + sign = sign2; + } + } + return UNKNOWN; + } + + /** + * Returns how many times rectangle stripe cross line or the are intersect + */ + public static int intersectLine(float x1, float y1, float x2, float y2, float rx1, float ry1, float rx2, float ry2) { + + // LEFT/RIGHT/UP + if ((rx2 < x1 && rx2 < x2) || + (rx1 > x1 && rx1 > x2) || + (ry1 > y1 && ry1 > y2)) + { + return 0; + } + + // DOWN + if (ry2 < y1 && ry2 < y2) { + } else { + + // INSIDE + if (x1 == x2) { + return CROSSING; + } + + // Build bound + float bx1, bx2; + if (x1 < x2) { + bx1 = x1 < rx1 ? rx1 : x1; + bx2 = x2 < rx2 ? x2 : rx2; + } else { + bx1 = x2 < rx1 ? rx1 : x2; + bx2 = x1 < rx2 ? x1 : rx2; + } + float k = (y2 - y1) / (x2 - x1); + float by1 = k * (bx1 - x1) + y1; + float by2 = k * (bx2 - x1) + y1; + + // BOUND-UP + if (by1 < ry1 && by2 < ry1) { + return 0; + } + + // BOUND-DOWN + if (by1 > ry2 && by2 > ry2) { + } else { + return CROSSING; + } + } + + // EMPTY + if (x1 == x2) { + return 0; + } + + // CURVE-START + if (rx1 == x1) { + return x1 < x2 ? 0 : -1; + } + + // CURVE-END + if (rx1 == x2) { + return x1 < x2 ? 1 : 0; + } + + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + + } + + /** + * Returns how many times rectangle stripe cross quad curve or the are intersect + */ + public static int intersectQuad(float x1, float y1, float cx, float cy, float x2, float y2, float rx1, float ry1, float rx2, float ry2) { + + // LEFT/RIGHT/UP ------------------------------------------------------ + if ((rx2 < x1 && rx2 < cx && rx2 < x2) || + (rx1 > x1 && rx1 > cx && rx1 > x2) || + (ry1 > y1 && ry1 > cy && ry1 > y2)) + { + return 0; + } + + // DOWN --------------------------------------------------------------- + if (ry2 < y1 && ry2 < cy && ry2 < y2 && rx1 != x1 && rx1 != x2) { + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + } + + // INSIDE ------------------------------------------------------------- + QuadCurve c = new QuadCurve(x1, y1, cx, cy, x2, y2); + float px1 = rx1 - x1; + float py1 = ry1 - y1; + float px2 = rx2 - x1; + float py2 = ry2 - y1; + + float res1[] = new float[3]; + float res2[] = new float[3]; + int rc1 = c.solvePoint(res1, px1); + int rc2 = c.solvePoint(res2, px2); + + // INSIDE-LEFT/RIGHT + if (rc1 == 0 && rc2 == 0) { + return 0; + } + + // Build bound -------------------------------------------------------- + float minX = px1 - DELTA; + float maxX = px2 + DELTA; + float bound[] = new float[28]; + int bc = 0; + // Add roots + bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1); + // Add extremal points` + rc2 = c.solveExtrem(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2); + // Add start and end + if (rx1 < x1 && x1 < rx2) { + bound[bc++] = 0.0f; + bound[bc++] = 0.0f; + bound[bc++] = 0.0f; + bound[bc++] = 4; + } + if (rx1 < x2 && x2 < rx2) { + bound[bc++] = 1.0f; + bound[bc++] = c.ax; + bound[bc++] = c.ay; + bound[bc++] = 5; + } + // End build bound ---------------------------------------------------- + + int cross = crossBound(bound, bc, py1, py2); + if (cross != UNKNOWN) { + return cross; + } + return c.cross(res1, rc1, py1, py2); + } + + /** + * Returns how many times rectangle stripe cross cubic curve or the are intersect + */ + public static int intersectCubic(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float rx1, float ry1, float rx2, float ry2) { + + // LEFT/RIGHT/UP + if ((rx2 < x1 && rx2 < cx1 && rx2 < cx2 && rx2 < x2) || + (rx1 > x1 && rx1 > cx1 && rx1 > cx2 && rx1 > x2) || + (ry1 > y1 && ry1 > cy1 && ry1 > cy2 && ry1 > y2)) + { + return 0; + } + + // DOWN + if (ry2 < y1 && ry2 < cy1 && ry2 < cy2 && ry2 < y2 && rx1 != x1 && rx1 != x2) { + if (x1 < x2) { + return x1 < rx1 && rx1 < x2 ? 1 : 0; + } + return x2 < rx1 && rx1 < x1 ? -1 : 0; + } + + // INSIDE + CubicCurve c = new CubicCurve(x1, y1, cx1, cy1, cx2, cy2, x2, y2); + float px1 = rx1 - x1; + float py1 = ry1 - y1; + float px2 = rx2 - x1; + float py2 = ry2 - y1; + + float res1[] = new float[3]; + float res2[] = new float[3]; + int rc1 = c.solvePoint(res1, px1); + int rc2 = c.solvePoint(res2, px2); + + // LEFT/RIGHT + if (rc1 == 0 && rc2 == 0) { + return 0; + } + + float minX = px1 - DELTA; + float maxX = px2 + DELTA; + + // Build bound -------------------------------------------------------- + float bound[] = new float[40]; + int bc = 0; + // Add roots + bc = c.addBound(bound, bc, res1, rc1, minX, maxX, false, 0); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, false, 1); + // Add extrimal points + rc2 = c.solveExtremX(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 2); + rc2 = c.solveExtremY(res2); + bc = c.addBound(bound, bc, res2, rc2, minX, maxX, true, 4); + // Add start and end + if (rx1 < x1 && x1 < rx2) { + bound[bc++] = 0.0f; + bound[bc++] = 0.0f; + bound[bc++] = 0.0f; + bound[bc++] = 6; + } + if (rx1 < x2 && x2 < rx2) { + bound[bc++] = 1.0f; + bound[bc++] = c.ax; + bound[bc++] = c.ay; + bound[bc++] = 7; + } + // End build bound ---------------------------------------------------- + + int cross = crossBound(bound, bc, py1, py2); + if (cross != UNKNOWN) { + return cross; + } + return c.cross(res1, rc1, py1, py2); + } + + /** + * Returns how many times rectangle stripe cross path or the are intersect + */ + public static int intersectPath(PathIterator p, float x, float y, float w, float h) { + + int cross = 0; + int count; + float mx, my, cx, cy; + mx = my = cx = cy = 0.0f; + float coords[] = new float[6]; + + float rx1 = x; + float ry1 = y; + float rx2 = x + w; + float ry2 = y + h; + + while (!p.isDone()) { + count = 0; + switch (p.currentSegment(coords)) { + case PathIterator.SEG_MOVETO: + if (cx != mx || cy != my) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + } + mx = cx = coords[0]; + my = cy = coords[1]; + break; + case PathIterator.SEG_LINETO: + count = intersectLine(cx, cy, cx = coords[0], cy = coords[1], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_QUADTO: + count = intersectQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_CUBICTO: + count = intersectCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], cy = coords[5], rx1, ry1, rx2, ry2); + break; + case PathIterator.SEG_CLOSE: + if (cy != my || cx != mx) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + } + cx = mx; + cy = my; + break; + } + if (count == CROSSING) { + return CROSSING; + } + cross += count; + p.next(); + } + if (cy != my) { + count = intersectLine(cx, cy, mx, my, rx1, ry1, rx2, ry2); + if (count == CROSSING) { + return CROSSING; + } + cross += count; + } + return cross; + } + + /** + * Returns how many times rectangle stripe cross shape or the are intersect + */ + public static int intersectShape(Path2D s, float x, float y, float w, float h) { + if (!s.getBounds2D().intersects(x, y, w, h)) { + return 0; + } + return intersectPath(s.iterator(null), x, y, w, h); + } + + /** + * Returns true if cross count correspond inside location for non zero path rule + */ + public static boolean isInsideNonZero(int cross) { + return cross != 0; + } + + /** + * Returns true if cross count correspond inside location for even-odd path rule + */ + public static boolean isInsideEvenOdd(int cross) { + return (cross & 1) != 0; + } +} |