aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java161
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java77
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java67
3 files changed, 259 insertions, 46 deletions
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java
new file mode 100644
index 000000000..76ee7389d
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java
@@ -0,0 +1,161 @@
+/**
+ * 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.demos.graph.ui;
+
+import java.io.IOException;
+
+import com.jogamp.common.os.Clock;
+import com.jogamp.graph.curve.Region;
+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.graph.ui.gl.Scene;
+import com.jogamp.graph.ui.gl.Shape;
+import com.jogamp.graph.ui.gl.shapes.Button;
+import com.jogamp.newt.event.WindowAdapter;
+import com.jogamp.newt.event.WindowEvent;
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.GL;
+import com.jogamp.opengl.GLAutoDrawable;
+import com.jogamp.opengl.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.GLRunnable;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Res independent Shape, in Scene attached to GLWindow showing simple linear Shape movement within one main function.
+ */
+public class UISceneDemo00 {
+ public static void main(final String[] args) throws IOException {
+ final int surface_width = 1280, surface_height = 720;
+ final int renderModes = Region.VBAA_RENDERING_BIT;
+ final GLProfile glp = GLProfile.getGL2ES2();
+
+ //
+ // Resolution independent, no screen size
+ //
+ final Font font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF);
+ System.err.println("Font: "+font.getFullFamilyName());
+
+ final Shape shape = new Button(SVertex.factory(), renderModes, font, "+", 0.10f, 0.10f/2.5f);
+ System.err.println("Shape bounds "+shape.getBounds(glp));
+
+ final Scene scene = new Scene();
+ scene.setClearParams(new float[] { 1f, 1f, 1f, 1f}, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+ scene.addShape(shape);
+
+ final Animator animator = new Animator();
+ animator.setUpdateFPSFrames(1*60, System.err);
+
+ final GLCapabilities caps = new GLCapabilities(glp);
+ caps.setAlphaBits(4);
+ System.out.println("Requested: " + caps);
+
+ final GLWindow window = GLWindow.create(caps);
+ window.setSize(surface_width, surface_height);
+ window.setTitle(UISceneDemo00.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ window.setVisible(true);
+ window.addGLEventListener(scene);
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowDestroyed(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ scene.attachInputListenerTo(window);
+
+ animator.add(window);
+ animator.start();
+
+ //
+ // After initial display we can use screen resolution post initial Scene.reshape(..)
+ // However, in this example we merely use the resolution to
+ // - Scale the shape to the sceneBox, i.e. normalizing to screen-size 1x1
+ // - Compute the animation values with DPI
+ scene.waitUntilDisplayed();
+
+ final AABBox sceneBox = scene.getBounds();
+ shape.scale(sceneBox.getWidth(), sceneBox.getWidth(), 1f); // scale shape to sceneBox, normalizing to screen-size 1x1
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+
+ //
+ // Compute the metric animation values -> shape obj-velocity
+ //
+ final float min_obj = sceneBox.getMinX();
+ final float max_obj = sceneBox.getMaxX() - shape.getScaledWidth();
+
+ final int[] shapeSizePx = shape.getSurfaceSize(scene, new PMVMatrix(), new int[2]); // [px]
+ final float[] pixPerShapeUnit = shape.getPixelPerShapeUnit(shapeSizePx, new float[2]); // [px]/[shapeUnit]
+
+ final float pixPerMM = window.getPixelsPerMM(new float[2])[0]; // [px]/[mm]
+ final float dist_px = scene.getWidth() - shapeSizePx[0]; // [px]
+ final float dist_m = dist_px/pixPerMM/1e3f; // [m]
+ final float velocity = 50/1e3f; // [m]/[s]
+ final float velocity_px = velocity * 1e3f * pixPerMM; // [px]/[s]
+ final float velovity_obj = velocity_px / pixPerShapeUnit[0]; // [shapeUnit]/[s]
+ final float exp_dur_s = dist_m / velocity; // [s]
+
+ System.err.println();
+ System.err.printf("Shape: %d x %d [pixel], %.4f px/shape_unit%n", shapeSizePx[0], shapeSizePx[1], pixPerShapeUnit[0]);
+ System.err.printf("Shape: %s%n", shape);
+ System.err.println();
+ System.err.printf("Distance: %.0f pixel @ %.3f px/mm, %.3f mm%n", dist_px, pixPerMM, dist_m*1e3f);
+ System.err.printf("Velocity: %.3f mm/s, %.3f px/s, %.6f obj/s, expected travel-duration %.3f s%n",
+ velocity*1e3f, velocity_px, velovity_obj, exp_dur_s);
+
+ final long t0_us = Clock.currentNanos() / 1000; // [us]
+ long t1_us = t0_us;
+ shape.moveTo(min_obj, 0f, 0f); // move shape to min start position
+ while( shape.getPosition()[0] < max_obj ) {
+ final long t2_us = Clock.currentNanos() / 1000;
+ final float dt_s = ( t2_us - t1_us ) / 1e6f;
+ t1_us = t2_us;
+
+ final float dx = velovity_obj * dt_s; // [shapeUnit]
+ // System.err.println("move ")
+
+ // Move on GL thread to have vsync for free
+ // Otherwise we would need to employ a sleep(..) w/ manual vsync
+ window.invoke(true, new GLRunnable() {
+ @Override
+ public boolean run(final GLAutoDrawable drawable) {
+ shape.move(dx, 0f, 0f);
+ return true;
+ }
+ });
+ }
+ final float has_dur_s = ( ( Clock.currentNanos() / 1000 ) - t0_us ) / 1e6f; // [us]
+ System.err.printf("Actual travel-duration %.3f s, delay %.3f s%n", has_dur_s, has_dur_s-exp_dur_s);
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+ window.destroy();
+ }
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java
index e580ded9a..221fb5f12 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java
@@ -63,7 +63,9 @@ import com.jogamp.opengl.util.av.GLMediaPlayer;
import com.jogamp.opengl.util.av.GLMediaPlayerFactory;
/**
- * Res independent Shape, in Scene attached to GLWindow showing simple linear Shape movement w/ listener attached.
+ * Res independent Shape, in Scene attached to GLWindow w/ listener attached.
+ *
+ * User can test Shape drag-move and drag-resize w/ 1-pointer
*/
public class UISceneDemo01 {
static final boolean DEBUG = false;
@@ -109,20 +111,6 @@ public class UISceneDemo01 {
}
}
}
- 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);
- if( sceneMSAASamples > 0 ) {
- caps.setSampleBuffers(true);
- caps.setNumSamples(sceneMSAASamples);
- }
- System.out.println("Requested: " + caps);
-
final int renderModes;
if( graphVBAAMode ) {
renderModes = Region.VBAA_RENDERING_BIT;
@@ -131,11 +119,23 @@ public class UISceneDemo01 {
} else {
renderModes = 0;
}
+ final GLProfile glp = GLProfile.getGL2ES2();
+
+ //
+ // Resolution independent, no screen size
+ //
+ if( null == font ) {
+ font = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_LIGHT, FontSet.STYLE_SERIF);
+ }
+ System.err.println("Font: "+font.getFullFamilyName());
+ final Shape shape = makeShape(font, renderModes);
+ System.err.println("m0 shape bounds "+shape.getBounds(glp));
+ System.err.println("m0 "+shape);
+ // Scene for Shape ...
final Scene scene = new Scene();
scene.setClearParams(new float[] { 1f, 1f, 1f, 1f}, GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
- final Shape shape = makeShape(font, renderModes);
shape.onMove(new Shape.Listener() {
@Override
public void run(final Shape shape) {
@@ -163,11 +163,13 @@ public class UISceneDemo01 {
} );
scene.addShape(shape);
- final AABBox shapeBox = shape.getBounds(glp);
- System.err.println("m0 "+shape);
-
-
- // scene.surfaceToPlaneSize(width, height, null);
+ final GLCapabilities caps = new GLCapabilities(glp);
+ caps.setAlphaBits(4);
+ if( sceneMSAASamples > 0 ) {
+ caps.setSampleBuffers(true);
+ caps.setNumSamples(sceneMSAASamples);
+ }
+ System.out.println("Requested: " + caps);
final GLWindow window = GLWindow.create(caps);
window.setSize(width, height);
@@ -201,35 +203,20 @@ public class UISceneDemo01 {
animator.start();
+ //
+ // After initial display we can use screen resolution post initial Scene.reshape(..)
+ // However, in this example we merely use the resolution to
+ // - Scale the shape to the sceneBox, i.e. normalizing to screen-size 1x1
scene.waitUntilDisplayed();
final AABBox sceneBox = scene.getBounds();
- System.err.println("m1 scene "+sceneBox);
- System.err.println("m1.0 "+shape);
shape.scale(sceneBox.getWidth(), sceneBox.getWidth(), 1f); // scale shape to sceneBox
System.err.println("m1.1 "+shape);
- shape.scale(0.4f, 0.4f, 1f); // scale shape even smaller
- System.err.println("m1.2 "+shape);
try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
- final float min = sceneBox.getMinX();
- final float max = sceneBox.getMaxX() - shapeBox.getWidth()*shape.getScaleX();
- shape.moveTo(min, 0f, 0f); // move shape to min start position
-
- final float step = (max-min)/1000f;
- System.err.println("m2 ["+min+" .. "+max+"], step "+step);
- for(float x=min; x < max; x+=step) {
- shape.move(step, 0f, 0f);
- final int[] glWinPos = shape.shapeToWinCoord(scene.getPMVMatrixSetup(), scene.getViewport(), shape.getBounds().getCenter(), new PMVMatrix(), new int[2]);
- if( null != glWinPos ) {
- window.warpPointer(glWinPos[0], window.getHeight() - glWinPos[1] - 1);
- }
- System.err.println("mm x "+x+", ["+min+" .. "+max+"], step "+step);
- try { Thread.sleep(5); } catch (final InterruptedException e1) { }
- }
- try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
- System.err.println("The End ..");
- window.destroy();
+ System.err.println("You may test moving the Shape by dragging the shape with 1-pointer.");
+ System.err.println("You may test resizing the Shape by dragging the shape on 1/5th of the bottom-left or bottom-right corner with 1-pointer.");
+ System.err.println("Press F4 or 'window close' to exit ..");
}
static void testProject(final Scene scene, final Shape shape, final int glWinX, final int glWinY) {
@@ -244,9 +231,8 @@ public class UISceneDemo01 {
@SuppressWarnings("unused")
static Shape makeShape(final Font font, final int renderModes) {
- final float sw = 0.2f;
+ final float sw = 0.10f;
final float sh = sw / 2.5f;
- System.err.println("Shape "+sw+" x "+sh);
if( false ) {
Uri filmUri;
@@ -292,7 +278,6 @@ public class UISceneDemo01 {
return b;
} else if( true ){
final Button b = new Button(SVertex.factory(), renderModes, font, "+", sw, sh);
- // b.setLabelColor(0.0f,0.0f,0.0f);
b.setCorner(0.0f);
return b;
} else {
diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
index 089348476..081ef11dc 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java
@@ -261,6 +261,32 @@ public abstract class Shape {
public final AABBox getBounds() { return box; }
/**
+ * Returns the scaled width of the bounding {@link AABBox} for this shape.
+ *
+ * The returned width will only cover the scaled shape
+ * after an initial call to {@link #draw(GL2ES2, RegionRenderer, int[]) draw(..)}
+ * or {@link #validate(GL2ES2)}.
+ *
+ * @see #getBounds()
+ */
+ public final float getScaledWidth() {
+ return box.getWidth() * getScaleX();
+ }
+
+ /**
+ * Returns the scaled height of the bounding {@link AABBox} for this shape.
+ *
+ * The returned height will only cover the scaled shape
+ * after an initial call to {@link #draw(GL2ES2, RegionRenderer, int[]) draw(..)}
+ * or {@link #validate(GL2ES2)}.
+ *
+ * @see #getBounds()
+ */
+ public final float getScaledHeight() {
+ return box.getHeight() * getScaleY();
+ }
+
+ /**
* Returns the unscaled bounding {@link AABBox} for this shape.
*
* This variant differs from {@link #getBounds()} as it
@@ -492,6 +518,47 @@ public abstract class Shape {
}
/**
+ * Retrieve pixel per shape-coordinate unit, i.e. [px]/[obj].
+ * <p>
+ * The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape
+ * including this shape's {@link #setTransform(PMVMatrix)}.
+ * </p>
+ * @param scene {@link Scene} to retrieve {@link Scene.PMVMatrixSetup} and the viewport.
+ * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup},
+ * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller.
+ * @param pixPerShape float[2] pixel per shape-coordinate unit
+ * @return given float[2] {@code pixPerShape} for successful gluProject(..) operation, otherwise {@code null}
+ * @see #getPixelPerShapeUnit(int[], float[])
+ * @see #getSurfaceSize(Scene, PMVMatrix, int[])
+ * @see #getScaledWidth()
+ * @see #getScaledHeight()
+ */
+ public float[] getPixelPerShapeUnit(final Scene scene, final PMVMatrix pmv, final float[] pixPerShape) {
+ final int[] shapeSizePx = new int[2];
+ if( null != getSurfaceSize(scene, new PMVMatrix(), shapeSizePx) ) {
+ return getPixelPerShapeUnit(shapeSizePx, pixPerShape);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieve pixel per shape-coordinate unit, i.e. [px]/[obj].
+ * @param shapeSizePx int[2] shape size in pixel as retrieved via e.g. {@link #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[])}
+ * @param pixPerShape float[2] pixel per shape-coordinate unit
+ * @return given float[2] {@code pixPerShape}
+ * @see #getPixelPerShapeUnit(Scene, PMVMatrix, float[])
+ * @see #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[])
+ * @see #getScaledWidth()
+ * @see #getScaledHeight()
+ */
+ public float[] getPixelPerShapeUnit(final int[] shapeSizePx, final float[] pixPerShape) {
+ pixPerShape[0] = shapeSizePx[0] / getScaledWidth();
+ pixPerShape[0] = shapeSizePx[1] / getScaledHeight();
+ return pixPerShape;
+ }
+
+ /**
* Map given object coordinate relative to this shape to window coordinates.
* <p>
* The given {@link PMVMatrix} has to be setup properly for this object,