diff options
Diffstat (limited to 'src')
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, |