/** * 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.gl; import java.util.ArrayList; import java.util.List; import com.jogamp.nativewindow.NativeWindowException; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.newt.event.GestureHandler.GestureEvent; import com.jogamp.newt.event.GestureHandler.GestureListener; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.NEWTEvent; import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.VectorUtil; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.PMVMatrix; /** * GraphUI Shape *
* A shape includes the following build-in user-interactions * - drag shape w/ 1-pointer click, see {@link #setDraggable(boolean)} * - resize shape w/ 1-pointer click and drag in 1/4th bottom-left and bottom-right corner, see {@link #setResizable(boolean)}. *
** GraphUI is GPU based and resolution independent. *
** GraphUI is intended to become an immediate- and retained-mode API. *
* @see Scene */ public abstract class Shape { public static interface Listener { void run(final Shape shape); } public static final boolean DRAW_DEBUG_BOX = false; private static final boolean DEBUG = false; protected static final int DIRTY_SHAPE = 1 << 0 ; protected static final int DIRTY_STATE = 1 << 1 ; protected final Factory extends Vertex> vertexFactory; private final int renderModes; protected final AABBox box; protected final AffineTransform tempT1 = new AffineTransform(); protected final AffineTransform tempT2 = new AffineTransform(); protected final AffineTransform tempT3 = new AffineTransform(); protected final AffineTransform tempT4 = new AffineTransform(); protected final float[] position = new float[] { 0f, 0f, 0f }; protected final Quaternion rotation = new Quaternion(); protected final float[] rotOrigin = new float[] { 0f, 0f, 0f }; protected final float[] scale = new float[] { 1f, 1f, 1f }; protected GLRegion region = null; protected int regionQuality = Region.MAX_QUALITY; protected List* No matrix operations (translate, scale, ..) are performed. *
* @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 draw(final GL2ES2 gl, final RegionRenderer renderer, final int[] sampleCount) { final float r, g, b, a; final boolean isPressed = isPressed(), isToggleOn = isToggleOn(); final boolean modBaseColor = !Region.hasColorChannel( renderModes ) && !Region.hasColorTexture( renderModes ); if( modBaseColor ) { if( isPressed ) { r = rgbaColor[0]*pressedRGBAModulate[0]; g = rgbaColor[1]*pressedRGBAModulate[1]; b = rgbaColor[2]*pressedRGBAModulate[2]; a = rgbaColor[3]*pressedRGBAModulate[3]; } else if( isToggleable() ) { if( isToggleOn ) { r = rgbaColor[0]*toggleOnRGBAModulate[0]; g = rgbaColor[1]*toggleOnRGBAModulate[1]; b = rgbaColor[2]*toggleOnRGBAModulate[2]; a = rgbaColor[3]*toggleOnRGBAModulate[3]; } else { r = rgbaColor[0]*toggleOffRGBAModulate[0]; g = rgbaColor[1]*toggleOffRGBAModulate[1]; b = rgbaColor[2]*toggleOffRGBAModulate[2]; a = rgbaColor[3]*toggleOffRGBAModulate[3]; } } else { r = rgbaColor[0]; g = rgbaColor[1]; b = rgbaColor[2]; a = rgbaColor[3]; } } else { if( isPressed ) { r = pressedRGBAModulate[0]; g = pressedRGBAModulate[1]; b = pressedRGBAModulate[2]; a = pressedRGBAModulate[3]; } else if( isToggleable() ) { if( isToggleOn ) { r = toggleOnRGBAModulate[0]; g = toggleOnRGBAModulate[1]; b = toggleOnRGBAModulate[2]; a = toggleOnRGBAModulate[3]; } else { r = toggleOffRGBAModulate[0]; g = toggleOffRGBAModulate[1]; b = toggleOffRGBAModulate[2]; a = toggleOffRGBAModulate[3]; } } else { r = rgbaColor[0]; g = rgbaColor[1]; b = rgbaColor[2]; a = rgbaColor[3]; } } renderer.getRenderState().setColorStatic(r, g, b, a); getRegion(gl).draw(gl, renderer, sampleCount); } protected GLRegion createGLRegion(final GLProfile glp) { return GLRegion.create(glp, renderModes, null); } /** * Validates the shape's underlying {@link GLRegion}. ** If the region is dirty, it gets {@link GLRegion#clear(GL2ES2) cleared} and is reused. *
* @param gl current {@link GL2ES2} object * @see #validate(GLProfile) */ public final void validate(final GL2ES2 gl) { validateImpl(gl.getGLProfile(), gl); } /** * Validates the shape's underlying {@link GLRegion} w/o a current {@link GL2ES2} object ** If the region is dirty a new region is created * and the old one gets pushed to a dirty-list to get disposed when a GL context is available. *
* @see #validate(GL2ES2) */ public final void validate(final GLProfile glp) { validateImpl(glp, null); } private final synchronized void validateImpl(final GLProfile glp, final GL2ES2 gl) { if( null != gl ) { clearDirtyRegions(gl); } if( isShapeDirty() || null == region ) { box.reset(); if( null == region ) { region = createGLRegion(glp); } else if( null == gl ) { dirtyRegions.add(region); region = createGLRegion(glp); } else { region.clear(gl); } addShapeToRegion(); region.setQuality(regionQuality); dirty &= ~(DIRTY_SHAPE|DIRTY_STATE); } else if( isStateDirty() ) { region.markStateDirty(); dirty &= ~DIRTY_STATE; } } /** * Setup the pre-selected {@link GLMatrixFunc#GL_MODELVIEW} {@link PMVMatrix} for this object. * @param pmv the matrix */ public void setTransform(final PMVMatrix pmv) { final float[] uiTranslate = getPosition(); final float[] ctr = box.getCenter(); final float[] low = box.getLow(); final float hw = ctr[0] - low[0]; // our center is half size final float hh = ctr[1] - low[1]; final float hd = ctr[2] - low[2]; // 1) text top moving; Button Good // pmv.glTranslatef(uiTranslate[0]-hw, uiTranslate[1]-hh, uiTranslate[2]-hd); // translate less unscaled-center // 2) text sticky moving ; Button sticky moving // pmv.glTranslatef(uiTranslate[0]+low[0], uiTranslate[1]+low[1], uiTranslate[2]+low[2]); // translate from origin // 3) text OK'sh, but sticky moving ; Button Good pmv.glTranslatef(uiTranslate[0]+low[0]-hw, uiTranslate[1]+low[1]-hh, uiTranslate[2]+low[2]-hd); // translate from origin less unscaled-center // pmv.glTranslatef(uiTranslate[0], uiTranslate[1], uiTranslate[2]); final Quaternion quat = getRotation(); final boolean rotate = !quat.isIdentity(); final float[] uiScale = getScale(); final boolean scale = !VectorUtil.isVec3Equal(uiScale, 0, VectorUtil.VEC3_ONE, 0, FloatUtil.EPSILON); if( rotate || scale ) { final float[] rotOrigin = getRotationOrigin(); final boolean pivot = !VectorUtil.isVec3Zero(rotOrigin, 0, FloatUtil.EPSILON); if( pivot ) { pmv.glTranslatef(rotOrigin[0], rotOrigin[1], rotOrigin[2]); } if( scale ) { pmv.glScalef(uiScale[0], uiScale[1], uiScale[2]); } if( rotate ) { pmv.glRotate(quat); } if( pivot ) { pmv.glTranslatef(-rotOrigin[0], -rotOrigin[1], -rotOrigin[2]); } } // TODO: Add alignment features. This also shall resolve scaling of multi-line text, i.e. sticky-edges to stay sticky! // pmv.glTranslatef(hw, hh, hd); // add-back center, scaled // pmv.glTranslatef(-low[0], -low[1], -low[2]); // add-back origin-distance, scaled pmv.glTranslatef(hw-low[0], hh-low[1], hd-low[2]); // add back origin-distance and center, scaled } /** * Retrieve surface (view) size of this shape. ** 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)}. *
* @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 given int[2] {@code surfaceSize} for successful gluProject(..) operation, otherwise {@code null} * @see #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[]) * @see #getSurfaceSize(Scene, PMVMatrix, int[]) */ public int[/*2*/] getSurfaceSize(final PMVMatrix pmv, final int[/*4*/] viewport, final int[/*2*/] surfaceSize) { // 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(); if( pmv.gluProject(high[0], high[1], high[2], 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) ) { // 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]); // System.err.printf("Shape::surfaceSize.S: shape %d: %f x %f -> %d x %d%n", getName(), winCoordHigh[0] - winCoordLow[0], winCoordHigh[1] - winCoordLow[1], surfaceSize[0], surfaceSize[1]); return surfaceSize; } } return null; } /** * Retrieve surface (view) size of this shape. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @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 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 surfaceSize int[2] target surface size * @return given int[2] {@code surfaceSize} for successful gluProject(..) operation, otherwise {@code null} * @see #getSurfaceSize(PMVMatrix, int[], int[]) * @see #getSurfaceSize(Scene, PMVMatrix, int[]) */ public int[/*2*/] getSurfaceSize(final Scene.PMVMatrixSetup pmvMatrixSetup, final int[/*4*/] viewport, final PMVMatrix pmv, final int[/*2*/] surfaceSize) { pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); return getSurfaceSize(pmv, viewport, surfaceSize); } /** * Retrieve surface (view) size of this shape. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @param scene {@link Scene} to retrieve {@link Scene.PMVMatrixSetup} and the viewport. * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller. * @param surfaceSize int[2] target surface size * @return given int[2] {@code surfaceSize} for successful gluProject(..) operation, otherwise {@code null} * @see #getSurfaceSize(PMVMatrix, int[], int[]) * @see #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[]) */ public int[/*2*/] getSurfaceSize(final Scene scene, final PMVMatrix pmv, final int[/*2*/] surfaceSize) { return getSurfaceSize(scene.getPMVMatrixSetup(), scene.getViewport(), pmv, surfaceSize); } /** * Retrieve pixel per shape-coordinate unit, i.e. [px]/[obj]. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @param scene {@link Scene} to retrieve {@link Scene.PMVMatrixSetup} and the viewport. * @param pmv a new {@link PMVMatrix} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) be setup}, * {@link #setTransform(PMVMatrix) shape-transformed} and can be reused by the caller. * @param pixPerShape float[2] pixel per shape-coordinate unit * @return given float[2] {@code pixPerShape} for successful gluProject(..) operation, otherwise {@code null} * @see #getPixelPerShapeUnit(int[], float[]) * @see #getSurfaceSize(Scene, PMVMatrix, int[]) * @see #getScaledWidth() * @see #getScaledHeight() */ public float[] getPixelPerShapeUnit(final Scene scene, final PMVMatrix pmv, final float[] pixPerShape) { final int[] shapeSizePx = new int[2]; if( null != getSurfaceSize(scene, new PMVMatrix(), shapeSizePx) ) { return getPixelPerShapeUnit(shapeSizePx, pixPerShape); } else { return null; } } /** * Retrieve pixel per shape-coordinate unit, i.e. [px]/[obj]. * @param shapeSizePx int[2] shape size in pixel as retrieved via e.g. {@link #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[])} * @param pixPerShape float[2] pixel per shape-coordinate unit * @return given float[2] {@code pixPerShape} * @see #getPixelPerShapeUnit(Scene, PMVMatrix, float[]) * @see #getSurfaceSize(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], PMVMatrix, int[]) * @see #getScaledWidth() * @see #getScaledHeight() */ public float[] getPixelPerShapeUnit(final int[] shapeSizePx, final float[] pixPerShape) { pixPerShape[0] = shapeSizePx[0] / getScaledWidth(); pixPerShape[0] = shapeSizePx[1] / getScaledHeight(); return pixPerShape; } /** * Map given object coordinate relative to this shape to window coordinates. ** 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)}. *
* @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 given int[2] {@code glWinPos} for successful gluProject(..) operation, otherwise {@code null} * @see #shapeToWinCoord(com.jogamp.graph.ui.gl.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) { // 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) ) { // 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]); // System.err.printf("Shape::objToWinCoord.X: shape %d: %f / %f -> %d / %d%n", getName(), winCoord[0], winCoord[1], glWinPos[0], glWinPos[1]); return glWinPos; } return null; } /** * Map given object coordinate relative to this shape to window coordinates. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @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 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 * @return given int[2] {@code glWinPos} for successful gluProject(..) operation, otherwise {@code null} * @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) { pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); return this.shapeToWinCoord(pmv, viewport, objPos, glWinPos); } /** * Map given object coordinate relative to this shape to window coordinates. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @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 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 * @return given int[2] {@code glWinPos} for successful gluProject(..) operation, otherwise {@code null} * @see #shapeToWinCoord(PMVMatrix, int[], float[], int[]) * @see #shapeToWinCoord(com.jogamp.graph.ui.gl.Scene.PMVMatrixSetup, int[], float[], PMVMatrix, int[]) */ public int[/*2*/] shapeToWinCoord(final Scene scene, final float[/*3*/] objPos, final PMVMatrix pmv, final int[/*2*/] glWinPos) { return this.shapeToWinCoord(scene.getPMVMatrixSetup(), scene.getViewport(), objPos, pmv, glWinPos); } /** * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. ** 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)}. *
* @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 given float[3] {@code objPos} for successful gluProject(..) and gluUnProject(..) operation, otherwise {@code null} * @see #winToShapeCoord(com.jogamp.graph.ui.gl.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(); final float[] tmp = new float[3]; if( pmv.gluProject(ctr[0], ctr[1], ctr[2], 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) ) { // 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]); return objPos; } } return null; } /** * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @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#gluUnProject(float, float, float, int[], int, float[], int)} * @param glWinX in GL window coordinates, origin bottom-left * @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} * @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) { pmvMatrixSetup.set(pmv, viewport[0], viewport[1], viewport[2], viewport[3]); setTransform(pmv); return this.winToShapeCoord(pmv, viewport, glWinX, glWinY, objPos); } /** * Map given gl-window-coordinates to object coordinates relative to this shape and its z-coordinate. ** The given {@link PMVMatrix} will be {@link Scene.PMVMatrixSetup#set(PMVMatrix, int, int, int, int) setup} properly for this shape * including this shape's {@link #setTransform(PMVMatrix)}. *
* @param scene {@link Scene} to retrieve {@link Scene.PMVMatrixSetup} and the viewport. * @param glWinX in GL window coordinates, origin bottom-left * @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} * @see #winToShapeCoord(PMVMatrix, int[], int, int, float[]) * @see #winToShapeCoord(com.jogamp.graph.ui.gl.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) { return this.winToShapeCoord(scene.getPMVMatrixSetup(), scene.getViewport(), glWinX, glWinY, pmv, objPos); } public float[] getColor() { return rgbaColor; } public final int getQuality() { return regionQuality; } public final void setQuality(final int q) { this.regionQuality = q; if( null != region ) { region.setQuality(q); } } public final void setSharpness(final float sharpness) { this.shapesSharpness = sharpness; markShapeDirty(); } public final float getSharpness() { return shapesSharpness; } /** * Set base color. ** Default base-color w/o color channel, will be modulated w/ pressed- and toggle color *
*/ public final void setColor(final float r, final float g, final float b, final float a) { this.rgbaColor[0] = r; this.rgbaColor[1] = g; this.rgbaColor[2] = b; this.rgbaColor[3] = a; } /** * Set pressed color. ** Default pressed color-factor w/o color channel, modulated base-color. 0.75 * 1.2 = 0.9 *
*/ public final void setPressedColorMod(final float r, final float g, final float b, final float a) { this.pressedRGBAModulate[0] = r; this.pressedRGBAModulate[1] = g; this.pressedRGBAModulate[2] = b; this.pressedRGBAModulate[3] = a; } /** * Set toggle-on color. ** Default toggle-on color-factor w/o color channel, modulated base-color. 0.75 * 1.13 ~ 0.85 *
*/ public final void setToggleOnColorMod(final float r, final float g, final float b, final float a) { this.toggleOnRGBAModulate[0] = r; this.toggleOnRGBAModulate[1] = g; this.toggleOnRGBAModulate[2] = b; this.toggleOnRGBAModulate[3] = a; } /** * Set toggle-off color. ** Default toggle-off color-factor w/o color channel, modulated base-color. 0.75 * 0.86 ~ 0.65 *
*/ public final void setToggleOffColorMod(final float r, final float g, final float b, final float a) { this.toggleOffRGBAModulate[0] = r; this.toggleOffRGBAModulate[1] = g; this.toggleOffRGBAModulate[2] = b; this.toggleOffRGBAModulate[3] = a; } @Override public final String toString() { return getClass().getSimpleName()+"["+getSubString()+"]"; } public String getSubString() { return "enabled "+enabled+", toggle[able "+toggleable+", state "+toggle+"], pos["+position[0]+", "+position[1]+", "+position[2]+ "], scale["+scale[0]+", "+scale[1]+", "+scale[2]+ "], box "+box; } // // Input // public void setPressed(final boolean b) { this.down = b; markStateDirty(); } public boolean isPressed() { return this.down; } public void setToggleable(final boolean toggleable) { this.toggleable = toggleable; } /** * Returns true if this shape is toggable, * i.e. rendered w/ {@link #setToggleOnColorMod(float, float, float, float)} or {@link #setToggleOffColorMod(float, float, float, float)}. */ public boolean isToggleable() { return toggleable; } public void setToggle(final boolean v) { toggle = v; markStateDirty(); } public void toggle() { if( isToggleable() ) { toggle = !toggle; } markStateDirty(); } public boolean isToggleOn() { return toggle; } /** * Set whether this shape is draggable, * i.e. translated by 1-pointer-click and drag. ** Default draggable is true. *
*/ public void setDraggable(final boolean draggable) { this.draggable = draggable; } public boolean isDraggable() { return draggable; } /** * Set whether this shape is resizable, * i.e. zoomed by 1-pointer-click and drag in 1/4th bottom-left and bottom-right corner. ** Default resizable is true. *
*/ public void setResizable(final boolean resizable) { this.resizable = resizable; } public boolean isResizable() { return resizable; } public final void addMouseListener(final MouseGestureListener l) { if(l == null) { return; } @SuppressWarnings("unchecked") final ArrayList