aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-04-05 10:06:26 +0200
committerSven Gothel <[email protected]>2023-04-05 10:06:26 +0200
commit1eb9d91bbf5d24a02c4d9e98501ff51eb7ecdcd0 (patch)
treedc1c7615f4b99d7b09c9b3c569f6e3d459dbb4e5
parent15e60161787224e85172685f74dc0ac195969b51 (diff)
GraphUI: Adopting Vec*f API; Adding Group; Scene + Group are Container, traversing the PMVMatrix throughout childs (-> see TreeTool).
Utilizing the Vec*f (and Matrix4f) API w/ AABBox et al renders our code more clean & safe, see commit 15e60161787224e85172685f74dc0ac195969b51. A Group allows to contain multiple Shapes, hence the PMVMatrix must be traversed accordingly using TreeTool for all operations (draw, picking, win->obj coordinates, ..). Hence Scene + Group are now implementing Container and reuse code via TreeTool and a Shape.Visitor*. This will allow further simplification of user code.
-rw-r--r--make/scripts/tests.sh4
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java2
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java2
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java2
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java17
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo10.java9
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo11.java238
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo20.java41
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java27
-rw-r--r--src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java22
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Container.java97
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/GraphShape.java5
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Group.java249
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Scene.java180
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/Shape.java349
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java69
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/shapes/Button.java12
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/shapes/Label.java2
-rw-r--r--src/graphui/classes/jogamp/graph/ui/TreeTool.java159
-rw-r--r--src/graphui/classes/jogamp/graph/ui/shapes/Label0.java5
20 files changed, 1216 insertions, 275 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index 2038ceee2..6340a7a1c 100644
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -577,7 +577,7 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestFloatUtil02MatrixMatrixMultNOUI $*
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestFloatUtil03InversionNOUI $*
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestMatrix4f01NOUI $*
-testnoawt com.jogamp.opengl.test.junit.jogl.math.TestMatrix4f02MulNOUI $*
+#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestMatrix4f02MulNOUI $*
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestMatrix4f03InversionNOUI $*
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestPMVMatrix01NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.math.TestPMVMatrix02NOUI $*
@@ -979,7 +979,7 @@ testnoawt com.jogamp.opengl.test.junit.jogl.math.TestMatrix4f02MulNOUI $*
#testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo02 $*
#testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo03 $*
#testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo10 $*
-#testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo11 $*
+testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo11 $*
#testnoawt com.jogamp.opengl.demos.graph.ui.UISceneDemo20 $*
#testnoawt com.jogamp.opengl.demos.av.MovieCube $*
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java
index e60bc95f3..fde29d9ea 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo00.java
@@ -150,7 +150,7 @@ public class UISceneDemo00 {
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 && window.isNativeValid() ) {
+ while( shape.getPosition().x() < max_obj && window.isNativeValid() ) {
final long t2_us = Clock.currentNanos() / 1000;
final float dt_s = ( t2_us - t1_us ) / 1e6f;
t1_us = t2_us;
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 3358804b7..3bf506e8f 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java
@@ -159,7 +159,7 @@ public class UISceneDemo01 {
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 && window.isNativeValid() ) {
+ while( shape.getPosition().x() < max_obj && window.isNativeValid() ) {
final long t2_us = Clock.currentNanos() / 1000;
final float dt_s = ( t2_us - t1_us ) / 1e6f;
t1_us = t2_us;
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java
index e6e210f69..77827656f 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo02.java
@@ -248,7 +248,7 @@ public class UISceneDemo02 {
final long t1_us = Clock.currentNanos() / 1000; // [us]
final long[] t2_us = { t1_us };
- while( movingGlyph.getPosition()[0] > end_pos && window.isNativeValid() ) {
+ while( movingGlyph.getPosition().x() > end_pos && window.isNativeValid() ) {
// Move on GL thread to have vsync for free
// Otherwise we would need to employ a sleep(..) w/ manual vsync
final long[] t3_us = { 0 };
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java
index ee48a684e..7df78ca99 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo03.java
@@ -57,7 +57,6 @@ import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.math.Quaternion;
import com.jogamp.opengl.math.Vec3f;
-import com.jogamp.opengl.math.VectorUtil;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.PMVMatrix;
@@ -82,7 +81,7 @@ public class UISceneDemo03 {
" JogAmp, Java™ libraries for 3D & Media "
};
- static GraphUIDemoArgs options = new GraphUIDemoArgs(1280, 720, 0);
+ static GraphUIDemoArgs options = new GraphUIDemoArgs(1280, 720, Region.VBAA_RENDERING_BIT);
static float velocity = 30 / 1e3f; // [m]/[s]
static float rot_step = velocity * 1;
@@ -306,7 +305,7 @@ public class UISceneDemo03 {
addedGlyphShapes.addAll(glyphShapes);
final float pos_eps = FloatUtil.EPSILON * 5000; // ~= 0.0005960
- final float rot_eps = FloatUtil.adegToRad(1f); // 1 adeg ~= 0.01745 rad
+ final float rot_eps = FloatUtil.adegToRad(0.5f); // 1 adeg ~= 0.01745 rad
final long t0_us = Clock.currentNanos() / 1000; // [us]
final long[] t2_us = { t0_us };
@@ -327,13 +326,19 @@ public class UISceneDemo03 {
final Vec3f p_t = target.minus(pos);
final float p_t_diff = p_t.length();
final Quaternion q = glyph.getRotation();
- final float radY = q.toAngleAxis(VectorUtil.VEC3_UNIT_Y);
- final float radYdiff = Math.min(radY, FloatUtil.TWO_PI - radY);
+ final Vec3f euler = q.toEuler(new Vec3f());
+ final float radY = euler.y();
+ final float radYdiff = Math.min(Math.abs(radY), FloatUtil.TWO_PI - Math.abs(radY));
final boolean pos_ok = p_t_diff <= pos_eps;
final boolean pos_near = p_t_diff <= glyph.getBounds().getSize() * fontScale * 2f;
final boolean rot_ok = pos_near && ( radYdiff <= rot_eps || radYdiff <= rot_step * 2f );
if ( pos_ok && rot_ok ) {
// arrived
+ if( DEBUG ) {
+ if( 0 == idx ) {
+ System.err.println("F: rot: "+radY+" ("+FloatUtil.radToADeg(radY)+"), diff "+radYdiff+" ("+FloatUtil.radToADeg(radYdiff)+"), step "+rot_step+" ("+FloatUtil.radToADeg(rot_step)+")");
+ }
+ }
glyph.moveTo(target.x(), target.y(), target.z());
q.setIdentity();
glyphShapes.remove(idx);
@@ -362,7 +367,7 @@ public class UISceneDemo03 {
} else {
if( DEBUG ) {
if( 0 == idx ) {
- System.err.println("rot: "+radY+" ("+FloatUtil.radToADeg(radY)+"), diff "+radYdiff+" ("+FloatUtil.radToADeg(radYdiff)+"), step "+rot_step+" ("+FloatUtil.radToADeg(rot_step)+")");
+ System.err.println("P: rot: "+radY+" ("+FloatUtil.radToADeg(radY)+"), diff "+radYdiff+" ("+FloatUtil.radToADeg(radYdiff)+"), step "+rot_step+" ("+FloatUtil.radToADeg(rot_step)+")");
}
}
if( radYdiff <= rot_step * 3f || radYdiff <= rot_eps ) {
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo10.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo10.java
index 52fe33e51..0874df04e 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo10.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo10.java
@@ -56,6 +56,7 @@ import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.demos.es2.GearsES2;
import com.jogamp.opengl.demos.util.MiscUtils;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.PMVMatrix;
@@ -149,8 +150,8 @@ public class UISceneDemo10 {
shape.onMove(new Shape.Listener() {
@Override
public void run(final Shape shape) {
- final float[] p = shape.getPosition();
- System.err.println("Shape moved: "+p[0]+", "+p[1]+", "+p[2]);
+ final Vec3f p = shape.getPosition();
+ System.err.println("Shape moved: "+p);
}
});
shape.addMouseListener(new Shape.MouseGestureAdapter() {
@@ -226,8 +227,8 @@ public class UISceneDemo10 {
static void testProject(final Scene scene, final Shape shape, final int glWinX, final int glWinY) {
final PMVMatrix pmv = new PMVMatrix();
- final float[] objPos = shape.winToShapeCoord(scene.getPMVMatrixSetup(), scene.getViewport(), glWinX, glWinY, pmv, new float[3]);
- System.err.printf("MM1: winToObjCoord: obj [%25.20ff, %25.20ff, %25.20ff]%n", objPos[0], objPos[1], objPos[2]);
+ final Vec3f objPos = shape.winToShapeCoord(scene.getPMVMatrixSetup(), scene.getViewport(), glWinX, glWinY, pmv, new Vec3f());
+ System.err.printf("MM1: winToObjCoord: obj %s%n", objPos);
final int[] glWinPos = shape.shapeToWinCoord(scene.getPMVMatrixSetup(), scene.getViewport(), objPos, pmv, new int[2]);
final int windx = glWinPos[0]-glWinX;
final int windy = glWinPos[1]-glWinY;
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo11.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo11.java
new file mode 100644
index 000000000..43cd9fc07
--- /dev/null
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo11.java
@@ -0,0 +1,238 @@
+/**
+ * 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.ui.Group;
+import com.jogamp.graph.ui.Scene;
+import com.jogamp.graph.ui.Shape;
+import com.jogamp.graph.ui.layout.GridLayout;
+import com.jogamp.graph.ui.shapes.Button;
+import com.jogamp.graph.ui.shapes.GLButton;
+import com.jogamp.newt.event.MouseEvent;
+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.GLCapabilities;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.demos.es2.GearsES2;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.Animator;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Res independent {@link Shape}s in a {@link Group} using a {@link GridLayout}, contained within a Scene attached to GLWindow.
+ * <p>
+ * Pass '-keep' to main-function to keep running after animation,
+ * then user can test Shape drag-move and drag-resize w/ 1-pointer.
+ * </p>
+ */
+public class UISceneDemo11 {
+ 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();
+
+ boolean keepRunning = false;
+ if( 0 != args.length ) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-keep")) {
+ keepRunning = true;
+ }
+ }
+ }
+
+ //
+ // 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 float shapeWidth = 1/8f;
+ final float shapeHeight = shapeWidth/2.5f;
+ final Group groupA0 = new Group(new GridLayout(2, shapeWidth*1.1f, shapeHeight*1.1f));
+ {
+ groupA0.addShape( new Button(renderModes, font, "1", shapeWidth, shapeHeight) );
+ groupA0.addShape( new Button(renderModes, font, "2", shapeWidth, shapeHeight) );
+ groupA0.addShape( new Button(renderModes, font, "3", shapeWidth, shapeHeight) );
+ groupA0.addShape( new Button(renderModes, font, "4", shapeWidth, shapeHeight) );
+ }
+ groupA0.validate(glp);
+ System.err.println("Group-A0 "+groupA0);
+ groupA0.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+
+ 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(groupA0);
+ scene.setFrustumCullingEnabled(true);
+
+ final Animator animator = new Animator();
+
+ 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(UISceneDemo11.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ window.setVisible(true);
+ window.addGLEventListener(scene);
+ window.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowResized(final WindowEvent e) {
+ window.setTitle(UISceneDemo11.class.getSimpleName()+": "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight());
+ }
+ @Override
+ public void windowDestroyNotify(final WindowEvent e) {
+ animator.stop();
+ }
+ });
+
+ scene.attachInputListenerTo(window);
+
+ animator.setUpdateFPSFrames(1*60, null); // System.err);
+ 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
+ // - Compute the animation values with DPI
+ scene.waitUntilDisplayed();
+
+ final AABBox sceneBox = scene.getBounds();
+ System.err.println("SceneBox "+sceneBox);
+ System.err.println("Group-A0 "+groupA0);
+ groupA0.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+
+ final Shape mobileShape = groupA0;
+
+ if( true ) {
+ //
+ // Compute the metric animation values -> shape obj-velocity
+ //
+ final float min_obj = sceneBox.getMinX();
+ final float max_obj = sceneBox.getMaxX() - mobileShape.getScaledWidth();
+
+ final int[] shapeSizePx = mobileShape.getSurfaceSize(scene, new PMVMatrix(), new int[2]); // [px]
+ final float[] pixPerShapeUnit = mobileShape.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", mobileShape);
+ 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;
+ mobileShape.moveTo(min_obj, 0f, 0f); // move shape to min start position
+ while( mobileShape.getPosition().x() < max_obj && window.isNativeValid() ) {
+ 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, (drawable) -> {
+ mobileShape.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);
+ System.err.println("Group-A0 bounds "+groupA0);
+ groupA0.forAll( (shape) -> { System.err.println("Shape... "+shape); return false; });
+ try { Thread.sleep(1000); } catch (final InterruptedException e1) { }
+ }
+ if( !keepRunning ) {
+ window.destroy();
+ }
+ }
+
+ static Shape makeGLButton(final float sw, final float sh, final Font font, final int renderModes) {
+ final GearsES2 gears = new GearsES2(0);
+ gears.setVerbose(false);
+ gears.setClearColor(new float[] { 0.9f, 0.9f, 0.9f, 1f } );
+ final boolean[] animate = { true };
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ System.err.println("Gears Anim: Waiting");
+ try {
+ gears.waitForInit(true);
+ } catch (final InterruptedException e) { }
+ System.err.println("Gears Anim: Started");
+ while( gears.isInit() ) {
+ if( animate[0] ) {
+ final float ry = ( gears.getRotY() + 1 ) % 360;
+ gears.setRotY(ry);
+ }
+ try {
+ Thread.sleep(15);
+ } catch (final InterruptedException e) { }
+ }
+ System.err.println("Gears Anim: End");
+ }
+ }).start();
+ final int texUnit = 1;
+ final GLButton b = new GLButton(renderModes, sw,
+ sh, texUnit, gears, false /* useAlpha */);
+ b.setToggleable(true);
+ b.setToggle(true); // toggle == true -> animation
+ b.setAnimate(true);
+ b.addMouseListener(new Shape.MouseGestureAdapter() {
+ @Override
+ public void mouseClicked(final MouseEvent e) {
+ b.setAnimate( b.isToggleOn() );
+ animate[0] = b.getAnimate();
+ } } );
+ return b;
+ }
+
+}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo20.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo20.java
index e4a50b2da..5e94758dc 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo20.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo20.java
@@ -80,7 +80,7 @@ import com.jogamp.opengl.demos.graph.MSAATool;
import com.jogamp.opengl.demos.util.MiscUtils;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.math.FloatUtil;
-import com.jogamp.opengl.math.VectorUtil;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.PMVMatrix;
@@ -306,8 +306,8 @@ public class UISceneDemo20 implements GLEventListener {
scene.setDebugBox(options.debugBoxThickness);
}
- private void rotateButtons(float[] angdeg) {
- angdeg = VectorUtil.scaleVec3(angdeg, angdeg, FloatUtil.PI / 180.0f);
+ private void rotateButtons(final Vec3f angdeg) {
+ angdeg.scale(FloatUtil.PI / 180.0f); // -> radians
for(int i=0; i<buttons.size(); i++) {
buttons.get(i).getRotation().rotateByEuler( angdeg );
}
@@ -434,15 +434,15 @@ public class UISceneDemo20 implements GLEventListener {
@Override
public void mouseClicked(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
- if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
- rotateButtons(new float[] { 0f, -5f, 0f}); // left-half pressed
+ if( shapeEvent.objPos.x() < shapeEvent.shape.getBounds().getCenter().x() ) {
+ rotateButtons(new Vec3f( 0f, -5f, 0f ) ); // left-half pressed
} else {
- rotateButtons(new float[] { 0f, 5f, 0f}); // right-half pressed
+ rotateButtons(new Vec3f( 0f, 5f, 0f ) ); // right-half pressed
}
}
@Override
public void mouseWheelMoved(final MouseEvent e) {
- rotateButtons(new float[] { 0f, e.getRotation()[1], 0f});
+ rotateButtons(new Vec3f( 0f, e.getRotation()[1], 0f ) );
} } );
buttons.add(button);
@@ -454,7 +454,7 @@ public class UISceneDemo20 implements GLEventListener {
public void mouseClicked(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
int sampleCount = scene.getSampleCount();
- if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ if( shapeEvent.objPos.x() < shapeEvent.shape.getBounds().getCenter().x() ) {
// left-half pressed
sampleCount--;
} else {
@@ -475,7 +475,7 @@ public class UISceneDemo20 implements GLEventListener {
if( shapeEvent.shape instanceof GraphShape ) {
int quality = ((GraphShape)shapeEvent.shape).getQuality();
- if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ if( shapeEvent.objPos.x() < shapeEvent.shape.getBounds().getCenter().x() ) {
// left-half pressed
if( quality > 0 ) {
quality--;
@@ -526,7 +526,7 @@ public class UISceneDemo20 implements GLEventListener {
button.addMouseListener(new Shape.MouseGestureAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
- rotateButtons(new float[] { 0f, 180f, 0f});
+ rotateButtons(new Vec3f ( 0f, 180f, 0f ));
} } );
button.addMouseListener(dragZoomRotateListener);
buttons.add(button);
@@ -537,7 +537,7 @@ public class UISceneDemo20 implements GLEventListener {
button.addMouseListener(new Shape.MouseGestureAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
- rotateButtons(new float[] { 180f, 0f, 0f});
+ rotateButtons(new Vec3f ( 180f, 0f, 0f ));
} } );
button.addMouseListener(dragZoomRotateListener);
buttons.add(button);
@@ -550,7 +550,7 @@ public class UISceneDemo20 implements GLEventListener {
public void mouseClicked(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
final float dx, dy;
- if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ if( shapeEvent.objPos.x() < shapeEvent.shape.getBounds().getCenter().x() ) {
dx=-0.01f; dy=-0.005f;
} else {
dx=0.01f; dy=0.005f;
@@ -571,7 +571,7 @@ public class UISceneDemo20 implements GLEventListener {
public void mouseClicked(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
final float dc;
- if( shapeEvent.objPos[0] < shapeEvent.shape.getBounds().getCenter()[0] ) {
+ if( shapeEvent.objPos.x() < shapeEvent.shape.getBounds().getCenter().x() ) {
dc=-0.1f;
} else {
dc=0.1f;
@@ -901,7 +901,7 @@ public class UISceneDemo20 implements GLEventListener {
- 1.5f * truePtSizeLabel.getScaledLineHeight()
- labels[currentText].getScaledHeight(), 0f);
System.err.println("Label["+currentText+"] MOVE: "+labels[currentText]);
- System.err.println("Label["+currentText+"] MOVE: "+Arrays.toString(labels[currentText].getPosition()));
+ System.err.println("Label["+currentText+"] MOVE: "+labels[currentText].getPosition());
}
}
@@ -936,7 +936,7 @@ public class UISceneDemo20 implements GLEventListener {
labels[currentText].addMouseListener(dragZoomRotateListener);
scene.addShape(labels[currentText]);
System.err.println("Label["+currentText+"] CTOR: "+labels[currentText]);
- System.err.println("Label["+currentText+"] CTOR: "+Arrays.toString(labels[currentText].getPosition()));
+ System.err.println("Label["+currentText+"] CTOR: "+labels[currentText].getPosition());
}
if( fpsLabel.isEnabled() ) {
final String text;
@@ -968,19 +968,18 @@ public class UISceneDemo20 implements GLEventListener {
public void mouseDragged(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
if( e.getPointerCount() == 1 ) {
- final float[] tx = shapeEvent.shape.getPosition();
- actionText = String.format((Locale)null, "Pos %6.2f / %6.2f / %6.2f", tx[0], tx[1], tx[2]);
+ final Vec3f tx = shapeEvent.shape.getPosition();
+ actionText = String.format((Locale)null, "Pos %s", tx);
}
}
@Override
public void mouseWheelMoved(final MouseEvent e) {
final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
- final float[] rot = VectorUtil.scaleVec3(e.getRotation(), e.getRotation(), FloatUtil.PI / 180.0f);
+ final Vec3f rot = new Vec3f(e.getRotation()).scale( FloatUtil.PI / 180.0f );
// swap axis for onscreen rotation matching natural feel
- final float tmp = rot[0]; rot[0] = rot[1]; rot[1] = tmp;
- VectorUtil.scaleVec3(rot, rot, 2f);
- shapeEvent.shape.getRotation().rotateByEuler( rot );
+ final float tmp = rot.x(); rot.setX( rot.y() ); rot.setY( tmp );
+ shapeEvent.shape.getRotation().rotateByEuler( rot.scale( 2f ) );
}
};
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java
index 05b3aa955..fc4b275de 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java
@@ -44,6 +44,7 @@ import com.jogamp.opengl.GLRunnable;
import com.jogamp.opengl.demos.graph.MSAATool;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.common.util.InterruptSource;
import com.jogamp.graph.curve.Region;
@@ -400,9 +401,9 @@ public class UIShapeDemo01 implements GLEventListener {
button.setTransform(pmv);
System.err.println("\n\nButton: "+button);
- final float[] objPos = button.winToShapeCoord(pmv, viewport, glWinX, glWinY, new float[3]);
+ final Vec3f objPos = button.winToShapeCoord(pmv, viewport, glWinX, glWinY, new Vec3f());
if( null != objPos ) {
- System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]);
+ System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos);
}
final int[] surfaceSize = button.getSurfaceSize(pmv, viewport, new int[2]);
@@ -416,26 +417,24 @@ public class UIShapeDemo01 implements GLEventListener {
pmv.glPushMatrix();
crossHair.setTransform(pmv);
- final float[] objPosC = crossHair.getBounds().getCenter();
+ final Vec3f objPosC = crossHair.getBounds().getCenter();
System.err.println("\n\nCrossHair: "+crossHair);
final int[] objWinPos = crossHair.shapeToWinCoord(pmv, viewport, objPosC, new int[2]);
- System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ System.err.println("CrossHair: Obj: Obj "+objPosC+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
- final float[] objPos2 = crossHair.winToShapeCoord(pmv, viewport, objWinPos[0], objWinPos[1], new float[3]);
- System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+ final Vec3f objPos2 = crossHair.winToShapeCoord(pmv, viewport, objWinPos[0], objWinPos[1], new Vec3f());
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2);
- final float[] winObjPos = crossHair.winToShapeCoord(pmv, viewport, glWinX, glWinY, new float[3]);
+ final Vec3f winObjPos = crossHair.winToShapeCoord(pmv, viewport, glWinX, glWinY, new Vec3f());
if( null != 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.move(dx, dy, 0f);
+ final Vec3f diff = winObjPos.minus(objPosC);
+ if( !FloatUtil.isZero(diff.x(), FloatUtil.EPSILON) || !FloatUtil.isZero(diff.y(), FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos+" -> diff "+diff);
+ crossHair.move(diff.x(), diff.y(), 0f);
} else {
- System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos+" -> diff "+diff);
}
}
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java
index 2097b6cfe..d4e7bfc0f 100644
--- a/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java
+++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java
@@ -68,6 +68,7 @@ import com.jogamp.opengl.demos.graph.ui.testshapes.Glyph03FreeMonoRegular_M;
import com.jogamp.opengl.demos.util.MiscUtils;
import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLReadBufferUtil;
@@ -469,26 +470,25 @@ public class UITypeDemo01 implements GLEventListener {
pmv.glPushMatrix();
crossHair.setTransform(pmv);
- final float[] objPosC = crossHair.getBounds().getCenter();
+ final Vec3f objPosC = crossHair.getBounds().getCenter();
System.err.println("\n\nCrossHair: "+crossHair);
final int[] objWinPos = crossHair.shapeToWinCoord(pmv, viewport, objPosC, new int[2]);
- System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
+ System.err.println("CrossHair: Obj: Obj "+objPosC+" -> Win "+objWinPos[0]+"/"+objWinPos[1]);
- final float[] objPos2 = crossHair.winToShapeCoord(pmv, viewport, objWinPos[0], objWinPos[1], new float[3]);
- System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]);
+ final Vec3f objPos2 = crossHair.winToShapeCoord(pmv, viewport, objWinPos[0], objWinPos[1], new Vec3f());
+ System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2);
- final float[] winObjPos = crossHair.winToShapeCoord(pmv, viewport, glWinX, glWinY, new float[3]);
+ final Vec3f winObjPos = crossHair.winToShapeCoord(pmv, viewport, glWinX, glWinY, new Vec3f());
if( null != 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 Vec3f diff = winObjPos.minus(objPosC);
// 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.move(dx, dy, 0f);
+ if( !FloatUtil.isZero(diff.x(), FloatUtil.EPSILON) || !FloatUtil.isZero(diff.y(), FloatUtil.EPSILON) ) {
+ System.err.println("CrossHair: Move.1: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos+" -> diff "+diff);
+ crossHair.move(diff.x(), diff.y(), 0f);
} else {
- System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos[0]+"/"+winObjPos[1]+"/"+winObjPos[1]+" -> diff "+dx+" / "+dy);
+ System.err.println("CrossHair: Move.0: Win "+glWinX+"/"+glWinY+" -> Obj "+winObjPos+" -> diff "+diff);
}
}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Container.java b/src/graphui/classes/com/jogamp/graph/ui/Container.java
new file mode 100644
index 000000000..e48476fcf
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/Container.java
@@ -0,0 +1,97 @@
+/**
+ * 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.graph.ui;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+import com.jogamp.graph.ui.Shape.Visitor2;
+import com.jogamp.graph.ui.Shape.Visitor1;
+import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/**
+ * Container interface of UI {@link Shape}s
+ * @see Scene
+ * @see Shape
+ */
+public interface Container {
+
+ List<Shape> getShapes();
+
+ void addShape(Shape s);
+
+ /** Removes given shape, , w/o {@link Shape#destroy(com.jogamp.opengl.GL2ES2, com.jogamp.graph.curve.opengl.RegionRenderer) destroying} them. */
+ void removeShape(Shape s);
+
+ void addShapes(Collection<? extends Shape> shapes);
+
+ /** Removes all given shapes, w/o {@link Shape#destroy(com.jogamp.opengl.GL2ES2, com.jogamp.graph.curve.opengl.RegionRenderer) destroying} them. */
+ void removeShapes(Collection<? extends Shape> shapes);
+
+ boolean contains(Shape s);
+
+ AABBox getBounds(final PMVMatrix pmv, Shape shape);
+
+ /**
+ * Traverses through the graph up until {@code shape} and apply {@code action} on it.
+ * @param pmv
+ * @param shape
+ * @param action
+ * @return true to signal operation complete, i.e. {@code shape} found, otherwise false
+ */
+ boolean forOne(final PMVMatrix pmv, final Shape shape, final Runnable action);
+
+ /**
+ * Traverses through the graph and apply {@link Visitor1#visit(Shape)} for each, stop if it returns true.
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor1#visit(Shape)} returned true, otherwise false
+ */
+ boolean forAll(Visitor1 v);
+
+ /**
+ * Traverses through the graph and apply {@link Visitor2#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ boolean forAll(final PMVMatrix pmv, Visitor2 v);
+
+ /**
+ * Traverses through the graph and apply {@link Visitor#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ *
+ * Each {@link Container} level is sorted using {@code sortComp}
+ * @param sortComp
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ boolean forSortedAll(final Comparator<Shape> sortComp, final PMVMatrix pmv, final Visitor2 v);
+} \ No newline at end of file
diff --git a/src/graphui/classes/com/jogamp/graph/ui/GraphShape.java b/src/graphui/classes/com/jogamp/graph/ui/GraphShape.java
index 6efd7f5f4..8b142210a 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/GraphShape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/GraphShape.java
@@ -38,7 +38,6 @@ import com.jogamp.graph.geom.Vertex;
import com.jogamp.graph.geom.Vertex.Factory;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLProfile;
-import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.texture.TextureSequence;
/**
@@ -137,7 +136,7 @@ public abstract class GraphShape extends Shape {
if( null != gl ) {
clearDirtyRegions(gl);
}
- if( isShapeDirty() || null == region ) {
+ if( isShapeDirty() ) {
if( null == region ) {
region = createGLRegion(glp);
} else if( null == gl ) {
@@ -164,7 +163,7 @@ public abstract class GraphShape extends Shape {
final float x2 = box.getMaxX();
final float y1 = box.getMinY();
final float y2 = box.getMaxY();
- final float z = box.getCenter()[2]; // 0; // box.getMinZ() + 0.025f;
+ final float z = box.getCenter().z(); // 0; // box.getMinZ() + 0.025f;
{
// Outer OutlineShape as Winding.CCW.
shape.moveTo(x1, y1, z);
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Group.java b/src/graphui/classes/com/jogamp/graph/ui/Group.java
new file mode 100644
index 000000000..8174b279c
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/Group.java
@@ -0,0 +1,249 @@
+/**
+ * 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.graph.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+import com.jogamp.graph.curve.opengl.GLRegion;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.math.geom.AABBox;
+import com.jogamp.opengl.util.PMVMatrix;
+
+import jogamp.graph.ui.TreeTool;
+
+/**
+ * Group of UI {@link Shape}s, optionally utilizing a {@link Group.Layout}.
+ * @see Scene
+ * @see Shape
+ * @see Group.Layout
+ */
+public class Group extends Shape implements Container {
+ /** Layout for the group, called @ {@link Group#validate(GL2ES2)} or {@link Group#validate(GLProfile)}. */
+ public static interface Layout {
+ /** Performing the layout, called @ {@link Group#validate(GL2ES2)} or {@link Group#validate(GLProfile)}. */
+ void layout(Group g);
+ }
+
+ private final List<Shape> shapes = new ArrayList<Shape>();
+ private Layout layouter;
+
+ /**
+ * Create a Graph based {@link GLRegion} UI {@link Shape}.
+ */
+ public Group() {
+ super();
+ }
+
+ /**
+ * Create a Graph based {@link GLRegion} UI {@link Shape} w/ given {@link Group.Layour}.
+ */
+ public Group(final Layout l) {
+ super();
+ this.layouter = l;
+ }
+
+ /** Return current {@link Group.Layout}. */
+ public Layout getLayour() { return layouter; }
+
+ /** Set {@link Group.Layout}. */
+ public void setLayout(final Layout l) { layouter = l; }
+
+ @Override
+ public List<Shape> getShapes() {
+ return shapes;
+ }
+ @Override
+ public void addShape(final Shape s) {
+ shapes.add(s);
+ }
+
+ /** Removes given shape, keeps it alive. */
+ @Override
+ public void removeShape(final Shape s) {
+ shapes.remove(s);
+ }
+
+ /** Removes all given shapes and destroys them. */
+ public void removeShape(final GL2ES2 gl, final RegionRenderer renderer, final Shape s) {
+ s.setDebugBox(0f);
+ shapes.remove(s);
+ s.destroy(gl, renderer);
+ }
+
+ @Override
+ public void addShapes(final Collection<? extends Shape> shapes) {
+ for(final Shape s : shapes) {
+ addShape(s);
+ }
+ }
+ /** Removes all given shapes, keeps them alive. */
+ @Override
+ public void removeShapes(final Collection<? extends Shape> shapes) {
+ for(final Shape s : shapes) {
+ removeShape(s);
+ }
+ }
+ /** Removes all given shapes and destroys them. */
+ public void removeShapes(final GL2ES2 gl, final RegionRenderer renderer, final Collection<? extends Shape> shapes) {
+ for(final Shape s : shapes) {
+ removeShape(gl, renderer, s);
+ }
+ }
+
+ @Override
+ public boolean hasColorChannel() {
+ return false; // FIXME
+ }
+
+ @Override
+ protected final void clearImpl0(final GL2ES2 gl, final RegionRenderer renderer) {
+ for(final Shape s : shapes) {
+ // s.clearImpl0(gl, renderer);;
+ s.clear(gl, renderer);;
+ }
+ }
+
+ @Override
+ protected final void destroyImpl0(final GL2ES2 gl, final RegionRenderer renderer) {
+ for(final Shape s : shapes) {
+ // s.destroyImpl0(gl, renderer);
+ s.destroy(gl, renderer);;
+ }
+ }
+
+ private void layout() {
+ if( null != layouter ) {
+ layouter.layout(this);
+ }
+ }
+
+ private final boolean doFrustumCulling = true; // FIXME
+
+ @Override
+ protected final void drawImpl0(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount, final float[] rgba) {
+ final PMVMatrix pmv = renderer.getMatrix();
+ final int shapeCount = shapes.size();
+ for(int i=0; i<shapeCount; i++) {
+ final Shape shape = shapes.get(i);
+ if( shape.isEnabled() ) {
+ pmv.glPushMatrix();
+ shape.setTransform(pmv);
+
+ if( !doFrustumCulling || !pmv.glGetFrustum().isAABBoxOutside( shape.getBounds() ) ) {
+ if( null == rgba ) {
+ shape.drawToSelect(gl, renderer, sampleCount);
+ } else {
+ shape.draw(gl, renderer, sampleCount);
+ }
+ }
+ pmv.glPopMatrix();
+ }
+ }
+ }
+
+ @Override
+ protected void validateImpl(final GLProfile glp, final GL2ES2 gl) {
+ if( isShapeDirty() ) {
+ layout();
+ final PMVMatrix pmv = new PMVMatrix();
+ final AABBox tmpBox = new AABBox();
+ final float[] vec3Tmp0 = new float[3];
+ final float[] vec3Tmp1 = new float[3];
+ for(final Shape s : shapes) {
+ // s.validateImpl(glp, gl);
+ if( null != gl ) {
+ s.validate(gl);
+ } else {
+ s.validate(glp);
+ }
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ s.getBounds().transformMv(tmpBox, pmv, vec3Tmp0, vec3Tmp0);
+ pmv.glPopMatrix();
+ box.resize(tmpBox);
+ }
+ }
+ }
+
+ @Override
+ public boolean contains(final Shape s) {
+ if( shapes.contains(s) ) {
+ return true;
+ }
+ for(final Shape shape : shapes) {
+ if( shape instanceof Container ) {
+ if( ((Container)shape).contains(s) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public AABBox getBounds(final PMVMatrix pmv, final Shape shape) {
+ pmv.reset();
+ setTransform(pmv);
+ final AABBox res = new AABBox();
+ if( null == shape ) {
+ return res;
+ }
+ final float[] vec3Tmp0 = new float[3];
+ final float[] vec3Tmp1 = new float[3];
+ forOne(pmv, shape, () -> {
+ shape.getBounds().transformMv(res, pmv, vec3Tmp0, vec3Tmp1);
+ });
+ return res;
+ }
+
+ @Override
+ public boolean forOne(final PMVMatrix pmv, final Shape shape, final Runnable action) {
+ return TreeTool.forOne(shapes, pmv, shape, action);
+ }
+
+ @Override
+ public boolean forAll(final Visitor1 v) {
+ return TreeTool.forAll(shapes, v);
+ }
+
+ @Override
+ public boolean forAll(final PMVMatrix pmv, final Visitor2 v) {
+ return TreeTool.forAll(shapes, pmv, v);
+ }
+
+ @Override
+ public boolean forSortedAll(final Comparator<Shape> sortComp, final PMVMatrix pmv, final Visitor2 v) {
+ return TreeTool.forSortedAll(sortComp, shapes, pmv, v);
+ }
+}
+
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
index 831a9d334..ac3bbe146 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Scene.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Scene.java
@@ -49,6 +49,8 @@ import com.jogamp.common.nio.Buffers;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.RegionRenderer;
import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.graph.ui.Shape.Visitor2;
+import com.jogamp.graph.ui.Shape.Visitor1;
import com.jogamp.newt.event.GestureHandler;
import com.jogamp.newt.event.InputEvent;
import com.jogamp.newt.event.MouseEvent;
@@ -58,11 +60,14 @@ import com.jogamp.newt.event.GestureHandler.GestureEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.math.Ray;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.GLPixelStorageModes;
import com.jogamp.opengl.util.GLReadBufferUtil;
import com.jogamp.opengl.util.PMVMatrix;
+import jogamp.graph.ui.TreeTool;
+
/**
* GraphUI Scene
* <p>
@@ -86,7 +91,7 @@ import com.jogamp.opengl.util.PMVMatrix;
* </p>
* @see Shape
*/
-public final class Scene implements GLEventListener {
+public final class Scene implements Container, GLEventListener {
/** Default scene distance on z-axis to projection is -1/5f. */
public static final float DEFAULT_SCENE_DIST = -1/5f;
/** Default projection angle in degrees value is 45.0. */
@@ -201,14 +206,16 @@ public final class Scene implements GLEventListener {
}
}
+ @Override
public List<Shape> getShapes() {
return shapes;
}
+ @Override
public void addShape(final Shape s) {
s.setDebugBox(dbgbox_thickness);
shapes.add(s);
}
- /** Removes given shape, keeps it alive. */
+ @Override
public void removeShape(final Shape s) {
s.setDebugBox(0f);
shapes.remove(s);
@@ -219,12 +226,13 @@ public final class Scene implements GLEventListener {
shapes.remove(s);
s.destroy(gl, renderer);
}
+ @Override
public void addShapes(final Collection<? extends Shape> shapes) {
for(final Shape s : shapes) {
addShape(s);
}
}
- /** Removes all given shapes, keeps them alive. */
+ @Override
public void removeShapes(final Collection<? extends Shape> shapes) {
for(final Shape s : shapes) {
removeShape(s);
@@ -236,6 +244,10 @@ public final class Scene implements GLEventListener {
removeShape(gl, s);
}
}
+ @Override
+ public boolean contains(final Shape s) {
+ return false;
+ }
public Shape getShapeByIdx(final int id) {
if( 0 > id ) {
return null;
@@ -449,17 +461,9 @@ public final class Scene implements GLEventListener {
* @param runnable the action to perform if {@link Shape} was found
* @return picked Shape if any or null as stored in {@code shape}
*/
- public Shape pickShape(final PMVMatrix pmv, final int glWinX, final int glWinY, final float[] objPos, final Shape[] shape, final Runnable runnable) {
+ public Shape pickShape(final PMVMatrix pmv, final int glWinX, final int glWinY, final Vec3f objPos, final Shape[] shape, final Runnable runnable) {
setupMatrix(pmv);
- final Shape pick = pickShapeImpl(pmv, glWinX, glWinY, objPos);
- shape[0] = pick;
- if( null != pick ) {
- runnable.run();
- }
- return pick;
- }
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private Shape pickShapeImpl(final PMVMatrix pmv, final int glWinX, final int glWinY, final float[] objPos) {
+
final float winZ0 = 0f;
final float winZ1 = 0.3f;
/**
@@ -467,34 +471,28 @@ public final class Scene implements GLEventListener {
gl.glReadPixels( x, y, 1, 1, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZRB);
winZ1 = winZRB.get(0); // dir
*/
-
+ final int[] viewport = getViewport();
final Ray ray = new Ray();
-
- final Object[] shapesS = shapes.toArray();
- Arrays.sort(shapesS, (Comparator)Shape.ZAscendingComparator);
-
- for(int i=shapesS.length-1; i>=0; i--) {
- final Shape uiShape = (Shape)shapesS[i];
-
- if( uiShape.isEnabled() ) {
- pmv.glPushMatrix();
- uiShape.setTransform(pmv);
- final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, getViewport(), 0, ray);
- if( ok ) {
- final AABBox sbox = uiShape.getBounds();
- if( sbox.intersectsRay(ray) ) {
- // System.err.printf("Pick.0: shape %d, [%d, %d, %f/%f] -> %s%n", i, glWinX, glWinY, winZ0, winZ1, ray);
- if( null == sbox.getRayIntersection(objPos, ray, FloatUtil.EPSILON, true, dpyTmp1V3, dpyTmp2V3, dpyTmp3V3) ) {
- throw new InternalError("Ray "+ray+", box "+sbox);
- }
- // System.err.printf("Pick.1: shape %d @ [%f, %f, %f], within %s%n", i, objPos[0], objPos[1], objPos[2], uiShape.getBounds());
- return uiShape;
+ shape[0] = null;
+
+ forSortedAll(Shape.ZAscendingComparator, pmv, (final Shape s, final PMVMatrix pmv2) -> {
+ final boolean ok = pmv.gluUnProjectRay(glWinX, glWinY, winZ0, winZ1, viewport, ray);
+ if( ok ) {
+ final AABBox sbox = s.getBounds();
+ if( sbox.intersectsRay(ray) ) {
+ // System.err.printf("Pick.0: shape %d, [%d, %d, %f/%f] -> %s%n", i, glWinX, glWinY, winZ0, winZ1, ray);
+ if( null == sbox.getRayIntersection(objPos, ray, FloatUtil.EPSILON, true) ) {
+ throw new InternalError("Ray "+ray+", box "+sbox);
}
+ // System.err.printf("Pick.1: shape %d @ [%f, %f, %f], within %s%n", i, objPos[0], objPos[1], objPos[2], uiShape.getBounds());
+ shape[0] = s;
+ runnable.run();
+ return true;
}
- pmv.glPopMatrix(); // we leave the stack open if picked above, allowing the modelview shape transform to be reused
}
- }
- return null;
+ return false;
+ });
+ return shape[0];
}
private final float[] dpyTmp1V3 = new float[3];
private final float[] dpyTmp2V3 = new float[3];
@@ -514,7 +512,7 @@ public final class Scene implements GLEventListener {
* @param shape storage for found {@link Shape} or null
* @param runnable the action to perform if {@link Shape} was found
*/
- public void pickShapeGL(final int glWinX, final int glWinY, final float[] objPos, final Shape[] shape, final Runnable runnable) {
+ public void pickShapeGL(final int glWinX, final int glWinY, final Vec3f objPos, final Shape[] shape, final Runnable runnable) {
if( null == cDrawable ) {
return;
}
@@ -586,10 +584,79 @@ public final class Scene implements GLEventListener {
* @param objPos resulting object position
* @param runnable action
*/
- public void winToShapeCoord(final Shape shape, final int glWinX, final int glWinY, final PMVMatrix pmv, final float[] objPos, final Runnable runnable) {
- if( null != shape && null != shape.winToShapeCoord(pmvMatrixSetup, renderer.getViewport(), glWinX, glWinY, pmv, objPos) ) {
- runnable.run();
+ public void winToShapeCoord(final Shape shape, final int glWinX, final int glWinY, final PMVMatrix pmv, final Vec3f objPos, final Runnable runnable) {
+ if( null == shape ) {
+ return;
}
+ final int[] viewport = getViewport();
+ setupMatrix(pmv);
+ forOne(pmv, shape, () -> {
+ if( null != shape.winToShapeCoord(pmv, viewport, glWinX, glWinY, objPos) ) {
+ runnable.run();
+ }
+ });
+ }
+
+ @Override
+ public AABBox getBounds(final PMVMatrix pmv, final Shape shape) {
+ final AABBox res = new AABBox();
+ if( null == shape ) {
+ return res;
+ }
+ setupMatrix(pmv);
+ forOne(pmv, shape, () -> {
+ shape.getBounds().transformMv(res, pmv, new float[3], new float[3]);
+ });
+ return res;
+ }
+
+ /**
+ * Traverses through the graph up until {@code shape} and apply {@code action} on it.
+ * @param pmv
+ * @param shape
+ * @param action
+ * @return true to signal operation complete, i.e. {@code shape} found, otherwise false
+ */
+ @Override
+ public boolean forOne(final PMVMatrix pmv, final Shape shape, final Runnable action) {
+ setupMatrix(pmv);
+ return TreeTool.forOne(shapes, pmv, shape, action);
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor2#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ @Override
+ public boolean forAll(final PMVMatrix pmv, final Visitor2 v) {
+ setupMatrix(pmv);
+ return TreeTool.forAll(shapes, pmv, v);
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor1#visit(Shape)} for each, stop if it returns true.
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor1#visit(Shape)} returned true, otherwise false
+ */
+ @Override
+ public boolean forAll(final Visitor1 v) {
+ return TreeTool.forAll(shapes, v);
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ *
+ * Each {@link Container} level is sorted using {@code sortComp}
+ * @param sortComp
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ @Override
+ public boolean forSortedAll(final Comparator<Shape> sortComp, final PMVMatrix pmv, final Visitor2 v) {
+ return TreeTool.forSortedAll(sortComp, shapes, pmv, v);
}
/**
@@ -788,16 +855,18 @@ public final class Scene implements GLEventListener {
// gesture .. delegate to active shape!
final InputEvent orig = gh.getTrigger();
if( orig instanceof MouseEvent ) {
- final MouseEvent e = (MouseEvent) orig;
- // flip to GL window coordinates
- final int glWinX = e.getX();
- final int glWinY = getHeight() - e.getY() - 1;
- final PMVMatrix pmv = new PMVMatrix();
- final float[] objPos = new float[3];
final Shape shape = activeShape;
- winToShapeCoord(shape, glWinX, glWinY, pmv, objPos, () -> {
- shape.dispatchGestureEvent(gh, glWinX, glWinY, pmv, renderer.getViewport(), objPos);
- });
+ if( shape.isInteractive() ) {
+ final MouseEvent e = (MouseEvent) orig;
+ // flip to GL window coordinates
+ final int glWinX = e.getX();
+ final int glWinY = getHeight() - e.getY() - 1;
+ final PMVMatrix pmv = new PMVMatrix();
+ final Vec3f objPos = new Vec3f();
+ winToShapeCoord(shape, glWinX, glWinY, pmv, objPos, () -> {
+ shape.dispatchGestureEvent(gh, glWinX, glWinY, pmv, renderer.getViewport(), objPos);
+ });
+ }
}
}
}
@@ -812,7 +881,7 @@ public final class Scene implements GLEventListener {
final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY) {
if( null == activeShape ) {
dispatchMouseEventPickShape(e, glWinX, glWinY);
- } else {
+ } else if( activeShape.isInteractive() ) {
dispatchMouseEventForShape(activeShape, e, glWinX, glWinY);
}
}
@@ -824,11 +893,13 @@ public final class Scene implements GLEventListener {
*/
final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY) {
final PMVMatrix pmv = new PMVMatrix();
- final float[] objPos = new float[3];
+ final Vec3f objPos = new Vec3f();
final Shape[] shape = { null };
if( null == pickShape(pmv, glWinX, glWinY, objPos, shape, () -> {
setActiveShape(shape[0]);
- shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos);
+ if( shape[0].isInteractive() ) {
+ shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos);
+ }
} ) )
{
releaseActiveShape();
@@ -843,7 +914,7 @@ public final class Scene implements GLEventListener {
*/
final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) {
final PMVMatrix pmv = new PMVMatrix();
- final float[] objPos = new float[3];
+ final Vec3f objPos = new Vec3f();
winToShapeCoord(shape, glWinX, glWinY, pmv, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); });
}
@@ -1059,4 +1130,5 @@ public final class Scene implements GLEventListener {
}
};
private PMVMatrixSetup pmvMatrixSetup = defaultPMVMatrixSetup;
+
}
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
index dcac39504..658d7848d 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/Shape.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/Shape.java
@@ -44,7 +44,8 @@ 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.Vec2f;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import com.jogamp.opengl.util.PMVMatrix;
@@ -67,6 +68,34 @@ import com.jogamp.opengl.util.PMVMatrix;
* @see Scene
*/
public abstract class Shape {
+ /**
+ * General {@link Shape} visitor
+ */
+ public static interface Visitor1 {
+ /**
+ * Visitor method
+ * @param s the {@link Shape} to process
+ * @return true to signal operation complete and to stop traversal, otherwise false
+ */
+ boolean visit(Shape s);
+ }
+
+ /**
+ * General {@link Shape} visitor
+ */
+ public static interface Visitor2 {
+ /**
+ * Visitor method
+ * @param s the {@link Shape} to process
+ * @param pmv the {@link PMVMatrix} setup from the {@link Scene} down to the {@link Shape}
+ * @return true to signal operation complete and to stop traversal, otherwise false
+ */
+ boolean visit(Shape s, final PMVMatrix pmv);
+ }
+
+ /**
+ * General {@link Shape} listener action
+ */
public static interface Listener {
void run(final Shape shape);
}
@@ -78,10 +107,10 @@ public abstract class Shape {
protected final AABBox box;
- private final float[] position = new float[] { 0f, 0f, 0f };
+ private final Vec3f position = new Vec3f();
private final Quaternion rotation = new Quaternion();
- private final float[] rotPivot = new float[] { 0f, 0f, 0f };
- private final float[] scale = new float[] { 1f, 1f, 1f };
+ private final Vec3f rotPivot = new Vec3f();
+ private final Vec3f scale = new Vec3f(1f, 1f, 1f);
private volatile int dirty = DIRTY_SHAPE | DIRTY_STATE;
private final Object dirtySync = new Object();
@@ -102,6 +131,7 @@ public abstract class Shape {
private boolean toggleable = false;
private boolean draggable = true;
private boolean resizable = true;
+ private boolean interactive = true;
private boolean enabled = true;
private float dbgbox_thickness = 0f; // fractional thickness of bounds, 0f for no debug box
private ArrayList<MouseGestureListener> mouseListeners = new ArrayList<MouseGestureListener>();
@@ -142,16 +172,10 @@ public abstract class Shape {
public final void clear(final GL2ES2 gl, final RegionRenderer renderer) {
synchronized ( dirtySync ) {
clearImpl0(gl, renderer);
- position[0] = 0f;
- position[1] = 0f;
- position[2] = 0f;
+ position.set(0f, 0f, 0f);
rotation.setIdentity();
- rotPivot[0] = 0f;
- rotPivot[1] = 0f;
- rotPivot[2] = 0f;
- scale[0] = 1f;
- scale[1] = 1f;
- scale[2] = 1f;
+ rotPivot.set(0f, 0f, 0f);
+ scale.set(1f, 1f, 1f);
box.reset();
markShapeDirty();
}
@@ -164,16 +188,10 @@ public abstract class Shape {
*/
public final void destroy(final GL2ES2 gl, final RegionRenderer renderer) {
destroyImpl0(gl, renderer);
- position[0] = 0f;
- position[1] = 0f;
- position[2] = 0f;
+ position.set(0f, 0f, 0f);
rotation.setIdentity();
- rotPivot[0] = 0f;
- rotPivot[1] = 0f;
- rotPivot[2] = 0f;
- scale[0] = 1f;
- scale[1] = 1f;
- scale[2] = 1f;
+ rotPivot.set(0f, 0f, 0f);
+ scale.set(1f, 1f, 1f);
box.reset();
markShapeDirty();
}
@@ -182,45 +200,53 @@ public abstract class Shape {
/** Move to scaled position. Position ends up in PMVMatrix unmodified. */
public final void moveTo(final float tx, final float ty, final float tz) {
- position[0] = tx;
- position[1] = ty;
- position[2] = tz;
+ position.set(tx, ty, tz);
+ if( null != onMoveListener ) {
+ onMoveListener.run(this);
+ }
+ }
+
+ /** Move to scaled position. Position ends up in PMVMatrix unmodified. */
+ public final void moveTo(final Vec3f t) {
+ position.set(t);
if( null != onMoveListener ) {
onMoveListener.run(this);
}
- // System.err.println("Shape.setTranslate: "+tx+"/"+ty+"/"+tz+": "+toString());
}
/** Move about scaled distance. Position ends up in PMVMatrix unmodified. */
public final void move(final float dtx, final float dty, final float dtz) {
- position[0] += dtx;
- position[1] += dty;
- position[2] += dtz;
+ position.add(dtx, dty, dtz);
if( null != onMoveListener ) {
onMoveListener.run(this);
}
- // System.err.println("Shape.translate: "+tx+"/"+ty+"/"+tz+": "+toString());
}
- /** Returns float[3] position, i.e. scaled translation as set via {@link #moveTo(float, float, float) or {@link #move(float, float, float)}}. */
- public final float[] getPosition() { return position; }
+ /** Move about scaled distance. Position ends up in PMVMatrix unmodified. */
+ public final void move(final Vec3f dt) {
+ position.add(dt);
+ if( null != onMoveListener ) {
+ onMoveListener.run(this);
+ }
+ }
+
+ /** Returns position, i.e. scaled translation as set via {@link #moveTo(float, float, float) or {@link #move(float, float, float)}}. */
+ public final Vec3f getPosition() { return position; }
/** Returns {@link Quaternion} for rotation. */
public final Quaternion getRotation() { return rotation; }
- /** Return float[3] unscaled rotation origin, aka pivot. */
- public final float[] getRotationPivot() { return rotPivot; }
+ /** Return unscaled rotation origin, aka pivot. */
+ public final Vec3f getRotationPivot() { return rotPivot; }
/** Set unscaled rotation origin, aka pivot. Usually the {@link #getBounds()} center and should be set while {@link #validateImpl(GLProfile, GL2ES2)}. */
- public final void setRotationPivot(final float rx, final float ry, final float rz) {
- rotPivot[0] = rx;
- rotPivot[1] = ry;
- rotPivot[2] = rz;
+ public final void setRotationPivot(final float px, final float py, final float pz) {
+ rotPivot.set(px, py, pz);
}
/**
* Set unscaled rotation origin, aka pivot. Usually the {@link #getBounds()} center and should be set while {@link #validateImpl(GLProfile, GL2ES2)}.
- * @param pivot float[3] rotation origin
+ * @param pivot rotation origin
*/
- public final void setRotationPivot(final float[/*3*/] pivot) {
- System.arraycopy(pivot, 0, rotPivot, 0, 3);
+ public final void setRotationPivot(final Vec3f pivot) {
+ rotPivot.set(pivot);
}
/**
@@ -229,9 +255,7 @@ public abstract class Shape {
* @see #getScale()
*/
public final void setScale(final float sx, final float sy, final float sz) {
- scale[0] = sx;
- scale[1] = sy;
- scale[2] = sz;
+ scale.set(sx, sy, sz);
}
/**
* Multiply current scale factor by given scale.
@@ -239,22 +263,14 @@ public abstract class Shape {
* @see #getScale()
*/
public final void scale(final float sx, final float sy, final float sz) {
- scale[0] *= sx;
- scale[1] *= sy;
- scale[2] *= sz;
+ scale.scale(sx, sy, sz);
}
/**
- * Returns float[3] scale factors.
+ * Returns scale factors.
* @see #setScale(float, float, float)
* @see #scale(float, float, float)
*/
- public final float[] getScale() { return scale; }
- /** Returns X-axis scale factor. */
- public final float getScaleX() { return scale[0]; }
- /** Returns Y-axis scale factor. */
- public final float getScaleY() { return scale[1]; }
- /** Returns Z-axis scale factor. */
- public final float getScaleZ() { return scale[2]; }
+ public final Vec3f getScale() { return scale; }
/**
* Marks the shape dirty, causing next {@link #draw(GL2ES2, RegionRenderer, int[]) draw()}
@@ -304,7 +320,7 @@ public abstract class Shape {
* @see #getBounds()
*/
public final float getScaledWidth() {
- return box.getWidth() * getScaleX();
+ return box.getWidth() * getScale().x();
}
/**
@@ -317,7 +333,7 @@ public abstract class Shape {
* @see #getBounds()
*/
public final float getScaledHeight() {
- return box.getHeight() * getScaleY();
+ return box.getHeight() * getScale().y();
}
/**
@@ -450,39 +466,39 @@ public abstract class Shape {
* @see #setScale(float, float, float)
*/
public void setTransform(final PMVMatrix pmv) {
- final boolean hasScale = !VectorUtil.isVec3Equal(scale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON);
+ final boolean hasScale = !scale.isEqual(Vec3f.ONE);
final boolean hasRotate = !rotation.isIdentity();
- final boolean hasRotPivot = !VectorUtil.isVec3Zero(rotPivot, 0, FloatUtil.EPSILON);
- final float[] ctr = box.getCenter();
- final boolean sameScaleRotatePivot = hasScale && hasRotate && ( !hasRotPivot || VectorUtil.isVec3Equal(rotPivot, 0, ctr, 0, FloatUtil.EPSILON) );
+ final boolean hasRotPivot = !rotPivot.isZero();
+ final Vec3f ctr = box.getCenter();
+ final boolean sameScaleRotatePivot = hasScale && hasRotate && ( !hasRotPivot || rotPivot.isEqual(ctr) );
- pmv.glTranslatef(position[0], position[1], position[2]); // translate, scaled
+ pmv.glTranslatef(position.x(), position.y(), position.z()); // translate, scaled
if( sameScaleRotatePivot ) {
// Scale shape from its center position and rotate around its center
- pmv.glTranslatef(ctr[0]*scale[0], ctr[1]*scale[1], ctr[2]*scale[2]); // add-back center, scaled
+ pmv.glTranslatef(ctr.x()*scale.x(), ctr.y()*scale.y(), ctr.z()*scale.z()); // add-back center, scaled
pmv.glRotate(rotation);
- pmv.glScalef(scale[0], scale[1], scale[2]);
- pmv.glTranslatef(-ctr[0], -ctr[1], -ctr[2]); // move to center
+ pmv.glScalef(scale.x(), scale.y(), scale.z());
+ pmv.glTranslatef(-ctr.x(), -ctr.y(), -ctr.z()); // move to center
} else if( hasRotate || hasScale ) {
if( hasRotate ) {
if( hasRotPivot ) {
// Rotate shape around its scaled pivot
- pmv.glTranslatef(rotPivot[0]*scale[0], rotPivot[1]*scale[1], rotPivot[2]*scale[2]); // pivot back from rot-pivot, scaled
+ pmv.glTranslatef(rotPivot.x()*scale.x(), rotPivot.y()*scale.y(), rotPivot.z()*scale.z()); // pivot back from rot-pivot, scaled
pmv.glRotate(rotation);
- pmv.glTranslatef(-rotPivot[0]*scale[0], -rotPivot[1]*scale[1], -rotPivot[2]*scale[2]); // pivot to rot-pivot, scaled
+ pmv.glTranslatef(-rotPivot.x()*scale.x(), -rotPivot.y()*scale.y(), -rotPivot.z()*scale.z()); // pivot to rot-pivot, scaled
} else {
// Rotate shape around its scaled center
- pmv.glTranslatef(ctr[0]*scale[0], ctr[1]*scale[1], ctr[2]*scale[2]); // pivot back from center-pivot, scaled
+ pmv.glTranslatef(ctr.x()*scale.x(), ctr.y()*scale.y(), ctr.z()*scale.z()); // pivot back from center-pivot, scaled
pmv.glRotate(rotation);
- pmv.glTranslatef(-ctr[0]*scale[0], -ctr[1]*scale[1], -ctr[2]*scale[2]); // pivot to center-pivot, scaled
+ pmv.glTranslatef(-ctr.x()*scale.x(), -ctr.y()*scale.y(), -ctr.z()*scale.z()); // pivot to center-pivot, scaled
}
}
if( hasScale ) {
// Scale shape from its center position
- pmv.glTranslatef(ctr[0]*scale[0], ctr[1]*scale[1], ctr[2]*scale[2]); // add-back center, scaled
- pmv.glScalef(scale[0], scale[1], scale[2]);
- pmv.glTranslatef(-ctr[0], -ctr[1], -ctr[2]); // move to center
+ pmv.glTranslatef(ctr.x()*scale.x(), ctr.y()*scale.y(), ctr.z()*scale.z()); // add-back center, scaled
+ pmv.glScalef(scale.x(), scale.y(), scale.z());
+ pmv.glTranslatef(-ctr.x(), -ctr.y(), -ctr.z()); // move to center
}
}
// TODO: Add alignment features.
@@ -506,12 +522,12 @@ public abstract class Shape {
// System.err.println("Shape::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();
+ final Vec3f high = box.getHigh();
+ final Vec3f low = box.getLow();
- if( pmv.gluProject(high[0], high[1], high[2], viewport, 0, winCoordHigh, 0) ) {
+ if( pmv.gluProject(high.x(), high.y(), high.z(), viewport, 0, winCoordHigh, 0) ) {
// System.err.printf("Shape::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) ) {
+ if( pmv.gluProject(low.x(), low.y(), low.z(), viewport, 0, winCoordLow, 0) ) {
// System.err.printf("Shape::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]);
@@ -611,17 +627,17 @@ public abstract class Shape {
* </p>
* @param pmv well formed {@link PMVMatrix}, e.g. could have been setup via {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)} and {@link #setTransform(PMVMatrix)}.
* @param viewport the int[4] viewport
- * @param objPos float[3] object position relative to this shape's center
+ * @param objPos object position relative to this shape's center
* @param glWinPos int[2] target window position of objPos relative to this shape
* @return given int[2] {@code glWinPos} for successful gluProject(..) operation, otherwise {@code null}
* @see #shapeToWinCoord(com.jogamp.graph.ui.Scene.PMVMatrixSetup, int[], float[], PMVMatrix, int[])
* @see #shapeToWinCoord(Scene, float[], PMVMatrix, int[])
*/
- public int[/*2*/] shapeToWinCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final float[/*3*/] objPos, final int[/*2*/] glWinPos) {
+ public int[/*2*/] shapeToWinCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final Vec3f objPos, final int[/*2*/] glWinPos) {
// System.err.println("Shape::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) ) {
+ if( pmv.gluProject(objPos.x(), objPos.y(), objPos.z(), viewport, 0, winCoord, 0) ) {
// System.err.printf("Shape::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]);
@@ -639,7 +655,7 @@ public abstract class Shape {
* </p>
* @param pmvMatrixSetup {@link Scene.PMVMatrixSetup} to {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} given {@link PMVMatrix} {@code pmv}.
* @param viewport used viewport for {@link PMVMatrix#gluProject(float, float, float, int[], int, float[], int)}
- * @param objPos float[3] object position relative to this shape's center
+ * @param objPos object position relative to this shape's center
* @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 glWinPos int[2] target window position of objPos relative to this shape
@@ -647,7 +663,7 @@ public abstract class Shape {
* @see #shapeToWinCoord(PMVMatrix, int[], float[], int[])
* @see #shapeToWinCoord(Scene, float[], PMVMatrix, int[])
*/
- public int[/*2*/] shapeToWinCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final float[/*3*/] objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) {
+ public int[/*2*/] shapeToWinCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final Vec3f objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) {
pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]);
setTransform(pmv);
return this.shapeToWinCoord(pmv, viewport, objPos, glWinPos);
@@ -660,7 +676,7 @@ public abstract class Shape {
* including this shape's {@link #setTransform(PMVMatrix)}.
* </p>
* @param scene {@link Scene} to retrieve {@link Scene.PMVMatrixSetup} and the viewport.
- * @param objPos float[3] object position relative to this shape's center
+ * @param objPos object position relative to this shape's center
* @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 glWinPos int[2] target window position of objPos relative to this shape
@@ -668,7 +684,7 @@ public abstract class Shape {
* @see #shapeToWinCoord(PMVMatrix, int[], float[], int[])
* @see #shapeToWinCoord(com.jogamp.graph.ui.Scene.PMVMatrixSetup, int[], float[], PMVMatrix, int[])
*/
- public int[/*2*/] shapeToWinCoord(final Scene scene, final float[/*3*/] objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) {
+ public int[/*2*/] shapeToWinCoord(final Scene scene, final Vec3f objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) {
return this.shapeToWinCoord(scene.getPMVMatrixSetup(), scene.getViewport(), objPos, pmv, glWinPos);
}
@@ -683,19 +699,21 @@ public abstract class Shape {
* @param viewport the int[4] viewport
* @param glWinX in GL window coordinates, origin bottom-left
* @param glWinY in GL window coordinates, origin bottom-left
- * @param objPos float[3] target object position of glWinX/glWinY relative to this shape
- * @return given float[3] {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
+ * @param objPos target object position of glWinX/glWinY relative to this shape
+ * @return given {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
* @see #winToShapeCoord(com.jogamp.graph.ui.Scene.PMVMatrixSetup, int[], int, int, PMVMatrix, float[])
* @see #winToShapeCoord(Scene, int, int, PMVMatrix, float[])
*/
- public float[/*3*/] winToShapeCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final int glWinX, final int glWinY, final float[/*3*/] objPos) {
- final float[] ctr = getBounds().getCenter();
+ public Vec3f winToShapeCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final int glWinX, final int glWinY, final Vec3f objPos) {
+ final Vec3f ctr = box.getCenter();
final float[] tmp = new float[3];
- if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, tmp, 0) ) {
+ if( pmv.gluProject(ctr.x(), ctr.y(), ctr.z(), viewport, 0, tmp, 0) ) {
// System.err.printf("Shape::winToObjCoord.0: shape %d: obj [%15.10ff, %15.10ff, %15.10ff] -> win [%d / %d -> %7.2ff, %7.2ff, %7.2ff, diff %7.2ff x %7.2ff]%n", getName(), ctr[0], ctr[1], ctr[2], glWinX, glWinY, tmp[0], tmp[1], tmp[2], glWinX-tmp[0], glWinY-tmp[1]);
- if( pmv.gluUnProject(glWinX, glWinY, tmp[2], viewport, 0, objPos, 0) ) {
+ final float winZ = tmp[2];
+ if( pmv.gluUnProject(glWinX, glWinY, winZ, viewport, 0, objPos.get(tmp), 0) ) {
// System.err.printf("Shape::winToObjCoord.X: shape %d: win [%d, %d, %7.2ff] -> obj [%15.10ff, %15.10ff, %15.10ff]%n", getName(), glWinX, glWinY, tmp[2], objPos[0], objPos[1], objPos[2]);
+ objPos.set(tmp);
return objPos;
}
}
@@ -714,12 +732,12 @@ public abstract class Shape {
* @param glWinY in GL window coordinates, origin bottom-left
* @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 objPos float[3] target object position of glWinX/glWinY relative to this shape
- * @return given float[3] {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
+ * @param objPos target object position of glWinX/glWinY relative to this shape
+ * @return given {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
* @see #winToShapeCoord(PMVMatrix, int[], int, int, float[])
* @see #winToShapeCoord(Scene, int, int, PMVMatrix, float[])
*/
- public float[/*3*/] winToShapeCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final int glWinX, final int glWinY, final PMVMatrix pmv, final float[/*3*/] objPos) {
+ public Vec3f winToShapeCoord(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final int glWinX, final int glWinY, final PMVMatrix pmv, final Vec3f objPos) {
pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]);
setTransform(pmv);
return this.winToShapeCoord(pmv, viewport, glWinX, glWinY, objPos);
@@ -736,12 +754,12 @@ public abstract class Shape {
* @param glWinY in GL window coordinates, origin bottom-left
* @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 objPos float[3] target object position of glWinX/glWinY relative to this shape
- * @return given float[3] {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
+ * @param objPos target object position of glWinX/glWinY relative to this shape
+ * @return given {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null}
* @see #winToShapeCoord(PMVMatrix, int[], int, int, float[])
* @see #winToShapeCoord(com.jogamp.graph.ui.Scene.PMVMatrixSetup, int[], int, int, PMVMatrix, float[])
*/
- public float[/*3*/] winToShapeCoord(final Scene scene, final int glWinX, final int glWinY, final PMVMatrix pmv, final float[/*3*/] objPos) {
+ public Vec3f winToShapeCoord(final Scene scene, final int glWinX, final int glWinY, final PMVMatrix pmv, final Vec3f objPos) {
return this.winToShapeCoord(scene.getPMVMatrixSetup(), scene.getViewport(), glWinX, glWinY, pmv, objPos);
}
@@ -808,14 +826,14 @@ public abstract class Shape {
public String getSubString() {
final String pivotS;
- if( !VectorUtil.isVec3Zero(rotPivot, 0, FloatUtil.EPSILON) ) {
- pivotS = "pivot["+rotPivot[0]+", "+rotPivot[1]+", "+rotPivot[2]+"], ";
+ if( !rotPivot.isZero() ) {
+ pivotS = "pivot["+rotPivot+"], ";
} else {
pivotS = "";
}
final String scaleS;
- if( !VectorUtil.isVec3Equal(scale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON) ) {
- scaleS = "scale["+scale[0]+", "+scale[1]+", "+scale[2]+"], ";
+ if( !scale.isEqual( Vec3f.ONE ) ) {
+ scaleS = "scale["+scale+"], ";
} else {
scaleS = "scale 1, ";
}
@@ -825,7 +843,7 @@ public abstract class Shape {
} else {
rotateS = "";
}
- return "enabled "+enabled+", toggle[able "+toggleable+", state "+toggle+"], pos["+position[0]+", "+position[1]+", "+position[2]+
+ return "enabled "+enabled+", toggle[able "+toggleable+", state "+toggle+"], pos["+position+
"], "+pivotS+scaleS+rotateS+
"box "+box;
}
@@ -842,6 +860,11 @@ public abstract class Shape {
return this.down;
}
+ /**
+ *
+ * @param toggleable
+ * @see #isInteractive()
+ */
public void setToggleable(final boolean toggleable) {
this.toggleable = toggleable;
}
@@ -849,6 +872,7 @@ public abstract class Shape {
/**
* Returns true if this shape is toggable,
* i.e. rendered w/ {@link #setToggleOnColorMod(float, float, float, float)} or {@link #setToggleOffColorMod(float, float, float, float)}.
+ * @see #isInteractive()
*/
public boolean isToggleable() {
return toggleable;
@@ -866,15 +890,36 @@ public abstract class Shape {
public boolean isToggleOn() { return toggle; }
/**
+ * Set whether this shape is interactive,
+ * i.e. any user interaction like
+ * - {@link #isToggleable()}
+ * - {@link #isDraggable()}
+ * - {@link #isResizable()}
+ * but excluding programmatic changes.
+ * @param v new value for {@link #isInteractive()}
+ */
+ public void setInteractive(final boolean v) { interactive = v; }
+ /**
+ * Returns if this shape allows user interaction, see {@link #setInteractive(boolean)}
+ * @see #setInteractive(boolean)
+ */
+ public boolean isInteractive() { return interactive; }
+
+ /**
* Set whether this shape is draggable,
* i.e. translated by 1-pointer-click and drag.
* <p>
* Default draggable is true.
* </p>
+ * @see #isInteractive()
*/
public void setDraggable(final boolean draggable) {
this.draggable = draggable;
}
+ /**
+ * Returns if this shape is draggable, a user interaction.
+ * @see #isInteractive()
+ */
public boolean isDraggable() {
return draggable;
}
@@ -885,10 +930,15 @@ public abstract class Shape {
* <p>
* Default resizable is true.
* </p>
+ * @see #isInteractive()
*/
public void setResizable(final boolean resizable) {
this.resizable = resizable;
}
+ /**
+ * Returns if this shape is resiable, a user interaction.
+ * @see #isInteractive()
+ */
public boolean isResizable() {
return resizable;
}
@@ -938,11 +988,11 @@ public abstract class Shape {
/** The associated {@link Shape} for this event */
public final Shape shape;
/** The relative object coordinate of glWinX/glWinY to the associated {@link Shape}. */
- public final float[] objPos;
+ public final Vec3f objPos;
/** The GL window coordinates, origin bottom-left */
public final int[] winPos;
/** The drag delta of the relative object coordinate of glWinX/glWinY to the associated {@link Shape}. */
- public final float[] objDrag = { 0f, 0f };
+ public final Vec2f objDrag = new Vec2f();
/** The drag delta of GL window coordinates, origin bottom-left */
public final int[] winDrag = { 0, 0 };
@@ -953,7 +1003,7 @@ public abstract class Shape {
* @param shape associated shape
* @param objPos relative object coordinate of glWinX/glWinY to the associated shape.
*/
- EventInfo(final int glWinX, final int glWinY, final Shape shape, final float[] objPos) {
+ EventInfo(final int glWinX, final int glWinY, final Shape shape, final Vec3f objPos) {
this.winPos = new int[] { glWinX, glWinY };
this.shape = shape;
this.objPos = objPos;
@@ -961,14 +1011,14 @@ public abstract class Shape {
@Override
public String toString() {
- return "EventDetails[winPos ["+winPos[0]+", "+winPos[1]+"], objPos ["+objPos[0]+", "+objPos[1]+", "+objPos[2]+"], "+shape+"]";
+ return "EventDetails[winPos ["+winPos[0]+", "+winPos[1]+"], objPos ["+objPos+"], "+shape+"]";
}
}
private boolean dragFirst = false;
- private final float[] objDraggedFirst = { 0f, 0f }; // b/c its relative to Shape and we stick to it
+ private final Vec2f objDraggedFirst = new Vec2f(); // b/c its relative to Shape and we stick to it
private final int[] winDraggedLast = { 0, 0 }; // b/c its absolute window pos
- private boolean inDrag = false;
+ private boolean inMove = false;
private int inResize = 0; // 1 br, 2 bl
private static final float resize_sxy_min = 1f/200f; // 1/2% - TODO: Maybe customizable?
private static final float resize_section = 1f/5f; // resize action in a corner
@@ -980,7 +1030,7 @@ public abstract class Shape {
* @param glWinY in GL window coordinates, origin bottom-left
* @param objPos object position of mouse event relative to this shape
*/
- /* pp */ final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final float[] objPos) {
+ /* pp */ final void dispatchMouseEvent(final MouseEvent e, final int glWinX, final int glWinY, final Vec3f objPos) {
final Shape.EventInfo shapeEvent = new EventInfo(glWinX, glWinY, this, objPos);
final short eventType = e.getEventType();
@@ -996,7 +1046,7 @@ public abstract class Shape {
case MouseEvent.EVENT_MOUSE_RELEASED:
// Release active shape: last pointer has been lifted!
setPressed(false);
- inDrag = false;
+ inMove = false;
inResize = 0;
break;
}
@@ -1005,21 +1055,22 @@ public abstract class Shape {
case MouseEvent.EVENT_MOUSE_DRAGGED: {
// 1 pointer drag and potential drag-resize
if(dragFirst) {
- objDraggedFirst[0] = objPos[0];
- objDraggedFirst[1] = objPos[1];
+ objDraggedFirst.set(objPos);
winDraggedLast[0] = glWinX;
winDraggedLast[1] = glWinY;
dragFirst=false;
- final float ix = objPos[0];
- final float iy = objPos[1];
+ final float ix = objPos.x();
+ final float iy = objPos.y();
final float minx_br = box.getMaxX() - box.getWidth() * resize_section;
final float miny_br = box.getMinY();
final float maxx_br = box.getMaxX();
final float maxy_br = box.getMinY() + box.getHeight() * resize_section;
if( minx_br <= ix && ix <= maxx_br &&
miny_br <= iy && iy <= maxy_br ) {
- inResize = 1; // bottom-right
+ if( interactive && resizable ) {
+ inResize = 1; // bottom-right
+ }
} else {
final float minx_bl = box.getMinX();
final float miny_bl = box.getMinY();
@@ -1027,57 +1078,59 @@ public abstract class Shape {
final float maxy_bl = box.getMinY() + box.getHeight() * resize_section;
if( minx_bl <= ix && ix <= maxx_bl &&
miny_bl <= iy && iy <= maxy_bl ) {
- inResize = 2; // bottom-left
+ if( interactive && resizable ) {
+ inResize = 2; // bottom-left
+ }
} else {
- inDrag = true;
+ inMove = interactive && draggable;
}
}
if( DEBUG ) {
- System.err.printf("DragFirst: drag %b, resize %d, obj[%.4f, %.4f, %.4f], drag +[%.4f, %.4f]%n",
- inDrag, inResize, objPos[0], objPos[1], objPos[2], shapeEvent.objDrag[0], shapeEvent.objDrag[1]);
+ System.err.printf("DragFirst: drag %b, resize %d, obj[%s], drag +[%s]%n",
+ inMove, inResize, objPos, shapeEvent.objDrag);
System.err.printf("DragFirst: %s%n", this);
}
return;
}
- shapeEvent.objDrag[0] = objPos[0] - objDraggedFirst[0];
- shapeEvent.objDrag[1] = objPos[1] - objDraggedFirst[1];
+ shapeEvent.objDrag.set( objPos.x() - objDraggedFirst.x(),
+ objPos.y() - objDraggedFirst.y() );
shapeEvent.winDrag[0] = glWinX - winDraggedLast[0];
shapeEvent.winDrag[1] = glWinY - winDraggedLast[1];
winDraggedLast[0] = glWinX;
winDraggedLast[1] = glWinY;
if( 1 == e.getPointerCount() ) {
- final float sdx = shapeEvent.objDrag[0] * scale[0]; // apply scale, since operation
- final float sdy = shapeEvent.objDrag[1] * scale[1]; // is from a scaled-model-viewpoint
- if( 0 != inResize && resizable ) {
+ final float sdx = shapeEvent.objDrag.x() * scale.x(); // apply scale, since operation
+ final float sdy = shapeEvent.objDrag.y() * scale.y(); // is from a scaled-model-viewpoint
+ if( 0 != inResize ) {
final float bw = box.getWidth();
final float bh = box.getHeight();
final float sx;
if( 1 == inResize ) {
- sx = scale[0] + sdx/bw; // bottom-right
+ sx = scale.x() + sdx/bw; // bottom-right
} else {
- sx = scale[0] - sdx/bw; // bottom-left
+ sx = scale.x() - sdx/bw; // bottom-left
}
- final float sy = scale[1] - sdy/bh;
+ final float sy = scale.y() - sdy/bh;
if( resize_sxy_min <= sx && resize_sxy_min <= sy ) { // avoid scale flip
if( DEBUG ) {
- System.err.printf("DragZoom: resize %d, win[%4d, %4d], obj[%.4f, %.4f, %.4f], dxy +[%.4f, %.4f], sdxy +[%.4f, %.4f], scale [%.4f, %.4f] -> [%.4f, %.4f]%n",
- inResize, glWinX, glWinY, objPos[0], objPos[1], objPos[2],
- shapeEvent.objDrag[0], shapeEvent.objDrag[1], sdx, sdy,
- scale[0], scale[1], sx, sy);
+ System.err.printf("DragZoom: resize %d, win[%4d, %4d], obj[%s], dxy +[%s], sdxy +[%.4f, %.4f], scale [%s] -> [%.4f, %.4f]%n",
+ inResize, glWinX, glWinY, objPos,
+ shapeEvent.objDrag, sdx, sdy,
+ scale, sx, sy);
}
if( 1 == inResize ) {
move( 0, sdy, 0f); // bottom-right, sticky left- and top-edge
} else {
move( sdx, sdy, 0f); // bottom-left, sticky right- and top-edge
}
- setScale(sx, sy, scale[2]);
+ setScale(sx, sy, scale.z());
}
return; // FIXME: pass through event? Issue zoom event?
- } else if( inDrag && draggable ) {
+ } else if( inMove ) {
if( DEBUG ) {
- System.err.printf("DragMove: win[%4d, %4d] +[%2d, %2d], obj[%.4f, %.4f, %.4f] +[%.4f, %.4f]%n",
+ System.err.printf("DragMove: win[%4d, %4d] +[%2d, %2d], obj[%s] +[%s]%n",
glWinX, glWinY, shapeEvent.winDrag[0], shapeEvent.winDrag[1],
- objPos[0], objPos[1], objPos[2], shapeEvent.objDrag[0], shapeEvent.objDrag[1]);
+ objPos, shapeEvent.objDrag);
}
move( sdx, sdy, 0f);
// FIXME: Pass through event? Issue move event?
@@ -1129,32 +1182,30 @@ public abstract class Shape {
* @param viewport the viewport
* @param objPos object position of mouse event relative to this shape
*/
- /* pp */ final void dispatchGestureEvent(final GestureEvent e, final int glWinX, final int glWinY, final PMVMatrix pmv, final int[] viewport, final float[] objPos) {
- if( resizable && e instanceof PinchToZoomGesture.ZoomEvent ) {
+ /* pp */ final void dispatchGestureEvent(final GestureEvent e, final int glWinX, final int glWinY, final PMVMatrix pmv, final int[] viewport, final Vec3f objPos) {
+ if( interactive && resizable && e instanceof PinchToZoomGesture.ZoomEvent ) {
final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) e;
final float pixels = ze.getDelta() * ze.getScale(); //
final int winX2 = glWinX + Math.round(pixels);
- final float[] objPos2 = winToShapeCoord(pmv, viewport, winX2, glWinY, new float[3]);
+ final Vec3f objPos2 = winToShapeCoord(pmv, viewport, winX2, glWinY, new Vec3f());
if( null == objPos2 ) {
return;
}
- final float dx = objPos2[0];
- final float dy = objPos2[1];
- final float sx = scale[0] + ( dx/box.getWidth() ); // bottom-right
- final float sy = scale[1] + ( dy/box.getHeight() );
+ final float dx = objPos2.x();
+ final float dy = objPos2.y();
+ final float sx = scale.x() + ( dx/box.getWidth() ); // bottom-right
+ final float sy = scale.y() + ( dy/box.getHeight() );
if( DEBUG ) {
- System.err.printf("DragZoom: resize %b, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n",
- inResize, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2],
- dx, dy, sx, sy);
+ System.err.printf("DragZoom: resize %b, win %4d/%4d, obj %s, %s + %.3f/%.3f -> %.3f/%.3f%n",
+ inResize, glWinX, glWinY, objPos, position, dx, dy, sx, sy);
}
if( resize_sxy_min <= sx && resize_sxy_min <= sy ) { // avoid scale flip
if( DEBUG ) {
- System.err.printf("PinchZoom: pixels %f, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n",
- pixels, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2],
- dx, dy, sx, sy);
+ System.err.printf("PinchZoom: pixels %f, win %4d/%4d, obj %s, %s + %.3f/%.3f -> %.3f/%.3f%n",
+ pixels, glWinX, glWinY, objPos, position, dx, dy, sx, sy);
}
// move(dx, dy, 0f);
- setScale(sx, sy, scale[2]);
+ setScale(sx, sy, scale.z());
}
return; // FIXME: pass through event? Issue zoom event?
}
@@ -1189,8 +1240,8 @@ public abstract class Shape {
public static Comparator<Shape> ZAscendingComparator = new Comparator<Shape>() {
@Override
public int compare(final Shape s1, final Shape s2) {
- final float s1Z = s1.getBounds().getMinZ()+s1.getPosition()[2];
- final float s2Z = s2.getBounds().getMinZ()+s2.getPosition()[2];
+ final float s1Z = s1.getBounds().getMinZ()+s1.getPosition().z();
+ final float s2Z = s2.getBounds().getMinZ()+s2.getPosition().z();
if( FloatUtil.isEqual(s1Z, s2Z, FloatUtil.EPSILON) ) {
return 0;
} else if( s1Z < s2Z ){
diff --git a/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java b/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java
new file mode 100644
index 000000000..bd2fd2d76
--- /dev/null
+++ b/src/graphui/classes/com/jogamp/graph/ui/layout/GridLayout.java
@@ -0,0 +1,69 @@
+/**
+ * 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.graph.ui.layout;
+
+import com.jogamp.graph.ui.Group;
+import com.jogamp.graph.ui.Shape;
+import com.jogamp.graph.ui.Group.Layout;
+
+public class GridLayout implements Group.Layout {
+ private final int columns;
+ private final float padX, padY;
+
+ /**
+ *
+ * @param columns [1..inf)
+ * @param padX
+ * @param padY
+ */
+ public GridLayout(final int columns, final float padX, final float padY) {
+ this.columns = Math.max(1, columns);
+ this.padX = padX;
+ this.padY = padY;
+ }
+
+ @Override
+ public void layout(final Group g) {
+ int col = 0;
+ float x=0;
+ float y=0;
+ for(final Shape s : g.getShapes()) {
+ s.moveTo(x, y, 0);
+ if( col + 1 == columns ) {
+ col = 0;
+ // row++;
+ x = 0;
+ y -= padY;
+ } else {
+ col++;
+ x += padX;
+ }
+ }
+ }
+}
+
diff --git a/src/graphui/classes/com/jogamp/graph/ui/shapes/Button.java b/src/graphui/classes/com/jogamp/graph/ui/shapes/Button.java
index ca726e30f..e8b0be863 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/shapes/Button.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/shapes/Button.java
@@ -35,6 +35,8 @@ import com.jogamp.graph.curve.opengl.RegionRenderer;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.geom.plane.AffineTransform;
import com.jogamp.graph.ui.GraphShape;
+import com.jogamp.opengl.math.Vec2f;
+import com.jogamp.opengl.math.Vec3f;
import com.jogamp.opengl.math.geom.AABBox;
import jogamp.graph.ui.shapes.Label0;
@@ -111,11 +113,11 @@ public class Button extends RoundButton {
final float lScale = lsx < lsy ? lsx : lsy;
// Setting left-corner transform using text-box in font em-size [0..1]
- final AABBox lbox1_s = new AABBox(lbox0_em).scale2(lScale, new float[3]);
+ final AABBox lbox1_s = new AABBox(lbox0_em).scale2(lScale);
// Center text .. (share same center w/ button)
- final float[] lctr = lbox1_s.getCenter();
- final float[] ctr = box.getCenter();
- final float[] ltxy = new float[] { ctr[0] - lctr[0], ctr[1] - lctr[1] };
+ final Vec3f lctr = lbox1_s.getCenter();
+ final Vec3f ctr = box.getCenter();
+ final Vec2f ltxy = new Vec2f(ctr.x() - lctr.x(), ctr.y() - lctr.y() );
if( DEBUG_DRAW ) {
System.err.println("Button: dim "+width+" x "+height+", spacing "+spacingX+", "+spacingY);
@@ -124,7 +126,7 @@ public class Button extends RoundButton {
System.err.println("Button: text_em "+lbox0_em+" em, "+label.getText());
System.err.println("Button: lscale "+lsx+" x "+lsy+" -> "+lScale);
System.err.printf ("Button: text_s %s%n", lbox1_s);
- System.err.printf ("Button: ltxy %f / %f, %f / %f%n", ltxy[0], ltxy[1], ltxy[0] * lScale, ltxy[1] * lScale);
+ System.err.printf ("Button: ltxy %s, %f / %f%n", ltxy, ltxy.x() * lScale, ltxy.y() * lScale);
}
final AABBox lbox2 = label.addShapeToRegion(lScale, region, ltxy, tempT1, tempT2, tempT3);
diff --git a/src/graphui/classes/com/jogamp/graph/ui/shapes/Label.java b/src/graphui/classes/com/jogamp/graph/ui/shapes/Label.java
index e7d89ade4..50b53e6f5 100644
--- a/src/graphui/classes/com/jogamp/graph/ui/shapes/Label.java
+++ b/src/graphui/classes/com/jogamp/graph/ui/shapes/Label.java
@@ -166,7 +166,7 @@ public class Label extends GraphShape {
/** Returns {@link Font#getLineHeight()} * {@link #getFontScale()} * {@link #getScaleY()}. */
public float getScaledLineHeight() {
- return getScaleY() * fontScale * font.getLineHeight();
+ return getScale().y() * fontScale * font.getLineHeight();
}
/**
diff --git a/src/graphui/classes/jogamp/graph/ui/TreeTool.java b/src/graphui/classes/jogamp/graph/ui/TreeTool.java
new file mode 100644
index 000000000..ff893ab56
--- /dev/null
+++ b/src/graphui/classes/jogamp/graph/ui/TreeTool.java
@@ -0,0 +1,159 @@
+/**
+ * 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 jogamp.graph.ui;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import com.jogamp.graph.ui.Container;
+import com.jogamp.graph.ui.Scene;
+import com.jogamp.graph.ui.Shape;
+import com.jogamp.graph.ui.Shape.Visitor1;
+import com.jogamp.graph.ui.Shape.Visitor2;
+import com.jogamp.opengl.util.PMVMatrix;
+
+/** Generic static {@link Shape} tree traversal tools, utilized by {@link Scene} and {@link Container} implementations. */
+public class TreeTool {
+
+ /**
+ * Traverses through the graph up until {@code shape} and apply {@code action} on it.
+ * @param pmv
+ * @param shape
+ * @param action
+ * @return true to signal operation complete, i.e. {@code shape} found, otherwise false
+ */
+ public static boolean forOne(final List<Shape> shapes, final PMVMatrix pmv, final Shape shape, final Runnable action) {
+ for(int i=shapes.size()-1; i>=0; i--) {
+ final Shape s = shapes.get(i);
+ if( s instanceof Container ) {
+ final Container c = (Container)s;
+ if( !c.contains(shape) ) { // fast-path: skip container
+ continue;
+ }
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ final boolean res = c.forOne(pmv, shape, action);
+ pmv.glPopMatrix();
+ if( !res ) { throw new InternalError("Not found "+shape+" in "+c+", but contained"); }
+ return true;
+ } else {
+ if( s.equals(shape) ) {
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ action.run();
+ pmv.glPopMatrix();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor1#visit(Shape)} for each, stop if it returns true.
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor1#visit(Shape)} returned true, otherwise false
+ */
+ public static boolean forAll(final List<Shape> shapes, final Visitor1 v) {
+ for(int i=shapes.size()-1; i>=0; i--) {
+ final Shape s = shapes.get(i);
+ boolean res;
+ if( s instanceof Container ) {
+ final Container c = (Container)s;
+ res = c.forAll(v);
+ } else {
+ res = v.visit(s);
+ }
+ if( res ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor2#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ public static boolean forAll(final List<Shape> shapes, final PMVMatrix pmv, final Visitor2 v) {
+ for(int i=shapes.size()-1; i>=0; i--) {
+ final Shape s = shapes.get(i);
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ boolean res;
+ if( s instanceof Container ) {
+ final Container c = (Container)s;
+ res = c.forAll(pmv, v);
+ } else {
+ res = v.visit(s, pmv);
+ }
+ pmv.glPopMatrix();
+ if( res ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Traverses through the graph and apply {@link Visitor#visit(Shape, PMVMatrix)} for each, stop if it returns true.
+ *
+ * Each {@link Container} level is sorted using {@code sortComp}
+ * @param sortComp
+ * @param pmv
+ * @param v
+ * @return true to signal operation complete and to stop traversal, i.e. {@link Visitor2#visit(Shape, PMVMatrix)} returned true, otherwise false
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static boolean forSortedAll(final Comparator<Shape> sortComp, final List<Shape> shapes, final PMVMatrix pmv, final Visitor2 v) {
+ final Object[] shapesS = shapes.toArray();
+ Arrays.sort(shapesS, (Comparator)sortComp);
+
+ for(int i=shapesS.length-1; i>=0; i--) {
+ final Shape s = (Shape)shapesS[i];
+ pmv.glPushMatrix();
+ s.setTransform(pmv);
+ boolean res;
+ if( s instanceof Container ) {
+ final Container c = (Container)s;
+ res = c.forAll(pmv, v);
+ } else {
+ res = v.visit(s, pmv);
+ }
+ pmv.glPopMatrix();
+ if( res ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/graphui/classes/jogamp/graph/ui/shapes/Label0.java b/src/graphui/classes/jogamp/graph/ui/shapes/Label0.java
index fe5f7f8e5..a091a89ed 100644
--- a/src/graphui/classes/jogamp/graph/ui/shapes/Label0.java
+++ b/src/graphui/classes/jogamp/graph/ui/shapes/Label0.java
@@ -31,6 +31,7 @@ import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.TextRegionUtil;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.geom.plane.AffineTransform;
+import com.jogamp.opengl.math.Vec2f;
import com.jogamp.opengl.math.geom.AABBox;
public class Label0 {
@@ -65,10 +66,10 @@ public class Label0 {
this.font = font;
}
- public final AABBox addShapeToRegion(final float scale, final Region region, final float[] txy,
+ public final AABBox addShapeToRegion(final float scale, final Region region, final Vec2f txy,
final AffineTransform tmp1, final AffineTransform tmp2, final AffineTransform tmp3)
{
- tmp1.setToTranslation(txy[0], txy[1]);
+ tmp1.setToTranslation(txy.x(), txy.y());
tmp1.scale(scale, scale, tmp2);
return TextRegionUtil.addStringToRegion(region, font, tmp1, text, rgbaColor, tmp2, tmp3);
}