diff options
7 files changed, 220 insertions, 184 deletions
diff --git a/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java index 8658fdf4d..19fa5f0a6 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/GPUUISceneTextAnim01.java @@ -35,21 +35,15 @@ import java.util.Locale; import com.jogamp.common.util.IOUtil; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; -import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; import com.jogamp.graph.font.FontScale; -import com.jogamp.graph.geom.SVertex; import com.jogamp.graph.ui.gl.Scene; import com.jogamp.graph.ui.gl.Shape; import com.jogamp.graph.ui.gl.shapes.Label; import com.jogamp.newt.MonitorDevice; import com.jogamp.newt.Window; -import com.jogamp.newt.event.GestureHandler.GestureEvent; -import com.jogamp.newt.event.InputEvent; import com.jogamp.newt.event.MouseEvent; -import com.jogamp.newt.event.MouseEvent.PointerClass; -import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; @@ -61,7 +55,6 @@ import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.demos.graph.FontSetDemos; import com.jogamp.opengl.demos.graph.MSAATool; import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.util.GLReadBufferUtil; public class GPUUISceneTextAnim01 implements GLEventListener { @@ -70,7 +63,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { private boolean trace = false; private final float noAADPIThreshold; - private final Scene sceneUICntrl; + private final Scene scene; /** -1 == AUTO, TBD @ init(..) */ private int renderModes; @@ -78,9 +71,6 @@ public class GPUUISceneTextAnim01 implements GLEventListener { private final Font font; private final Font fontFPS; - private final float sceneDist = 3000f; - private final float zNear = 0.1f, zFar = 7000f; - // private final float relTop = 80f/100f; private final float relMiddle = 22f/100f; // private final float relLeft = 11f/100f; @@ -135,23 +125,15 @@ public class GPUUISceneTextAnim01 implements GLEventListener { throw new RuntimeException(ioe); } - { - final RenderState rs = RenderState.createRenderState(SVertex.factory()); - final RegionRenderer renderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable); - rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED); - // renderer = RegionRenderer.create(rs, null, null); - - sceneUICntrl = new Scene(renderer, sceneDist, zNear, zFar); - // sceneUIController.setSampleCount(3); // easy on embedded devices w/ just 3 samples (default is 4)? - } + scene = new Scene(); screenshot = new GLReadBufferUtil(false, false); } private void setupUI(final GLAutoDrawable drawable) { final float pixelSizeFixed = fontSizeFixedPVP * drawable.getSurfaceHeight(); - jogampLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, font, pixelSizeFixed, jogamp); + jogampLabel = new Label(scene.getVertexFactory(), renderModes, font, pixelSizeFixed, jogamp); jogampLabel.addMouseListener(dragZoomRotateListener); - sceneUICntrl.addShape(jogampLabel); + scene.addShape(jogampLabel); jogampLabel.setEnabled(true); final float pixelSize10Pt = FontScale.toPixels(fontSizePt, dpiV); @@ -163,9 +145,9 @@ public class GPUUISceneTextAnim01 implements GLEventListener { * [FPS] Display 112.88889 dpi, fontSize 12.0 ppi -> pixelSize 15.679012 */ final float pixelSizeFPS = fontSizeFpsPVP * drawable.getSurfaceHeight(); - fpsLabel = new Label(sceneUICntrl.getVertexFactory(), renderModes, fontFPS, pixelSizeFPS, "Nothing there yet"); + fpsLabel = new Label(scene.getVertexFactory(), renderModes, fontFPS, pixelSizeFPS, "Nothing there yet"); fpsLabel.addMouseListener(dragZoomRotateListener); - sceneUICntrl.addShape(fpsLabel); + scene.addShape(fpsLabel); fpsLabel.setEnabled(true); fpsLabel.setColor(0.1f, 0.1f, 0.1f, 1.0f); fpsLabel.move(0f, pixelSizeFPS * (fontFPS.getMetrics().getLineGap() - fontFPS.getMetrics().getDescent()), 0f); @@ -196,7 +178,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { if(drawable instanceof GLWindow) { System.err.println("GPUUISceneGLListener0A: init (1)"); final GLWindow glw = (GLWindow) drawable; - sceneUICntrl.attachInputListenerTo(glw); + scene.attachInputListenerTo(glw); } else { System.err.println("GPUUISceneGLListener0A: init (0)"); } @@ -217,7 +199,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_BLEND); - sceneUICntrl.init(drawable); + scene.init(drawable); final GLAnimatorControl a = drawable.getAnimator(); if( null != a ) { @@ -241,7 +223,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { jogampLabel.setPosition(dxMiddleAbs, dyTopLabelAbs, dz); fpsLabel.move(0f, 0f, 0f); - sceneUICntrl.reshape(drawable, x, y, width, height); + scene.reshape(drawable, x, y, width, height); lastWidth = width; lastHeight = height; @@ -252,7 +234,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { public void dispose(final GLAutoDrawable drawable) { System.err.println("GPUUISceneGLListener0A: dispose"); - sceneUICntrl.dispose(drawable); // disposes all registered UIShapes + scene.dispose(drawable); // disposes all registered UIShapes final GL2ES2 gl = drawable.getGL().getGL2ES2(); screenshot.dispose(gl); @@ -261,11 +243,11 @@ public class GPUUISceneTextAnim01 implements GLEventListener { private int shotCount = 0; public void printScreen(final GL gl) { - final RegionRenderer renderer = sceneUICntrl.getRenderer(); + final RegionRenderer renderer = scene.getRenderer(); final String modeS = Region.getRenderModeString(jogampLabel.getRenderModes()); final String filename = String.format((Locale)null, "GraphUIDemo-shot%03d-%03dx%03d-S_%s_%02d.png", shotCount++, renderer.getWidth(), renderer.getHeight(), - modeS, sceneUICntrl.getSampleCount()); + modeS, scene.getSampleCount()); gl.glFinish(); // just make sure rendering finished .. if(screenshot.readPixels(gl, false)) { screenshot.write(new File(filename)); @@ -278,7 +260,7 @@ public class GPUUISceneTextAnim01 implements GLEventListener { if( fpsLabel.isEnabled() ) { final String text; if( null == actionText ) { - text = sceneUICntrl.getStatusText(drawable, renderModes, fpsLabel.getQuality(), dpiV); + text = scene.getStatusText(drawable, renderModes, fpsLabel.getQuality(), dpiV); } else if( null != drawable.getAnimator() ) { text = Scene.getStatusText(drawable.getAnimator())+", "+actionText; } else { @@ -288,15 +270,15 @@ public class GPUUISceneTextAnim01 implements GLEventListener { System.err.println(text); } } - sceneUICntrl.display(drawable); + scene.display(drawable); } public void attachInputListenerTo(final GLWindow window) { - sceneUICntrl.attachInputListenerTo(window); + scene.attachInputListenerTo(window); } public void detachInputListenerFrom(final GLWindow window) { - sceneUICntrl.detachInputListenerFrom(window); + scene.detachInputListenerFrom(window); } /** 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 7ac982a48..bc2353bb1 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UISceneDemo01.java @@ -36,7 +36,6 @@ import com.jogamp.common.net.Uri; import com.jogamp.common.util.InterruptSource; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; -import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; import com.jogamp.graph.font.FontSet; @@ -44,7 +43,6 @@ import com.jogamp.graph.geom.SVertex; import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.ui.gl.Scene; import com.jogamp.graph.ui.gl.Shape; -import com.jogamp.graph.ui.gl.Shape.EventInfo; import com.jogamp.graph.ui.gl.shapes.Button; import com.jogamp.graph.ui.gl.shapes.GLButton; import com.jogamp.graph.ui.gl.shapes.MediaButton; @@ -62,10 +60,8 @@ import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLPipelineFactory; import com.jogamp.opengl.GLProfile; -import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.demos.es2.GearsES2; import com.jogamp.opengl.demos.graph.MSAATool; -import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.PMVMatrix; @@ -156,9 +152,6 @@ public class UISceneDemo01 implements GLEventListener { private final float[] position = new float[] {0,0,0}; - private final float sceneDist = -1/5f; - private final float zNear = 0.1f, zFar = 7000f; - boolean ignoreInput = false; protected final AffineTransform tempT1 = new AffineTransform(); @@ -253,11 +246,11 @@ public class UISceneDemo01 implements GLEventListener { s.setTransform(pmv); final float[] objPos = new float[3]; - s.winToObjCoord(renderer, glWinX, glWinY, objPos); + s.winToObjCoord(pmv, viewport, glWinX, glWinY, objPos); System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]); final int[] surfaceSize = new int[2]; - s.getSurfaceSize(renderer, surfaceSize); + s.getSurfaceSize(pmv, viewport, surfaceSize); System.err.println("Button: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); pmv.glPopMatrix(); @@ -314,6 +307,22 @@ public class UISceneDemo01 implements GLEventListener { @Override public void display(final GLAutoDrawable drawable) { scene.display(drawable); + if( once ) { + once = false; + final RegionRenderer renderer = scene.getRenderer(); + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glPushMatrix(); + shape.setTransform(pmv); + + final int[] winPosSize = { 0, 0 }; + System.err.println("draw.0: "+shape); + boolean ok = shape.getSurfaceSize(pmv, renderer.getViewport(), winPosSize); + System.err.println("draw.1: ok "+ok+", surfaceSize "+winPosSize[0]+" x "+winPosSize[1]); + ok = shape.objToWinCoord(pmv, renderer.getViewport(), shape.getPosition(), winPosSize); + System.err.println("draw.2: ok "+ok+", winCoord "+winPosSize[0]+" x "+winPosSize[1]); + + pmv.glPopMatrix(); + } } static boolean once = true; 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 81d657385..61f5a37ac 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UIShapeDemo01.java @@ -104,8 +104,10 @@ public class UIShapeDemo01 implements GLEventListener { final GLProfile glp = GLProfile.getGL2ES2(); final GLCapabilities caps = new GLCapabilities(glp); caps.setAlphaBits(4); - caps.setSampleBuffers(true); - caps.setNumSamples(4); + if( false ) { + caps.setSampleBuffers(true); + caps.setNumSamples(4); + } System.out.println("Requested: " + caps); final GLWindow window = GLWindow.create(caps); @@ -180,7 +182,9 @@ public class UIShapeDemo01 implements GLEventListener { this.trace = trace; this.screenshot = new GLReadBufferUtil(false, false); - button = new Button(SVertex.factory(), renderModes, font, "Click me!", 1/8f, 1/16f); + final float sz1_w = 1/8f; + final float sz2 = 1/20f; + button = new Button(SVertex.factory(), renderModes, font, "Click me!", sz1_w, sz1_w/2f); button.setLabelColor(0.0f,0.0f,0.0f); /** Button defaults ! button.setLabelColor(1.0f,1.0f,1.0f); @@ -189,7 +193,7 @@ public class UIShapeDemo01 implements GLEventListener { button.setSpacing(2.0f); */ System.err.println(button); - crossHair = new CrossHair(SVertex.factory(), renderModes, 1/20f, 1/20f, 1/1000f); + crossHair = new CrossHair(SVertex.factory(), renderModes, sz2, sz2, 1/1000f); crossHair.setColor(0f,0f,1f,1f); crossHair.setEnabled(true); } @@ -218,25 +222,37 @@ public class UIShapeDemo01 implements GLEventListener { @Override public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) { - final GL2ES2 gl = drawable.getGL().getGL2ES2(); + // final GL2ES2 gl = drawable.getGL().getGL2ES2(); + // gl.glViewport(xstart, ystart, width, height); rRenderer.reshapePerspective(45.0f, width, height, zNear, zFar); // rRenderer.reshapeOrtho(width, height, zNear, zFar); - lastWidth = width; - lastHeight = height; + final PMVMatrix pmv = rRenderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmv.glLoadIdentity(); + pmv.glTranslatef(xTran, yTran, zTran); + if( drawable instanceof Window ) { ((Window)drawable).setTitle(UIShapeDemo01.class.getSimpleName()+": "+drawable.getSurfaceWidth()+" x "+drawable.getSurfaceHeight()); } } - float lastWidth = 0f, lastHeight = 0f; final int[] sampleCount = { 4 }; - private void drawShape(final GL2ES2 gl, final PMVMatrix pmv, final RegionRenderer renderer, final Shape shape) { + private void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final Shape shape) { + final PMVMatrix pmv = renderer.getMatrix(); pmv.glPushMatrix(); shape.setTransform(pmv); shape.drawShape(gl, renderer, sampleCount); + if( once ) { + final int[] winPosSize = { 0, 0 }; + System.err.println("draw.0: "+shape); + boolean ok = shape.getSurfaceSize(pmv, renderer.getViewport(), winPosSize); + System.err.println("draw.1: ok "+ok+", surfaceSize "+winPosSize[0]+" x "+winPosSize[1]); + ok = shape.objToWinCoord(pmv, renderer.getViewport(), shape.getPosition(), winPosSize); + System.err.println("draw.2: ok "+ok+", winCoord "+winPosSize[0]+" x "+winPosSize[1]); + } pmv.glPopMatrix(); } @@ -249,12 +265,9 @@ public class UIShapeDemo01 implements GLEventListener { final RegionRenderer renderer = getRegionRenderer(); final PMVMatrix pmv = renderer.getMatrix(); - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmv.glLoadIdentity(); - pmv.glTranslatef(xTran, yTran, zTran); renderer.enable(gl, true); - drawShape(gl, pmv, renderer, button); - drawShape(gl, pmv, renderer, crossHair); + drawShape(gl, renderer, button); + drawShape(gl, renderer, crossHair); { final String text = "Hello Origin."; final float full_width_o; @@ -392,11 +405,11 @@ public class UIShapeDemo01 implements GLEventListener { final float[] objPos = new float[3]; System.err.println("\n\nButton: "+button); - button.winToObjCoord(renderer, glWinX, glWinY, objPos); + button.winToObjCoord(pmv, viewport, glWinX, glWinY, objPos); System.err.println("Button: Click: Win "+glWinX+"/"+glWinY+" -> Obj "+objPos[0]+"/"+objPos[1]+"/"+objPos[1]); final int[] surfaceSize = new int[2]; - button.getSurfaceSize(renderer, surfaceSize); + button.getSurfaceSize(pmv, viewport, surfaceSize); System.err.println("Button: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); pmv.glPopMatrix(); @@ -408,16 +421,16 @@ public class UIShapeDemo01 implements GLEventListener { final float[] objPosC = crossHair.getBounds().getCenter(); final int[] objWinPos = new int[2]; System.err.println("\n\nCrossHair: "+crossHair); - if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) { + if( crossHair.objToWinCoord(pmv, viewport, objPosC, objWinPos) ) { System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]); } final float[] objPos2 = new float[3]; - crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2); + crossHair.winToObjCoord(pmv, viewport, objWinPos[0], objWinPos[1], objPos2); System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]); final float[] winObjPos = new float[3]; - if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) { + if( crossHair.winToObjCoord(pmv, viewport, glWinX, glWinY, winObjPos) ) { // final float[] translate = crossHair.getTranslate(); // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] }; final float dx = winObjPos[0] - objPosC[0]; @@ -432,7 +445,7 @@ public class UIShapeDemo01 implements GLEventListener { } final int[] surfaceSize = new int[2]; - crossHair.getSurfaceSize(renderer, surfaceSize); + crossHair.getSurfaceSize(pmv, viewport, surfaceSize); System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); pmv.glPopMatrix(); @@ -448,20 +461,14 @@ public class UIShapeDemo01 implements GLEventListener { @Override public void mouseMoved(final MouseEvent e) { - // TODO Auto-generated method stub - } @Override public void mouseDragged(final MouseEvent e) { - // TODO Auto-generated method stub - } @Override public void mouseWheelMoved(final MouseEvent e) { - // TODO Auto-generated method stub - } } 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 56962e110..41235077e 100644 --- a/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java +++ b/src/demos/com/jogamp/opengl/demos/graph/ui/UITypeDemo01.java @@ -475,16 +475,16 @@ public class UITypeDemo01 implements GLEventListener { final float[] objPosC = crossHair.getBounds().getCenter(); final int[] objWinPos = new int[2]; System.err.println("\n\nCrossHair: "+crossHair); - if( crossHair.objToWinCoord(renderer, objPosC, objWinPos) ) { + if( crossHair.objToWinCoord(pmv, viewport, objPosC, objWinPos) ) { System.err.println("CrossHair: Obj: Obj "+objPosC[0]+"/"+objPosC[1]+"/"+objPosC[1]+" -> Win "+objWinPos[0]+"/"+objWinPos[1]); } final float[] objPos2 = new float[3]; - crossHair.winToObjCoord(renderer, objWinPos[0], objWinPos[1], objPos2); + crossHair.winToObjCoord(pmv, viewport, objWinPos[0], objWinPos[1], objPos2); System.err.println("CrossHair: Obj: Win "+objWinPos[0]+"/"+objWinPos[1]+" -> Obj "+objPos2[0]+"/"+objPos2[1]+"/"+objPos2[1]); final float[] winObjPos = new float[3]; - if( crossHair.winToObjCoord(renderer, glWinX, glWinY, winObjPos) ) { + if( crossHair.winToObjCoord(pmv, viewport, glWinX, glWinY, winObjPos) ) { // final float[] translate = crossHair.getTranslate(); // final float[] objPosT = new float[] { objPosC[0]+translate[0], objPosC[1]+translate[1], objPosC[2]+translate[2] }; final float dx = winObjPos[0] - objPosC[0]; @@ -499,7 +499,7 @@ public class UITypeDemo01 implements GLEventListener { } final int[] surfaceSize = new int[2]; - crossHair.getSurfaceSize(renderer, surfaceSize); + crossHair.getSurfaceSize(pmv, viewport, surfaceSize); System.err.println("CrossHair: Size: Pixel "+surfaceSize[0]+" x "+surfaceSize[1]); pmv.glPopMatrix(); diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java index 88e87b6cf..ac5258595 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java +++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Scene.java @@ -66,7 +66,7 @@ import com.jogamp.opengl.util.PMVMatrix; * </p> * @see Shape */ -public class Scene implements GLEventListener { +public final class Scene implements 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. */ @@ -208,7 +208,7 @@ public class Scene implements GLEventListener { public void removeShape(final Shape b) { shapes.remove(b); } - public final Shape getShapeByIdx(final int id) { + public Shape getShapeByIdx(final int id) { if( 0 > id ) { return null; } @@ -303,19 +303,26 @@ public class Scene implements GLEventListener { renderer.enable(gl, false); } + /** + * Attempt to pick a {@link Shape} using the window coordinates and contained {@ling Shape}'s {@link AABBox} {@link Shape#getBounds() bounds} + * using a ray-intersection algorithm. + * <p> + * If {@link Shape} was found the given action is performed. + * </p> + * <p> + * Method performs on current thread and returns after probing every {@link Shape}. + * </p> + * @param glWinX window X coordinate, bottom-left origin + * @param glWinY window Y coordinate, bottom-left origin + * @param objPos storage for found object position in model-space of found {@link Shape} + * @param shape storage for found {@link Shape} or null + * @param runnable the action to perform if {@link Shape} was found + */ public void pickShape(final int glWinX, final int glWinY, final float[] objPos, final Shape[] shape, final Runnable runnable) { - if( null == cDrawable ) { - return; + shape[0] = pickShapeImpl(glWinX, glWinY, objPos); + if( null != shape[0] ) { + runnable.run(); } - cDrawable.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - shape[0] = pickShapeImpl(glWinX, glWinY, objPos); - if( null != shape[0] ) { - runnable.run(); - } - return true; - } } ); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Shape pickShapeImpl(final int glWinX, final int glWinY, final float[] objPos) { @@ -326,8 +333,8 @@ public class Scene implements GLEventListener { gl.glReadPixels( x, y, 1, 1, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZRB); winZ1 = winZRB.get(0); // dir */ - final PMVMatrix pmv = renderer.getMatrix(); - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + final PMVMatrix pmv = new PMVMatrix(); + setupMatrix(pmv); final Ray ray = new Ray(); @@ -370,26 +377,9 @@ public class Scene implements GLEventListener { * @param runnable action */ public void winToObjCoord(final Shape shape, final int glWinX, final int glWinY, final float[] objPos, final Runnable runnable) { - if( null == cDrawable || null == shape ) { - return; + if( null != shape && shape.winToObjCoord(this, glWinX, glWinY, objPos) ) { + runnable.run(); } - cDrawable.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - final boolean ok; - { - final PMVMatrix pmv = renderer.getMatrix(); - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmv.glPushMatrix(); - shape.setTransform(pmv); - ok = shape.winToObjCoord(renderer, glWinX, glWinY, objPos); - pmv.glPopMatrix(); - } - if( ok ) { - runnable.run(); - } - return true; - } } ); } /** @@ -450,17 +440,7 @@ public class Scene implements GLEventListener { final int[] viewport = { 0, 0, width, height }; final PMVMatrix pmv = new PMVMatrix(); - { - final float ratio = (float)width/(float)height; - pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION); - pmv.glLoadIdentity(); - pmv.gluPerspective(projAngle, ratio, zNear, zFar); - } - { - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmv.glLoadIdentity(); - pmv.glTranslatef(0f, 0f, sceneDist); - } + setupMatrix(pmv, width, height); { final float orthoDist = -sceneDist; final float[] obj00Coord = new float[3]; @@ -477,10 +457,10 @@ public class Scene implements GLEventListener { /** * Reshape scene {@link #setupMatrix(PMVMatrix, int, int)}. * <p> - * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. + * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. * </p> * <p> - * Modelview is translated to given {@link #getProjSceneDist()} + * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()} * and and origin 0/0 becomes the bottom-left corner. * </p> * @see #setupMatrix(PMVMatrix, int, int) @@ -493,7 +473,8 @@ public class Scene implements GLEventListener { renderer.reshapeNotify(x, y, width, height); final PMVMatrix pmv = renderer.getMatrix(); - setupMatrix(pmv, width, height); + setupMatrix0(pmv, width, height); + pmv.glTranslatef(0f, 0f, sceneDist); { final float orthoDist = -sceneDist; final float[] obj00Coord = new float[3]; @@ -549,32 +530,43 @@ public class Scene implements GLEventListener { public final int[/*4*/] getViewport(final int[/*4*/] target) { return renderer.getViewport(target); } /** Borrows the current int[4] viewport w/o copying. It is set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */ - public final int[/*4*/] getViewport() { return renderer.getViewport(); } + public int[/*4*/] getViewport() { return renderer.getViewport(); } /** Returns the {@link #getViewport()}'s width, set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */ public int getWidth() { return renderer.getWidth(); } /** Returns the {@link #getViewport()}'s height, set after initial {@link #reshape(GLAutoDrawable, int, int, int, int)}. */ public int getHeight() { return renderer.getHeight(); } + /** Borrow the current {@link PMVMatrix}. */ + public PMVMatrix getMatrix() { return renderer.getMatrix(); } + /** Translate current matrix to {@link #getBounds()}'s origin (minx/miny) and {@link #getProjSceneDist()}, a convenience method. */ - public void translate(final PMVMatrix pmv) { + private void translate(final PMVMatrix pmv) { pmv.glTranslatef(planeBoxCtr.getMinX(), planeBoxCtr.getMinY(), sceneDist); } /** - * Setup {@link PMVMatrix} projection and modelview using explicit surface width and height before {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method. + * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} + * using explicit surface width and height before {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method. * <p> - * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. + * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. * </p> * <p> - * Modelview is translated to given {@link #getProjSceneDist()} + * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()} * and and origin 0/0 becomes the bottom-left corner. * </p> + * <p> + * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected. + * </p> * @param pmv the {@link PMVMatrix} to setup * @param surface_width explicit surface width * @param surface_height explicit surface height */ public void setupMatrix(final PMVMatrix pmv, final int surface_width, final int surface_height) { + setupMatrix0(pmv, surface_width, surface_height); + translate(pmv); + } + private void setupMatrix0(final PMVMatrix pmv, final int surface_width, final int surface_height) { final float ratio = (float)surface_width/(float)surface_height; pmv.glMatrixMode(GLMatrixFunc.GL_PROJECTION); pmv.glLoadIdentity(); @@ -582,23 +574,26 @@ public class Scene implements GLEventListener { pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); - translate(pmv); } /** - * Setup {@link PMVMatrix} projection and modelview using implicit {@link #getViewport()} surface dimension after {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method. + * Setup {@link PMVMatrix} {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} + * using implicit {@link #getViewport()} surface dimension after {@link #reshape(GLAutoDrawable, int, int, int, int)} happened, a convenience method. * <p> - * Projection will be setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. + * {@link GLMatrixFunc#GL_PROJECTION} is setup using perspective {@link #getProjAngle()} with {@link #getProjZNear()} and {@link #getProjZFar()}. * </p> * <p> - * Modelview is translated to given {@link #getProjSceneDist()} + * {@link GLMatrixFunc#GL_MODELVIEW} is translated to given {@link #getProjSceneDist()} * and and origin 0/0 becomes the bottom-left corner. * </p> + * <p> + * At the end of operations, the {@link GLMatrixFunc#GL_MODELVIEW} matrix is selected. + * </p> * @param pmv the {@link PMVMatrix} to setup * @param surface_width explicit surface width * @param surface_height explicit surface height */ - public void setup(final PMVMatrix pmv) { + public void setupMatrix(final PMVMatrix pmv) { setupMatrix(pmv, getWidth(), getHeight()); } @@ -651,11 +646,9 @@ public class Scene implements GLEventListener { final int glWinY = getHeight() - e.getY() - 1; final float[] objPos = new float[3]; final Shape shape = activeShape; - winToObjCoord(shape, glWinX, glWinY, objPos, new Runnable() { - @Override - public void run() { - shape.dispatchGestureEvent(renderer, gh, glWinX, glWinY, objPos); - } } ); + winToObjCoord(shape, glWinX, glWinY, objPos, () -> { + shape.dispatchGestureEvent(Scene.this, gh, glWinX, glWinY, objPos); + }); } } } @@ -684,14 +677,12 @@ public class Scene implements GLEventListener { final void dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY, final boolean setActive) { final float[] objPos = new float[3]; final Shape[] shape = { null }; - pickShape(glWinX, glWinY, objPos, shape, new Runnable() { - @Override - public void run() { + pickShape(glWinX, glWinY, objPos, shape, () -> { if( setActive ) { setActiveShape(shape[0]); } shape[0].dispatchMouseEvent(e, glWinX, glWinY, objPos); - } } ); + } ); } /** * Dispatch event to shape @@ -702,11 +693,7 @@ public class Scene implements GLEventListener { */ final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) { final float[] objPos = new float[3]; - winToObjCoord(shape, glWinX, glWinY, objPos, new Runnable() { - @Override - public void run() { - shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); - } } ); + winToObjCoord(shape, glWinX, glWinY, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); }); } private class SBCMouseListener implements MouseListener { diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java index f8967e437..4f81f4ff7 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java +++ b/src/graphui/classes/com/jogamp/graph/ui/gl/Shape.java @@ -235,9 +235,9 @@ public abstract class Shape { * <p> * No matrix operations (translate, scale, ..) are performed. * </p> - * @param gl - * @param renderer - * @param sampleCount + * @param gl the current GL object + * @param renderer the used {@link RegionRenderer}, also source of {@link RegionRenderer#getMatrix()} and {@link RegionRenderer#getViewport()}. + * @param sampleCount sample count if used by Graph renderModes */ public void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) { final float r, g, b, a; @@ -361,24 +361,25 @@ public abstract class Shape { } /** - * Retrieve window surface size of this shape + * Retrieve window surface size of this shape reusing a given setup {@link PMVMatrix}. * <p> - * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object, - * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}. + * The given {@link PMVMatrix} has to be setup properly for this object, + * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene + * including this shape's {@link #setTransform(PMVMatrix)}. * </p> - * @param renderer source of viewport and {@link PMVMatrix} + * @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 surfaceSize int[2] target surface size * @return true for successful gluProject(..) operation, otherwise false + * @see #getSurfaceSize(Scene, int[]) */ - public boolean getSurfaceSize(final RegionRenderer renderer, final int[/*2*/] surfaceSize) { + public boolean getSurfaceSize(final PMVMatrix pmv, final int[/*4*/] viewport, final int[/*2*/] surfaceSize) { boolean res = false; - final int[/*4*/] viewport = renderer.getViewport(new int[4]); // System.err.println("UIShape::getSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]); final float[] winCoordHigh = new float[3]; final float[] winCoordLow = new float[3]; final float[] high = getBounds().getHigh(); final float[] low = getBounds().getLow(); - final PMVMatrix pmv = renderer.getMatrix(); if( pmv.gluProject(high[0], high[1], high[2], viewport, 0, winCoordHigh, 0) ) { // System.err.printf("UIShape::surfaceSize.H: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), high[0], high[1], high[2], winCoordHigh[0], winCoordHigh[1], winCoordHigh[2]); @@ -394,22 +395,40 @@ public abstract class Shape { } /** - * Map given object coordinate relative to this shape to window coordinates + * Retrieve window surface size of this shape using a local {@link PMVMatrix}. + * <p> + * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. + * </p> + * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}. + * @param surfaceSize int[2] target surface size + * @return true for successful gluProject(..) operation, otherwise false + * @see #getSurfaceSize(PMVMatrix, int[], int[]) + */ + public boolean getSurfaceSize(final Scene scene, final int[/*2*/] surfaceSize) { + final PMVMatrix pmv = new PMVMatrix(); + scene.setupMatrix(pmv); + setTransform(pmv); + return getSurfaceSize(pmv, scene.getViewport(), surfaceSize); + } + + /** + * Map given object coordinate relative to this shape to window coordinates reusing a given setup {@link PMVMatrix}. * <p> - * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object, - * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}. + * The given {@link PMVMatrix} has to be setup properly for this object, + * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene + * including this shape's {@link #setTransform(PMVMatrix)}. * </p> - * @param renderer source of viewport and {@link PMVMatrix} + * @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 glWinPos int[2] target window position of objPos relative to this shape * @return true for successful gluProject(..) operation, otherwise false + * @see #objToWinCoord(Scene, float[], int[]) */ - public boolean objToWinCoord(final RegionRenderer renderer, final float[/*3*/] objPos, final int[/*2*/] glWinPos) { + public boolean objToWinCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final float[/*3*/] objPos, final int[/*2*/] glWinPos) { boolean res = false; - final int[/*4*/] viewport = renderer.getViewport(new int[4]); // System.err.println("UIShape::objToWinCoordgetSurfaceSize.VP "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]); final float[] winCoord = new float[3]; - final PMVMatrix pmv = renderer.getMatrix(); if( pmv.gluProject(objPos[0], objPos[1], objPos[2], viewport, 0, winCoord, 0) ) { // System.err.printf("UIShape::objToWinCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), objPos[0], objPos[1], objPos[2], winCoord[0], winCoord[1], winCoord[2]); @@ -422,23 +441,43 @@ public abstract class Shape { } /** - * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. + * Map given object coordinate relative to this shape to window coordinates using a local {@link PMVMatrix}. * <p> - * The {@link RegionRenderer#getMatrix()} has to be setup properly for this object, - * i.e. reshape for {@link GLMatrixFunc#GL_PROJECTION} and {@link #setTransform(PMVMatrix)} for {@link GLMatrixFunc#GL_MODELVIEW}. + * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. * </p> - * @param renderer source of viewport and {@link PMVMatrix} + * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}. + * @param objPos float[3] object position relative to this shape's center + * @param glWinPos int[2] target window position of objPos relative to this shape + * @return true for successful gluProject(..) operation, otherwise false + * @see #objToWinCoord(PMVMatrix, int[], float[], int[]) + */ + public boolean objToWinCoord(final Scene scene, final float[/*3*/] objPos, final int[/*2*/] glWinPos) { + final PMVMatrix pmv = new PMVMatrix(); + scene.setupMatrix(pmv); + setTransform(pmv); + return this.objToWinCoord(pmv, scene.getViewport(), objPos, glWinPos); + } + + /** + * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate + * reusing a given setup {@link PMVMatrix}. + * <p> + * The given {@link PMVMatrix} has to be setup properly for this object, + * i.e. its {@link GLMatrixFunc#GL_PROJECTION} and {@link GLMatrixFunc#GL_MODELVIEW} for the surrounding scene + * including this shape's {@link #setTransform(PMVMatrix)}. + * </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 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 @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false + * @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false + * @see #winToObjCoord(Scene, int, int, float[]) */ - public boolean winToObjCoord(final RegionRenderer renderer, final int glWinX, final int glWinY, final float[/*3*/] objPos) { + public boolean winToObjCoord(final PMVMatrix pmv, final int[/*4*/] viewport, final int glWinX, final int glWinY, final float[/*3*/] objPos) { boolean res = false; final float[] ctr = getBounds().getCenter(); - final int[] viewport = renderer.getViewport(new int[4]); final float[] tmp = new float[3]; - final PMVMatrix pmv = renderer.getMatrix(); if( pmv.gluProject(ctr[0], ctr[1], ctr[2], viewport, 0, tmp, 0) ) { // System.err.printf("UIShape::winToObjCoord.0: shape %d: obj [%f, %f, %f] -> win [%f, %f, %f]%n", getName(), ctr[0], ctr[1], ctr[2], tmp[0], tmp[1], tmp[2]); @@ -450,6 +489,26 @@ public abstract class Shape { return res; } + /** + * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate + * using a local {@link PMVMatrix}. + * <p> + * The {@link Scene} has be {@link Scene#reshape(com.jogamp.opengl.GLAutoDrawable, int, int, int, int) reshape(..)}ed once. + * </p> + * @param scene {@link Scene} source of viewport and local {@link PMVMatrix} {@link Scene#setupMatrix(PMVMatrix) setupMatrix(..)}. + * @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 @return true for successful gluProject(..) and gluUnProject(..) operations, otherwise false + * @see #winToObjCoord(PMVMatrix, int[], int, int, float[]) + */ + public boolean winToObjCoord(final Scene scene, final int glWinX, final int glWinY, final float[/*3*/] objPos) { + final PMVMatrix pmv = new PMVMatrix(); + scene.setupMatrix(pmv); + setTransform(pmv); + return this.winToObjCoord(pmv, scene.getViewport(), glWinX, glWinY, objPos); + } + public float[] getColor() { return rgbaColor; } @@ -826,31 +885,23 @@ public abstract class Shape { * @param glWinX x-position in OpenGL model space * @param glWinY y-position in OpenGL model space */ - /* pp */ final void dispatchGestureEvent(final RegionRenderer renderer, final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) { + /* pp */ final void dispatchGestureEvent(final Scene scene, final GestureEvent e, final int glWinX, final int glWinY, final float[] objPos) { if( resizable && e instanceof PinchToZoomGesture.ZoomEvent ) { final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) e; final float pixels = ze.getDelta() * ze.getScale(); // final float[] objPos2 = { 0f, 0f, 0f }; final int winX2 = glWinX + Math.round(pixels); - final boolean ok; - { - final PMVMatrix pmv = renderer.getMatrix(); - pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmv.glPushMatrix(); - setTransform(pmv); - ok = winToObjCoord(null, winX2, glWinY, objPos2); - pmv.glPopMatrix(); - } + final boolean ok = winToObjCoord(scene, winX2, glWinY, objPos2); 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() ); 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], + System.err.printf("DragZoom: resize %b, ok %b, obj %4d/%4d, %.3f/%.3f/%.3f %.3f/%.3f/%.3f + %.3f/%.3f -> %.3f/%.3f%n", + inResize, ok, glWinX, glWinY, objPos[0], objPos[1], objPos[2], position[0], position[1], position[2], dx, dy, sx, sy); } - if( resize_sxy_min <= sx && sx <= resize_sxy_max && resize_sxy_min <= sy && sy <= resize_sxy_max ) { + if( ok && resize_sxy_min <= sx && sx <= resize_sxy_max && resize_sxy_min <= sy && sy <= resize_sxy_max ) { 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], diff --git a/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java b/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java index 7fc2e6f7e..77ac99861 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java +++ b/src/graphui/classes/com/jogamp/graph/ui/gl/shapes/GLButton.java @@ -94,7 +94,7 @@ public class GLButton extends TexSeqButton { @Override public void drawShape(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) { final int[/*2*/] surfaceSize = new int[2]; - final boolean got_sz = getSurfaceSize(renderer, surfaceSize) && 0 < surfaceSize[0] && 0 < surfaceSize[1]; + final boolean got_sz = getSurfaceSize(renderer.getMatrix(), renderer.getViewport(), surfaceSize) && 0 < surfaceSize[0] && 0 < surfaceSize[1]; if( null == fboGLAD ) { final ImageSequence imgSeq = (ImageSequence)texSeq; |