aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make/scripts/tests.sh9
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java151
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java33
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java152
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/Font.java176
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/FontFactory.java2
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/FontSet.java2
-rw-r--r--src/jogl/classes/com/jogamp/graph/geom/Outline.java83
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java30
-rw-r--r--src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java31
-rw-r--r--src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java8
-rw-r--r--src/jogl/classes/jogamp/graph/curve/tess/Loop.java107
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java200
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java20
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java2
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java341
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/Glyph.java20
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java27
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java4
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java4
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT01.java98
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/TextRendererGLELBase.java6
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/GPURegionGLListener01.java36
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextGLListener0A.java6
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextNewtDemo.java2
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java119
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java38
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java5
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java30
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java36
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Rectangle.java113
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java94
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject01.java314
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject02.java650
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIListener01.java401
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UINewtDemo01.java97
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java176
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java556
-rw-r--r--src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UITypeDemo01.java584
39 files changed, 3529 insertions, 1234 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index 990ef497f..0293d298d 100644
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -331,6 +331,7 @@ function jrun() {
#D_ARGS="-Djogl.debug.graph.curve -Djogl.debug.graph.curve.Instance -Djogl.debug.GLSLCode"
#D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.graph.curve.triangulation.LINE_AA -Djogl.debug.graph.curve.Triangulation -Djogl.debug.graph.font.Renderer"
#D_ARGS="-Djogl.debug.graph.font.Renderer"
+ #D_ARGS="-Djogl.debug.graph.font.Renderer.Code"
#D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.graph.curve.vbaa.resizeLowerBoundary=100"
#D_ARGS="-Djogl.debug.GLSLCode"
#D_ARGS="-Djogl.debug.graph.curve.instance"
@@ -935,11 +936,13 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.graph.TestTextRendererNEWT10 $*
#testnoawt com.jogamp.opengl.test.junit.graph.TestFontsNEWT00 $*
#testnoawt com.jogamp.opengl.test.junit.graph.TestTextRendererNEWTBugXXXX $*
-#testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUTextNewtDemo $*
+
#testnoawt com.jogamp.opengl.test.junit.graph.TestTextRendererNEWT01 $*
+#testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUTextNewtDemo $*
#testnoawt com.jogamp.opengl.test.junit.graph.demos.GPURegionNewtDemo $*
-#testnoawt com.jogamp.opengl.test.junit.graph.demos.ui.UINewtDemo01 $*
-testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtDemo $*
+#testnoawt com.jogamp.opengl.test.junit.graph.demos.ui.UIShapeDemo01 $*
+testnoawt com.jogamp.opengl.test.junit.graph.demos.ui.UITypeDemo01 $*
+#testnoawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtDemo $*
#testawt com.jogamp.opengl.test.junit.graph.demos.GPUUISceneNewtCanvasAWTDemo $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube $*
diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
index be5a1d1bf..fb0ff6a7e 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2010 JogAmp Community. All rights reserved.
+ * Copyright 2010-2023 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:
@@ -43,19 +43,32 @@ import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.math.VectorUtil;
import com.jogamp.opengl.math.geom.AABBox;
-
/**
* A Generic shape objects which is defined by a list of Outlines.
* This Shape can be transformed to triangulations.
* The list of triangles generated are render-able by a Region object.
* The triangulation produced by this Shape will define the
* closed region defined by the outlines.
- *
+ * <p>
* One or more OutlineShape Object can be associated to a region
* this is left as a high-level representation of the Objects. For
* optimizations, flexibility requirements for future features.
- *
- * <br><br>
+ * </p>
+ * <p>
+ * <a name="windingrules">
+ * Outline shape general {@link Winding} rules
+ * <ul>
+ * <li>Outer boundary shapes are required as {@link Winding#CCW}, if unsure
+ * <ul>
+ * <li>You may check {@link Winding} via {@link #getWindingOfLastOutline()} or {@link Outline#getWinding()} (optional)</li>
+ * <li>Use {@link #setWindingOfLastOutline(Winding)} before {@link #closeLastOutline(boolean)} or {@link #closePath()} } to enforce {@link Winding#CCW}, or</li>
+ * <li>use {@link Outline#setWinding(Winding)} on a specific {@link Outline} to enforce {@link Winding#CCW}.</li>
+ * <li>If e.g. the {@link Winding} has changed for an {@link Outline} by above operations, its vertices have been reversed.</li>
+ * </ul></li>
+ * <li>Inner shapes or holes are adjusted to be {@link Winding#CW}, no user consideration is required here.</li>
+ * <li>Safe path: Simply create all shapes with {@link Winding#CCW} or apply {@link Outline#setWinding(Winding)}.</li>
+ * </ul>
+ * </p>
* Example to creating an Outline Shape:
* <pre>
addVertex(...)
@@ -67,33 +80,35 @@ import com.jogamp.opengl.math.geom.AABBox;
addVertex(...)
* </pre>
*
+ * <p>
* The above will create two outlines each with three vertices. By adding these two outlines to
* the OutlineShape, we are stating that the combination of the two outlines represent the shape.
- * <br>
- *
+ * </p>
+ * <p>
* To specify that the shape is curved at a region, the on-curve flag should be set to false
* for the vertex that is in the middle of the curved region (if the curved region is defined by 3
* vertices (quadratic curve).
- * <br>
+ * </p>
+ * <p>
* In case the curved region is defined by 4 or more vertices the middle vertices should both have
* the on-curve flag set to false.
- *
- * <br>Example: <br>
+ * </p>
+ * Example:
* <pre>
addVertex(0,0, true);
addVertex(0,1, false);
addVertex(1,1, false);
addVertex(1,0, true);
* </pre>
- *
+ * <p>
* The above snippet defines a cubic nurbs curve where (0,1 and 1,1)
* do not belong to the final rendered shape.
+ * </p>
*
* <i>Implementation Notes:</i><br>
* <ul>
* <li> The first vertex of any outline belonging to the shape should be on-curve</li>
* <li> Intersections between off-curved parts of the outline is not handled</li>
- * <li> Outline shape winding shall be constructed counter clock wise ({@link Winding#CCW}).</li>
* </ul>
*
* @see Outline
@@ -113,6 +128,18 @@ public final class OutlineShape implements Comparable<OutlineShape> {
}
}
+ /**
+ * General purpose {@link OutlineShape} visitor.
+ */
+ public static interface Visitor {
+ /**
+ * Visiting the given {@link OutlineShape} with it's corresponding {@link AffineTransform}.
+ * @param shape may be used as is, otherwise a copy shall be made if intended to be modified.
+ * @param t may be used immediately as is, otherwise a copy shall be made if stored.
+ */
+ public void visit(final OutlineShape shape, final AffineTransform t);
+ }
+
/** Initial {@link #getSharpness()} value, which can be modified via {@link #setSharpness(float)}. */
public static final float DEFAULT_SHARPNESS = 0.5f;
@@ -214,6 +241,21 @@ public final class OutlineShape implements Comparable<OutlineShape> {
}
/**
+ * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link #area(ArrayList)} function over all of its vertices.
+ * @return {@link Winding#CCW} or {@link Winding#CW}
+ */
+ public final Winding getWindingOfLastOutline() {
+ return getLastOutline().getWinding();
+ }
+
+ /**
+ * Sets the enforced {@link Winding} of the {@link #getLastOutline()}.
+ */
+ public final void setWindingOfLastOutline(final Winding enforced) {
+ getLastOutline().setWinding(enforced);
+ }
+
+ /**
* Add a new empty {@link Outline}
* to the end of this shape's outline list.
* <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p>
@@ -346,9 +388,8 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Adds a vertex to the last open outline to the shape's tail.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param v the vertex to be added to the OutlineShape
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final Vertex v) {
final Outline lo = getLastOutline();
@@ -363,10 +404,9 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Adds a vertex to the last open outline to the shape at {@code position}
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param position index within the last open outline, at which the vertex will be added
* @param v the vertex to be added to the OutlineShape
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final int position, final Vertex v) {
final Outline lo = getLastOutline();
@@ -381,12 +421,10 @@ public final class OutlineShape implements Comparable<OutlineShape> {
* Add a 2D {@link Vertex} to the last open outline to the shape's tail.
* The 2D vertex will be represented as Z=0.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x the x coordinate
* @param y the y coordniate
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final float x, final float y, final boolean onCurve) {
addVertex(vertexFactory.create(x, y, 0f, onCurve));
@@ -396,13 +434,11 @@ public final class OutlineShape implements Comparable<OutlineShape> {
* Add a 2D {@link Vertex} to the last open outline to the shape at {@code position}.
* The 2D vertex will be represented as Z=0.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param position index within the last open outline, at which the vertex will be added
* @param x the x coordinate
* @param y the y coordniate
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final int position, final float x, final float y, final boolean onCurve) {
addVertex(position, vertexFactory.create(x, y, 0f, onCurve));
@@ -411,13 +447,11 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Add a 3D {@link Vertex} to the last open outline to the shape's tail.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final float x, final float y, final float z, final boolean onCurve) {
addVertex(vertexFactory.create(x, y, z, onCurve));
@@ -426,14 +460,12 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Add a 3D {@link Vertex} to the last open outline to the shape at {@code position}.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param position index within the last open outline, at which the vertex will be added
* @param x the x coordinate
* @param y the y coordniate
* @param z the z coordinate
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final int position, final float x, final float y, final float z, final boolean onCurve) {
addVertex(position, vertexFactory.create(x, y, z, onCurve));
@@ -442,8 +474,6 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Add a vertex to the last open outline to the shape's tail.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* The vertex is passed as a float array and its offset where its attributes are located.
* The attributes should be continuous (stride = 0).
* Attributes which value are not set (when length less than 3)
@@ -451,8 +481,8 @@ public final class OutlineShape implements Comparable<OutlineShape> {
* @param coordsBuffer the coordinate array where the vertex attributes are to be picked from
* @param offset the offset in the buffer to the x coordinate
* @param length the number of attributes to pick from the buffer (maximum 3)
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) {
addVertex(vertexFactory.create(coordsBuffer, offset, length, onCurve));
@@ -461,8 +491,6 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Add a vertex to the last open outline to the shape at {@code position}.
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* The vertex is passed as a float array and its offset where its attributes are located.
* The attributes should be continuous (stride = 0).
* Attributes which value are not set (when length less than 3)
@@ -471,8 +499,8 @@ public final class OutlineShape implements Comparable<OutlineShape> {
* @param coordsBuffer the coordinate array where the vertex attributes are to be picked from
* @param offset the offset in the buffer to the x coordinate
* @param length the number of attributes to pick from the buffer (maximum 3)
- * @param onCurve flag if this vertex is on the final curve or defines a curved region
- * of the shape around this vertex.
+ * @param onCurve flag if this vertex is on the final curve or defines a curved region of the shape around this vertex.
+ * @see <a href="#windingrules">see winding rules</a>
*/
public final void addVertex(final int position, final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) {
addVertex(position, vertexFactory.create(coordsBuffer, offset, length, onCurve));
@@ -647,72 +675,75 @@ public final class OutlineShape implements Comparable<OutlineShape> {
/**
* Start a new position for the next line segment at given point x/y (P1).
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x point (P1)
* @param y point (P1)
+ * @param z point (P1)
* @see Path2F#moveTo(float, float)
* @see #addPath(com.jogamp.graph.geom.plane.Path2F.Iterator, boolean)
+ * @see <a href="#windingrules">see winding rules</a>
*/
- public final void moveTo(final float x, final float y) {
+ public final void moveTo(final float x, final float y, final float z) {
if ( 0 == getLastOutline().getVertexCount() ) {
- addVertex(x, y, true);
+ addVertex(x, y, z, true);
} else {
closeLastOutline(false);
addEmptyOutline();
- addVertex(x, y, true);
+ addVertex(x, y, z, true);
}
}
/**
* Add a line segment, intersecting the last point and the given point x/y (P1).
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x final point (P1)
* @param y final point (P1)
+ * @param z final point (P1)
* @see Path2F#lineTo(float, float)
* @see #addPath(com.jogamp.graph.geom.plane.Path2F.Iterator, boolean)
+ * @see <a href="#windingrules">see winding rules</a>
*/
- public final void lineTo(final float x, final float y) {
- addVertex(x, y, true);
+ public final void lineTo(final float x, final float y, final float z) {
+ addVertex(x, y, z, true);
}
/**
* Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x1 quadratic parametric control point (P1)
* @param y1 quadratic parametric control point (P1)
+ * @param z1 quadratic parametric control point (P1)
* @param x2 final interpolated control point (P2)
* @param y2 final interpolated control point (P2)
+ * @param z2 quadratic parametric control point (P2)
* @see Path2F#quadTo(float, float, float, float)
* @see #addPath(com.jogamp.graph.geom.plane.Path2F.Iterator, boolean)
+ * @see <a href="#windingrules">see winding rules</a>
*/
- public final void quadTo(final float x1, final float y1, final float x2, final float y2) {
- addVertex(x1, y1, false);
- addVertex(x2, y2, true);
+ public final void quadTo(final float x1, final float y1, final float z1, final float x2, final float y2, final float z2) {
+ addVertex(x1, y1, z1, false);
+ addVertex(x2, y2, z2, true);
}
/**
* Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
*
- * The constructed shape should be {@link Winding#CCW}.
- *
* @param x1 Bézier control point (P1)
* @param y1 Bézier control point (P1)
+ * @param z1 Bézier control point (P1)
* @param x2 Bézier control point (P2)
* @param y2 Bézier control point (P2)
+ * @param z2 Bézier control point (P2)
* @param x3 final interpolated control point (P3)
* @param y3 final interpolated control point (P3)
+ * @param z3 final interpolated control point (P3)
* @see Path2F#cubicTo(float, float, float, float, float, float)
* @see #addPath(com.jogamp.graph.geom.plane.Path2F.Iterator, boolean)
+ * @see <a href="#windingrules">see winding rules</a>
*/
- public final void cubicTo(final float x1, final float y1, final float x2, final float y2, final float x3, final float y3) {
- addVertex(x1, y1, false);
- addVertex(x2, y2, false);
- addVertex(x3, y3, true);
+ public final void cubicTo(final float x1, final float y1, final float z1, final float x2, final float y2, final float z2, final float x3, final float y3, final float z3) {
+ addVertex(x1, y1, z1, false);
+ addVertex(x2, y2, z2, false);
+ addVertex(x3, y3, z3, true);
}
/**
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
index 0c782d93e..1014553d3 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
@@ -152,17 +152,25 @@ public class RegionRenderer {
private final GLCallback enableCallback;
private final GLCallback disableCallback;
- private int vp_width;
- private int vp_height;
+ private final int[] viewport = new int[] { 0, 0, 0, 0 };
private boolean initialized;
private boolean vboSupported = false;
public final boolean isInitialized() { return initialized; }
+ /** Copies the current viewport in given target and returns it for chaining. */
+ public final int[/*4*/] getViewport(final int[/*4*/] target) {
+ System.arraycopy(viewport, 0, target, 0, 4);
+ return target;
+ }
+ /** Borrows the current viewport w/o copying. */
+ public final int[/*4*/] getViewport() {
+ return viewport;
+ }
/** Return width of current viewport */
- public final int getWidth() { return vp_width; }
+ public final int getWidth() { return viewport[2]; }
/** Return height of current viewport */
- public final int getHeight() { return vp_height; }
+ public final int getHeight() { return viewport[3]; }
public final PMVMatrix getMatrix() { return rs.getMatrix(); }
@@ -261,15 +269,16 @@ public class RegionRenderer {
}
}
- /** No PMVMatrix operation is performed here. PMVMatrix is marked dirty. */
+ /**
+ * No PMVMatrix operation is performed here.
+ */
public final void reshapeNotify(final int width, final int height) {
- this.vp_width = width;
- this.vp_height = height;
+ viewport[2] = width;
+ viewport[3] = height;
}
public final void reshapePerspective(final float angle, final int width, final int height, final float near, final float far) {
- this.vp_width = width;
- this.vp_height = height;
+ reshapeNotify(width, height);
final float ratio = (float)width/(float)height;
final PMVMatrix p = rs.getMatrix();
p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
@@ -277,9 +286,11 @@ public class RegionRenderer {
p.gluPerspective(angle, ratio, near, far);
}
+ /**
+ * Perspective orthogonal, method calls {@link #reshapeNotify(int, int, int, int)}.
+ */
public final void reshapeOrtho(final int width, final int height, final float near, final float far) {
- this.vp_width = width;
- this.vp_height = height;
+ reshapeNotify(width, height);
final PMVMatrix p = rs.getMatrix();
p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
p.glLoadIdentity();
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
index 4af40bf1c..fb77775ad 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2014 JogAmp Community. All rights reserved.
+ * Copyright 2014-2023 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:
@@ -33,12 +33,11 @@ import java.util.Iterator;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.font.Font.Glyph;
-import com.jogamp.graph.geom.Vertex;
-import com.jogamp.graph.geom.Vertex.Factory;
import com.jogamp.graph.geom.plane.AffineTransform;
/**
@@ -55,18 +54,6 @@ public class TextRegionUtil {
this.renderModes = renderModes;
}
- public static interface ShapeVisitor {
- /**
- * Visiting the given {@link OutlineShape} with it's corresponding {@link AffineTransform}.
- * <p>
- * The shape is in font em-size [0..1].
- * </p>
- * @param shape may be used as is, otherwise a copy shall be made if intended to be modified.
- * @param t may be used immediately as is, otherwise a copy shall be made if stored.
- */
- public void visit(final OutlineShape shape, final AffineTransform t);
- }
-
public static int getCharCount(final String s, final char c) {
final int sz = s.length();
int count = 0;
@@ -79,85 +66,53 @@ public class TextRegionUtil {
}
/**
- * Visit each {@link Font.Glyph}'s {@link OutlineShape} with the given {@link ShapeVisitor}
- * additionally passing the progressed {@link AffineTransform}.
+ * Add the string in 3D space w.r.t. the font in font em-size [0..1] at the end of the {@link GLRegion}
+ * while passing the progressed {@link AffineTransform}.
* <p>
- * The produced shapes are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor.
+ * The shapes added to the GLRegion are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor.
* </p>
- * @param visitor
- * @param transform optional given transform
+ * <p>
+ * Origin of rendered text is 0/0 at bottom left.
+ * </p>
+ * @param region the {@link GLRegion} sink
* @param font the target {@link Font}
+ * @param transform optional given transform
* @param str string text
- * @param temp1 temporary AffineTransform storage, mandatory
- * @param temp2 temporary AffineTransform storage, mandatory
+ * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
+ * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] OutlineShape into account.
*/
- public static void processString(final ShapeVisitor visitor, final AffineTransform transform,
- final Font font, final CharSequence str,
- final AffineTransform temp1, final AffineTransform temp2) {
- final int charCount = str.length();
-
- // region.setFlipped(true);
- final float lineHeight = font.getLineHeight();
-
- float y = 0;
- float advanceTotal = 0;
- Font.Glyph left_glyph = null;
-
- for(int i=0; i< charCount; i++) {
- final char character = str.charAt(i);
- if( '\n' == character ) {
- y -= lineHeight;
- advanceTotal = 0;
- left_glyph = null;
- } else if (character == ' ') {
- advanceTotal += font.getAdvanceWidth(Glyph.ID_SPACE);
- left_glyph = null;
- } else {
- // reset transform
- if( null != transform ) {
- temp1.setTransform(transform);
- } else {
- temp1.setToIdentity();
- }
- final Font.Glyph glyph = font.getGlyph(character);
- final OutlineShape glyphShape = glyph.getShape();
- if( null == glyphShape ) {
- left_glyph = null;
- continue;
- }
- if( null != left_glyph ) {
- advanceTotal += left_glyph.getKerning(glyph.getID());
- }
- temp1.translate(advanceTotal, y, temp2);
- visitor.visit(glyphShape, temp1);
- advanceTotal += glyph.getAdvance();
- left_glyph = glyph;
- }
- }
+ public static AABBox addStringToRegion(final Region region, final Font font, final AffineTransform transform,
+ final CharSequence str, final float[] rgbaColor) {
+ return addStringToRegion(region, font, transform, str, rgbaColor, new AffineTransform(), new AffineTransform());
}
/**
- * Add the string in 3D space w.r.t. the font in font em-size [0..1] at the end of the {@link GLRegion}.
+ * Add the string in 3D space w.r.t. the font in font em-size [0..1] at the end of the {@link GLRegion}
+ * while passing the progressed {@link AffineTransform}.
* <p>
- * The shapes added to the GLRegion are in font em-size [0..1].
+ * The shapes added to the GLRegion are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor.
+ * </p>
+ * <p>
+ * Origin of rendered text is 0/0 at bottom left.
* </p>
* @param region the {@link GLRegion} sink
- * @param vertexFactory vertex impl factory {@link Factory}
* @param font the target {@link Font}
+ * @param transform optional given transform
* @param str string text
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param temp1 temporary AffineTransform storage, mandatory
* @param temp2 temporary AffineTransform storage, mandatory
+ * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] OutlineShape into account.
*/
- public static void addStringToRegion(final GLRegion region, final Factory<? extends Vertex> vertexFactory,
- final Font font, final CharSequence str, final float[] rgbaColor,
- final AffineTransform temp1, final AffineTransform temp2) {
- final ShapeVisitor visitor = new ShapeVisitor() {
+ public static AABBox addStringToRegion(final Region region, final Font font, final AffineTransform transform,
+ final CharSequence str, final float[] rgbaColor,
+ final AffineTransform temp1, final AffineTransform temp2) {
+ final OutlineShape.Visitor visitor = new OutlineShape.Visitor() {
@Override
public final void visit(final OutlineShape shape, final AffineTransform t) {
region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null);
} };
- processString(visitor, null, font, str, temp1, temp2);
+ return font.processString(visitor, transform, str, temp1, temp2);
}
/**
@@ -166,6 +121,9 @@ public class TextRegionUtil {
* The shapes added to the GLRegion are in font em-size [0..1].
* </p>
* <p>
+ * Origin of rendered text is 0/0 at bottom left.
+ * </p>
+ * <p>
* Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory.
* </p>
* @param gl the current GL state
@@ -175,22 +133,28 @@ public class TextRegionUtil {
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
* The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched.
+ * @return the bounding box of the given string from the produced and rendered GLRegion
* @throws Exception if TextRenderer not initialized
*/
- public void drawString3D(final GL2ES2 gl,
- final RegionRenderer renderer, final Font font, final CharSequence str,
- final float[] rgbaColor, final int[/*1*/] sampleCount) {
+ public AABBox drawString3D(final GL2ES2 gl,
+ final RegionRenderer renderer, final Font font, final CharSequence str,
+ final float[] rgbaColor, final int[/*1*/] sampleCount) {
if( !renderer.isInitialized() ) {
throw new GLException("TextRendererImpl01: not initialized!");
}
final int special = 0;
GLRegion region = getCachedRegion(font, str, special);
+ AABBox res;
if(null == region) {
region = GLRegion.create(renderModes, null);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, tempT1, tempT2);
+ res = addStringToRegion(region, font, null, str, rgbaColor, tempT1, tempT2);
addCachedRegion(gl, font, str, special, region);
+ } else {
+ res = new AABBox();
+ res.copy(region.getBounds());
}
region.draw(gl, renderer, sampleCount);
+ return res;
}
/**
@@ -199,9 +163,12 @@ public class TextRegionUtil {
* The shapes added to the GLRegion are in font em-size [0..1].
* </p>
* <p>
+ * Origin of rendered text is 0/0 at bottom left.
+ * </p>
+ * <p>
* In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion}
* is a huge performance impact.
- * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)}
+ * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[])}
* instead.
* </p>
* @param gl the current GL state
@@ -211,21 +178,20 @@ public class TextRegionUtil {
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
* The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched.
- * @param temp1 temporary AffineTransform storage, mandatory
- * @param temp2 temporary AffineTransform storage, mandatory
* @throws Exception if TextRenderer not initialized
+ * @return the bounding box of the given string from the produced and rendered GLRegion
*/
- public static void drawString3D(final GL2ES2 gl, final int renderModes,
- final RegionRenderer renderer, final Font font, final CharSequence str,
- final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform temp1,
- final AffineTransform temp2) {
+ public static AABBox drawString3D(final GL2ES2 gl, final int renderModes,
+ final RegionRenderer renderer, final Font font, final CharSequence str,
+ final float[] rgbaColor, final int[/*1*/] sampleCount) {
if(!renderer.isInitialized()){
throw new GLException("TextRendererImpl01: not initialized!");
}
final GLRegion region = GLRegion.create(renderModes, null);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
+ final AABBox res = addStringToRegion(region, font, null, str, rgbaColor);
region.draw(gl, renderer, sampleCount);
region.destroy(gl);
+ return res;
}
/**
@@ -234,26 +200,28 @@ public class TextRegionUtil {
* <p>
* The shapes added to the GLRegion are in font em-size [0..1].
* </p>
+ * <p>
+ * Origin of rendered text is 0/0 at bottom left.
+ * </p>
* @param gl the current GL state
* @param font {@link Font} to be used
* @param str text to be rendered
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
* The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched.
- * @param temp1 temporary AffineTransform storage, mandatory
- * @param temp2 temporary AffineTransform storage, mandatory
+ * @return the bounding box of the given string from the produced and rendered GLRegion
* @throws Exception if TextRenderer not initialized
*/
- public static void drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer,
- final Font font, final CharSequence str, final float[] rgbaColor,
- final int[/*1*/] sampleCount, final AffineTransform temp1,
- final AffineTransform temp2) {
+ public static AABBox drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer,
+ final Font font, final CharSequence str, final float[] rgbaColor,
+ final int[/*1*/] sampleCount) {
if(!renderer.isInitialized()){
throw new GLException("TextRendererImpl01: not initialized!");
}
region.clear(gl);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
+ final AABBox res = addStringToRegion(region, font, null, str, rgbaColor);
region.draw(gl, renderer, sampleCount);
+ return res;
}
/**
diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java
index 2d26b1a85..fdef6a612 100644
--- a/src/jogl/classes/com/jogamp/graph/font/Font.java
+++ b/src/jogl/classes/com/jogamp/graph/font/Font.java
@@ -1,5 +1,5 @@
/**
-// * Copyright 2010 JogAmp Community. All rights reserved.
+ * Copyright 2010-2023 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:
@@ -81,42 +81,42 @@ public interface Font {
*/
public interface Metrics {
/**
- * @return ascent in font-units to be divided by {@link #getUnitsPerEM()}
+ * @return ascent in font-units, sourced from `hheaTable' table.
*/
int getAscentFU();
/**
- * @return ascent in font em-size [0..1]
+ * @return ascent in font em-size [0..1], sourced from `hheaTable' table.
*/
float getAscent();
/**
- * @return descent in font-units to be divided by {@link #getUnitsPerEM()}
+ * @return descent in font-units, sourced from `hheaTable' table.
*/
int getDescentFU();
/**
- * @return descend in font em-size [0..1]
+ * @return descend in font em-size [0..1], sourced from `hheaTable' table.
*/
float getDescent();
/**
- * @return line-gap in font-units to be divided by {@link #getUnitsPerEM()}
+ * @return line-gap in font-units, sourced from `hheaTable' table.
*/
int getLineGapFU();
/**
- * @return line-gap in font em-size [0..1]
+ * @return line-gap in font em-size [0..1], sourced from `hheaTable' table.
*/
float getLineGap();
/**
- * @return max-extend in font-units to be divided by {@link #getUnitsPerEM()}
+ * @return max-extend in font-units, sourced from `hheaTable' table.
*/
int getMaxExtendFU();
/**
- * @return max-extend in font em-size [0..1]
+ * @return max-extend in font em-size [0..1], sourced from `hheaTable' table.
*/
float getMaxExtend();
@@ -161,7 +161,6 @@ public interface Font {
public static final int ID_SPACE = 3;
Font getFont();
- char getSymbol();
/** Return this glyph's ID */
int getID();
@@ -177,25 +176,31 @@ public interface Font {
float getScale(final int funits);
/**
- * Return the AABBox in font-units to be divided by unitsPerEM
+ * Return the AABBox in font-units, borrowing internal instance.
*/
AABBox getBBoxFU();
/**
- * Return the AABBox in font-units to be divided by unitsPerEM
+ * Return the AABBox in font-units, copying into given dest.
* @param dest AABBox instance set to this metrics boundary in font-units
* @return the given and set AABBox 'dest' in font-units
*/
AABBox getBBoxFU(final AABBox dest);
/**
+ * Return the AABBox in font em-size [0..1], copying into given dest.
* @param dest AABBox instance set to this metrics boundary in font em-size [0..1]
* @param tmpV3 caller provided temporary 3-component vector
* @return the given and set AABBox 'dest' in font em-size [0..1]
*/
AABBox getBBox(final AABBox dest, float[] tmpV3);
- /** Return advance in font units to be divided by unitsPerEM */
+ /**
+ * Return the AABBox in font em-size [0..1], creating a new copy.
+ */
+ AABBox getBBox();
+
+ /** Return advance in font units, sourced from `hmtx` table. */
int getAdvanceFU();
/** Return advance in font em-size [0..1] */
@@ -210,10 +215,10 @@ public interface Font {
int getKerningPairCount();
/**
- * Returns the optional kerning inter-glyph distance within words between this glyph and the given right glyph_id in font-units to be divided by unitsPerEM
+ * Returns the optional kerning inter-glyph distance within words between this glyph and the given right glyph_id in font-units.
*
* @param right_glyphid right glyph code id
- * @return font-units to be divided by unitsPerEM
+ * @return font-units
*/
int getKerningFU(final int right_glyphid);
@@ -247,7 +252,7 @@ public interface Font {
StringBuilder getAllNames(final StringBuilder string, final String separator);
/**
- * Return advance-width of given glyphID in font-units to be divided by unitsPerEM
+ * Return advance-width of given glyphID in font-units, sourced from `hmtx` table.
* @param glyphID
*/
int getAdvanceWidthFU(final int glyphID);
@@ -262,12 +267,22 @@ public interface Font {
int getGlyphID(final char symbol);
- Glyph getGlyph(final char symbol);
+ Glyph getGlyph(final int glyph_id);
int getNumGlyphs();
/**
- * Return line height in font-units to be divided by unitsPerEM
+ * Return line height in font-units, composed from `hheaTable' table entries.
+ * <pre>
+ * return abs(lineGap) + abs(descent) abs(ascent);
+ * </pre>
+ * or
+ * <pre>
+ * // lineGap negative value
+ * // descent positive value
+ * // ascent negative value
+ * return -1 * ( lineGap - descent + ascent );
+ * </pre>
*/
int getLineHeightFU();
@@ -276,43 +291,126 @@ public interface Font {
*/
float getLineHeight();
- /** Return metric-width in font-units */
- int getMetricWidthFU(final CharSequence string);
-
- /** Return metric-width in font em-size */
- float getMetricWidth(final CharSequence string);
-
- /** Return metric-height in font-units */
- int getMetricHeightFU(final CharSequence string);
-
- /** Return metric-height in font em-size */
- float getMetricHeight(final CharSequence string);
-
- /** Return layout metric-bounds in font-units, see {@link #getMetricBounds(CharSequence, float)} */
+ /**
+ * Returns metric-bounds in font-units.
+ * <p>
+ * Metric bounds is based on the `hmtx` table's advance of each glyph and `hheaTable' composed line height.
+ * </p>
+ * <p>
+ * For accurate layout consider using {@link #getGlyphBoundsFU(CharSequence)}.
+ * </p>
+ * @see #getMetricBounds(CharSequence)
+ * @see #getGlyphBoundsFU(CharSequence)
+ */
AABBox getMetricBoundsFU(final CharSequence string);
- /** Return layout metric-bounds in font em-size, see {@link #getMetricBounds(CharSequence, float)} */
+ /**
+ * Returns metric-bounds in font em-size.
+ * <p>
+ * Metric bounds is based on the `hmtx` table's advance of each glyph and `hheaTable' composed line height.
+ * </p>
+ * <p>
+ * For accurate layout consider using {@link #getGlyphBounds(CharSequence)}.
+ * </p>
+ * @see #getMetricBoundsFU(CharSequence)
+ * @see #getGlyphBounds(CharSequence)
+ * @see #getGlyphShapeBounds(CharSequence)
+ */
AABBox getMetricBounds(final CharSequence string);
/**
- * Return the bounding box by taking each glyph's font-unit sized bounding box into account.
- * @param transform optional given transform
+ * Returns accurate bounding box by taking each glyph's font em-sized bounding box into account.
+ * <p>
+ * Glyph bounds is based on each glyph's bounding box and `hheaTable' composed line height.
+ * </p>
+ * @param string string text
+ * @return the bounding box of the given string in font em-size [0..1]
+ * @see #getGlyphBoundsFU(CharSequence)
+ * @see #getGlyphShapeBounds(CharSequence)
+ * @see #getMetricBounds(CharSequence)
+ */
+ AABBox getGlyphBounds(final CharSequence string);
+
+ /**
+ * Returns accurate bounding box by taking each glyph's font-units sized bounding box into account.
+ * <p>
+ * Glyph bounds is based on each glyph's bounding box and `hheaTable' composed line height.
+ * </p>
* @param string string text
* @return the bounding box of the given string in font-units [0..1]
+ * @see #getGlyphBounds(CharSequence)
*/
- AABBox getPointsBoundsFU(final AffineTransform transform, final CharSequence string);
+ AABBox getGlyphBoundsFU(final CharSequence string);
/**
- * Return the bounding box by taking each glyph's font em-sized bounding box into account.
+ * Returns accurate bounding box by taking each glyph's font em-sized {@link OutlineShape} into account.
+ * <p>
+ * Glyph shape bounds is based on each glyph's {@link OutlineShape} and `hheaTable' composed line height.
+ * </p>
+ * <p>
+ * This method is only exposed to validate the produced {@link OutlineShape} against {@link #getGlyphBounds(CharSequence)}.
+ * </p>
* @param transform optional given transform
* @param string string text
- * @return the bounding box of the given string in font em-size [0..1]
+ * @return the bounding box of the given string in font-units [0..1]
+ * @see #getGlyphShapeBounds(CharSequence)
+ * @see #getGlyphBounds(CharSequence)
+ * @see #getMetricBounds(CharSequence)
*/
- AABBox getPointsBounds(final AffineTransform transform, final CharSequence string);
+ AABBox getGlyphShapeBounds(final AffineTransform transform, final CharSequence string);
+
+ /**
+ * Returns accurate bounding box by taking each glyph's font em-sized {@link OutlineShape} into account.
+ * <p>
+ * Glyph shape bounds is based on each glyph's {@link OutlineShape} and `hheaTable' composed line height.
+ * </p>
+ * <p>
+ * This method is only exposed to validate the produced {@link OutlineShape} against {@link #getGlyphBounds(CharSequence)}.
+ * </p>
+ * @param string string text
+ * @return the bounding box of the given string in font-units [0..1]
+ * @see #getGlyphShapeBounds(AffineTransform, CharSequence)
+ * @see #getGlyphBounds(CharSequence)
+ * @see #getMetricBounds(CharSequence)
+ */
+ AABBox getGlyphShapeBounds(final CharSequence string);
boolean isPrintableChar(final char c);
- /** Shall return {@link #getFullFamilyName()} */
+ /**
+ * Visit each {@link Glyph}'s {@link OutlineShape} of the string with the {@link OutlineShape.Visitor}
+ * while passing the progressed {@link AffineTransform}.
+ * <p>
+ * The produced shapes are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor.
+ * </p>
+ * @param visitor handling each glyph's outline shape in font em-size [0..1] and the given {@link AffineTransform}
+ * @param transform optional given transform
+ * @param font the target {@link Font}
+ * @param string string text
+ * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] {@link OutlineShape} into account.
+ */
+ AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform,
+ final CharSequence string);
+
+ /**
+ * Visit each {@link Glyph}'s {@link OutlineShape} of the string with the {@link OutlineShape.Visitor}
+ * while passing the progressed {@link AffineTransform}.
+ * <p>
+ * The produced shapes are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor.
+ * </p>
+ * @param visitor handling each glyph's outline shape in font em-size [0..1] and the given {@link AffineTransform}
+ * @param transform optional given transform
+ * @param font the target {@link Font}
+ * @param string string text
+ * @param temp1 temporary AffineTransform storage, mandatory
+ * @param temp2 temporary AffineTransform storage, mandatory
+ * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] {@link OutlineShape} into account.
+ */
+ AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform,
+ final CharSequence string,
+ final AffineTransform temp1, final AffineTransform temp2);
+
+ /** Returns {@link #getFullFamilyName()} */
@Override
public String toString();
diff --git a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java
index 5c317bd2b..ac42274f6 100644
--- a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java
+++ b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2010-2023 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:
diff --git a/src/jogl/classes/com/jogamp/graph/font/FontSet.java b/src/jogl/classes/com/jogamp/graph/font/FontSet.java
index 60a16b241..607aab433 100644
--- a/src/jogl/classes/com/jogamp/graph/font/FontSet.java
+++ b/src/jogl/classes/com/jogamp/graph/font/FontSet.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2010-2023 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:
diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java
index b18d51849..7c9cb69c9 100644
--- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java
+++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java
@@ -30,6 +30,7 @@ package com.jogamp.graph.geom;
import java.util.ArrayList;
import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.graph.geom.plane.Winding;
import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.curve.Region;
import com.jogamp.opengl.math.FloatUtil;
@@ -54,6 +55,8 @@ public class Outline implements Comparable<Outline> {
private boolean closed;
private final AABBox bbox;
private boolean dirtyBBox;
+ private Winding winding;
+ private boolean dirtyWinding;
/**Create an outline defined by control vertices.
* An outline can contain off Curve vertices which define curved
@@ -64,14 +67,19 @@ public class Outline implements Comparable<Outline> {
closed = false;
bbox = new AABBox();
dirtyBBox = false;
+ winding = Winding.CCW;
+ dirtyWinding = false;
}
/**
* Copy ctor
*/
public Outline(final Outline src) {
- vertices = new ArrayList<Vertex>(src.vertices.size());
- for(int i=0; i<vertices.size(); i++) {
+ final int count = src.vertices.size();
+ vertices = new ArrayList<Vertex>(count);
+ winding = src.getWinding();
+ dirtyWinding = false;
+ for(int i=0; i<count; i++) {
vertices.add( src.vertices.get(i).clone() );
}
closed = src.closed;
@@ -79,6 +87,74 @@ public class Outline implements Comparable<Outline> {
dirtyBBox = src.dirtyBBox;
}
+ /**
+ * Copy ctor w/ enforced Winding
+ * <p>
+ * If the enforced {@link Winding} doesn't match the source Outline, the vertices reversed copied into this new instance.
+ * </p>
+ * @param src the source Outline
+ * @param enforce {@link Winding} to be enforced on this copy
+ */
+ public Outline(final Outline src, final Winding enforce) {
+ final int count = src.vertices.size();
+ vertices = new ArrayList<Vertex>(count);
+ final Winding had_winding = src.getWinding();;
+ winding = had_winding;
+ dirtyWinding = false;
+ if( enforce != had_winding ) {
+ for(int i=count-1; i>=0; --i) {
+ vertices.add( src.vertices.get(i).clone() );
+ }
+ winding = enforce;
+ } else {
+ for(int i=0; i<count; ++i) {
+ vertices.add( src.vertices.get(i).clone() );
+ }
+ }
+ closed = src.closed;
+ bbox = new AABBox(src.bbox);
+ dirtyBBox = src.dirtyBBox;
+ }
+
+ /**
+ * Sets {@link Winding} to this outline
+ * <p>
+ * If the enforced {@link Winding} doesn't match this Outline, the vertices are reversed.
+ * </p>
+ * @param enforce to be enforced {@link Winding}
+ */
+ public final void setWinding(final Winding enforce) {
+ final Winding had_winding = getWinding();
+ if( enforce != had_winding ) {
+ final int count = vertices.size();
+ final ArrayList<Vertex> ccw = new ArrayList<Vertex>(count);
+ for(int i=count-1; i>=0; --i) {
+ ccw.add(vertices.get(i));
+ }
+ vertices = ccw;
+ winding = enforce;
+ }
+ }
+
+ /**
+ * Compute the winding of the {@link #getLastOutline()} using the {@link #area(ArrayList)} function over all of its vertices.
+ * @return {@link Winding#CCW} or {@link Winding#CW}
+ */
+ public final Winding getWinding() {
+ if( !dirtyWinding ) {
+ return winding;
+ }
+ final int count = getVertexCount();
+ if( 3 > count ) {
+ winding = Winding.CCW;
+ } else {
+ final ArrayList<Vertex> vertices = getVertices();
+ winding = VectorUtil.getWinding(vertices);
+ }
+ dirtyWinding = false;
+ return winding;
+ }
+
public final int getVertexCount() {
return vertices.size();
}
@@ -107,6 +183,7 @@ public class Outline implements Comparable<Outline> {
if(!dirtyBBox) {
bbox.resize(vertex.getCoord());
}
+ dirtyWinding = true;
}
/** Replaces the {@link Vertex} element at the given {@code position}.
@@ -123,6 +200,7 @@ public class Outline implements Comparable<Outline> {
}
vertices.set(position, vertex);
dirtyBBox = true;
+ dirtyWinding = true;
}
public final Vertex getVertex(final int index){
@@ -141,6 +219,7 @@ public class Outline implements Comparable<Outline> {
*/
public final Vertex removeVertex(final int position) throws IndexOutOfBoundsException {
dirtyBBox = true;
+ dirtyWinding = true;
return vertices.remove(position);
}
diff --git a/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java
index 3ee504a29..d7e72e245 100644
--- a/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java
+++ b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java
@@ -888,25 +888,35 @@ public final class VectorUtil {
return triAreaVec2(a,b,c) > 0;
}
- /** Compute the winding of given points
+ /**
+ * Compute the winding of the 3 given points
+ * <p>
+ * Consider using {@link #getWinding(ArrayList)} using the {@link #area(ArrayList)} function over all points
+ * on complex shapes for a reliable result!
+ * </p>
* @param a first vertex
* @param b second vertex
* @param c third vertex
- * @return Winding
+ * @return {@link Winding#CCW} or {@link Winding#CW}
+ * @see #getWinding(ArrayList)
*/
public static Winding getWinding(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c) {
return triAreaVec2(a,b,c) > 0 ? Winding.CCW : Winding.CW ;
}
- /** Computes the area of a list of vertices to check if ccw
+ /**
+ * Computes the area of a list of vertices.
+ * <p>
+ * This method is utilized e.g. to reliably compute the {@link Winding} of complex shapes.
+ * </p>
* @param vertices
* @return positive area if ccw else negative area value
+ * @see #getWinding(ArrayList)
*/
public static float area(final ArrayList<? extends Vert2fImmutable> vertices) {
final int n = vertices.size();
float area = 0.0f;
- for (int p = n - 1, q = 0; q < n; p = q++)
- {
+ for (int p = n - 1, q = 0; q < n; p = q++) {
final float[] pCoord = vertices.get(p).getCoord();
final float[] qCoord = vertices.get(q).getCoord();
area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1];
@@ -914,9 +924,15 @@ public final class VectorUtil {
return area;
}
- /** Compute the general winding of the vertices
+ /**
+ * Compute the winding using the {@link #area(ArrayList)} function over all vertices for complex shapes.
+ * <p>
+ * Uses the {@link #area(ArrayList)} function over all points
+ * on complex shapes for a reliable result!
+ * </p>
* @param vertices array of Vertices
- * @return CCW or CW {@link Winding}
+ * @return {@link Winding#CCW} or {@link Winding#CW}
+ * @see #area(ArrayList)
*/
public static Winding getWinding(final ArrayList<? extends Vert2fImmutable> vertices) {
return area(vertices) >= 0 ? Winding.CCW : Winding.CW ;
diff --git a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java
index 369d0b493..331116f7e 100644
--- a/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java
+++ b/src/jogl/classes/jogamp/graph/curve/tess/CDTriangulator2D.java
@@ -80,20 +80,20 @@ public class CDTriangulator2D implements Triangulator {
@Override
public final void addCurve(final List<Triangle> sink, final Outline polyline, final float sharpness) {
- Loop loop = null;
-
- if(!loops.isEmpty()) {
- loop = getContainerLoop(polyline);
- }
+ Loop loop = getContainerLoop(polyline);
if(loop == null) {
- final GraphOutline outline = new GraphOutline(polyline);
+ final Winding winding = Winding.CCW; // -> HEdge.BOUNDARY
+ // Too late: polyline.setWinding(winding);
+ final GraphOutline outline = new GraphOutline(polyline); // , winding);
final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, false, sharpness);
// vertices.addAll(polyline.getVertices());
- loop = new Loop(innerPoly, Winding.CCW);
+ loop = new Loop(innerPoly, winding);
loops.add(loop);
} else {
- final GraphOutline outline = new GraphOutline(polyline);
+ // final Winding winding = Winding.CW; // -> HEdge.HOLE
+ // Not required, handled in Loop.initFromPolyline(): polyline.setWinding(winding);
+ final GraphOutline outline = new GraphOutline(polyline); // , winding);
final GraphOutline innerPoly = extractBoundaryTriangles(sink, outline, true, sharpness);
// vertices.addAll(innerPoly.getVertices());
loop.addConstraintCurve(innerPoly);
@@ -221,12 +221,15 @@ public class CDTriangulator2D implements Triangulator {
}
private Loop getContainerLoop(final Outline polyline) {
- final ArrayList<Vertex> vertices = polyline.getVertices();
- for(int i=0; i < loops.size(); i++) {
- final Loop loop = loops.get(i);
- for(int j=0; j < vertices.size(); j++) {
- if( loop.checkInside( vertices.get(j) ) ) {
- return loop;
+ final int count = loops.size();
+ if( 0 < count ) {
+ final ArrayList<Vertex> vertices = polyline.getVertices();
+ for(int i=0; i < count; i++) {
+ final Loop loop = loops.get(i);
+ for(int j=0; j < vertices.size(); j++) {
+ if( loop.checkInside( vertices.get(j) ) ) {
+ return loop;
+ }
}
}
}
diff --git a/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java b/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java
index 81e6efdad..75192d45a 100644
--- a/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java
+++ b/src/jogl/classes/jogamp/graph/curve/tess/GraphOutline.java
@@ -31,6 +31,7 @@ import java.util.ArrayList;
import com.jogamp.graph.geom.Outline;
import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.plane.Winding;
public class GraphOutline {
final private Outline outline;
@@ -40,11 +41,14 @@ public class GraphOutline {
this.outline = new Outline();
}
- /**Create a control polyline of control vertices
+ /**
+ * 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
+ *
+ * @param ol the source {@link Outline}
*/
- public GraphOutline(final Outline ol){
+ public GraphOutline(final Outline ol) {
this.outline = ol;
final ArrayList<Vertex> vertices = this.outline.getVertices();
for(int i = 0; i< vertices.size(); i++){
diff --git a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java
index 5d1bc051f..1d8264607 100644
--- a/src/jogl/classes/jogamp/graph/curve/tess/Loop.java
+++ b/src/jogl/classes/jogamp/graph/curve/tess/Loop.java
@@ -34,6 +34,7 @@ import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.plane.Winding;
import com.jogamp.graph.geom.Triangle;
import com.jogamp.opengl.math.VectorUtil;
+import com.jogamp.opengl.math.Vert2fImmutable;
import com.jogamp.opengl.math.geom.AABBox;
public class Loop {
@@ -91,9 +92,24 @@ public class Loop {
return (root.getNext().getNext().getNext() == root);
}
- /**Create a connected list of half edges (loop)
+ private static float area(final ArrayList<GraphVertex> vertices) {
+ final int n = vertices.size();
+ float area = 0.0f;
+ for (int p = n - 1, q = 0; q < n; p = q++) {
+ final float[] pCoord = vertices.get(p).getCoord();
+ final float[] qCoord = vertices.get(q).getCoord();
+ area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1];
+ }
+ return area;
+ }
+ private static Winding getWinding(final ArrayList<GraphVertex> vertices) {
+ return area(vertices) >= 0 ? Winding.CCW : Winding.CW ;
+ }
+
+ /**
+ * Create a connected list of half edges (loop)
* from the boundary profile
- * @param reqWinding requested winding of edges (CCW or CW)
+ * @param reqWinding requested winding of edges, either {@link Winding#CCW} for {@link HEdge#BOUNDARY} or {@link Winding#CW} for {@link HEdge#HOLE}
*/
private HEdge initFromPolyline(final GraphOutline outline, final Winding reqWinding){
final ArrayList<GraphVertex> vertices = outline.getGraphPoint();
@@ -101,57 +117,64 @@ public class Loop {
if(vertices.size()<3) {
throw new IllegalArgumentException("outline's vertices < 3: " + vertices.size());
}
- final Winding hasWinding = VectorUtil.getWinding(
- vertices.get(0).getPoint(),
- vertices.get(1).getPoint(),
- vertices.get(2).getPoint());
- //FIXME: handle case when vertices come inverted - Rami
- // skips inversion CW -> CCW
- final boolean invert = hasWinding != reqWinding &&
- reqWinding == Winding.CW;
-
- final int max;
+ final Winding hasWinding = getWinding( vertices ); // requires area-winding detection
+
final int edgeType = reqWinding == Winding.CCW ? HEdge.BOUNDARY : HEdge.HOLE ;
- int index;
HEdge firstEdge = null;
HEdge lastEdge = null;
- if(!invert) {
- max = vertices.size();
- index = 0;
- } else {
- max = -1;
- index = vertices.size() -1;
- }
-
- while(index != max){
- final GraphVertex v1 = vertices.get(index);
- box.resize(v1.getX(), v1.getY(), v1.getZ());
-
- final 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) {
+ /**
+ * The winding conversion CW -> CCW can't be resolved here (-> Rami?)
+ * Therefore we require outline boundaries to be in CCW, see API-doc comment in OutlineShape.
+ *
+ * Original comment:
+ * FIXME: handle case when vertices come inverted - Rami
+ * Skips inversion CW -> CCW
+ */
+ if( hasWinding == reqWinding || reqWinding == Winding.CCW ) {
+ // Correct Winding or skipped CW -> CCW (no inversion possible here, too late ??)
+ final int max = vertices.size() - 1;
+ for(int index = 0; index <= max; ++index) {
+ final GraphVertex v1 = vertices.get(index);
+ box.resize(v1.getX(), v1.getY(), v1.getZ());
+
+ final HEdge edge = new HEdge(v1, edgeType);
+
+ v1.addEdge(edge);
+ if(lastEdge != null) {
+ lastEdge.setNext(edge);
+ edge.setPrev(lastEdge);
+ } else {
+ firstEdge = edge;
+ }
+ if(index == max ) {
edge.setNext(firstEdge);
firstEdge.setPrev(edge);
}
- index++;
- } else {
+ lastEdge = edge;
+ }
+ } else { // if( reqWinding == Winding.CW ) {
+ // CCW -> CW
+ for(int index = vertices.size() - 1; index >= 0; --index) {
+ final GraphVertex v1 = vertices.get(index);
+ box.resize(v1.getX(), v1.getY(), v1.getZ());
+
+ final HEdge edge = new HEdge(v1, edgeType);
+
+ v1.addEdge(edge);
+ if(lastEdge != null) {
+ lastEdge.setNext(edge);
+ edge.setPrev(lastEdge);
+ } else {
+ firstEdge = edge;
+ }
+
if (index == 0) {
edge.setNext(firstEdge);
firstEdge.setPrev(edge);
}
- index--;
+ lastEdge = edge;
}
- lastEdge = edge;
}
return firstEdge;
}
@@ -159,7 +182,7 @@ public class Loop {
public void addConstraintCurve(final GraphOutline polyline) {
// GraphOutline outline = new GraphOutline(polyline);
/**needed to generate vertex references.*/
- initFromPolyline(polyline, Winding.CW);
+ initFromPolyline(polyline, Winding.CW); // -> HEdge.HOLE
final GraphVertex v3 = locateClosestVertex(polyline);
final HEdge v3Edge = v3.findBoundEdge();
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
index 382d35a73..f5358b74b 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2011-2023 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:
@@ -56,7 +56,7 @@ class TypecastFont implements Font {
/* pp */ final TTFont font;
private final CmapFormat cmapFormat;
private final int cmapentries;
- private final IntObjectHashMap char2Glyph;
+ private final IntObjectHashMap idToGlyph;
private final TypecastHMetrics metrics;
// FIXME: Add cache size to limit memory usage ??
@@ -147,7 +147,7 @@ class TypecastFont implements Font {
}
}
}
- char2Glyph = new IntObjectHashMap(cmapentries + cmapentries/4);
+ idToGlyph = new IntObjectHashMap(cmapentries + cmapentries/4);
metrics = new TypecastHMetrics(this);
}
@@ -197,11 +197,9 @@ class TypecastFont implements Font {
}
@Override
- public Glyph getGlyph(final char symbol) {
- TypecastGlyph result = (TypecastGlyph) char2Glyph.get(symbol);
+ public Glyph getGlyph(final int glyph_id) {
+ TypecastGlyph result = (TypecastGlyph) idToGlyph.get(glyph_id);
if (null == result) {
- final int glyph_id = getGlyphID( symbol );
-
jogamp.graph.font.typecast.ot.Glyph glyph = font.getGlyph(glyph_id);
final int glyph_advance;
final AABBox glyph_bbox;
@@ -222,9 +220,9 @@ class TypecastFont implements Font {
break;
}
if(null == glyph) {
- throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+glyph_id);
+ throw new RuntimeException("Could not retrieve glyph for glyph id "+glyph_id);
}
- final OutlineShape shape = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), symbol, glyph, vertexFactory);
+ final OutlineShape shape = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), glyph, vertexFactory);
KernSubtable kernSub = null;
{
final KernTable kern = font.getKernTable();
@@ -232,17 +230,17 @@ class TypecastFont implements Font {
kernSub = kern.getSubtable0();
}
}
- result = new TypecastGlyph(this, symbol, glyph_id, glyph_bbox, glyph_advance, kernSub, shape);
+ result = new TypecastGlyph(this, glyph_id, glyph_bbox, glyph_advance, kernSub, shape);
if(DEBUG) {
final PostTable post = font.getPostTable();
final String glyph_name = null != post ? post.getGlyphName(glyph_id) : "n/a";
- System.err.println("New glyph: " + (int)symbol + " ( " + symbol +" ) -> " + glyph_id + "/'"+glyph_name+"', contours " + glyph.getPointCount() + ": " + shape);
+ System.err.println("New glyph: " + glyph_id + "/'"+glyph_name+"', contours " + glyph.getPointCount() + ": " + shape);
System.err.println(" "+glyph);
System.err.println(" "+result);
}
glyph.clearPointData();
- char2Glyph.put(symbol, result);
+ idToGlyph.put(glyph_id, result);
}
return result;
}
@@ -256,106 +254,154 @@ class TypecastFont implements Font {
public int getLineHeightFU() {
final Metrics metrics = getMetrics();
final int lineGap = metrics.getLineGapFU() ; // negative value!
- final int ascent = metrics.getAscentFU() ; // negative value!
final int descent = metrics.getDescentFU() ; // positive value!
+ final int ascent = metrics.getAscentFU() ; // negative value!
final int advanceY = lineGap - descent + ascent; // negative value!
return -advanceY;
}
@Override
- public float getMetricWidth(final CharSequence string) {
- return metrics.getScale( getMetricWidthFU(string) );
+ public AABBox getMetricBounds(final CharSequence string) {
+ return getMetricBoundsFU(string).scale2(1.0f/metrics.getUnitsPerEM(), new float[3]);
}
@Override
- public int getMetricWidthFU(final CharSequence string) {
- int width = 0;
- final int len = string.length();
- for (int i=0; i< len; i++) {
+ public AABBox getMetricBoundsFU(final CharSequence string) {
+ if (null == string || 0 == string.length() ) {
+ return new AABBox();
+ }
+ final AABBox res = new AABBox();
+ final int charCount = string.length();
+
+ final int lineHeight = getLineHeightFU();
+
+ int y = 0;
+ int advanceTotal = 0;
+
+ for (int i=0; i<charCount; i++) {
final char character = string.charAt(i);
if (character == '\n') {
- width = 0;
+ advanceTotal = 0;
+ y -= lineHeight;
+ } else if (character == ' ') {
+ advanceTotal += getAdvanceWidthFU(Glyph.ID_SPACE);
} else {
- final Glyph glyph = getGlyph(character);
- width += glyph.getAdvanceFU();
+ advanceTotal += getAdvanceWidthFU( getGlyphID( character ) );
}
+ res.resize(advanceTotal, y, 0f);
+ }
+ if( 0 < advanceTotal ) {
+ // add one line for current non '\n' terminated
+ y -= lineHeight;
+ res.resize(advanceTotal, y, 0f);
}
- return width;
+ return res;
}
@Override
- public float getMetricHeight(final CharSequence string) {
- return metrics.getScale( getMetricHeightFU(string) );
+ public AABBox getGlyphBounds(final CharSequence string) {
+ return getGlyphBoundsFU(string).scale2(1.0f/metrics.getUnitsPerEM(), new float[3]);
}
@Override
- public int getMetricHeightFU(final CharSequence string) {
- int height = 0;
+ public AABBox getGlyphBoundsFU(final CharSequence string) {
+ if (null == string || 0 == string.length() ) {
+ return new AABBox();
+ }
+ final AffineTransform temp1 = new AffineTransform();
+ final AffineTransform temp2 = new AffineTransform();
+
+ final AABBox res = new AABBox();
+ final int charCount = string.length();
+
+ final int lineHeight = getLineHeightFU();
- for (int i=0; i<string.length(); i++) {
+ int y = 0;
+ int advanceTotal = 0;
+ Font.Glyph left_glyph = null;
+ final AABBox temp_box = new AABBox();
+
+ for(int i=0; i< charCount; i++) {
final char character = string.charAt(i);
- if (character != ' ') {
- final Glyph glyph = getGlyph(character);
- height = (int)Math.ceil(Math.max(glyph.getBBoxFU().getHeight(), height));
+ if( '\n' == character ) {
+ y -= lineHeight;
+ advanceTotal = 0;
+ left_glyph = null;
+ } else if (character == ' ') {
+ advanceTotal += getAdvanceWidthFU(Glyph.ID_SPACE);
+ left_glyph = null;
+ } else {
+ // reset transform
+ temp1.setToIdentity();
+ final int glyph_id = getGlyphID(character);
+ final Font.Glyph glyph = getGlyph(glyph_id);
+ final OutlineShape glyphShape = glyph.getShape();
+ if( null == glyphShape ) {
+ left_glyph = null;
+ continue;
+ }
+ if( null != left_glyph ) {
+ advanceTotal += left_glyph.getKerningFU(glyph_id);
+ }
+ temp1.translate(advanceTotal, y, temp2);
+ res.resize(temp1.transform(glyph.getBBoxFU(), temp_box));
+ advanceTotal += glyph.getAdvanceFU();
+ left_glyph = glyph;
}
}
- return height;
+ return res;
}
@Override
- public AABBox getMetricBounds(final CharSequence string) {
- return getMetricBoundsFU(string).scale2(1.0f/metrics.getUnitsPerEM(), new float[3]);
+ public AABBox getGlyphShapeBounds(final CharSequence string) {
+ return getGlyphShapeBounds(null, string);
}
-
@Override
- public AABBox getMetricBoundsFU(final CharSequence string) {
- if (string == null) {
+ public AABBox getGlyphShapeBounds(final AffineTransform transform, final CharSequence string) {
+ if (null == string || 0 == string.length() ) {
return new AABBox();
}
- final int charCount = string.length();
- final int lineHeight = getLineHeightFU();
- int totalHeight = 0;
- int totalWidth = 0;
- int curLineWidth = 0;
- for (int i=0; i<charCount; i++) {
- final char character = string.charAt(i);
- if (character == '\n') {
- totalWidth = Math.max(curLineWidth, totalWidth);
- curLineWidth = 0;
- totalHeight += lineHeight;
- continue;
- }
- curLineWidth += getAdvanceWidthFU( getGlyphID( character ) );
- }
- if (curLineWidth > 0) {
- totalHeight += lineHeight;
- totalWidth = Math.max(curLineWidth, totalWidth);
- }
- return new AABBox(0, 0, 0, totalWidth, totalHeight,0);
+ final OutlineShape.Visitor visitor = new OutlineShape.Visitor() {
+ @Override
+ public final void visit(final OutlineShape shape, final AffineTransform t) {
+ // nop
+ } };
+ return processString(visitor, transform, string);
+ }
+
+ @Override
+ public AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform,
+ final CharSequence string) {
+ return processString(visitor, transform, string, new AffineTransform(), new AffineTransform());
}
@Override
- public AABBox getPointsBoundsFU(final AffineTransform transform, final CharSequence string) {
- if (string == null) {
+ public AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform,
+ final CharSequence string,
+ final AffineTransform temp1, final AffineTransform temp2) {
+ if (null == string || 0 == string.length() ) {
return new AABBox();
}
- final AffineTransform temp1 = new AffineTransform();
- final AffineTransform temp2 = new AffineTransform();
- final int charCount = string.length();
- final int lineHeight = getLineHeightFU();
- final AABBox tbox = new AABBox();
final AABBox res = new AABBox();
+ final int charCount = string.length();
+
+ // region.setFlipped(true);
+ final float lineHeight = getLineHeight();
float y = 0;
float advanceTotal = 0;
+ Font.Glyph left_glyph = null;
+ final AABBox temp_box = new AABBox();
for(int i=0; i< charCount; i++) {
final char character = string.charAt(i);
if( '\n' == character ) {
y -= lineHeight;
advanceTotal = 0;
+ left_glyph = null;
} else if (character == ' ') {
- advanceTotal += getAdvanceWidthFU(Glyph.ID_SPACE);
+ advanceTotal += getAdvanceWidth(Glyph.ID_SPACE);
+ left_glyph = null;
} else {
// reset transform
if( null != transform ) {
@@ -363,28 +409,28 @@ class TypecastFont implements Font {
} else {
temp1.setToIdentity();
}
- temp1.translate(advanceTotal, y, temp2);
- tbox.reset();
-
- final Font.Glyph glyph = getGlyph(character);
- res.resize(temp1.transform(glyph.getBBoxFU(), tbox));
-
+ final int glyph_id = getGlyphID(character);
+ final Font.Glyph glyph = getGlyph(glyph_id);
final OutlineShape glyphShape = glyph.getShape();
if( null == glyphShape ) {
+ left_glyph = null;
continue;
}
- advanceTotal += glyph.getAdvanceFU();
+ if( null != left_glyph ) {
+ advanceTotal += left_glyph.getKerning(glyph_id);
+ }
+ temp1.translate(advanceTotal, y, temp2);
+ res.resize(temp1.transform(glyphShape.getBounds(), temp_box));
+
+ visitor.visit(glyphShape, temp1);
+ advanceTotal += glyph.getAdvance();
+ left_glyph = glyph;
}
}
return res;
}
@Override
- public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string) {
- return getPointsBoundsFU(transform, string).scale2(1.0f/metrics.getUnitsPerEM(), new float[3]);
- }
-
- @Override
final public int getNumGlyphs() {
return font.getNumGlyphs();
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
index e6a655661..12d492f6e 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2011-2023 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:
@@ -65,7 +65,7 @@ public final class TypecastGlyph implements Font.Glyph {
/** in font-units */
public final AABBox getBBoxFU() { return this.bbox; }
- /** Return advance in font units to be divided by unitsPerEM */
+ /** Return advance in font units, sourced from `hmtx` table. */
public final int getAdvanceFU() { return this.advance; }
@Override
@@ -104,7 +104,6 @@ public final class TypecastGlyph implements Font.Glyph {
return dst;
}
- private final char symbol;
private final int id;
private final int[/*right_glyphid*/][/*value*/] kerning;
private final boolean kerning_horizontal;
@@ -115,15 +114,13 @@ public final class TypecastGlyph implements Font.Glyph {
/**
*
* @param font
- * @param symbol
* @param id
* @param bbox in font-units
* @param advance from hmtx in font-units
* @param shape
*/
- protected TypecastGlyph(final TypecastFont font, final char symbol, final int id, final AABBox bbox, final int advance,
+ protected TypecastGlyph(final TypecastFont font, final int id, final AABBox bbox, final int advance,
final KernSubtable kernSub, final OutlineShape shape) {
- this.symbol = symbol;
this.id = id;
if( null != kernSub && kernSub.areKerningValues() ) {
int pair_sz = 64;
@@ -160,11 +157,6 @@ public final class TypecastGlyph implements Font.Glyph {
return this.metrics.getFont();
}
- @Override
- public final char getSymbol() {
- return this.symbol;
- }
-
public final Metrics getMetrics() {
return this.metrics;
}
@@ -195,6 +187,12 @@ public final class TypecastGlyph implements Font.Glyph {
}
@Override
+ public final AABBox getBBox() {
+ final AABBox dest = new AABBox();
+ return dest.copy(metrics.getBBoxFU()).scale2(1.0f/metrics.getUnitsPerEM(), new float[2]);
+ }
+
+ @Override
public final int getAdvanceFU() { return metrics.getAdvanceFU(); }
@Override
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
index 274114e4a..cae8b2755 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2011-2023 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:
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
index 60319d842..2a895a2f1 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2011 JogAmp Community. All rights reserved.
+ * Copyright 2011-2023 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:
@@ -27,7 +27,9 @@
*/
package jogamp.graph.font.typecast;
+import jogamp.graph.font.typecast.ot.Glyph;
import jogamp.graph.font.typecast.ot.Point;
+import jogamp.graph.font.typecast.ot.T2Glyph;
import jogamp.opengl.Debug;
import com.jogamp.graph.curve.OutlineShape;
@@ -46,50 +48,76 @@ import com.jogamp.graph.geom.Vertex.Factory;
*/
public class TypecastRenderer {
private static final boolean DEBUG = Debug.debug("graph.font.Renderer");
+ private static final boolean PRINT_CODE = Debug.debug("graph.font.Renderer.Code");
private static void addShapeMoveTo(final float unitsPerEM, final OutlineShape shape, final Point p1) {
- if( DEBUG ) { System.err.println("Shape.MoveTo: "+p1); }
+ if( PRINT_CODE ) {
+ System.err.println("// Shape.MoveTo:");
+ System.err.printf("shape.closeLastOutline(false);%n");
+ System.err.printf("shape.addEmptyOutline();%n");
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p1.x/unitsPerEM, p1.y/unitsPerEM, true);
+ }
shape.closeLastOutline(false);
shape.addEmptyOutline();
- shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, p1.onCurve);
+ shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, true);
}
private static void addShapeLineTo(final float unitsPerEM, final OutlineShape shape, final Point p1) {
- if( DEBUG ) { System.err.println("Shape.LineTo: "+p1); }
- shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, p1.onCurve);
+ if( PRINT_CODE ) {
+ System.err.println("// Shape.LineTo:");
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p1.x/unitsPerEM, p1.y/unitsPerEM, true);
+ }
+ shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, true);
}
private static void addShapeQuadTo(final float unitsPerEM, final OutlineShape shape, final Point p1, final Point p2) {
- if( DEBUG ) { System.err.println("Shape.QuadTo: "+p1+", "+p2); }
- shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, p1.onCurve);
- shape.addVertex(0, p2.x/unitsPerEM, p2.y/unitsPerEM, p2.onCurve);
+ if( PRINT_CODE ) {
+ System.err.println("// Shape.QuadTo:");
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p2.x/unitsPerEM, p2.y/unitsPerEM, true);
+ }
+ shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ shape.addVertex(0, p2.x/unitsPerEM, p2.y/unitsPerEM, true);
+ }
+ private static void addShapeQuadTo(final float unitsPerEM, final OutlineShape shape, final Point p1, final float p2x, final float p2y) {
+ if( PRINT_CODE ) {
+ System.err.println("// Shape.QuadTo:");
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p2x/unitsPerEM, p2y/unitsPerEM, true);
+ }
+ shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ shape.addVertex(0, p2x/unitsPerEM, p2y/unitsPerEM, true);
}
- private static void addShapeQuadTo(final float unitsPerEM, final OutlineShape shape, final Point p1,
- final float p2x, final float p2y, final boolean p2OnCurve) {
- if( DEBUG ) { System.err.println("Shape.QuadTo: "+p1+", p2 "+p2x+", "+p2y+", onCurve "+p2OnCurve); }
- shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, p1.onCurve);
- shape.addVertex(0, p2x/unitsPerEM, p2y/unitsPerEM, p2OnCurve);
+ private static void addShapeCubicTo(final float unitsPerEM, final OutlineShape shape, final Point p1, final Point p2, final Point p3) {
+ if( PRINT_CODE ) {
+ System.err.println("// Shape.CubicTo:");
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p2.x/unitsPerEM, p2.y/unitsPerEM, false);
+ System.err.printf("shape.addVertex(%d, %ff, %ff, %b);%n", 0, p3.x/unitsPerEM, p3.y/unitsPerEM, true);
+ }
+ shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, false);
+ shape.addVertex(0, p2.x/unitsPerEM, p2.y/unitsPerEM, false);
+ shape.addVertex(0, p3.x/unitsPerEM, p3.y/unitsPerEM, true);
}
- /**
- private static void addShapeCubicTo(final float unitsPerEM, final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1, Point p2, Point p3) {
- shape.addVertex(0, p1.x/unitsPerEM, p1.y/unitsPerEM, p1.onCurve);
- shape.addVertex(0, p2.x/unitsPerEM, p2.y/unitsPerEM, p2.onCurve);
- shape.addVertex(0, p3.x/unitsPerEM, p3.y/unitsPerEM, p3.onCurve);
- } */
- public static OutlineShape buildShape(final int unitsPerEM, final char symbol, final jogamp.graph.font.typecast.ot.Glyph glyph, final Factory<? extends Vertex> vertexFactory) {
- //
- // See Typecast: GlyphPathFactory.addContourToPath(..)
- //
+ public static OutlineShape buildShape(final int unitsPerEM, final Glyph glyph, final Factory<? extends Vertex> vertexFactory) {
if (glyph == null) {
return null;
}
-
final OutlineShape shape = new OutlineShape(vertexFactory);
- buildShapeImpl(unitsPerEM, shape, symbol, glyph);
- shape.setIsQuadraticNurbs();
+ if (glyph instanceof T2Glyph) {
+ // Type 1/2: Cubic
+ if( PRINT_CODE ) { System.err.printf("%n// Start Type-2 Shape for Glyph %d%n", glyph.getID()); }
+ buildShapeType2(unitsPerEM, shape, (T2Glyph)glyph);
+ } else {
+ // TTF: quadratic only
+ if( PRINT_CODE ) { System.err.printf("%n// Start TTF Shape for Glyph %d%n", glyph.getID()); }
+ buildShapeTTF(unitsPerEM, shape, glyph);
+ shape.setIsQuadraticNurbs();
+ }
+ if( PRINT_CODE ) { System.err.printf("// End Shape for Glyph %d%n%n", glyph.getID()); }
return shape;
}
- private static void buildShapeImpl(final float unitsPerEM, final OutlineShape shape, final char symbol, final jogamp.graph.font.typecast.ot.Glyph glyph) {
+ private static void buildShapeTTF(final float unitsPerEM, final OutlineShape shape, final Glyph glyph) {
// 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 startIndex = 0;
@@ -99,98 +127,125 @@ public class TypecastRenderer {
count++;
if ( glyph.getPoint(i).endOfContour ) {
int offset = 0;
- while ( offset < count - 1 ) { // require at least +1 point (last one is end-of-contour)
- final Point p0 = glyph.getPoint(startIndex + offset%count);
- final Point p1 = glyph.getPoint(startIndex + (offset+1)%count);
- final Point p2 = glyph.getPoint(startIndex + (offset+2)%count);
- final Point p3 = offset+3 < count ? glyph.getPoint(startIndex + offset+3) : null;
+ while ( offset < count ) {
+ final int point_0_idx = startIndex + offset%count;
+ final Point point_m = glyph.getPoint((offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count);
+ final Point point_0 = glyph.getPoint(point_0_idx);
+ final Point point_1 = glyph.getPoint(startIndex + (offset+1)%count);
+ final Point point_2 = glyph.getPoint(startIndex + (offset+2)%count);
+ // final Point point_3 = offset+3 < count ? glyph.getPoint(startIndex + offset+3) : null;
if( DEBUG ) {
- System.err.println("GlyphShape<"+symbol+">: offset "+offset+" of "+count+"/"+totalPoints+" points");
- final int pMIdx= (offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count;
- final Point pM = glyph.getPoint(pMIdx);
- final int p0Idx = startIndex + offset%count;
- final int p1Idx = startIndex + (offset+1)%count;
- final int p2Idx = startIndex + (offset+2)%count;
- final int p3Idx = startIndex + (offset+3)%count;
- System.err.println("\t pM["+pMIdx+"] "+pM);
- System.err.println("\t p0["+p0Idx+"] "+p0);
- System.err.println("\t p1["+p1Idx+"] "+p1);
- System.err.println("\t p2["+p2Idx+"] "+p2);
- System.err.println("\t p3["+p3Idx+"] "+p3);
+ System.err.println("GlyphShape<"+glyph.getID()+">: offset "+offset+" of "+count+"/"+totalPoints+" points");
+ final int point_m_idx= (offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count;
+ final int point_1_idx = startIndex + (offset+1)%count;
+ final int point_2_idx = startIndex + (offset+2)%count;
+ // final int point_3_idx = startIndex + (offset+3)%count;
+ System.err.println("\t pM["+point_m_idx+"] "+point_m);
+ System.err.println("\t p0["+point_0_idx+"] "+point_0);
+ System.err.println("\t p1["+point_1_idx+"] "+point_1);
+ System.err.println("\t p2["+point_2_idx+"] "+point_2);
+ // System.err.println("\t p3["+point_3_idx+"] "+point_3);
}
if(offset == 0) {
- addShapeMoveTo(unitsPerEM, shape, p0);
- // gp.moveTo(point.x, point.y);
+ addShapeMoveTo(unitsPerEM, shape, point_0); // OK
}
-
- if( p0.endOfContour ) {
- // Branch-0: EOC ** SHALL NEVER HAPPEN **
- if( DEBUG ) { System.err.println("B0 .. end-of-contour **** EOC"); }
- shape.closeLastOutline(false);
- break;
- } else if (p0.onCurve) {
- if (p1.onCurve) {
+ if (point_0.onCurve) {
+ if (point_1.onCurve) {
// Branch-1: point.onCurve && p1.onCurve
- if( DEBUG ) { System.err.println("B1 .. line-to p0-p1"); }
-
- // s = new Line2D.Float(point.x, point.y, p1.x, p1.y);
- // gp.lineTo( p1.x, p1.y );
- addShapeLineTo(unitsPerEM, shape, p1);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B1: line-to p0-p1%n", point_0_idx); }
+ addShapeLineTo(unitsPerEM, shape, point_1); // OK
offset++;
} else {
- if (p2.onCurve) {
+ if (point_2.onCurve) {
// Branch-2: point.onCurve && !p1.onCurve && p2.onCurve
- if( DEBUG ) { System.err.println("B2 .. quad-to p0-p1-p2"); }
-
- // s = new QuadCurve2D.Float( point.x, point.y, p1.x, p1.y, p2.x, p2.y);
- // gp.quadTo(p1.x, p1.y, p2.x, p2.y);
- addShapeQuadTo(unitsPerEM, shape, p1, p2);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B2: quad-to p0-p1-p2%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_1, point_2); // OK
offset+=2;
} else {
- if (null != p3 && p3.onCurve) {
+ /** if (null != point_3 && point_3.onCurve) {
+ // Not required, handled via B4 and subsequent B6!
// Branch-3: point.onCurve && !p1.onCurve && !p2.onCurve && p3.onCurve
- if( DEBUG ) { System.err.println("B3 .. 2-quad p0-p1-p1_2, p1_2-p2-p3 **** 2QUAD"); }
- // addShapeCubicTo(shape, vertexFactory, p1, p2, p3);
- addShapeQuadTo(unitsPerEM, shape, p1, midValue(p1.x, p2.x),
- midValue(p1.y, p2.y),
- true);
- addShapeQuadTo(unitsPerEM, shape, p2, p3);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B3: p0-p1-p1_2, p1_2-p2-p3 **** 2QUAD%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_1, midValue(point_1.x, point_2.x),
+ midValue(point_1.y, point_2.y));
+ addShapeQuadTo(unitsPerEM, shape, point_2, point_3);
offset+=3;
- } else {
+ } else */ {
// Branch-4: point.onCurve && !p1.onCurve && !p2.onCurve && !p3.onCurve
- if( DEBUG ) { System.err.println("B4 .. quad-to p0-p1-p2h **** MID"); }
-
- // s = new QuadCurve2D.Float(point.x,point.y,p1.x,p1.y,
- // midValue(p1.x, p2.x), midValue(p1.y, p2.y));
- // gp.quadTo(p1.x, p1.y, midValue(p1.x, p2.x), midValue(p1.y, p2.y));
- addShapeQuadTo(unitsPerEM, shape, p1, midValue(p1.x, p2.x),
- midValue(p1.y, p2.y),
- true);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B4: quad-to p0-p1-p2h **** MID%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_1, midValue(point_1.x, point_2.x),
+ midValue(point_1.y, point_2.y));
offset+=2; // Skip p2 as done in Typecast
}
}
}
} else {
- if (!p1.onCurve) {
+ if (!point_1.onCurve) {
// Branch-5: !point.onCurve && !p1.onCurve
- if( DEBUG ) { System.err.println("B5 .. quad-to pMh-p0-p1h ***** MID"); }
- // s = new QuadCurve2D.Float(midValue(pM.x, point.x), midValue(pM.y, point.y),
- // point.x, point.y,
- // midValue(point.x, p1.x), midValue(point.y, p1.y));
- addShapeQuadTo(unitsPerEM, shape, p0, midValue(p0.x, p1.x),
- midValue(p0.y, p1.y), true);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B5: quad-to pMh-p0-p1h ***** MID%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_0, midValue(point_0.x, point_1.x), // OK
+ midValue(point_0.y, point_1.y) );
offset++;
} else {
// Branch-6: !point.onCurve && p1.onCurve
- if( DEBUG ) { System.err.println("B6 .. quad-to pMh-p0-p1"); }
- // s = new QuadCurve2D.Float(midValue(pM.x, point.x), midValue(pM.y, point.y),
- // point.x, point.y, p1.x, p1.y);
- // gp.quadTo(point.x, point.y, p1.x, p1.y);
- addShapeQuadTo(unitsPerEM, shape, p0, p1);
+ if( PRINT_CODE ) { System.err.printf("// %03d: B6: quad-to pMh-p0-p1%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_0, point_1); // OK
offset++;
}
}
}
+ if( PRINT_CODE ) { System.err.printf("shape.closeLastOutline(false);%n%n"); }
+ shape.closeLastOutline(false);
+ startIndex = i + 1;
+ count = 0;
+ }
+ }
+ }
+
+ private static void buildShapeType2(final float unitsPerEM, final OutlineShape shape, final T2Glyph glyph) {
+ // 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 startIndex = 0;
+ int count = 0;
+ final int totalPoints = glyph.getPointCount();
+ for (int i = 0; i < totalPoints; i++) {
+ count++;
+ if ( glyph.getPoint(i).endOfContour ) {
+ int offset = 0;
+ while ( offset < count ) {
+ final int point_0_idx = startIndex + offset%count;
+ final Point point_0 = glyph.getPoint(point_0_idx);
+ final Point point_1 = glyph.getPoint(startIndex + (offset+1)%count);
+ final Point point_2 = glyph.getPoint(startIndex + (offset+2)%count);
+ final Point point_3 = glyph.getPoint(startIndex + (offset+3)%count);
+ if( DEBUG ) {
+ System.err.println("GlyphShape<"+glyph.getID()+">: offset "+offset+" of "+count+"/"+totalPoints+" points");
+ final int point_1_idx = startIndex + (offset+1)%count;
+ final int point_2_idx = startIndex + (offset+2)%count;
+ final int point_3_idx = startIndex + (offset+3)%count;
+ System.err.println("\t p0["+point_0_idx+"] "+point_0);
+ System.err.println("\t p1["+point_1_idx+"] "+point_1);
+ System.err.println("\t p2["+point_2_idx+"] "+point_2);
+ System.err.println("\t p3["+point_3_idx+"] "+point_3);
+ }
+ if(offset == 0) {
+ addShapeMoveTo(unitsPerEM, shape, point_0); // OK
+ }
+ if (point_0.onCurve && point_1.onCurve) {
+ // Branch-1: point.onCurve && p1.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: C1: line-to p0-p1%n", point_0_idx); }
+ addShapeLineTo(unitsPerEM, shape, point_1); // OK
+ offset++;
+ } else if (point_0.onCurve && !point_1.onCurve && !point_2.onCurve && point_3.onCurve) {
+ if( PRINT_CODE ) { System.err.printf("// %03d: C2: cubic-to p0-p1-p2%n", point_0_idx); }
+ addShapeCubicTo(unitsPerEM, shape, point_0, point_2, point_3);
+ offset+=3;
+ } else {
+ System.out.println("addContourToPath case not catered for!!");
+ break;
+ }
+ }
+ if( PRINT_CODE ) { System.err.printf("shape.closeLastOutline(false);%n%n"); }
shape.closeLastOutline(false);
startIndex = i + 1;
count = 0;
@@ -198,7 +253,103 @@ public class TypecastRenderer {
}
}
- private static float midValue(final float a, final float b) {
- return a + (b - a)/2f;
+ /**
+ * Returns the mid-value of two.
+ * <p>
+ * Intentionally using integer arithmetic on unitPerEM sized values w/o rounding.
+ * </p>
+ */
+ private static int midValue(final int a, final int b) {
+ return a + (b - a)/2;
+ }
+
+ //
+ // Leaving Typecast's orig rendering loop in here, transformed to using our OutlineShape.
+ // This is now actually the same since ours has been re-aligned on 2023-02-15.
+ //
+
+ @SuppressWarnings("unused")
+ private static void buildShapeImplX(final float unitsPerEM, final OutlineShape shape, final Glyph glyph) {
+ // 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;
+ final int totalPoints = glyph.getPointCount();
+ if (glyph instanceof T2Glyph) {
+ // addContourToPath(unitsPerEM, shape, (T2Glyph) glyph, firstIndex, count);
+ throw new RuntimeException("T2Glyph Not Yet Supported: "+glyph);
+ }
+ if( PRINT_CODE ) { System.err.printf("%n// Start Shape for Glyph %d%n", glyph.getID()); }
+ for (int i = 0; i < totalPoints; i++) {
+ count++;
+ if ( glyph.getPoint(i).endOfContour ) {
+ addContourToPathX1(unitsPerEM, shape, glyph, firstIndex, count);
+ firstIndex = i + 1;
+ count = 0;
+ }
+ }
+ if( PRINT_CODE ) { System.err.printf("// End Shape for Glyph %d%n%n", glyph.getID()); }
+ }
+ private static void addContourToPathX1(final float unitsPerEM, final OutlineShape shape, final Glyph glyph, final int startIndex, final int count) {
+ int offset = 0;
+ while ( offset < count ) {
+ final int point_0_idx = startIndex + offset%count;
+ final Point point_m = glyph.getPoint((offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count);
+ final Point point_0 = glyph.getPoint(point_0_idx);
+ final Point point_1 = glyph.getPoint(startIndex + (offset+1)%count);
+ final Point point_2 = glyph.getPoint(startIndex + (offset+2)%count);
+ // final Point point_3 = offset+3 < count ? glyph.getPoint(startIndex + offset+3) : null;
+ if( DEBUG ) {
+ System.err.println("GlyphShape<"+glyph.getID()+">: offset "+offset+" of "+count+" points");
+ final int point_m_idx= (offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count;
+ final int point_1_idx = startIndex + (offset+1)%count;
+ final int point_2_idx = startIndex + (offset+2)%count;
+ // final int p3Idx = startIndex + (offset+3)%count;
+ System.err.println("\t pM["+point_m_idx+"] "+point_m);
+ System.err.println("\t p0["+point_0_idx+"] "+point_0);
+ System.err.println("\t p1["+point_1_idx+"] "+point_1);
+ System.err.println("\t p2["+point_2_idx+"] "+point_2);
+ // System.err.println("\t p3["+p3Idx+"] "+point_3);
+ }
+ if(offset == 0) {
+ addShapeMoveTo(unitsPerEM, shape, point_0);
+ }
+ if (point_0.onCurve && point_1.onCurve) {
+ // Branch-1: point.onCurve && p1.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: B1: line-to p0-p1%n", point_0_idx); }
+ addShapeLineTo(unitsPerEM, shape, point_1);
+ offset++;
+ } else if (point_0.onCurve && !point_1.onCurve && point_2.onCurve) {
+ // Branch-2: point.onCurve && !p1.onCurve && p2.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: B2: quad-to p0-p1-p2%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_1, point_2);
+ offset+=2;
+ } else if (point_0.onCurve && !point_1.onCurve && !point_2.onCurve) {
+ // Branch-4: point.onCurve && !p1.onCurve && !p2.onCurve && !p3.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: B4: quad-to p0-p1-p2h **** MID%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_1,
+ midValue(point_1.x, point_2.x),
+ midValue(point_1.y, point_2.y));
+
+ offset+=2;
+ } else if (!point_0.onCurve && !point_1.onCurve) {
+ // Branch-5: !point.onCurve && !p1.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: B5: quad-to pMh-p0-p1h ***** MID%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_0,
+ midValue(point_0.x, point_1.x),
+ midValue(point_0.y, point_1.y) );
+ offset++;
+ } else if (!point_0.onCurve && point_1.onCurve) {
+ // Branch-6: !point.onCurve && p1.onCurve
+ if( PRINT_CODE ) { System.err.printf("// %03d: B6: quad-to pMh-p0-p1%n", point_0_idx); }
+ addShapeQuadTo(unitsPerEM, shape, point_0, point_1);
+ offset++;
+ } else {
+ System.out.println("addContourToPath case not catered for!!");
+ break;
+ }
+ }
+ if( PRINT_CODE ) { System.err.printf("shape.closeLastOutline(false);%n%n"); }
+ shape.closeLastOutline(false);
}
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/Glyph.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/Glyph.java
index 7b29a6c48..5bb610a1c 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/ot/Glyph.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/Glyph.java
@@ -31,25 +31,31 @@ public abstract class Glyph {
public Glyph(final int glyph_id) {
_glyph_id = glyph_id;
}
-
+
/** Return the assigned glyph ID of this instance */
- public final int getGlyphIndex() { return _glyph_id; }
-
+ public final int getID() { return _glyph_id; }
+
public abstract void clearPointData();
- /** Return the AABBox in font-units */
+ /**
+ * Return the AABBox in font-units.
+ * <p>
+ * This is either the GlyphDescripton's min- and maximum for TTF
+ * or the calculated box over all points.
+ * </p>
+ */
public final AABBox getBBox() { return _bbox; }
-
+
/** hmtx value */
public abstract int getAdvanceWidth();
-
+
/** hmtx value */
public abstract short getLeftSideBearing();
public abstract Point getPoint(int i);
public abstract int getPointCount();
-
+
@Override
public abstract String toString();
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java
index 2a8deaeeb..45d06e8cb 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java
@@ -67,19 +67,20 @@ public abstract class GlyfDescript extends Program implements GlyphDescription {
static final byte yDual = 0x20;
final GlyfTable _parentTable;
- private int _glyphIndex;
+ private final int _glyphIndex;
private final int _numberOfContours;
- private short _xMin;
- private short _yMin;
- private short _xMax;
- private short _yMax;
+ private final short _xMin;
+ private final short _yMin;
+ private final short _xMax;
+ private final short _yMax;
GlyfDescript(
- GlyfTable parentTable,
- int glyphIndex,
- short numberOfContours,
- DataInput di) throws IOException {
+ final GlyfTable parentTable,
+ final int glyphIndex,
+ final short numberOfContours,
+ final DataInput di) throws IOException {
_parentTable = parentTable;
+ _glyphIndex = glyphIndex;
_numberOfContours = numberOfContours;
_xMin = di.readShort();
_yMin = di.readShort();
@@ -91,26 +92,32 @@ public abstract class GlyfDescript extends Program implements GlyphDescription {
return _numberOfContours;
}
+ @Override
public int getGlyphIndex() {
return _glyphIndex;
}
+ @Override
public short getXMaximum() {
return _xMax;
}
+ @Override
public short getXMinimum() {
return _xMin;
}
+ @Override
public short getYMaximum() {
return _yMax;
}
+ @Override
public short getYMinimum() {
return _yMin;
}
-
+
+ @Override
public String toString() {
return " numberOfContours: " + _numberOfContours +
"\n xMin: " + _xMin +
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java b/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java
index 38eda9adf..a2dc8430d 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java
@@ -12,8 +12,8 @@ public class FontSet01 {
final Font[] fonts = new Font[11];
int i = 0;
fonts[i++] = FontFactory.get(FontFactory.UBUNTU).getDefault(); // FontSet.FAMILY_REGULAR, FontSet.STYLE_NONE
- fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_REGULAR, FontSet.STYLE_ITALIC);
- fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_REGULAR, FontSet.STYLE_BOLD);
+ fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE);
+ fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_ITALIC);
fonts[i++] = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMono.ttf",
TestTextRendererNEWTBugXXXX.class.getClassLoader(), TestTextRendererNEWTBugXXXX.class).getInputStream(), true);
fonts[i++] = FontFactory.get(IOUtil.getResource("fonts/freefont/FreeMonoBold.ttf",
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java b/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java
index be654d345..5b254d46d 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java
@@ -83,7 +83,7 @@ public class TestFontsNEWT00 extends UITestCase {
void testFontGlyph01(final Font font, final char c, final float pixelSize) {
final int glyphID = font.getGlyphID(c);
final int s0 = font.getAdvanceWidthFU(glyphID);
- final Font.Glyph glyph = font.getGlyph(c);
+ final Font.Glyph glyph = font.getGlyph(glyphID);
final int s1 = glyph.getAdvanceFU();
final int unitsPerEM = font.getMetrics().getUnitsPerEM();
@@ -117,7 +117,7 @@ public class TestFontsNEWT00 extends UITestCase {
void testFontGlyph02(final Font font, final char left, final char right) {
final int glyphid_left = font.getGlyphID(left);
final int glyphid_right = font.getGlyphID(right);
- final Font.Glyph glyph_left = font.getGlyph(left);
+ final Font.Glyph glyph_left = font.getGlyph(glyphid_left);
final int k_val = glyph_left.getKerningFU(glyphid_right);
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT01.java b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT01.java
index 8826275b1..ec539d6db 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT01.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT01.java
@@ -48,8 +48,8 @@ import org.junit.runners.MethodSorters;
import com.jogamp.common.os.Platform;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.font.Font;
import com.jogamp.graph.font.FontFactory;
-import com.jogamp.graph.font.FontSet;
import com.jogamp.graph.geom.SVertex;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.test.junit.graph.demos.GPUTextRendererListenerBase01;
@@ -142,51 +142,87 @@ public class TestTextRendererNEWT01 extends UITestCase {
final Runnable action_per_font = new Runnable() {
@Override
public void run() {
+ if( false ) {
+ textGLListener.setHeadBox(1, false);
+ textGLListener.setSampleCount(2);
+ window.display();
+ textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
+ sleep();
+
+ textGLListener.setHeadBox(2, false);
+ textGLListener.setSampleCount(2);
+ window.display();
+ textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
+ sleep();
+ }
+
textGLListener.setHeadBox(1, false);
- textGLListener.setSampleCount(2);
+ textGLListener.setSampleCount(4);
window.display();
textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
sleep();
textGLListener.setHeadBox(2, false);
- textGLListener.setSampleCount(2);
+ textGLListener.setSampleCount(4);
window.display();
textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
sleep();
+ } };
- textGLListener.setHeadBox(1, false);
- textGLListener.setSampleCount(3);
- window.display();
- textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
- sleep();
+ final Font[] fonts = FontSet01.getSet01();
+ for(final Font f : fonts) {
+ if( textGLListener.setFont(f) ) {
+ action_per_font.run();
+ }
+ }
+ if(textGLListener.setFontSet(FontFactory.JAVA, 0, 0)) {
+ action_per_font.run();
+ }
+ destroyWindow(window);
+ }
- textGLListener.setHeadBox(2, false);
- textGLListener.setSampleCount(3);
- window.display();
- textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
- sleep();
+ @Test
+ public void testTextRendererMSAA01() throws InterruptedException, GLException, IOException {
+ final GLProfile glp = GLProfile.get(GLProfile.GL2ES2);
+ final GLCapabilities caps = new GLCapabilities(glp);
+ caps.setAlphaBits(4);
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(4);
+ System.err.println("Requested: "+caps);
+
+ final GLWindow window = createWindow("text-vbaa0-msaa1", caps, 1024, 640);
+ window.display();
+ System.err.println("Chosen: "+window.getChosenGLCapabilities());
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final TextGLListener textGLListener = new TextGLListener(rs, 0, 0 /* sampleCount */, DEBUG, TRACE);
+ textGLListener.attachInputListenerTo(window);
+ window.addGLEventListener(textGLListener);
+ textGLListener.setHeadBox(2, true);
+ window.display();
+
+ final Runnable action_per_font = new Runnable() {
+ @Override
+ public void run() {
textGLListener.setHeadBox(1, false);
- textGLListener.setSampleCount(4);
+ textGLListener.setSampleCount(0);
window.display();
textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
sleep();
textGLListener.setHeadBox(2, false);
- textGLListener.setSampleCount(4);
+ textGLListener.setSampleCount(0);
window.display();
textGLListener.printScreenOnGLThread(window, "./", window.getTitle(), "", false);
sleep();
} };
- if(textGLListener.setFontSet(FontFactory.UBUNTU, FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE)) {
- action_per_font.run();
- }
-
- if(textGLListener.setFontSet(FontFactory.UBUNTU, FontSet.FAMILY_REGULAR, FontSet.STYLE_NONE)) {
- action_per_font.run();
+ final Font[] fonts = FontSet01.getSet01();
+ for(final Font f : fonts) {
+ if( textGLListener.setFont(f) ) {
+ action_per_font.run();
+ }
}
-
if(textGLListener.setFontSet(FontFactory.JAVA, 0, 0)) {
action_per_font.run();
}
@@ -195,15 +231,13 @@ public class TestTextRendererNEWT01 extends UITestCase {
}
@Test
- public void testTextRendererMSAA01() throws InterruptedException, GLException, IOException {
+ public void testTextRendererNoSampling() throws InterruptedException, GLException, IOException {
final GLProfile glp = GLProfile.get(GLProfile.GL2ES2);
final GLCapabilities caps = new GLCapabilities(glp);
caps.setAlphaBits(4);
- caps.setSampleBuffers(true);
- caps.setNumSamples(4);
System.err.println("Requested: "+caps);
- final GLWindow window = createWindow("text-vbaa0-msaa1", caps, 1024, 640);
+ final GLWindow window = createWindow("text-vbaa0-msaa0", caps, 1024, 640);
window.display();
System.err.println("Chosen: "+window.getChosenGLCapabilities());
@@ -230,14 +264,12 @@ public class TestTextRendererNEWT01 extends UITestCase {
sleep();
} };
- if(textGLListener.setFontSet(FontFactory.UBUNTU, FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE)) {
- action_per_font.run();
- }
-
- if(textGLListener.setFontSet(FontFactory.UBUNTU, FontSet.FAMILY_REGULAR, FontSet.STYLE_NONE)) {
- action_per_font.run();
+ final Font[] fonts = FontSet01.getSet01();
+ for(final Font f : fonts) {
+ if( textGLListener.setFont(f) ) {
+ action_per_font.run();
+ }
}
-
if(textGLListener.setFontSet(FontFactory.JAVA, 0, 0)) {
action_per_font.run();
}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TextRendererGLELBase.java b/src/test/com/jogamp/opengl/test/junit/graph/TextRendererGLELBase.java
index 6bc120cde..f307a1196 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/TextRendererGLELBase.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/TextRendererGLELBase.java
@@ -272,11 +272,9 @@ public abstract class TextRendererGLELBase implements GLEventListener {
if( cacheRegion ) {
textRenderUtil.drawString3D(gl, renderer, font, text, null, vbaaSampleCount);
} else if( null != region ) {
- TextRegionUtil.drawString3D(gl, region, renderer, font, text, null, vbaaSampleCount,
- textRenderUtil.tempT1, textRenderUtil.tempT2);
+ TextRegionUtil.drawString3D(gl, region, renderer, font, text, null, vbaaSampleCount);
} else {
- TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, null, vbaaSampleCount,
- textRenderUtil.tempT1, textRenderUtil.tempT2);
+ TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, null, vbaaSampleCount);
}
renderer.enable(gl, false);
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPURegionGLListener01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPURegionGLListener01.java
index 7dc217c2d..b78826a6d 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPURegionGLListener01.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPURegionGLListener01.java
@@ -99,30 +99,30 @@ public class GPURegionGLListener01 extends GPURendererListenerBase01 {
}
private void createTestOutline01(){
- outlineShape.moveTo(0.0f,-10.0f);
- outlineShape.lineTo(15.0f,-10.0f);
- outlineShape.quadTo(10.0f,5.0f, 15.0f,10.0f);
- outlineShape.cubicTo(6.0f,15.0f, 5.0f,8.0f, 0.0f,10.0f);
+ outlineShape.moveTo(0.0f,-10.0f, 0f);
+ outlineShape.lineTo(15.0f,-10.0f, 0f);
+ outlineShape.quadTo(10.0f,5.0f,0f, 15.0f,10.0f,0f);
+ outlineShape.cubicTo(6.0f,15.0f,0f, 5.0f,8.0f,0f, 0.0f,10.0f,0f);
outlineShape.closePath();
- outlineShape.moveTo(5.0f,-5.0f);
- outlineShape.quadTo(10.0f,-5.0f, 10.0f,0.0f);
- outlineShape.quadTo(5.0f,0.0f, 5.0f,-5.0f);
+ outlineShape.moveTo(5.0f,-5.0f,0f);
+ outlineShape.quadTo(10.0f,-5.0f,0f, 10.0f,0.0f,0f);
+ outlineShape.quadTo(5.0f,0.0f,0f, 5.0f,-5.0f,0f);
outlineShape.closePath();
/** Same shape as above but without any off-curve vertices */
final float offset = 30;
- outlineShape.moveTo(offset+0.0f,-10.0f);
- outlineShape.lineTo(offset+17.0f,-10.0f);
- outlineShape.lineTo(offset+11.0f,5.0f);
- outlineShape.lineTo(offset+16.0f,10.0f);
- outlineShape.lineTo(offset+7.0f,15.0f);
- outlineShape.lineTo(offset+6.0f,8.0f);
- outlineShape.lineTo(offset+0.0f,10.0f);
+ outlineShape.moveTo(offset+0.0f,-10.0f,0f);
+ outlineShape.lineTo(offset+17.0f,-10.0f,0f);
+ outlineShape.lineTo(offset+11.0f,5.0f,0f);
+ outlineShape.lineTo(offset+16.0f,10.0f,0f);
+ outlineShape.lineTo(offset+7.0f,15.0f,0f);
+ outlineShape.lineTo(offset+6.0f,8.0f,0f);
+ outlineShape.lineTo(offset+0.0f,10.0f,0f);
outlineShape.closePath();
- outlineShape.moveTo(offset+5.0f,0.0f);
- outlineShape.lineTo(offset+5.0f,-5.0f);
- outlineShape.lineTo(offset+10.0f,-5.0f);
- outlineShape.lineTo(offset+10.0f,0.0f);
+ outlineShape.moveTo(offset+5.0f,0.0f,0f);
+ outlineShape.lineTo(offset+5.0f,-5.0f,0f);
+ outlineShape.lineTo(offset+10.0f,-5.0f,0f);
+ outlineShape.lineTo(offset+10.0f,0.0f,0f);
outlineShape.closePath();
}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextGLListener0A.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextGLListener0A.java
index bf0a907b0..5b7044fee 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextGLListener0A.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextGLListener0A.java
@@ -39,14 +39,11 @@ import com.jogamp.newt.opengl.GLWindow;
public class GPUTextGLListener0A extends GPUTextRendererListenerBase01 {
- public GPUTextGLListener0A() {
- super(RenderState.createRenderState(SVertex.factory()), Region.VBAA_RENDERING_BIT, 4, true, false, false);
- }
-
public GPUTextGLListener0A(final RenderState rs, final int renderModes, final int sampleCount, final boolean blending, final boolean debug, final boolean trace) {
super(rs, renderModes, sampleCount, blending, debug, trace);
}
+ @Override
public void init(final GLAutoDrawable drawable) {
if(drawable instanceof GLWindow) {
final GLWindow glw = (GLWindow) drawable;
@@ -64,6 +61,7 @@ public class GPUTextGLListener0A extends GPUTextRendererListenerBase01 {
rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f);
}
+ @Override
public void dispose(final GLAutoDrawable drawable) {
if(drawable instanceof GLWindow) {
final GLWindow glw = (GLWindow) drawable;
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextNewtDemo.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextNewtDemo.java
index ade84b3a3..59956b435 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextNewtDemo.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextNewtDemo.java
@@ -148,7 +148,7 @@ public class GPUTextNewtDemo {
// FPSAnimator animator = new FPSAnimator(60);
final Animator animator = new Animator();
- animator.setUpdateFPSFrames(60, System.err);
+ // animator.setUpdateFPSFrames(60, System.err);
animator.add(window);
window.addKeyListener(new KeyAdapter() {
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java
index abf7151d0..7b1fe6177 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUTextRendererListenerBase01.java
@@ -34,7 +34,6 @@ import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLAnimatorControl;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLException;
-import com.jogamp.opengl.GLRunnable;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.common.util.InterruptSource;
import com.jogamp.graph.curve.Region;
@@ -46,7 +45,6 @@ import com.jogamp.graph.font.Font;
import com.jogamp.graph.font.FontFactory;
import com.jogamp.graph.font.FontScale;
import com.jogamp.graph.font.FontSet;
-import com.jogamp.graph.font.Font.Glyph;
import com.jogamp.newt.Window;
import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.event.KeyListener;
@@ -114,21 +112,21 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
"in lorem. Maecenas in ipsum ac justo scelerisque sollicitudin. Quisque sit amet neque lorem, \n" +
"-------Press H to change text---------";
- public static final String textX2 = // Kvæven -> Kvaven (error)
+ public static final String textX2 =
"I “Ask Jeff” or ‘Ask Jeff’. Take the chef d’œuvre! Two of [of] (of) ‘of’ “of” of? of! of*. X\n"+
"Les Woëvres, the Fôret de Wœvres, the Voire and Vauvise. Yves is in heaven; D’Amboise is in jail. X\n"+
"Lyford’s in Texas & L’Anse-aux-Griffons in Québec; the Łyna in Poland. Yriarte, Yciar and Ysaÿe are at Yale. X\n"+
- "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvaven in Norway, Kyulu in Kenya, not in Rwanda.… X\n"+
+ "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvæven in Norway, Kyulu in Kenya, not in Rwanda.… X\n"+
"Von-Vincke-Straße in Münster, Vdovino in Russia, Ytterbium in the periodic table. Are Toussaint L’Ouverture, Wölfflin, Wolfe, X\n"+
"Miłosz and Wū Wŭ all in the library? 1510–1620, 11:00 pm, and the 1980s are over. X\n"+
"-------Press H to change text---------";
- public static final String textX20 = // Kvæven -> Kvaven (error)
+ public static final String textX20 =
"I “Ask Jeff” or ‘Ask Jeff’. Take the chef d’œuvre! Two of [of] (of) ‘of’ “of” of? of! of*.\n"+
"Two of [of] (of) ‘of’ “of” of? of! of*. Ydes, Yffignac and Ygrande are in France: so are Ypres,\n"+
"Les Woëvres, the Fôret de Wœvres, the Voire and Vauvise. Yves is in heaven; D’Amboise is in jail.\n"+
"Lyford’s in Texas & L’Anse-aux-Griffons in Québec; the Łyna in Poland. Yriarte, Yciar and Ysaÿe are at Yale.\n"+
- "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvaven in Norway, Kyulu in Kenya, not in Rwanda.…\n"+
+ "Kyoto and Ryotsu are both in Japan, Kwikpak on the Yukon delta, Kvæven in Norway, Kyulu in Kenya, not in Rwanda.…\n"+
"Walton’s in West Virginia, but «Wren» is in Oregon. Tlálpan is near Xochimilco in México.\n"+
"The Zygos & Xylophagou are in Cyprus, Zwettl in Austria, Fænø in Denmark, the Vøringsfossen and Værøy in Norway.\n"+
"Tchula is in Mississippi, the Tittabawassee in Michigan. Twodot is here in Montana, Ywamun in Burma.\n"+
@@ -154,15 +152,7 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
this.regionFPS = GLRegion.create(renderModes, null);
this.regionHead = GLRegion.create(renderModes, null);
this.regionBottom = GLRegion.create(renderModes, null);
- try {
- // this.font = FontFactory.get(fontSet).getDefault();
- this.font = FontFactory.get(fontSet).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE);
-
- this.fontName = font.toString();
- } catch (final IOException ioe) {
- System.err.println("Caught: "+ioe.getMessage());
- ioe.printStackTrace();
- }
+ setFontSet(fontSet, FontSet.FAMILY_LIGHT, FontSet.STYLE_NONE);
setMatrix(0, 0, 0, 0f, sampleCount);
}
@@ -206,6 +196,7 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
}
final int w2 = Math.max(window.getSurfaceWidth(), w);
final int h2 = Math.max(window.getSurfaceHeight(), h);
+ System.err.println("upsizeWindowSurface: "+window.getSurfaceWidth()+"x"+window.getSurfaceHeight()+" -> "+w+"x"+h+" -> "+w2+"x"+h2);
if( off_thread ) {
new InterruptSource.Thread() {
@Override
@@ -231,9 +222,17 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
} else {
System.err.println("Using vertical default DPI of "+dpiV+", "+ppmmV+" pixel/mm");
}
- fontNameBox = font.getMetricBounds(fontName);
+ fontNameBox = font.getGlyphBounds(fontName);
setHeadBox(headType, true);
-
+ {
+ final float pixelSizeFName = FontScale.toPixels(fontSizeFName, dpiV);
+ System.err.println("XXX: fontName size "+fontSizeFName+"pt, dpiV "+dpiV+" -> "+pixelSizeFName+"px");
+ System.err.println("XXX: fontName boxM fu "+font.getMetricBoundsFU(fontName));
+ System.err.println("XXX: fontName boxG fu "+font.getGlyphBoundsFU(fontName));
+ System.err.println("XXX: fontName boxM em "+font.getMetricBounds(fontName));
+ System.err.println("XXX: fontName boxG em "+font.getGlyphBounds(fontName));
+ System.err.println("XXX: fontName box height px "+(fontNameBox.getHeight() * pixelSizeFName));
+ }
}
@Override
@@ -299,6 +298,7 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
renderer.enable(gl, true);
if( drawFPS ) {
+ pmv.glPushMatrix();
final float pixelSizeFPS = FontScale.toPixels(fontSizeFPS, dpiV);
final float lfps, tfps, td;
final GLAnimatorControl animator = drawable.getAnimator();
@@ -327,29 +327,30 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
pmv.glScalef(sxy, sxy, 1.0f);
}
// No cache, keep region alive!
- TextRegionUtil.drawString3D(gl, regionFPS, renderer, font, text, null, sampleCountFPS, textRegionUtil.tempT1,
- textRegionUtil.tempT2);
+ TextRegionUtil.drawString3D(gl, regionFPS, renderer, font, text, null, sampleCountFPS);
+ pmv.glPopMatrix();
}
- float dx = width - ( fontNameBox.getWidth() - font.getAdvanceWidth(Glyph.ID_SPACE) ) * pixelSizeFName;
+ // float dx = width - ( fontNameBox.getWidth() + font.getAdvanceWidth( Glyph.ID_SPACE ) ) * pixelSizeFName;
+ float dx = width - ( fontNameBox.getWidth() + 2 * font.getAdvanceWidth( font.getGlyphID('X') ) ) * pixelSizeFName;
float dy = height - fontNameBox.getHeight() * pixelSizeFName;
-
- pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
- pmv.glLoadIdentity();
- pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
{
- final float sxy = nearPlaneS * pixelSizeFName;
- pmv.glScalef(sxy, sxy, 1.0f);
+ pmv.glPushMatrix();
+ pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
+ {
+ final float sxy = nearPlaneS * pixelSizeFName;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ // System.err.printf("FontN: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
+ textRegionUtil.drawString3D(gl, renderer, font, fontName, null, getSampleCount());
+ pmv.glPopMatrix();
}
- // System.err.printf("FontN: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
- textRegionUtil.drawString3D(gl, renderer, font, fontName, null, getSampleCount());
dx = 10f;
dy += -fontNameBox.getHeight() * pixelSizeFName - 10f;
if(null != headtext) {
- pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
- pmv.glLoadIdentity();
+ pmv.glPushMatrix();
// System.err.printf("Head: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
{
@@ -358,39 +359,40 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
}
// pmv.glTranslatef(x0, y1, z0);
textRegionUtil.drawString3D(gl, renderer, font, headtext, null, getSampleCount());
+ pmv.glPopMatrix();
}
dy += ( -headbox.getHeight() - font.getLineHeight() ) * pixelSizeCenter;
- pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
- pmv.glLoadIdentity();
- pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
- // System.err.printf("Bottom: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
- pmv.glTranslatef(getXTran(), getYTran(), getZTran());
- pmv.glRotatef(getAngle(), 0, 1, 0);
{
- final float sxy = nearPlaneS * pixelSizeCenter;
- pmv.glScalef(sxy, sxy, 1.0f);
- }
- rs.setColorStatic(0.9f, 0.0f, 0.0f, 1.0f);
+ pmv.glPushMatrix();
+ pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0);
+ // System.err.printf("Bottom: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy));
+ pmv.glTranslatef(getXTran(), getYTran(), getZTran());
+ pmv.glRotatef(getAngle(), 0, 1, 0);
+ {
+ final float sxy = nearPlaneS * pixelSizeCenter;
+ pmv.glScalef(sxy, sxy, 1.0f);
+ }
+ rs.setColorStatic(0.9f, 0.0f, 0.0f, 1.0f);
- if( bottomTextUseFrustum ) {
- regionBottom.setFrustum(pmv.glGetFrustum());
- }
- if(!userInput) {
if( bottomTextUseFrustum ) {
- TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, text2, null, getSampleCount(), textRegionUtil.tempT1,
- textRegionUtil.tempT2);
- } else {
- textRegionUtil.drawString3D(gl, renderer, font, text2, null, getSampleCount());
+ regionBottom.setFrustum(pmv.glGetFrustum());
}
- } else {
- if( bottomTextUseFrustum ) {
- TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, userString.toString(), null, getSampleCount(), textRegionUtil.tempT1,
- textRegionUtil.tempT2);
+ if(!userInput) {
+ if( bottomTextUseFrustum ) {
+ TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, text2, null, getSampleCount());
+ } else {
+ textRegionUtil.drawString3D(gl, renderer, font, text2, null, getSampleCount());
+ }
} else {
- textRegionUtil.drawString3D(gl, renderer, font, userString.toString(), null, getSampleCount());
+ if( bottomTextUseFrustum ) {
+ TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, userString.toString(), null, getSampleCount());
+ } else {
+ textRegionUtil.drawString3D(gl, renderer, font, userString.toString(), null, getSampleCount());
+ }
}
+ pmv.glPopMatrix();
}
renderer.enable(gl, false);
}
@@ -444,6 +446,17 @@ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerB
return false;
}
+ public boolean setFont(final Font _font) {
+ if(null != _font) {
+ // fontSet = ???
+ font = _font;
+ fontName = font.getFullFamilyName()+" (head "+fontSizeHead+"pt)";
+ fontNameBox = font.getMetricBounds(fontName);
+ return true;
+ }
+ return false;
+ }
+
public boolean isUserInputMode() { return userInput; }
void dumpMatrix(final boolean bbox) {
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java
index a7c0bf84b..0c8595c70 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/CrossHair.java
@@ -35,7 +35,7 @@ import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.Vertex.Factory;
/**
- * GPU based resolution independent Button impl
+ * GPU based resolution independent Crosshair
*/
public class CrossHair extends UIShape {
private float width, height, lineWidth;
@@ -70,31 +70,29 @@ public class CrossHair extends UIShape {
protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+ final float lwh = lineWidth/2f;
+
final float tw = getWidth();
final float th = getHeight();
final float twh = tw/2f;
final float thh = th/2f;
- final float lwh = lineWidth/2f;
final float ctrX = 0f, ctrY = 0f;
- float ctrZ = 0f;
-
- // vertical (CCW!)
- shape.addVertex(ctrX-lwh, ctrY-thh, ctrZ, true);
- shape.addVertex(ctrX+lwh, ctrY-thh, ctrZ, true);
- shape.addVertex(ctrX+lwh, ctrY+thh, ctrZ, true);
- shape.addVertex(ctrX-lwh, ctrY+thh, ctrZ, true);
- shape.closeLastOutline(true);
-
- ctrZ -= 0.05f;
-
- // horizontal (CCW!)
- shape.addEmptyOutline();
- shape.addVertex(ctrX-twh, ctrY-lwh, ctrZ, true);
- shape.addVertex(ctrX+twh, ctrY-lwh, ctrZ, true);
- shape.addVertex(ctrX+twh, ctrY+lwh, ctrZ, true);
- shape.addVertex(ctrX-twh, ctrY+lwh, ctrZ, true);
- shape.closeLastOutline(true);
+ final float ctrZ = 0f;
+
+ // middle vertical (CCW!)
+ shape.moveTo(ctrX-lwh, ctrY-thh, ctrZ);
+ shape.lineTo(ctrX+lwh, ctrY-thh, ctrZ);
+ shape.lineTo(ctrX+lwh, ctrY+thh, ctrZ);
+ shape.lineTo(ctrX-lwh, ctrY+thh, ctrZ);
+ shape.closePath();
+
+ // middle horizontal (CCW!)
+ shape.moveTo(ctrX-twh, ctrY-lwh, ctrZ);
+ shape.lineTo(ctrX+twh, ctrY-lwh, ctrZ);
+ shape.lineTo(ctrX+twh, ctrY+lwh, ctrZ);
+ shape.lineTo(ctrX-twh, ctrY+lwh, ctrZ);
+ shape.closePath();
shape.setIsQuadraticNurbs();
shape.setSharpness(shapesSharpness);
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
index eb86aba81..749ac7965 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
@@ -30,7 +30,6 @@ package com.jogamp.opengl.test.junit.graph.demos.ui;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.curve.opengl.RegionRenderer;
-import com.jogamp.graph.curve.opengl.TextRegionUtil;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.Vertex.Factory;
@@ -91,7 +90,7 @@ public class Label extends UIShape {
private final AffineTransform tempT1 = new AffineTransform();
private final AffineTransform tempT2 = new AffineTransform();
- private final TextRegionUtil.ShapeVisitor shapeVisitor = new TextRegionUtil.ShapeVisitor() {
+ private final OutlineShape.Visitor shapeVisitor = new OutlineShape.Visitor() {
@Override
public void visit(final OutlineShape shape, final AffineTransform t) {
shape.setSharpness(shapesSharpness);
@@ -104,7 +103,7 @@ public class Label extends UIShape {
protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
final AffineTransform t_sxy = new AffineTransform();
t_sxy.setToScale(pixelSize, pixelSize);
- TextRegionUtil.processString(shapeVisitor, t_sxy, font, text, tempT1, tempT2);
+ font.processString(shapeVisitor, t_sxy, text, tempT1, tempT2);
final float[] ctr = box.getCenter();
setRotationOrigin( ctr[0], ctr[1], ctr[2]);
}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java
index c43714b31..5cf0f9af5 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label0.java
@@ -27,7 +27,6 @@
*/
package com.jogamp.opengl.test.junit.graph.demos.ui;
-import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.TextRegionUtil;
import com.jogamp.graph.font.Font;
@@ -38,13 +37,11 @@ public class Label0 {
protected Font font;
protected String text;
protected final float[] rgbaColor;
- protected final AABBox box;
public Label0(final Font font, final String text, final float[] rgbaColor) {
this.font = font;
this.text = text;
this.rgbaColor = rgbaColor;
- this.box = new AABBox();
}
public final String getText() { return text; }
@@ -68,31 +65,10 @@ public class Label0 {
this.font = font;
}
- public final AABBox getBounds() { return box; }
-
- private final float[] tmpV3 = new float[3];
- private final AffineTransform tempT1 = new AffineTransform();
- private final AffineTransform tempT2 = new AffineTransform();
-
- private final TextRegionUtil.ShapeVisitor shapeVisitor = new TextRegionUtil.ShapeVisitor() {
- @Override
- public void visit(final OutlineShape shape, final AffineTransform t) {
- region.addOutlineShape(shape, t, rgbaColor);
- box.resize(shape.getBounds(), t, tmpV3);
- }
- };
-
- private Region region;
-
- public final AABBox addShapeToRegion(final float pixelSize, final Region region, final AffineTransform tLeft) {
- box.reset();
- this.region = region;
+ public final AABBox addShapeToRegion(final float scale, final Region region, final AffineTransform tLeft) {
final AffineTransform t_sxy = new AffineTransform(tLeft);
- final AffineTransform tmp = new AffineTransform();
- t_sxy.scale(pixelSize, pixelSize, tmp);
- TextRegionUtil.processString(shapeVisitor, t_sxy, font, text, tempT1, tempT2);
- this.region = null;
- return box;
+ t_sxy.scale(scale, scale, new AffineTransform());
+ return TextRegionUtil.addStringToRegion(region, font, t_sxy, text, rgbaColor);
}
@Override
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java
index db1ec239a..59514375c 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/LabelButton.java
@@ -47,7 +47,7 @@ public class LabelButton extends RoundButton {
/** {@value} */
public static final float DEFAULT_SPACING_Y = 0.40f;
- private static final float DEFAULT_2PASS_LABEL_ZOFFSET = -0.05f;
+ private static final float DEFAULT_2PASS_LABEL_ZOFFSET = -0.005f; // -0.05f;
private final Label0 label;
private float spacingX = DEFAULT_SPACING_X;
@@ -64,6 +64,8 @@ public class LabelButton extends RoundButton {
setToggleOnColorMod(0.85f, 0.85f, 0.85f, 1.0f);
}
+ public Font getFont() { return label.font; }
+
@Override
public void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) {
if( false ) {
@@ -91,35 +93,33 @@ public class LabelButton extends RoundButton {
box.resize(shape.getBounds());
// Precompute text-box size .. guessing pixelSize
- final float lPixelSize0 = 10f;
final float lw = width * ( 1f - spacingX ) ;
final float lh = height * ( 1f - spacingY ) ;
- final AABBox lbox0_em = label.font.getMetricBounds(label.text);
+ final AABBox lbox0_em = label.font.getGlyphBounds(label.text);
final float lsx = lw / lbox0_em.getWidth();
final float lsy = lh / lbox0_em.getHeight();
- final float lPixelSize1 = lsx < lsy ? lsx : lsy;
- if( DRAW_DEBUG_BOX ) {
- final AABBox lbox0_px = new AABBox(lbox0_em).scale2(lPixelSize0, new float[3]);
- System.err.println("RIButton: dim "+width+" x "+height+", spacing "+spacingX+", "+spacingY);
- System.err.println("RIButton: net-text "+lw+" x "+lh+" px");
- System.err.println("RIButton: shape "+box+" px");
- System.err.println("RIButton: text "+lbox0_em+" em, "+label.text);
- System.err.println("RIButton: text "+lbox0_px+" px, "+label.text);
- System.err.println("RIButton: lscale "+lsx+" x "+lsy+": pixelSize "+lPixelSize0+" -> "+lPixelSize1);
- }
+ final float lScale = lsx < lsy ? lsx : lsy;
// Setting left-corner transform using text-box in font em-size [0..1]
- final AABBox lbox1_s = label.font.getPointsBounds(null, label.text).scale2(lPixelSize1, new float[3]);
+ final AABBox lbox1_s = new AABBox(lbox0_em).scale2(lScale, new float[3]);
// Center text .. (share same center w/ button)
final float[] lctr = lbox1_s.getCenter();
final float[] ctr = box.getCenter();
final float[] ltx = new float[] { ctr[0] - lctr[0], ctr[1] - lctr[1], 0f };
- final AABBox lbox2 = label.addShapeToRegion(lPixelSize1, region, tempT1.setToTranslation(ltx[0], ltx[1]));
if( DRAW_DEBUG_BOX ) {
- System.err.printf("RIButton.0: tleft %f / %f, %f / %f%n", ltx[0], ltx[1], ltx[0] * lPixelSize1, ltx[1] * lPixelSize1);
- System.err.printf("RIButton.0: lbox1 %s scaled%n", lbox1_s);
- System.err.printf("RIButton.0: lbox2 %s%n", lbox2);
+ System.err.println("RIButton: dim "+width+" x "+height+", spacing "+spacingX+", "+spacingY);
+ System.err.println("RIButton: net-text "+lw+" x "+lh);
+ System.err.println("RIButton: shape "+box);
+ System.err.println("RIButton: text_em "+lbox0_em+" em, "+label.text);
+ System.err.println("RIButton: lscale "+lsx+" x "+lsy+" -> "+lScale);
+ System.err.printf ("RIButton: text_s %s%n", lbox1_s);
+ System.err.printf ("RIButton: tleft %f / %f, %f / %f%n", ltx[0], ltx[1], ltx[0] * lScale, ltx[1] * lScale);
+ }
+
+ final AABBox lbox2 = label.addShapeToRegion(lScale, region, tempT1.setToTranslation(ltx[0], ltx[1]));
+ if( DRAW_DEBUG_BOX ) {
+ System.err.printf("RIButton.X: lbox2 %s%n", lbox2);
}
setRotationOrigin( ctr[0], ctr[1], ctr[2]);
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Rectangle.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Rectangle.java
new file mode 100644
index 000000000..aa9400050
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Rectangle.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2010-2023 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 com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import com.jogamp.opengl.GL2ES2;
+
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+
+/**
+ * GPU based resolution independent Rectangle
+ */
+public class Rectangle extends UIShape {
+ private float width, height, lineWidth;
+
+ public Rectangle(final Factory<? extends Vertex> factory, final int renderModes, final float width, final float height, final float linewidth) {
+ super(factory, renderModes);
+ this.width = width;
+ this.height = height;
+ this.lineWidth = linewidth;
+ }
+
+ public final float getWidth() { return width; }
+ public final float getHeight() { return height; }
+ public final float getLineWidth() { return lineWidth; }
+
+ public void setDimension(final float width, final float height, final float lineWidth) {
+ this.width = width;
+ this.height = height;
+ this.lineWidth = lineWidth;
+ markShapeDirty();
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ final float lwh = lineWidth/2f;
+
+ final float tw = getWidth();
+ final float th = getHeight();
+
+ final float twh = tw/2f;
+ final float twh_o = twh+lwh;
+ final float twh_i = twh-lwh;
+ final float thh = th/2f;
+ final float thh_o = thh+lwh;
+ final float thh_i = thh-lwh;
+
+ final float ctrX = 0f, ctrY = 0f;
+ final float ctrZ = 0f;
+
+ // outer (CCW!)
+ shape.moveTo(ctrX-twh_o, ctrY-thh_o, ctrZ);
+ shape.lineTo(ctrX+twh_o, ctrY-thh_o, ctrZ);
+ shape.lineTo(ctrX+twh_o, ctrY+thh_o, ctrZ);
+ shape.lineTo(ctrX-twh_o, ctrY+thh_o, ctrZ);
+ shape.closePath();
+
+ // inner (CCW!)
+ shape.moveTo(ctrX-twh_i, ctrY-thh_i, ctrZ);
+ shape.lineTo(ctrX+twh_i, ctrY-thh_i, ctrZ);
+ shape.lineTo(ctrX+twh_i, ctrY+thh_i, ctrZ);
+ shape.lineTo(ctrX-twh_i, ctrY+thh_i, ctrZ);
+ shape.closePath();
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString()+", dim "+getWidth() + "x" + getHeight();
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java
index 98ddc1db2..9de206cac 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java
@@ -20,9 +20,7 @@ import com.jogamp.newt.event.PinchToZoomGesture;
import com.jogamp.newt.event.GestureHandler.GestureEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.math.FloatUtil;
-import com.jogamp.opengl.math.Quaternion;
import com.jogamp.opengl.math.Ray;
-import com.jogamp.opengl.math.VectorUtil;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.PMVMatrix;
@@ -130,35 +128,6 @@ public class SceneUIController implements GLEventListener{
cDrawable = drawable;
}
- private void transformShape(final PMVMatrix pmv, final UIShape uiShape) {
- final float[] uiTranslate = uiShape.getTranslate();
- pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]);
- // final float dz = 100f;
-
- final Quaternion quat = uiShape.getRotation();
- final boolean rotate = !quat.isIdentity();
- final float[] uiScale = uiShape.getScale();
- final boolean scale = !VectorUtil.isVec3Equal(uiScale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON);
- if( rotate || scale ) {
- final float[] rotOrigin = uiShape.getRotationOrigin();
- final boolean pivot = !VectorUtil.isVec3Zero(rotOrigin, 0, FloatUtil.EPSILON);
- // pmv.glTranslatef(0f, 0f, dz);
- if( pivot ) {
- pmv.glTranslatef(rotOrigin[0], rotOrigin[1], rotOrigin[2]);
- }
- if( scale ) {
- pmv.glScalef(uiScale[0], uiScale[1], uiScale[2]);
- }
- if( rotate ) {
- pmv.glRotate(quat);
- }
- if( pivot ) {
- pmv.glTranslatef(-rotOrigin[0], -rotOrigin[1], -rotOrigin[2]);
- }
- // pmv.glTranslatef(0f, 0f, -dz);
- }
- }
-
private static Comparator<UIShape> shapeZAscComparator = new Comparator<UIShape>() {
@Override
public int compare(final UIShape s1, final UIShape s2) {
@@ -198,7 +167,7 @@ public class SceneUIController implements GLEventListener{
if( uiShape.isEnabled() ) {
uiShape.validate(gl, renderer);
pmv.glPushMatrix();
- transformShape(pmv, uiShape);
+ uiShape.setTransform(pmv);
uiShape.drawShape(gl, renderer, sampleCount);
pmv.glPopMatrix();
}
@@ -243,7 +212,7 @@ public class SceneUIController implements GLEventListener{
if( uiShape.isEnabled() ) {
pmv.glPushMatrix();
- transformShape(pmv, uiShape);
+ uiShape.setTransform(pmv);
final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, viewport, 0, ray);
pmv.glPopMatrix();
if( ok ) {
@@ -265,6 +234,14 @@ public class SceneUIController implements GLEventListener{
private final float[] dpyTmp2V3 = new float[3];
private final float[] dpyTmp3V3 = new float[3];
+ /**
+ * Calling {@link UIShape#winToObjCoord(RegionRenderer, int, int, float[])}, retrieving its object position.
+ * @param activeShape
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ * @param objPos resulting object position
+ * @param runnable action
+ */
public void windowToShapeCoords(final UIShape activeShape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) {
if( null == cDrawable || null == activeShape ) {
return;
@@ -272,30 +249,12 @@ public class SceneUIController implements GLEventListener{
cDrawable.invoke(false, new GLRunnable() {
@Override
public boolean run(final GLAutoDrawable drawable) {
- if( windowToShapeCoordsImpl(activeShape, glWinX, glWinY, objPos) ) {
+ if( activeShape.winToObjCoord(renderer, glWinX, glWinY, objPos) ) {
runnable.run();
}
return true;
} } );
}
- private boolean windowToShapeCoordsImpl(final UIShape activeShape, final int glWinX, final int glWinY, final float[] objPos) {
- final PMVMatrix pmv = renderer.getMatrix();
- pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
-
- pmv.glPushMatrix();
- transformShape(pmv, activeShape);
- boolean res = false;
- final float[] ctr = activeShape.getBounds().getCenter();
- if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, dpyTmp1V3, 0) ) {
- // System.err.printf("winToShapeCoords.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", shapeId, ctr[0], ctr[1], ctr[2], dpyTmp1V3[0], dpyTmp1V3[1], dpyTmp1V3[2]);
- if( pmv.gluUnProject(glWinX, glWinY, dpyTmp1V3[2], viewport, 0, objPos, 0) ) {
- // System.err.printf("winToShapeCoords.1: shape %d: win [%d, %d, %f] -> obj [%f, %f, %f]%n", shapeId, glWinX, glWinY, dpyTmp1V3[2], objPos[0], objPos[1], objPos[2]);
- res = true;
- }
- }
- pmv.glPopMatrix();
- return res;
- }
@Override
public void dispose(final GLAutoDrawable drawable) {
@@ -397,6 +356,7 @@ public class SceneUIController implements GLEventListener{
final float[] objPos = new float[3];
final UIShape shape = activeShape;
windowToShapeCoords(shape, glWinX, glWinY, objPos, new Runnable() {
+ @Override
public void run() {
shape.dispatchGestureEvent(gh, glWinX, glWinY, objPos);
} } );
@@ -405,6 +365,12 @@ public class SceneUIController implements GLEventListener{
}
}
+ /**
+ * Dispatch mouse event, either directly sending to activeShape or picking one
+ * @param e original Newt {@link MouseEvent}
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ */
final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) {
if( null == activeShape ) {
dispatchMouseEventPickShape(e, glWinX, glWinY, true);
@@ -412,20 +378,36 @@ public class SceneUIController implements GLEventListener{
dispatchMouseEventForShape(activeShape, e, glWinX, glWinY);
}
}
+ /**
+ * Pick the shape using the event coordinates
+ * @param e original Newt {@link MouseEvent}
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ * @param setActive
+ */
final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY, final boolean setActive) {
final float[] objPos = new float[3];
final UIShape[] shape = { null };
pickShape(glWinX, glWinY, objPos, shape, new Runnable() {
- public void run() {
+ @Override
+ public void run() {
if( setActive ) {
setActiveShape(shape[0]);
}
shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos);
} } );
}
+ /**
+ * Dispatch event to shape
+ * @param shape target active shape of event
+ * @param e original Newt {@link MouseEvent}
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ */
final void dispatchMouseEventForShape(final UIShape shape, final MouseEvent e, final int glWinX, final int glWinY) {
final float[] objPos = new float[3];
windowToShapeCoords(shape, glWinX, glWinY, objPos, new Runnable() {
+ @Override
public void run() {
shape.dispatchMouseEvent(e, glWinX, glWinY, objPos);
} } );
@@ -445,7 +427,7 @@ public class SceneUIController implements GLEventListener{
ly = e.getY();
lId = e.getPointerId(0);
}
- // flip to GL window coordinates
+ // flip to GL window coordinates, origin bottom-left
final int glWinX = e.getX();
final int glWinY = viewport[3] - e.getY() - 1;
dispatchMouseEvent(e, glWinX, glWinY);
@@ -453,7 +435,7 @@ public class SceneUIController implements GLEventListener{
@Override
public void mouseReleased(final MouseEvent e) {
- // flip to GL window coordinates
+ // flip to GL window coordinates, origin bottom-left
final int glWinX = e.getX();
final int glWinY = viewport[3] - e.getY() - 1;
dispatchMouseEvent(e, glWinX, glWinY);
@@ -484,7 +466,7 @@ public class SceneUIController implements GLEventListener{
ly = e.getY();
// dragged .. delegate to active shape!
- // flip to GL window coordinates
+ // flip to GL window coordinates, origin bottom-left
final int glWinX = lx;
final int glWinY = viewport[3] - ly - 1;
dispatchMouseEventForShape(activeShape, e, glWinX, glWinY);
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject01.java
new file mode 100644
index 000000000..d2bff1fee
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject01.java
@@ -0,0 +1,314 @@
+/**
+ * Copyright 2023 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 com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import com.jogamp.opengl.GL2ES2;
+
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.graph.geom.plane.Winding;
+
+/**
+ * GPU based resolution independent test object
+ */
+public class TestObject01 extends UIShape {
+
+ public TestObject01(final Factory<? extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // lower case 'o'
+ // Start TTF Shape for Glyph 82
+ if( false ) {
+ // Original Outer shape: Winding.CW
+ // Moved into OutlineShape reverse -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.527000f, 0.258000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.527000f, 0.197000f, false);
+ shape.addVertex(0, 0.510000f, 0.147000f, true);
+ // 002: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.492000f, 0.097000f, false);
+ shape.addVertex(0, 0.461000f, 0.062000f, true);
+ // 003: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.429000f, 0.027000f, false);
+ shape.addVertex(0, 0.386000f, 0.008000f, true);
+ // 004: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.343000f, -0.012000f, false);
+ shape.addVertex(0, 0.291000f, -0.012000f, true);
+ // 005: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, -0.012000f, false);
+ shape.addVertex(0, 0.196000f, 0.007000f, true);
+ // 007: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.153000f, 0.027000f, false);
+ shape.addVertex(0, 0.122000f, 0.062000f, true);
+ // 008: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.090000f, 0.097000f, false);
+ shape.addVertex(0, 0.073000f, 0.147000f, true);
+ // 009: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.055000f, 0.197000f, false);
+ shape.addVertex(0, 0.055000f, 0.258000f, true);
+ // 010: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.055000f, 0.319000f, false);
+ shape.addVertex(0, 0.072000f, 0.369000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.090000f, 0.419000f, false);
+ shape.addVertex(0, 0.121000f, 0.454000f, true);
+ // 013: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.153000f, 0.490000f, false);
+ shape.addVertex(0, 0.196000f, 0.509000f, true);
+ // 014: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, 0.529000f, false);
+ shape.addVertex(0, 0.291000f, 0.529000f, true);
+ // 015: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.343000f, 0.529000f, false);
+ shape.addVertex(0, 0.386000f, 0.510000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.429000f, 0.490000f, false);
+ shape.addVertex(0, 0.460000f, 0.455000f, true);
+ // 018: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.492000f, 0.419000f, false);
+ shape.addVertex(0, 0.509000f, 0.369000f, true);
+ // 019: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.527000f, 0.319000f, false);
+ shape.addVertex(0, 0.527000f, 0.258000f, true);
+ System.err.println("TestObject01.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Outer shape: Winding.CW
+ // Moved into OutlineShape same-order -> Winding.CW -> ERROR (so we fix it in the end, see below)
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.527000f, 0.258000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.527000f, 0.197000f, false);
+ shape.addVertex(0.510000f, 0.147000f, true);
+ // 002: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.492000f, 0.097000f, false);
+ shape.addVertex(0.461000f, 0.062000f, true);
+ // 003: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.429000f, 0.027000f, false);
+ shape.addVertex(0.386000f, 0.008000f, true);
+ // 004: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.343000f, -0.012000f, false);
+ shape.addVertex(0.291000f, -0.012000f, true);
+ // 005: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, -0.012000f, false);
+ shape.addVertex(0.196000f, 0.007000f, true);
+ // 007: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.153000f, 0.027000f, false);
+ shape.addVertex(0.122000f, 0.062000f, true);
+ // 008: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.090000f, 0.097000f, false);
+ shape.addVertex(0.073000f, 0.147000f, true);
+ // 009: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.055000f, 0.197000f, false);
+ shape.addVertex(0.055000f, 0.258000f, true);
+ // 010: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.055000f, 0.319000f, false);
+ shape.addVertex(0.072000f, 0.369000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.090000f, 0.419000f, false);
+ shape.addVertex(0.121000f, 0.454000f, true);
+ // 013: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.153000f, 0.490000f, false);
+ shape.addVertex(0.196000f, 0.509000f, true);
+ // 014: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, 0.529000f, false);
+ shape.addVertex(0.291000f, 0.529000f, true);
+ // 015: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.343000f, 0.529000f, false);
+ shape.addVertex(0.386000f, 0.510000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.429000f, 0.490000f, false);
+ shape.addVertex(0.460000f, 0.455000f, true);
+ // 018: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.492000f, 0.419000f, false);
+ shape.addVertex(0.509000f, 0.369000f, true);
+ // 019: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.527000f, 0.319000f, false);
+ shape.addVertex(0.527000f, 0.258000f, true);
+ System.err.println("TestObject01.shape01b.1.winding_area: "+shape.getWindingOfLastOutline());
+ shape.setWindingOfLastOutline(Winding.CCW);
+ System.err.println("TestObject01.shape01b.2.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Inner shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.458000f, 0.258000f, true);
+ // 020: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.458000f, 0.355000f, false);
+ shape.addVertex(0, 0.413000f, 0.412000f, true);
+ // 022: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.368000f, 0.470000f, false);
+ shape.addVertex(0, 0.291000f, 0.470000f, true);
+ // 023: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.214000f, 0.470000f, false);
+ shape.addVertex(0, 0.169000f, 0.413000f, true);
+ // 025: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.124000f, 0.355000f, false);
+ shape.addVertex(0, 0.124000f, 0.258000f, true);
+ // 026: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.124000f, 0.161000f, false);
+ shape.addVertex(0, 0.169000f, 0.104000f, true);
+ // 028: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.214000f, 0.047000f, false);
+ shape.addVertex(0, 0.291000f, 0.047000f, true);
+ // 029: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.368000f, 0.047000f, false);
+ shape.addVertex(0, 0.413000f, 0.104000f, true);
+ // 031: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.458000f, 0.161000f, false);
+ shape.addVertex(0, 0.458000f, 0.258000f, true);
+ System.err.println("TestObject01.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+
+ shape.addVertex(0.458000f, 0.258000f, true);
+ // 020: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.458000f, 0.355000f, false);
+ shape.addVertex(0.413000f, 0.412000f, true);
+ // 022: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.368000f, 0.470000f, false);
+ shape.addVertex(0.291000f, 0.470000f, true);
+ // 023: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.214000f, 0.470000f, false);
+ shape.addVertex(0.169000f, 0.413000f, true);
+ // 025: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.124000f, 0.355000f, false);
+ shape.addVertex(0.124000f, 0.258000f, true);
+ // 026: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.124000f, 0.161000f, false);
+ shape.addVertex(0.169000f, 0.104000f, true);
+ // 028: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.214000f, 0.047000f, false);
+ shape.addVertex(0.291000f, 0.047000f, true);
+ // 029: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.368000f, 0.047000f, false);
+ shape.addVertex(0.413000f, 0.104000f, true);
+ // 031: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.458000f, 0.161000f, false);
+ shape.addVertex(0.458000f, 0.258000f, true);
+
+ System.err.println("TestObject01.shape02b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+ // End Shape for Glyph 82
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject02.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject02.java
new file mode 100644
index 000000000..fe1d965ee
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/TestObject02.java
@@ -0,0 +1,650 @@
+/**
+ * Copyright 2023 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 com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import com.jogamp.opengl.GL2ES2;
+
+import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.geom.Vertex;
+import com.jogamp.graph.geom.Vertex.Factory;
+
+/**
+ * GPU based resolution independent test object
+ */
+public class TestObject02 extends UIShape {
+
+ public TestObject02(final Factory<? extends Vertex> factory, final int renderModes) {
+ super(factory, renderModes);
+ }
+
+ @Override
+ protected void clearImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @Override
+ protected void destroyImpl(final GL2ES2 gl, final RegionRenderer renderer) {
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void addShapeToRegion(final GL2ES2 gl, final RegionRenderer renderer) {
+ final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
+
+ // lower case 'æ'
+
+ // Start TTF Shape for Glyph 193
+ if( true ) {
+ // Original Inner e-shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.728000f, 0.300000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.726000f, 0.381000f, false);
+ shape.addVertex(0, 0.690000f, 0.426000f, true);
+ // 002: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.654000f, 0.471000f, false);
+ shape.addVertex(0, 0.588000f, 0.471000f, true);
+ // 003: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.553000f, 0.471000f, false);
+ shape.addVertex(0, 0.526000f, 0.457000f, true);
+ // 005: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.498000f, 0.443000f, false);
+ shape.addVertex(0, 0.478000f, 0.420000f, true);
+ // 006: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.457000f, 0.396000f, false);
+ shape.addVertex(0, 0.446000f, 0.365000f, true);
+ // 007: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.434000f, 0.334000f, false);
+ shape.addVertex(0, 0.432000f, 0.300000f, true);
+ // 008: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.728000f, 0.300000f, true);
+ System.err.println("TestObject02.shape01a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner e-shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> ??
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.728000f, 0.300000f, true);
+ // 000: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.726000f, 0.381000f, false);
+ shape.addVertex(0.690000f, 0.426000f, true);
+ // 002: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.654000f, 0.471000f, false);
+ shape.addVertex(0.588000f, 0.471000f, true);
+ // 003: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.553000f, 0.471000f, false);
+ shape.addVertex(0.526000f, 0.457000f, true);
+ // 005: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.498000f, 0.443000f, false);
+ shape.addVertex(0.478000f, 0.420000f, true);
+ // 006: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.457000f, 0.396000f, false);
+ shape.addVertex(0.446000f, 0.365000f, true);
+ // 007: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.434000f, 0.334000f, false);
+ shape.addVertex(0.432000f, 0.300000f, true);
+ // 008: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.728000f, 0.300000f, true);
+ System.err.println("TestObject02.shape01b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Outer shape: Winding.CW
+ // Moved into OutlineShape reverse -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.252000f, -0.011000f, true);
+ // 009: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.208000f, -0.011000f, false);
+ shape.addVertex(0, 0.171000f, -0.002000f, true);
+ // 011: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.133000f, 0.007000f, false);
+ shape.addVertex(0, 0.106000f, 0.026000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.079000f, 0.046000f, false);
+ shape.addVertex(0, 0.064000f, 0.076000f, true);
+ // 013: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.048000f, 0.107000f, false);
+ shape.addVertex(0, 0.048000f, 0.151000f, true);
+ // 014: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.048000f, 0.193000f, false);
+ shape.addVertex(0, 0.064000f, 0.223000f, true);
+ // 016: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.080000f, 0.253000f, false);
+ shape.addVertex(0, 0.109000f, 0.272000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.138000f, 0.292000f, false);
+ shape.addVertex(0, 0.178000f, 0.301000f, true);
+ // 018: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.218000f, 0.310000f, false);
+ shape.addVertex(0, 0.265000f, 0.310000f, true);
+ // 019: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.279000f, 0.310000f, false);
+ shape.addVertex(0, 0.294000f, 0.309000f, true);
+ // 021: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.310000f, 0.307000f, false);
+ shape.addVertex(0, 0.324000f, 0.305000f, true);
+ // 022: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.339000f, 0.302000f, false);
+ shape.addVertex(0, 0.349000f, 0.300000f, true);
+ // 023: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.360000f, 0.297000f, false);
+ shape.addVertex(0, 0.364000f, 0.295000f, true);
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.364000f, 0.327000f, true);
+ // 025: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.364000f, 0.354000f, false);
+ shape.addVertex(0, 0.360000f, 0.379000f, true);
+ // 027: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.356000f, 0.405000f, false);
+ shape.addVertex(0, 0.343000f, 0.425000f, true);
+ // 028: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.329000f, 0.446000f, false);
+ shape.addVertex(0, 0.305000f, 0.458000f, true);
+ // 029: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.280000f, 0.471000f, false);
+ shape.addVertex(0, 0.240000f, 0.471000f, true);
+ // 030: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.186000f, 0.471000f, false);
+ shape.addVertex(0, 0.156000f, 0.464000f, true);
+ // 032: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.126000f, 0.456000f, false);
+ shape.addVertex(0, 0.113000f, 0.451000f, true);
+ // 033: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.105000f, 0.507000f, true);
+ // 034: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.122000f, 0.515000f, false);
+ shape.addVertex(0, 0.158000f, 0.522000f, true);
+ // 036: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.194000f, 0.529000f, false);
+ shape.addVertex(0, 0.243000f, 0.529000f, true);
+ // 037: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.314000f, 0.529000f, false);
+ shape.addVertex(0, 0.354000f, 0.503000f, true);
+ // 039: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.395000f, 0.476000f, false);
+ shape.addVertex(0, 0.412000f, 0.431000f, true);
+ // 040: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.445000f, 0.480000f, false);
+ shape.addVertex(0, 0.491000f, 0.504000f, true);
+ // 042: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.537000f, 0.529000f, false);
+ shape.addVertex(0, 0.587000f, 0.529000f, true);
+ // 043: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.682000f, 0.529000f, false);
+ shape.addVertex(0, 0.738000f, 0.467000f, true);
+ // 045: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.795000f, 0.405000f, false);
+ shape.addVertex(0, 0.795000f, 0.276000f, true);
+ // 046: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.795000f, 0.268000f, false);
+ shape.addVertex(0, 0.795000f, 0.260000f, true);
+ // 048: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.794000f, 0.252000f, false);
+ shape.addVertex(0, 0.793000f, 0.245000f, true);
+ // 049: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.430000f, 0.245000f, true);
+ // 050: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.433000f, 0.150000f, false);
+ shape.addVertex(0, 0.477000f, 0.099000f, true);
+ // 052: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.521000f, 0.048000f, false);
+ shape.addVertex(0, 0.617000f, 0.048000f, true);
+ // 053: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.670000f, 0.048000f, false);
+ shape.addVertex(0, 0.701000f, 0.058000f, true);
+ // 055: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.732000f, 0.068000f, false);
+ shape.addVertex(0, 0.746000f, 0.075000f, true);
+ // 056: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0, 0.758000f, 0.019000f, true);
+ // 057: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.744000f, 0.011000f, false);
+ shape.addVertex(0, 0.706000f, 0.000000f, true);
+ // 059: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.667000f, -0.011000f, false);
+ shape.addVertex(0, 0.615000f, -0.011000f, true);
+ // 060: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.558000f, -0.011000f, false);
+ shape.addVertex(0, 0.514000f, 0.003000f, true);
+ // 062: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.470000f, 0.017000f, false);
+ shape.addVertex(0, 0.437000f, 0.049000f, true);
+ // 063: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.426000f, 0.040000f, false);
+ shape.addVertex(0, 0.410000f, 0.030000f, true);
+ // 065: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.393000f, 0.019000f, false);
+ shape.addVertex(0, 0.370000f, 0.010000f, true);
+ // 066: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.347000f, 0.001000f, false);
+ shape.addVertex(0, 0.318000f, -0.005000f, true);
+ // 067: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.289000f, -0.011000f, false);
+ shape.addVertex(0, 0.252000f, -0.011000f, true);
+ System.err.println("TestObject02.shape02a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Outer shape: Winding.CW
+ // Moved into OutlineShape same-order -> Winding.CW -> OK now
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.252000f, -0.011000f, true);
+ // 009: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.208000f, -0.011000f, false);
+ shape.addVertex(0.171000f, -0.002000f, true);
+ // 011: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.133000f, 0.007000f, false);
+ shape.addVertex(0.106000f, 0.026000f, true);
+ // 012: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.079000f, 0.046000f, false);
+ shape.addVertex(0.064000f, 0.076000f, true);
+ // 013: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.048000f, 0.107000f, false);
+ shape.addVertex(0.048000f, 0.151000f, true);
+ // 014: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.048000f, 0.193000f, false);
+ shape.addVertex(0.064000f, 0.223000f, true);
+ // 016: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.080000f, 0.253000f, false);
+ shape.addVertex(0.109000f, 0.272000f, true);
+ // 017: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.138000f, 0.292000f, false);
+ shape.addVertex(0.178000f, 0.301000f, true);
+ // 018: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.218000f, 0.310000f, false);
+ shape.addVertex(0.265000f, 0.310000f, true);
+ // 019: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.279000f, 0.310000f, false);
+ shape.addVertex(0.294000f, 0.309000f, true);
+ // 021: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.310000f, 0.307000f, false);
+ shape.addVertex(0.324000f, 0.305000f, true);
+ // 022: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.339000f, 0.302000f, false);
+ shape.addVertex(0.349000f, 0.300000f, true);
+ // 023: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.360000f, 0.297000f, false);
+ shape.addVertex(0.364000f, 0.295000f, true);
+ // 024: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.364000f, 0.327000f, true);
+ // 025: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.364000f, 0.354000f, false);
+ shape.addVertex(0.360000f, 0.379000f, true);
+ // 027: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.356000f, 0.405000f, false);
+ shape.addVertex(0.343000f, 0.425000f, true);
+ // 028: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.329000f, 0.446000f, false);
+ shape.addVertex(0.305000f, 0.458000f, true);
+ // 029: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.280000f, 0.471000f, false);
+ shape.addVertex(0.240000f, 0.471000f, true);
+ // 030: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.186000f, 0.471000f, false);
+ shape.addVertex(0.156000f, 0.464000f, true);
+ // 032: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.126000f, 0.456000f, false);
+ shape.addVertex(0.113000f, 0.451000f, true);
+ // 033: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.105000f, 0.507000f, true);
+ // 034: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.122000f, 0.515000f, false);
+ shape.addVertex(0.158000f, 0.522000f, true);
+ // 036: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.194000f, 0.529000f, false);
+ shape.addVertex(0.243000f, 0.529000f, true);
+ // 037: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.314000f, 0.529000f, false);
+ shape.addVertex(0.354000f, 0.503000f, true);
+ // 039: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.395000f, 0.476000f, false);
+ shape.addVertex(0.412000f, 0.431000f, true);
+ // 040: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.445000f, 0.480000f, false);
+ shape.addVertex(0.491000f, 0.504000f, true);
+ // 042: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.537000f, 0.529000f, false);
+ shape.addVertex(0.587000f, 0.529000f, true);
+ // 043: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.682000f, 0.529000f, false);
+ shape.addVertex(0.738000f, 0.467000f, true);
+ // 045: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.795000f, 0.405000f, false);
+ shape.addVertex(0.795000f, 0.276000f, true);
+ // 046: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.795000f, 0.268000f, false);
+ shape.addVertex(0.795000f, 0.260000f, true);
+ // 048: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.794000f, 0.252000f, false);
+ shape.addVertex(0.793000f, 0.245000f, true);
+ // 049: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.430000f, 0.245000f, true);
+ // 050: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.433000f, 0.150000f, false);
+ shape.addVertex(0.477000f, 0.099000f, true);
+ // 052: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.521000f, 0.048000f, false);
+ shape.addVertex(0.617000f, 0.048000f, true);
+ // 053: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.670000f, 0.048000f, false);
+ shape.addVertex(0.701000f, 0.058000f, true);
+ // 055: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.732000f, 0.068000f, false);
+ shape.addVertex(0.746000f, 0.075000f, true);
+ // 056: B1: line-to p0-p1
+ // Shape.LineTo:
+ shape.addVertex(0.758000f, 0.019000f, true);
+ // 057: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.744000f, 0.011000f, false);
+ shape.addVertex(0.706000f, 0.000000f, true);
+ // 059: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.667000f, -0.011000f, false);
+ shape.addVertex(0.615000f, -0.011000f, true);
+ // 060: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.558000f, -0.011000f, false);
+ shape.addVertex(0.514000f, 0.003000f, true);
+ // 062: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.470000f, 0.017000f, false);
+ shape.addVertex(0.437000f, 0.049000f, true);
+ // 063: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.426000f, 0.040000f, false);
+ shape.addVertex(0.410000f, 0.030000f, true);
+ // 065: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.393000f, 0.019000f, false);
+ shape.addVertex(0.370000f, 0.010000f, true);
+ // 066: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.347000f, 0.001000f, false);
+ shape.addVertex(0.318000f, -0.005000f, true);
+ // 067: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.289000f, -0.011000f, false);
+ shape.addVertex(0.252000f, -0.011000f, true);
+ System.err.println("TestObject02.shape02b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+
+ if( true ) {
+ // Original Inner a-shape: Winding.CCW
+ // Moved into OutlineShape reverse -> Winding.CW -> OK now
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0, 0.365000f, 0.238000f, true);
+ // 068: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.354000f, 0.243000f, false);
+ shape.addVertex(0, 0.330000f, 0.248000f, true);
+ // 070: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.305000f, 0.254000f, false);
+ shape.addVertex(0, 0.263000f, 0.254000f, true);
+ // 071: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.239000f, 0.254000f, false);
+ shape.addVertex(0, 0.213000f, 0.251000f, true);
+ // 073: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.187000f, 0.247000f, false);
+ shape.addVertex(0, 0.165000f, 0.236000f, true);
+ // 074: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.143000f, 0.224000f, false);
+ shape.addVertex(0, 0.129000f, 0.204000f, true);
+ // 075: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.115000f, 0.184000f, false);
+ shape.addVertex(0, 0.115000f, 0.151000f, true);
+ // 076: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.115000f, 0.122000f, false);
+ shape.addVertex(0, 0.125000f, 0.102000f, true);
+ // 078: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.135000f, 0.082000f, false);
+ shape.addVertex(0, 0.153000f, 0.070000f, true);
+ // 079: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.172000f, 0.058000f, false);
+ shape.addVertex(0, 0.197000f, 0.053000f, true);
+ // 080: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.222000f, 0.047000f, false);
+ shape.addVertex(0, 0.252000f, 0.047000f, true);
+ // 081: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.314000f, 0.047000f, false);
+ shape.addVertex(0, 0.350000f, 0.063000f, true);
+ // 083: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.386000f, 0.080000f, false);
+ shape.addVertex(0, 0.400000f, 0.093000f, true);
+ // 084: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.384000f, 0.119000f, false);
+ shape.addVertex(0, 0.375000f, 0.154000f, true);
+ // 086: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0, 0.366000f, 0.190000f, false);
+ shape.addVertex(0, 0.365000f, 0.238000f, true);
+ System.err.println("TestObject02.shape03a.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ } else {
+ // Inner a-shape: Winding.CCW
+ // Moved into OutlineShape same-order -> Winding.CCW -> OK
+ //
+ // Shape.MoveTo:
+ shape.closeLastOutline(false);
+ shape.addEmptyOutline();
+ shape.addVertex(0.365000f, 0.238000f, true);
+ // 068: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.354000f, 0.243000f, false);
+ shape.addVertex(0.330000f, 0.248000f, true);
+ // 070: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.305000f, 0.254000f, false);
+ shape.addVertex(0.263000f, 0.254000f, true);
+ // 071: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.239000f, 0.254000f, false);
+ shape.addVertex(0.213000f, 0.251000f, true);
+ // 073: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.187000f, 0.247000f, false);
+ shape.addVertex(0.165000f, 0.236000f, true);
+ // 074: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.143000f, 0.224000f, false);
+ shape.addVertex(0.129000f, 0.204000f, true);
+ // 075: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.115000f, 0.184000f, false);
+ shape.addVertex(0.115000f, 0.151000f, true);
+ // 076: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.115000f, 0.122000f, false);
+ shape.addVertex(0.125000f, 0.102000f, true);
+ // 078: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.135000f, 0.082000f, false);
+ shape.addVertex(0.153000f, 0.070000f, true);
+ // 079: B5: quad-to pMh-p0-p1h ***** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.172000f, 0.058000f, false);
+ shape.addVertex(0.197000f, 0.053000f, true);
+ // 080: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.222000f, 0.047000f, false);
+ shape.addVertex(0.252000f, 0.047000f, true);
+ // 081: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.314000f, 0.047000f, false);
+ shape.addVertex(0.350000f, 0.063000f, true);
+ // 083: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.386000f, 0.080000f, false);
+ shape.addVertex(0.400000f, 0.093000f, true);
+ // 084: B4: quad-to p0-p1-p2h **** MID
+ // Shape.QuadTo:
+ shape.addVertex(0.384000f, 0.119000f, false);
+ shape.addVertex(0.375000f, 0.154000f, true);
+ // 086: B6: quad-to pMh-p0-p1
+ // Shape.QuadTo:
+ shape.addVertex(0.366000f, 0.190000f, false);
+ shape.addVertex(0.365000f, 0.238000f, true);
+ System.err.println("TestObject02.shape03b.winding_area: "+shape.getWindingOfLastOutline());
+ shape.closeLastOutline(false);
+ }
+ // End Shape for Glyph 193
+
+ shape.setIsQuadraticNurbs();
+ shape.setSharpness(shapesSharpness);
+ region.addOutlineShape(shape, null, rgbaColor);
+
+ box.resize(shape.getBounds());
+ }
+
+ @Override
+ public String getSubString() {
+ return super.getSubString();
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIListener01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIListener01.java
deleted file mode 100644
index f18491a2a..000000000
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIListener01.java
+++ /dev/null
@@ -1,401 +0,0 @@
-/**
- * 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 com.jogamp.opengl.test.junit.graph.demos.ui;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import com.jogamp.opengl.FPSCounter;
-import com.jogamp.opengl.GL;
-import com.jogamp.opengl.GL2ES2;
-import com.jogamp.opengl.GLAnimatorControl;
-import com.jogamp.opengl.GLAutoDrawable;
-import com.jogamp.opengl.GLEventListener;
-import com.jogamp.opengl.GLException;
-import com.jogamp.opengl.GLPipelineFactory;
-import com.jogamp.opengl.GLRunnable;
-import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
-import com.jogamp.opengl.test.junit.graph.demos.MSAATool;
-import com.jogamp.graph.curve.Region;
-import com.jogamp.graph.curve.opengl.RegionRenderer;
-import com.jogamp.graph.curve.opengl.RenderState;
-import com.jogamp.graph.font.Font;
-import com.jogamp.graph.font.FontFactory;
-import com.jogamp.graph.geom.SVertex;
-import com.jogamp.newt.event.KeyEvent;
-import com.jogamp.newt.event.KeyListener;
-import com.jogamp.newt.event.MouseEvent;
-import com.jogamp.newt.event.MouseListener;
-import com.jogamp.newt.opengl.GLWindow;
-import com.jogamp.opengl.util.GLReadBufferUtil;
-import com.jogamp.opengl.util.PMVMatrix;
-
-/**
- *
- * Action Keys:
- * - 1/2: zoom in/out
- * - 4/5: increase/decrease shape/text spacing
- * - 6/7: increase/decrease corner size
- * - 0/9: rotate
- * - v: toggle v-sync
- * - s: screenshot
- */
-public class UIListener01 implements GLEventListener {
- private final GLReadBufferUtil screenshot;
- private final int renderModes;
- private final RegionRenderer rRenderer;
- private final boolean debug;
- private final boolean trace;
-
- protected LabelButton button;
-
- private KeyAction keyAction;
- private MouseAction mouseAction;
-
- private volatile GLAutoDrawable autoDrawable = null;
-
- private final float[] position = new float[] {0,0,0};
-
- private float xTran = -10;
- private float yTran = 10;
- private float ang = 0f;
- private float zoom = -70f;
-
- boolean ignoreInput = false;
-
- public UIListener01(final int renderModes, final RenderState rs, final boolean debug, final boolean trace) {
- this.renderModes = renderModes;
- this.rRenderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);
- this.debug = debug;
- this.trace = trace;
- this.screenshot = new GLReadBufferUtil(false, false);
-
- setMatrix(-4, -2, 0f, -10);
- try {
- final Font font = FontFactory.get(FontFactory.UBUNTU).getDefault();
- button = new LabelButton(SVertex.factory(), 0, font, "Click me!", 4f, 2f);
- button.translate(2,1,0);
- /** Button defaults !
- button.setLabelColor(1.0f,1.0f,1.0f);
- button.setButtonColor(0.6f,0.6f,0.6f);
- button.setCorner(1.0f);
- button.setSpacing(2.0f);
- */
- System.err.println(button);
- } catch (final IOException ex) {
- System.err.println("Caught: "+ex.getMessage());
- ex.printStackTrace();
- }
- }
-
- public final RegionRenderer getRegionRenderer() { return rRenderer; }
- public final float getZoom() { return zoom; }
- public final float getXTran() { return xTran; }
- public final float getYTran() { return yTran; }
- public final float getAngle() { return ang; }
- public final float[] getPosition() { return position; }
-
- public void setMatrix(final float xtrans, final float ytrans, final float angle, final int zoom) {
- this.xTran = xtrans;
- this.yTran = ytrans;
- this.ang = angle;
- this.zoom = zoom;
- }
-
- @Override
- public void init(final GLAutoDrawable drawable) {
- autoDrawable = drawable;
- GL2ES2 gl = drawable.getGL().getGL2ES2();
- if(debug) {
- gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
- }
- if(trace) {
- gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
- }
- gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
- getRegionRenderer().init(gl, renderModes);
-
- gl.setSwapInterval(1);
- gl.glEnable(GL.GL_DEPTH_TEST);
- gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
- MSAATool.dump(drawable);
- }
-
- @Override
- public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
-
- gl.glViewport(xstart, ystart, width, height);
- rRenderer.reshapePerspective(45.0f, width, height, 0.1f, 7000.0f);
- dumpMatrix();
- }
-
- @Override
- public void display(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
-
- gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
- gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
-
- final int[] sampleCount = { 4 };
- final float[] translate = button.getTranslate();
-
- final RegionRenderer regionRenderer = getRegionRenderer();
- final PMVMatrix pmv = regionRenderer.getMatrix();
- pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
- pmv.glLoadIdentity();
- pmv.glTranslatef(getXTran(), getYTran(), getZoom());
- pmv.glRotatef(getAngle(), 0, 1, 0);
- pmv.glTranslatef(translate[0], translate[1], 0);
- button.drawShape(gl, regionRenderer, sampleCount);
- }
-
- @Override
- public void dispose(final GLAutoDrawable drawable) {
- final GL2ES2 gl = drawable.getGL().getGL2ES2();
- button.destroy(gl, getRegionRenderer());
-
- autoDrawable = null;
- screenshot.dispose(gl);
- rRenderer.destroy(gl);
- }
-
- public void zoom(final int v){
- zoom += v;
- dumpMatrix();
- }
-
- public void move(final float x, final float y){
- xTran += x;
- yTran += y;
- dumpMatrix();
- }
- public void rotate(final float delta){
- ang += delta;
- ang %= 360.0f;
- dumpMatrix();
- }
-
- void dumpMatrix() {
- System.err.println("Matrix: " + xTran + "/" + yTran + " x"+zoom + " @"+ang);
- }
-
- /** Attach the input listener to the window */
- public void attachInputListenerTo(final GLWindow window) {
- if ( null == keyAction ) {
- keyAction = new KeyAction();
- window.addKeyListener(keyAction);
- }
- if ( null == mouseAction ) {
- mouseAction = new MouseAction();
- window.addMouseListener(mouseAction);
- }
- }
-
- public void detachFrom(final GLWindow window) {
- if ( null == keyAction ) {
- return;
- }
- if ( null == mouseAction ) {
- return;
- }
- window.removeGLEventListener(this);
- window.removeKeyListener(keyAction);
- window.removeMouseListener(mouseAction);
- }
-
- public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new PrintWriter(sw);
- pw.printf("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zoom), 0, objName);
-
- final String filename = dir + tech + sw +".png";
- if(screenshot.readPixels(drawable.getGL(), false)) {
- screenshot.write(new File(filename));
- }
- }
-
- int screenshot_num = 0;
-
- public void setIgnoreInput(final boolean v) {
- ignoreInput = v;
- }
- public boolean getIgnoreInput() {
- return ignoreInput;
- }
-
- public class MouseAction implements MouseListener{
-
- @Override
- public void mouseClicked(final MouseEvent e) {
-
- }
-
- @Override
- public void mouseEntered(final MouseEvent e) {
- }
-
- @Override
- public void mouseExited(final MouseEvent e) {
- }
-
- @Override
- public void mousePressed(final MouseEvent e) {
- button.setLabelColor(0.8f,0.8f,0.8f);
- button.setColor(0.1f, 0.1f, 0.1f, 1.0f);
- }
-
- @Override
- public void mouseReleased(final MouseEvent e) {
- button.setLabelColor(1.0f,1.0f,1.0f);
- button.setColor(0.6f,0.6f,0.6f, 1.0f);
- }
-
- @Override
- public void mouseMoved(final MouseEvent e) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void mouseDragged(final MouseEvent e) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void mouseWheelMoved(final MouseEvent e) {
- // TODO Auto-generated method stub
-
- }
-
- }
-
- public class KeyAction implements KeyListener {
- @Override
- public void keyPressed(final KeyEvent arg0) {
- if(ignoreInput) {
- return;
- }
-
- if(arg0.getKeyCode() == KeyEvent.VK_1){
- zoom(2);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_2){
- zoom(-2);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_UP){
- move(0, 1);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
- move(0, -1);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
- move(-1, 0);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
- move(1, 0);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_4){
- button.setSpacing(button.getSpacingX()-0.01f, button.getSpacingY()-0.005f);
- System.err.println("Button Spacing: " + button.getSpacingX());
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_5){
- button.setSpacing(button.getSpacingX()+0.01f, button.getSpacingY()+0.005f);
- System.err.println("Button Spacing: " + button.getSpacingX());
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_6){
- button.setCorner(button.getCorner()-0.01f);
- System.err.println("Button Corner: " + button.getCorner());
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_7){
- button.setCorner(button.getCorner()+0.01f);
- System.err.println("Button Corner: " + button.getCorner());
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_0){
- rotate(1);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_9){
- rotate(-1);
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_V) {
- if(null != autoDrawable) {
- autoDrawable.invoke(false, new GLRunnable() {
- @Override
- public boolean run(final GLAutoDrawable drawable) {
- final GL gl = drawable.getGL();
- final int _i = gl.getSwapInterval();
- final int i;
- switch(_i) {
- case 0: i = 1; break;
- case 1: i = -1; break;
- case -1: i = 0; break;
- default: i = 1; break;
- }
- gl.setSwapInterval(i);
-
- final GLAnimatorControl a = drawable.getAnimator();
- if( null != a ) {
- a.resetFPSCounter();
- }
- if(drawable instanceof FPSCounter) {
- ((FPSCounter)drawable).resetFPSCounter();
- }
- System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
- return true;
- }
- });
- }
- }
- else if(arg0.getKeyCode() == KeyEvent.VK_S){
- rotate(-1);
- if(null != autoDrawable) {
- autoDrawable.invoke(false, new GLRunnable() {
- @Override
- public boolean run(final GLAutoDrawable drawable) {
- try {
- final String type = Region.getRenderModeString(renderModes);
- printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false);
- screenshot_num++;
- } catch (final GLException e) {
- e.printStackTrace();
- } catch (final IOException e) {
- e.printStackTrace();
- }
- return true;
- }
- });
- }
- }
- }
- @Override
- public void keyReleased(final KeyEvent arg0) {}
- }
-}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UINewtDemo01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UINewtDemo01.java
deleted file mode 100644
index d211a9b80..000000000
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UINewtDemo01.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * 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 com.jogamp.opengl.test.junit.graph.demos.ui;
-
-import com.jogamp.opengl.GLCapabilities;
-import com.jogamp.opengl.GLProfile;
-import com.jogamp.common.util.InterruptSource;
-import com.jogamp.graph.curve.opengl.RenderState;
-import com.jogamp.graph.geom.SVertex;
-import com.jogamp.newt.event.KeyAdapter;
-import com.jogamp.newt.event.KeyEvent;
-import com.jogamp.newt.event.WindowAdapter;
-import com.jogamp.newt.event.WindowEvent;
-import com.jogamp.newt.opengl.GLWindow;
-import com.jogamp.opengl.util.Animator;
-
-/** Demonstrate the rendering of multiple outlines into one region/OutlineShape
- * These Outlines are not necessary connected or contained.
- * The output of this demo shows two identical shapes but the left one
- * has some vertices with off-curve flag set to true, and the right allt he vertices
- * are on the curve. Demos the Res. Independent Nurbs based Curve rendering
- *
- */
-public class UINewtDemo01 {
- static final boolean DEBUG = false;
- static final boolean TRACE = false;
-
- public static void main(final String[] args) {
- final GLProfile glp = GLProfile.getGL2ES2();
- final GLCapabilities caps = new GLCapabilities(glp);
- caps.setAlphaBits(4);
- caps.setSampleBuffers(true);
- caps.setNumSamples(4);
- System.out.println("Requested: " + caps);
-
- final GLWindow window = GLWindow.create(caps);
- window.setPosition(10, 10);
- window.setSize(800, 400);
- window.setTitle("GPU UI Newt Demo 01");
- final RenderState rs = RenderState.createRenderState(SVertex.factory());
- final UIListener01 uiGLListener = new UIListener01 (0, rs, DEBUG, TRACE);
- uiGLListener.attachInputListenerTo(window);
- window.addGLEventListener(uiGLListener);
- window.setVisible(true);
-
- final Animator animator = new Animator();
- animator.setUpdateFPSFrames(60, System.err);
- animator.add(window);
-
- window.addKeyListener(new KeyAdapter() {
- @Override
- public void keyPressed(final KeyEvent arg0) {
- if(arg0.getKeyCode() == KeyEvent.VK_F4) {
- new InterruptSource.Thread() {
- @Override
- public void run() {
- window.destroy();
- } }.start();
- }
- }
- });
- window.addWindowListener(new WindowAdapter() {
- @Override
- public void windowDestroyed(final WindowEvent e) {
- animator.stop();
- }
- });
-
- animator.start();
- }
-}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
index 4976c78e8..f8c04d4bf 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
@@ -31,7 +31,7 @@ import java.util.ArrayList;
import com.jogamp.nativewindow.NativeWindowException;
import com.jogamp.opengl.GL2ES2;
-
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.GLRegion;
@@ -44,11 +44,14 @@ import com.jogamp.newt.event.MouseAdapter;
import com.jogamp.newt.event.NEWTEvent;
import com.jogamp.newt.event.MouseEvent;
import com.jogamp.newt.event.MouseListener;
+import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.math.Quaternion;
+import com.jogamp.opengl.math.VectorUtil;
import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.PMVMatrix;
public abstract class UIShape {
- public static final boolean DRAW_DEBUG_BOX = true;
+ public static final boolean DRAW_DEBUG_BOX = false;
protected static final int DIRTY_SHAPE = 1 << 0 ;
protected static final int DIRTY_STATE = 1 << 1 ;
@@ -280,7 +283,7 @@ public abstract class UIShape {
region.clear(gl);
}
addShapeToRegion(gl, renderer);
- if( false && DRAW_DEBUG_BOX ) {
+ if( DRAW_DEBUG_BOX ) {
region.clear(gl);
final OutlineShape shape = new OutlineShape(renderer.getRenderState().getVertexFactory());
shape.setSharpness(shapesSharpness);
@@ -295,6 +298,124 @@ public abstract class UIShape {
}
}
+ public void setTransform(final PMVMatrix pmv) {
+ final float[] uiTranslate = getTranslate();
+ pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]);
+
+ final Quaternion quat = getRotation();
+ final boolean rotate = !quat.isIdentity();
+ final float[] uiScale = getScale();
+ final boolean scale = !VectorUtil.isVec3Equal(uiScale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON);
+ if( rotate || scale ) {
+ final float[] rotOrigin = getRotationOrigin();
+ final boolean pivot = !VectorUtil.isVec3Zero(rotOrigin, 0, FloatUtil.EPSILON);
+ if( pivot ) {
+ pmv.glTranslatef(rotOrigin[0], rotOrigin[1], rotOrigin[2]);
+ }
+ if( scale ) {
+ pmv.glScalef(uiScale[0], uiScale[1], uiScale[2]);
+ }
+ if( rotate ) {
+ pmv.glRotate(quat);
+ }
+ if( pivot ) {
+ pmv.glTranslatef(-rotOrigin[0], -rotOrigin[1], -rotOrigin[2]);
+ }
+ }
+ }
+
+ /**
+ * Retrieve window surface size of this shape
+ * @param renderer source of viewport and PMVMatrix
+ * @param surfaceSize target surface size
+ * @return true for successful gluProject(..) operation, otherwise false
+ */
+ public boolean getSurfaceSize(final RegionRenderer renderer, final int[/*2*/] surfaceSize) {
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+
+ pmv.glPushMatrix();
+ setTransform(pmv);
+ boolean res = false;
+ final int[/*4*/] viewport = renderer.getViewport(new int[4]);
+ // System.err.println("UIShape::getSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]);
+ final float[] winCoordHigh = new float[3];
+ final float[] winCoordLow = new float[3];
+ final float[] high = getBounds().getHigh();
+ final float[] low = getBounds().getLow();
+
+ if( pmv.gluProject(high[0], high[1], high[2], viewport, 0, winCoordHigh, 0) ) {
+ // System.err.printf("UIShape::surfaceSize.H: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), high[0], high[1], high[2], winCoordHigh[0], winCoordHigh[1], winCoordHigh[2]);
+ if( pmv.gluProject(low[0], low[1], low[2], viewport, 0, winCoordLow, 0) ) {
+ // System.err.printf("UIShape::surfaceSize.L: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), low[0], low[1], low[2], winCoordLow[0], winCoordLow[1], winCoordLow[2]);
+ surfaceSize[0] = (int)(winCoordHigh[0] - winCoordLow[0]);
+ surfaceSize[1] = (int)(winCoordHigh[1] - winCoordLow[1]);
+ // System.err.printf("UIShape::surfaceSize.S: shape %d: %f x %f -> %d x %d%n", getName(), winCoordHigh[0] - winCoordLow[0], winCoordHigh[1] - winCoordLow[1], surfaceSize[0], surfaceSize[1]);
+ res = true;
+ }
+ }
+ pmv.glPopMatrix();
+ return res;
+ }
+
+ /**
+ * Map given object coordinate relative to this shape to window coordinates
+ * @param renderer source of viewport and PMVMatrix
+ * @param objPos object position relative to this shape's center
+ * @param glWinPos target window position of objPos relative to this shape
+ * @return true for successful gluProject(..) operation, otherwise false
+ */
+ public boolean objToWinCoord(final RegionRenderer renderer, final float[/*3*/] objPos, final int[/*2*/] glWinPos) {
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+
+ pmv.glPushMatrix();
+ setTransform(pmv);
+ boolean res = false;
+ final int[/*4*/] viewport = renderer.getViewport(new int[4]);
+ // System.err.println("UIShape::objToWinCoordgetSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]);
+ final float[] winCoord = new float[3];
+
+ if( pmv.gluProject(objPos[0], objPos[1], objPos[2], viewport, 0, winCoord, 0) ) {
+ // System.err.printf("UIShape::objToWinCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), objPos[0], objPos[1], objPos[2], winCoord[0], winCoord[1], winCoord[2]);
+ glWinPos[0] = (int)(winCoord[0]);
+ glWinPos[1] = (int)(winCoord[1]);
+ // System.err.printf("UIShape::objToWinCoord.X: shape %d: %f / %f -> %d / %d%n", getName(), winCoord[0], winCoord[1], glWinPos[0], glWinPos[1]);
+ res = true;
+ }
+ pmv.glPopMatrix();
+ return res;
+ }
+
+ /**
+ * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate.
+ * @param renderer source of viewport and PMVMatrix
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ * @param objPos target object position of glWinX/glWinY relative to this shape
+ * @return @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false
+ */
+ public boolean winToObjCoord(final RegionRenderer renderer, final int glWinX, final int glWinY, final float[/*3*/] objPos) {
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+
+ pmv.glPushMatrix();
+ setTransform(pmv);
+ boolean res = false;
+ final float[] ctr = getBounds().getCenter();
+ final int[] viewport = renderer.getViewport(new int[4]);
+ final float[] tmp = new float[3];
+ if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, tmp, 0) ) {
+ // System.err.printf("UIShape::winToObjCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), ctr[0], ctr[1], ctr[2], tmp[0], tmp[1], tmp[2]);
+ if( pmv.gluUnProject(glWinX, glWinY, tmp[2], viewport, 0, objPos, 0) ) {
+ // System.err.printf("UIShape::winToObjCoord.1: shape %d: win [%d, %d, %f] -> obj [%f, %f, %f]%n", getName(), glWinX, glWinY, tmp[2], objPos[0], objPos[1], objPos[2]);
+ res = true;
+ }
+ }
+ pmv.glPopMatrix();
+ return res;
+ }
+
public float[] getColor() {
return rgbaColor;
}
@@ -420,15 +541,22 @@ public abstract class UIShape {
* The latter fields are also normalized to lower-left zero origin, allowing easier usage.
*/
public static class PointerEventInfo {
- /** The intended {@link UIShape} instance for this event */
+ /** The associated {@link UIShape} for this event */
public final UIShape shape;
- /** The relative pointer position inside the intended {@link UIShape}. */
+ /** The relative object coordinate of glWinX/glWinY to the associated {@link UIShape}. */
public final float[] objPos;
- /** window x-position in OpenGL model space */
+ /** X-coordinate in GL window coordinates, origin bottom-left */
public final int glWinX;
- /** window y-position in OpenGL model space */
+ /** Y-coordinate in GL window coordinates, origin bottom-left */
public final int glWinY;
+ /**
+ * Ctor
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ * @param shape associated shape
+ * @param objPos relative object coordinate of glWinX/glWinY to the associated shape.
+ */
PointerEventInfo(final int glWinX, final int glWinY, final UIShape shape, final float[] objPos) {
this.glWinX = glWinX;
this.glWinY = glWinY;
@@ -455,10 +583,11 @@ public abstract class UIShape {
}
/**
- *
+ * Dispatch given NEWT mouse event to this shape
* @param e original Newt {@link MouseEvent}
- * @param glX x-position in OpenGL model space
- * @param glY y-position in OpenGL model space
+ * @param glWinX in GL window coordinates, origin bottom-left
+ * @param glWinY in GL window coordinates, origin bottom-left
+ * @param objPos object position of mouse event within this shape
*/
public final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final float[] objPos) {
e.setAttachment(new PointerEventInfo(glWinX, glWinY, this, objPos));
@@ -524,19 +653,26 @@ public abstract class UIShape {
//
protected OutlineShape createDebugOutline(final OutlineShape shape, final AABBox box) {
- final float tw = box.getWidth();
- final float th = box.getHeight();
+ final float d = 0.025f;
+ final float tw = box.getWidth() + d*2f;
+ final float th = box.getHeight() + d*2f;
- final float minX = box.getMinX();
- final float minY = box.getMinY();
- final float z = box.getMinZ() + 0.025f;
+ final float minX = box.getMinX() - d;
+ final float minY = box.getMinY() - d;
+ final float z = 0; // box.getMinZ() + 0.025f;
// CCW!
- shape.addVertex(minX, minY, z, true);
- shape.addVertex(minX+tw, minY, z, true);
- shape.addVertex(minX+tw, minY + th, z, true);
- shape.addVertex(minX, minY + th, z, true);
- shape.closeLastOutline(true);
+ shape.moveTo(minX, minY, z);
+ shape.lineTo(minX+tw, minY, z);
+ shape.lineTo(minX+tw, minY + th, z);
+ shape.lineTo(minX, minY + th, z);
+ shape.closePath();
+
+ // shape.addVertex(minX, minY, z, true);
+ // shape.addVertex(minX+tw, minY, z, true);
+ // shape.addVertex(minX+tw, minY + th, z, true);
+ // shape.addVertex(minX, minY + th, z, true);
+ // shape.closeLastOutline(true);
return shape;
}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java
new file mode 100644
index 000000000..4a61a2be0
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShapeDemo01.java
@@ -0,0 +1,556 @@
+/**
+ * Copyright 2010-2023 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 com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import com.jogamp.opengl.FPSCounter;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.test.junit.graph.demos.MSAATool;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Basic UIShape and Type Rendering demo.
+ *
+ * Action Keys:
+ * - 1/2: zoom in/out
+ * - 4/5: increase/decrease shape/text spacing
+ * - 6/7: increase/decrease corner size
+ * - 0/9: rotate
+ * - v: toggle v-sync
+ * - s: screenshot
+ */
+public class UIShapeDemo01 implements GLEventListener {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ public static void main(final String[] args) throws IOException {
+ Font font = null;
+ if( 0 != args.length ) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-font")) {
+ i++;
+ font = FontFactory.get(new File(args[i]));
+ }
+ }
+ }
+ if( null == font ) {
+ font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF);
+ }
+ System.err.println("Font: "+font.getFullFamilyName());
+
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities(glp);
+ caps.setAlphaBits(4);
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(4);
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ // window.setPosition(10, 10);
+ window.setSize(800, 400);
+ window.setTitle(UIShapeDemo01.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final UIShapeDemo01 uiGLListener = new UIShapeDemo01(font, Region.COLORCHANNEL_RENDERING_BIT, rs, DEBUG, TRACE);
+ uiGLListener.attachInputListenerTo(window);
+ window.addGLEventListener(uiGLListener);
+ window.setVisible(true);
+
+ final Animator animator = new Animator();
+ // animator.setUpdateFPSFrames(60, System.err);
+ animator.add(window);
+
+ window.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(arg0.getKeyCode() == KeyEvent.VK_F4) {
+ new InterruptSource.Thread() {
+ @Override
+ public void run() {
+ window.destroy();
+ } }.start();
+ }
+ }
+ });
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ animator.start();
+ }
+
+ private final Font font;
+ private final GLReadBufferUtil screenshot;
+ private final int renderModes;
+ private final RegionRenderer rRenderer;
+ private final boolean debug;
+ private final boolean trace;
+
+ private final LabelButton button;
+ private final CrossHair crossHair;
+
+ private KeyAction keyAction;
+ private MouseAction mouseAction;
+
+ private volatile GLAutoDrawable autoDrawable = null;
+
+ private final float[] position = new float[] {0,0,0};
+
+ private static final float xTran = 0f;
+ private static final float yTran = 0f;
+ private static final float zTran = -1/5f;
+ private static final float zNear = 0.1f;
+ private static final float zFar = 7000.0f;
+
+ boolean ignoreInput = false;
+
+ public UIShapeDemo01(final Font font, final int renderModes, final RenderState rs, final boolean debug, final boolean trace) {
+ this.font = font;
+ this.renderModes = renderModes;
+ this.rRenderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);
+ this.debug = debug;
+ this.trace = trace;
+ this.screenshot = new GLReadBufferUtil(false, false);
+
+ button = new LabelButton(SVertex.factory(), renderModes, font, "Click me!", 1/8f, 1/16f);
+ button.setLabelColor(0.0f,0.0f,0.0f);
+ /** Button defaults !
+ button.setLabelColor(1.0f,1.0f,1.0f);
+ button.setButtonColor(0.6f,0.6f,0.6f);
+ button.setCorner(1.0f);
+ button.setSpacing(2.0f);
+ */
+ System.err.println(button);
+ crossHair = new CrossHair(SVertex.factory(), renderModes, 1/20f, 1/20f, 1/1000f);
+ crossHair.setColor(0f,0f,1f,1f);
+ crossHair.setEnabled(true);
+ }
+
+ public final RegionRenderer getRegionRenderer() { return rRenderer; }
+ public final float[] getPosition() { return position; }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ autoDrawable = drawable;
+ GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(debug) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
+ }
+ if(trace) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
+ }
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ getRegionRenderer().init(gl, renderModes);
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
+ MSAATool.dump(drawable);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glViewport(xstart, ystart, width, height);
+ rRenderer.reshapePerspective(45.0f, width, height, zNear, zFar);
+ // rRenderer.reshapeOrtho(width, height, zNear, zFar);
+
+ lastWidth = width;
+ lastHeight = height;
+ if( drawable instanceof Window ) {
+ ((Window)drawable).setTitle(UIShapeDemo01.class.getSimpleName()+": "+drawable.getSurfaceWidth()+" x "+drawable.getSurfaceHeight());
+ }
+ }
+ float lastWidth = 0f, lastHeight = 0f;
+
+ final int[] sampleCount = { 4 };
+
+ private void drawShape(final GL2ES2 gl, final PMVMatrix pmv, final RegionRenderer renderer, final UIShape shape) {
+ pmv.glPushMatrix();
+ shape.setTransform(pmv);
+ shape.drawShape(gl, renderer, sampleCount);
+ pmv.glPopMatrix();
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+ renderer.enable(gl, true);
+ drawShape(gl, pmv, renderer, button);
+ drawShape(gl, pmv, renderer, crossHair);
+ {
+ final String text = "Hello Origin.";
+ final float full_width_o;
+ {
+ final float orthoDist = -zTran; // assume orthogonal plane at -zTran
+ float glWinX = 0;
+ float glWinY = 0;
+ final float winZ = FloatUtil.getOrthoWinZ(orthoDist, zNear, zFar);
+ final float[] objCoord0 = new float[3];
+ final float[] objCoord1 = new float[3];
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord0, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord0[0], objCoord0[1], objCoord0[2]);
+ }
+ }
+ glWinX = drawable.getSurfaceWidth();
+ glWinY = drawable.getSurfaceHeight();
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord1, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord1[0], objCoord1[1], objCoord1[2]);
+ }
+ }
+ full_width_o = objCoord1[0] - objCoord0[0];
+ }
+ final AABBox txt_box_em = font.getGlyphBounds(text);
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float txt_scale = full_width_s/2f;
+ pmv.glPushMatrix();
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ final AABBox txt_box_r = TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, new float[] { 0, 0, 0, 1 }, sampleCount);
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ System.err.println("XXX: txt_box_rg "+txt_box_r);
+ once = false;
+ }
+ pmv.glPopMatrix();
+ }
+ renderer.enable(gl, false);
+ }
+ static boolean once = true;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ button.destroy(gl, getRegionRenderer());
+ crossHair.destroy(gl, getRegionRenderer());
+
+ autoDrawable = null;
+ screenshot.dispose(gl);
+ rRenderer.destroy(gl);
+ }
+
+ /** Attach the input listener to the window */
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ }
+ if ( null == mouseAction ) {
+ mouseAction = new MouseAction();
+ window.addMouseListener(mouseAction);
+ }
+ }
+
+ public void detachFrom(final GLWindow window) {
+ if ( null == keyAction ) {
+ return;
+ }
+ if ( null == mouseAction ) {
+ return;
+ }
+ window.removeGLEventListener(this);
+ window.removeKeyListener(keyAction);
+ window.removeMouseListener(mouseAction);
+ }
+
+ public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ pw.printf("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zTran), 0, objName);
+
+ final String filename = dir + tech + sw +".png";
+ if(screenshot.readPixels(drawable.getGL(), false)) {
+ screenshot.write(new File(filename));
+ }
+ }
+
+ int screenshot_num = 0;
+
+ public void setIgnoreInput(final boolean v) {
+ ignoreInput = v;
+ }
+ public boolean getIgnoreInput() {
+ return ignoreInput;
+ }
+
+ public class MouseAction implements MouseListener{
+
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(final MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ autoDrawable.invoke(false, new GLRunnable() { // avoid data-race
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ System.err.println("\n\nMouse: "+e);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+
+ // flip to GL window coordinates, origin bottom-left
+ final int[] viewport = renderer.getViewport(new int[4]);
+ final int glWinX = e.getX();
+ final int glWinY = viewport[3] - e.getY() - 1;
+
+ {
+ final float[] objPos = new float[3];
+ System.err.println("\n\nButton: "+button);
+ button.winToObjCoord(renderer, glWinX, glWinY, objPos);
+ System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]);
+
+ final int[] surfaceSize = new int[2];
+ button.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("Button: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+ }
+ {
+ final float[] objPosC = crossHair.getBounds().getCenter();
+ final int[] objWinPos = new int[2];
+ System.err.println("\n\nCrossHair: "+crossHair);
+ if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) {
+ System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ }
+
+ final float[] objPos2 = new float[3];
+ crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2);
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+
+ final float[] winObjPos = new float[3];
+ if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) {
+ // final float[] translate = crossHair.getTranslate();
+ // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] };
+ final float dx = winObjPos[0] - objPosC[0];
+ final float dy = winObjPos[1] - objPosC[1];
+ // final float dz = winObjPos[2] - objPosT[2];
+ if( !FloatUtil.isZero(dx, FloatUtil.EPSILON) || !FloatUtil.isZero(dy, FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ crossHair.translate(dx, dy, 0f);
+ } else {
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ }
+ }
+
+ final int[] surfaceSize = new int[2];
+ crossHair.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+ }
+ return true;
+ } } );
+
+ }
+
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseDragged(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(ignoreInput) {
+ return;
+ }
+
+ if(arg0.getKeyCode() == KeyEvent.VK_1){
+ button.translate(0f, 0f, -zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_2){
+ button.translate(0f, 0f, zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_UP){
+ button.translate(0f, button.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
+ button.translate(0f, -button.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
+ button.translate(-button.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
+ button.translate(button.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_4){
+ button.setSpacing(button.getSpacingX()-0.01f, button.getSpacingY()-0.005f);
+ System.err.println("Button Spacing: " + button.getSpacingX());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_5){
+ button.setSpacing(button.getSpacingX()+0.01f, button.getSpacingY()+0.005f);
+ System.err.println("Button Spacing: " + button.getSpacingX());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_6){
+ button.setCorner(button.getCorner()-0.01f);
+ System.err.println("Button Corner: " + button.getCorner());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_7){
+ button.setCorner(button.getCorner()+0.01f);
+ System.err.println("Button Corner: " + button.getCorner());
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_0){
+ // rotate(1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_9){
+ // rotate(-1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_V) {
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ final int _i = gl.getSwapInterval();
+ final int i;
+ switch(_i) {
+ case 0: i = 1; break;
+ case 1: i = -1; break;
+ case -1: i = 0; break;
+ default: i = 1; break;
+ }
+ gl.setSwapInterval(i);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ if(drawable instanceof FPSCounter) {
+ ((FPSCounter)drawable).resetFPSCounter();
+ }
+ System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
+ return true;
+ }
+ });
+ }
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_S){
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ try {
+ final String type = Region.getRenderModeString(renderModes);
+ printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false);
+ screenshot_num++;
+ } catch (final GLException e) {
+ e.printStackTrace();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+ @Override
+ public void keyReleased(final KeyEvent arg0) {}
+ }
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UITypeDemo01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UITypeDemo01.java
new file mode 100644
index 000000000..b3a56d9b7
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UITypeDemo01.java
@@ -0,0 +1,584 @@
+/**
+ * Copyright 2010-2023 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 com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import com.jogamp.opengl.FPSCounter;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLAnimatorControl;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLEventListener;
+import com.jogamp.opengl.GLException;
+import com.jogamp.opengl.GLPipelineFactory;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.test.junit.graph.demos.MSAATool;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.curve.opengl.TextRegionUtil;
+import com.jogamp.graph.font.Font;
+import com.jogamp.graph.font.FontFactory;
+import com.jogamp.graph.font.FontSet;
+import com.jogamp.graph.font.Font.Glyph;
+import com.jogamp.graph.geom.SVertex;
+import com.jogamp.newt.Window;
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.event.KeyListener;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.GLReadBufferUtil;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Basic UIShape and Type Rendering demo.
+ *
+ * Action Keys:
+ * - 1/2: zoom in/out
+ * - 4/5: increase/decrease shape/text spacing
+ * - 6/7: increase/decrease corner size
+ * - 0/9: rotate
+ * - v: toggle v-sync
+ * - s: screenshot
+ */
+public class UITypeDemo01 implements GLEventListener {
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ public static void main(final String[] args) throws IOException {
+ Font font = null;
+ String text = "Hello Origin.";
+ int glyph_id = 0;
+ if( 0 != args.length ) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-font")) {
+ i++;
+ font = FontFactory.get(new File(args[i]));
+ } else if(args[i].equals("-text")) {
+ i++;
+ text = args[i];
+ } else if(args[i].equals("-glyph")) {
+ i++;
+ glyph_id = MiscUtils.atoi(args[i], 0);
+ }
+ }
+ }
+ if( null == font ) {
+ font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF);
+ }
+ System.err.println("Font: "+font.getFullFamilyName());
+ System.err.println("Text: "+text);
+
+ final GLProfile glp = GLProfile.getGL2ES2();
+ final GLCapabilities caps = new GLCapabilities(glp);
+ caps.setAlphaBits(4);
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(4);
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ // window.setPosition(10, 10);
+ window.setSize(800, 400);
+ window.setTitle(UITypeDemo01.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ final RenderState rs = RenderState.createRenderState(SVertex.factory());
+ final UITypeDemo01 uiGLListener = new UITypeDemo01(font, glyph_id, text, Region.COLORCHANNEL_RENDERING_BIT, rs, DEBUG, TRACE);
+ uiGLListener.attachInputListenerTo(window);
+ window.addGLEventListener(uiGLListener);
+ window.setVisible(true);
+
+ final Animator animator = new Animator();
+ // animator.setUpdateFPSFrames(60, System.err);
+ animator.add(window);
+
+ window.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(arg0.getKeyCode() == KeyEvent.VK_F4) {
+ new InterruptSource.Thread() {
+ @Override
+ public void run() {
+ window.destroy();
+ } }.start();
+ }
+ }
+ });
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ animator.start();
+ }
+
+ private final float[] fg_color = new float[] { 0, 0, 0, 1 };
+ private final Font font;
+ private final String text;
+ private final int glyph_id;
+ private final GLReadBufferUtil screenshot;
+ private final int renderModes;
+ private final RegionRenderer rRenderer;
+ private final boolean debug;
+ private final boolean trace;
+
+ private final CrossHair crossHair;
+ private final UIShape testObj;
+
+ private KeyAction keyAction;
+ private MouseAction mouseAction;
+
+ private volatile GLAutoDrawable autoDrawable = null;
+
+ private final float[] position = new float[] {0,0,0};
+
+ private static final float xTran = 0f;
+ private static final float yTran = 0f;
+ private static final float zTran = -1/5f;
+ private static final float zNear = 0.1f;
+ private static final float zFar = 7000.0f;
+
+ boolean ignoreInput = false;
+
+ @SuppressWarnings("unused")
+ public UITypeDemo01(final Font font, final int glyph_id, final String text, final int renderModes, final RenderState rs, final boolean debug, final boolean trace) {
+ this.font = font;
+ this.text = text;
+ this.glyph_id = glyph_id;
+ this.renderModes = renderModes;
+ this.rRenderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);
+ this.debug = debug;
+ this.trace = trace;
+ this.screenshot = new GLReadBufferUtil(false, false);
+
+ crossHair = new CrossHair(SVertex.factory(), renderModes, 1/20f, 1/20f, 1/1000f);
+ crossHair.setColor(0f,0f,1f,1f);
+ crossHair.setEnabled(true);
+
+ if (false ) {
+ final Rectangle o = new Rectangle(SVertex.factory(), renderModes, 1/10f, 1/20f, 1/1000f);
+ o.translate(o.getWidth(), -o.getHeight(), 0f);
+ testObj = o;
+ } else {
+ final float scale = 0.15312886f;
+ final float size_xz = 0.541f;
+ final UIShape o = new TestObject02(SVertex.factory(), renderModes);
+ o.scale(scale, scale, 1f);
+ // o.translate(size_xz, -size_xz, 0f);
+ testObj = o;
+ }
+ testObj.setColor(0f, 0f, 0f, 1f);
+ testObj.setEnabled(true);
+ }
+
+ public final RegionRenderer getRegionRenderer() { return rRenderer; }
+ public final float[] getPosition() { return position; }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ autoDrawable = drawable;
+ GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(debug) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ).getGL2ES2();
+ }
+ if(trace) {
+ gl = gl.getContext().setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", null, gl, new Object[] { System.err } ) ).getGL2ES2();
+ }
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ getRegionRenderer().init(gl, renderModes);
+
+ gl.setSwapInterval(1);
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
+ MSAATool.dump(drawable);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glViewport(xstart, ystart, width, height);
+ rRenderer.reshapePerspective(45.0f, width, height, zNear, zFar);
+ // rRenderer.reshapeOrtho(width, height, zNear, zFar);
+
+ lastWidth = width;
+ lastHeight = height;
+ if( drawable instanceof Window ) {
+ ((Window)drawable).setTitle(UITypeDemo01.class.getSimpleName()+": "+drawable.getSurfaceWidth()+" x "+drawable.getSurfaceHeight());
+ }
+ }
+ float lastWidth = 0f, lastHeight = 0f;
+
+ final int[] sampleCount = { 4 };
+
+ private void drawShape(final GL2ES2 gl, final PMVMatrix pmv, final RegionRenderer renderer, final UIShape shape) {
+ pmv.glPushMatrix();
+ shape.setTransform(pmv);
+ shape.drawShape(gl, renderer, sampleCount);
+ pmv.glPopMatrix();
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+ renderer.enable(gl, true);
+ drawShape(gl, pmv, renderer, testObj);
+ drawShape(gl, pmv, renderer, crossHair);
+ {
+ final float full_width_o;
+ final float full_height_o;
+ {
+ final float orthoDist = -zTran; // assume orthogonal plane at -zTran
+ float glWinX = 0;
+ float glWinY = 0;
+ final float winZ = FloatUtil.getOrthoWinZ(orthoDist, zNear, zFar);
+ final float[] objCoord0 = new float[3];
+ final float[] objCoord1 = new float[3];
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord0, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord0[0], objCoord0[1], objCoord0[2]);
+ }
+ }
+ glWinX = drawable.getSurfaceWidth();
+ glWinY = drawable.getSurfaceHeight();
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, renderer.getViewport(), 0, objCoord1, 0) ) {
+ if( once ) {
+ System.err.printf("winToObjCoord: win [%f, %f, %f] -> obj [%f, %f, %f]%n", glWinX, glWinY, winZ, objCoord1[0], objCoord1[1], objCoord1[2]);
+ }
+ }
+ full_width_o = objCoord1[0] - objCoord0[0];
+ full_height_o = objCoord1[1] - objCoord0[1];
+ }
+ final Font.Glyph glyph = Glyph.ID_UNKNOWN != glyph_id ? font.getGlyph(glyph_id) : null;
+ if( null != glyph && null != glyph.getShape() && glyph.getID() != Glyph.ID_UNKNOWN ) {
+ final AABBox txt_box_em = glyph.getBBox();
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float full_height_s = full_height_o / txt_box_em.getHeight();
+ final float txt_scale = full_width_s < full_height_s ? full_width_s/2f : full_height_s/2f;
+ pmv.glPushMatrix();
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ {
+ final GLRegion region = GLRegion.create(renderModes, null);
+ region.addOutlineShape(glyph.getShape(), null, region.hasColorChannel() ? fg_color : null);
+ region.draw(gl, renderer, sampleCount);
+ region.destroy(gl);
+ }
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: full_height: "+full_height_o+" / "+txt_box_em.getHeight()+" -> "+full_height_s);
+ System.err.println("XXX: txt_scale: "+txt_scale);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ once = false;
+ }
+ } else {
+ final AABBox txt_box_em = font.getGlyphBounds(text);
+ final float full_width_s = full_width_o / txt_box_em.getWidth();
+ final float full_height_s = full_height_o / txt_box_em.getHeight();
+ final float txt_scale = full_width_s < full_height_s ? full_width_s/2f : full_height_s/2f;
+ pmv.glPushMatrix();
+ pmv.glScalef(txt_scale, txt_scale, 1f);
+ pmv.glTranslatef(-txt_box_em.getWidth(), 0f, 0f);
+ final AABBox txt_box_r = TextRegionUtil.drawString3D(gl, renderModes, renderer, font, text, fg_color, sampleCount);
+ if( once ) {
+ final AABBox txt_box_em2 = font.getGlyphShapeBounds(null, text);
+ System.err.println("XXX: full_width: "+full_width_o+" / "+txt_box_em.getWidth()+" -> "+full_width_s);
+ System.err.println("XXX: full_height: "+full_height_o+" / "+txt_box_em.getHeight()+" -> "+full_height_s);
+ System.err.println("XXX: txt_scale: "+txt_scale);
+ System.err.println("XXX: txt_box_em "+txt_box_em);
+ System.err.println("XXX: txt_box_e2 "+txt_box_em2);
+ System.err.println("XXX: txt_box_rg "+txt_box_r);
+ once = false;
+ }
+ }
+ pmv.glPopMatrix();
+ }
+ renderer.enable(gl, false);
+ }
+ static boolean once = true;
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ crossHair.destroy(gl, getRegionRenderer());
+ testObj.destroy(gl, getRegionRenderer());
+
+ autoDrawable = null;
+ screenshot.dispose(gl);
+ rRenderer.destroy(gl);
+ }
+
+ /** Attach the input listener to the window */
+ public void attachInputListenerTo(final GLWindow window) {
+ if ( null == keyAction ) {
+ keyAction = new KeyAction();
+ window.addKeyListener(keyAction);
+ }
+ if ( null == mouseAction ) {
+ mouseAction = new MouseAction();
+ window.addMouseListener(mouseAction);
+ }
+ }
+
+ public void detachFrom(final GLWindow window) {
+ if ( null == keyAction ) {
+ return;
+ }
+ if ( null == mouseAction ) {
+ return;
+ }
+ window.removeGLEventListener(this);
+ window.removeKeyListener(keyAction);
+ window.removeMouseListener(mouseAction);
+ }
+
+ public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final String objName, final boolean exportAlpha) throws GLException, IOException {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ pw.printf("-%03dx%03d-Z%04d-T%04d-%s", drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), (int)Math.abs(zTran), 0, objName);
+
+ final String filename = dir + tech + sw +".png";
+ if(screenshot.readPixels(drawable.getGL(), false)) {
+ screenshot.write(new File(filename));
+ }
+ }
+
+ int screenshot_num = 0;
+
+ public void setIgnoreInput(final boolean v) {
+ ignoreInput = v;
+ }
+ public boolean getIgnoreInput() {
+ return ignoreInput;
+ }
+
+ public class MouseAction implements MouseListener{
+
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseExited(final MouseEvent e) {
+ }
+
+ @Override
+ public void mousePressed(final MouseEvent e) {
+ autoDrawable.invoke(false, new GLRunnable() { // avoid data-race
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ System.err.println("\n\nMouse: "+e);
+
+ final RegionRenderer renderer = getRegionRenderer();
+ final PMVMatrix pmv = renderer.getMatrix();
+ pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
+ pmv.glLoadIdentity();
+ pmv.glTranslatef(xTran, yTran, zTran);
+
+ // flip to GL window coordinates, origin bottom-left
+ final int[] viewport = renderer.getViewport(new int[4]);
+ final int glWinX = e.getX();
+ final int glWinY = viewport[3] - e.getY() - 1;
+
+ {
+ final float[] objPosC = crossHair.getBounds().getCenter();
+ final int[] objWinPos = new int[2];
+ System.err.println("\n\nCrossHair: "+crossHair);
+ if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) {
+ System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ }
+
+ final float[] objPos2 = new float[3];
+ crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2);
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+
+ final float[] winObjPos = new float[3];
+ if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) {
+ // final float[] translate = crossHair.getTranslate();
+ // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] };
+ final float dx = winObjPos[0] - objPosC[0];
+ final float dy = winObjPos[1] - objPosC[1];
+ // final float dz = winObjPos[2] - objPosT[2];
+ if( !FloatUtil.isZero(dx, FloatUtil.EPSILON) || !FloatUtil.isZero(dy, FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ crossHair.translate(dx, dy, 0f);
+ } else {
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ }
+ }
+
+ final int[] surfaceSize = new int[2];
+ crossHair.getSurfaceSize(renderer, surfaceSize);
+ System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]);
+ }
+ return true;
+ } } );
+
+ }
+
+ @Override
+ public void mouseReleased(final MouseEvent e) {
+ }
+
+ @Override
+ public void mouseMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseDragged(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void mouseWheelMoved(final MouseEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ public class KeyAction implements KeyListener {
+ @Override
+ public void keyPressed(final KeyEvent arg0) {
+ if(ignoreInput) {
+ return;
+ }
+
+ if(arg0.getKeyCode() == KeyEvent.VK_1){
+ crossHair.translate(0f, 0f, -zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_2){
+ crossHair.translate(0f, 0f, zTran/10f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_UP){
+ crossHair.translate(0f, crossHair.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_DOWN){
+ crossHair.translate(0f, -crossHair.getHeight()/10f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_LEFT){
+ crossHair.translate(-crossHair.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_RIGHT){
+ crossHair.translate(crossHair.getWidth()/10f, 0f, 0f);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_0){
+ // rotate(1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_9){
+ // rotate(-1);
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_V) {
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ final GL gl = drawable.getGL();
+ final int _i = gl.getSwapInterval();
+ final int i;
+ switch(_i) {
+ case 0: i = 1; break;
+ case 1: i = -1; break;
+ case -1: i = 0; break;
+ default: i = 1; break;
+ }
+ gl.setSwapInterval(i);
+
+ final GLAnimatorControl a = drawable.getAnimator();
+ if( null != a ) {
+ a.resetFPSCounter();
+ }
+ if(drawable instanceof FPSCounter) {
+ ((FPSCounter)drawable).resetFPSCounter();
+ }
+ System.err.println("Swap Interval: "+_i+" -> "+i+" -> "+gl.getSwapInterval());
+ return true;
+ }
+ });
+ }
+ }
+ else if(arg0.getKeyCode() == KeyEvent.VK_S){
+ if(null != autoDrawable) {
+ autoDrawable.invoke(false, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ try {
+ final String type = Region.getRenderModeString(renderModes);
+ printScreen(drawable, "./", "demo-"+type, "snap"+screenshot_num, false);
+ screenshot_num++;
+ } catch (final GLException e) {
+ e.printStackTrace();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+ @Override
+ public void keyReleased(final KeyEvent arg0) {}
+ }
+}