From ce027cf5287d008f83c70303d38f125430195b16 Mon Sep 17 00:00:00 2001
From: Rami Santina <rami.santina@gmail.com>
Date: Tue, 9 Aug 2011 21:10:12 +0300
Subject: Graph UI: Added scene controller and general scenegraph behavior.

User can implement onClick, onPressed, onReleased for selected
UIShape called by the controller. Selection done using
color coding indexes.
Controller also provides a default generic InputEventListener and GlEventListener
---
 .../opengl/test/junit/graph/demos/ui/Label.java    |  14 +-
 .../opengl/test/junit/graph/demos/ui/RIButton.java | 139 +++++++++---
 .../junit/graph/demos/ui/SceneUIController.java    | 245 +++++++++++++++++++++
 .../test/junit/graph/demos/ui/UIGLListener01.java  |  13 +-
 .../opengl/test/junit/graph/demos/ui/UIShape.java  |  54 +++++
 5 files changed, 431 insertions(+), 34 deletions(-)
 create mode 100644 src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java

(limited to 'src/test')

diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
index 70dd489b0..83fc08b86 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/Label.java
@@ -27,13 +27,17 @@
  */
 package com.jogamp.opengl.test.junit.graph.demos.ui;
 
+import javax.media.opengl.GL2ES2;
+
 import jogamp.graph.curve.text.GlyphString;
 
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
 import com.jogamp.graph.font.Font;
 import com.jogamp.graph.geom.Vertex;
 import com.jogamp.graph.geom.Vertex.Factory;
 
-public class Label extends UIShape implements UITextShape {
+public abstract class Label extends UIShape implements UITextShape {
     protected Font font;
     protected int size;
     protected String text;
@@ -50,7 +54,7 @@ public class Label extends UIShape implements UITextShape {
         return glyphString;
     }
     
-    public String getText(){
+    public String getText() {
         return text;
     }
     
@@ -93,4 +97,10 @@ public class Label extends UIShape implements UITextShape {
         clearImpl();
         glyphString = GlyphString.createString(shape, getVertexFactory(), font, size, text);        
     }
+
+    @Override
+    public void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer,
+            boolean selection) {
+        
+    }
 }
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java
index e9296752e..60f79e2d7 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/RIButton.java
@@ -27,32 +27,42 @@
  */
 package com.jogamp.opengl.test.junit.graph.demos.ui;
 
+import javax.media.opengl.GL2ES2;
+
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
 import com.jogamp.graph.font.Font;
 import com.jogamp.graph.geom.AABBox;
 import com.jogamp.graph.geom.Vertex;
 import com.jogamp.graph.geom.Vertex.Factory;
+import com.jogamp.opengl.test.junit.graph.demos.ui.opengl.UIRegion;
 
 /** GPU based resolution independent Button impl
  */
-public class RIButton extends UIShape {
+public abstract class RIButton extends UIShape {
     private float width, height;
     private Label label;
-    private float spacing = 2.0f;
-    private float[] scale = new float[]{1.0f,1.0f};
+    private float spacing = 4.0f;
     private float corner = 1.0f;
     private float labelZOffset = -0.05f;
     
     private float[] buttonColor = {0.6f, 0.6f, 0.6f};
+    private float[] buttonSelectedColor = {0.8f,0.8f,0.8f};
     private float[] labelColor = {1.0f, 1.0f, 1.0f};
+    private float[] labelSelectedColor = {0.1f, 0.1f, 0.1f};
+ 
     
     public RIButton(Factory<? extends Vertex> factory, Font labelFont, String labelText, float width, float height) {
-        // w 4.0f, h 3.0f
-        // FontFactory.get(FontFactory.UBUNTU).getDefault()
         super(factory);
         
         // FIXME: Determine font size - PMV Matrix relation ?
         // this.label = new Label(factory, labelFont, (int)(height - 2f * spacing), labelText);
-        this.label = new Label(factory, labelFont, 10, labelText);
+        this.label = new Label(factory, labelFont, 10, labelText){
+            public void onClick() { }
+            public void onPressed() { }
+            public void onRelease() { }
+        };
+        
         this.width = width;
         this.height = height;
     }
@@ -60,7 +70,6 @@ public class RIButton extends UIShape {
     public final float getWidth() { return width; }
     public final float getHeight() { return height; }
     public float getCorner() { return corner; }
-    public float[] getScale() { return scale; }    
     public Label getLabel() { return label; }
 
     public void setDimension(int width, int height) {
@@ -68,7 +77,7 @@ public class RIButton extends UIShape {
         this.height = height;
         dirty |= DIRTY_SHAPE;
     }
-
+    
     @Override
     protected void clearImpl() {
         label.clear();
@@ -88,21 +97,26 @@ public class RIButton extends UIShape {
         } else {
             createCurvedOutline(lbox);
         }
-        scale[0] = getWidth() / ( 2f*spacing + lbox.getWidth() );
-        scale[1] = getHeight() / ( 2f*spacing + lbox.getHeight() );
+        float sx = getWidth() / ( 2f*spacing + lbox.getWidth() );
+        float sy = getHeight() / ( 2f*spacing + lbox.getHeight() );
+        
+        setScale(sx, sy, 1);
     }
     
     
     private void createSharpOutline(AABBox lbox) {
         float th = (2f*spacing) + lbox.getHeight();
         float tw = (2f*spacing) + lbox.getWidth();
+        
+        float[] pos = getPosition();
         float minX = lbox.getMinX()-spacing;
         float minY = lbox.getMinY()-spacing;
+        float minZ = labelZOffset;
         
-        shape.addVertex(minX, minY, labelZOffset,  true);
-        shape.addVertex(minX+tw, minY,  labelZOffset, true);
-        shape.addVertex(minX+tw, minY + th, labelZOffset,  true);
-        shape.addVertex(minX, minY + th, labelZOffset,  true);
+        shape.addVertex(minX, minY, minZ,  true);
+        shape.addVertex(minX+tw, minY,  minZ, true);
+        shape.addVertex(minX+tw, minY + th, minZ,  true);
+        shape.addVertex(minX, minY + th, minZ,  true);
         shape.closeLastOutline();
     }
     
@@ -113,25 +127,25 @@ public class RIButton extends UIShape {
         float cw = 0.5f*corner*Math.min(tw, th);
         float ch = 0.5f*corner*Math.min(tw, th);
         
+        float[] pos = getPosition();
         float minX = lbox.getMinX()-spacing;
         float minY = lbox.getMinY()-spacing;
-        
-        shape.addVertex(minX, minY + ch, labelZOffset, true);
-        shape.addVertex(minX, minY,  labelZOffset, false);
-        shape.addVertex(minX + cw, minY, labelZOffset,  true);
-        shape.addVertex(minX + tw - cw, minY,  labelZOffset, true);
-        shape.addVertex(minX + tw, minY, labelZOffset,  false);
-        shape.addVertex(minX + tw, minY + ch, labelZOffset,  true);
-        shape.addVertex(minX + tw, minY + th- ch, labelZOffset,  true);
-        shape.addVertex(minX + tw, minY + th, labelZOffset,  false);
-        shape.addVertex(minX + tw - cw, minY + th, labelZOffset,  true);
-        shape.addVertex(minX + cw, minY + th, labelZOffset,  true);
-        shape.addVertex(minX, minY + th, labelZOffset,  false);
-        shape.addVertex(minX, minY + th - ch, labelZOffset,  true);
+        float minZ = labelZOffset;
+        shape.addVertex(minX, minY + ch, minZ, true);
+        shape.addVertex(minX, minY,  minZ, false);
+        shape.addVertex(minX + cw, minY, minZ,  true);
+        shape.addVertex(minX + tw - cw, minY,  minZ, true);
+        shape.addVertex(minX + tw, minY, minZ,  false);
+        shape.addVertex(minX + tw, minY + ch, minZ,  true);
+        shape.addVertex(minX + tw, minY + th- ch, minZ,  true);
+        shape.addVertex(minX + tw, minY + th, minZ,  false);
+        shape.addVertex(minX + tw - cw, minY + th, minZ,  true);
+        shape.addVertex(minX + cw, minY + th, minZ,  true);
+        shape.addVertex(minX, minY + th, minZ,  false);
+        shape.addVertex(minX, minY + th - ch, minZ,  true);
         shape.closeLastOutline();
     }
-
-
+    
     public void setCorner(float corner) {
         if(corner > 1.0f){
             this.corner = 1.0f;
@@ -172,6 +186,7 @@ public class RIButton extends UIShape {
     }
 
     public void setButtonColor(float r, float g, float b) {
+        this.buttonColor = new float[3];
         this.buttonColor[0] = r;
         this.buttonColor[1] = g;
         this.buttonColor[2] = b;
@@ -180,16 +195,80 @@ public class RIButton extends UIShape {
     public float[] getLabelColor() {
         return labelColor;
     }
+    
+    private UIRegion buttonRegion = null;
+    private UIRegion labelRegion = null;
+    private boolean toggle =false;
+    private boolean toggleable = false;
+
 
+    public void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer, boolean selection) {
+        if(null == buttonRegion) {
+            buttonRegion = new UIRegion(this);
+            labelRegion = new UIRegion(getLabel());
+        }  
+        
+        gl.glEnable(GL2ES2.GL_POLYGON_OFFSET_FILL);
+        gl.glPolygonOffset(0.0f, 1f);
+        
+        float[] bColor = buttonColor;
+        if(isPressed() || toggle){
+            bColor = buttonSelectedColor;
+        }
+        if(!selection){
+            renderer.setColorStatic(gl, bColor[0], bColor[1], bColor[2]);
+        }
+        renderer.draw(gl, buttonRegion.getRegion(gl, rs, 0), getPosition(), 0);
+        gl.glDisable(GL2ES2.GL_POLYGON_OFFSET_FILL);
+        
+        float[] lColor = labelColor;
+        if(isPressed() || toggle ){
+            lColor = labelSelectedColor;
+        }
+        if(!selection){
+            renderer.setColorStatic(gl, lColor[0], lColor[1], lColor[2]);
+        }
+        renderer.draw(gl, labelRegion.getRegion(gl, rs, 0), getPosition(), 0);
+    }
+    public void setPressed(boolean b) {
+        super.setPressed(b);
+        if(isToggleable() && b) {
+            toggle = !toggle;
+        }
+    }
+    
     public void setLabelColor(float r, float g, float b) {
+        this.labelColor = new float[3];
         this.labelColor[0] = r;
         this.labelColor[1] = g;
         this.labelColor[2] = b;
     }
     
+    public void setButtonSelectedColor(float r, float g, float b){
+        this.buttonSelectedColor = new float[3];
+        this.buttonSelectedColor[0] = r;
+        this.buttonSelectedColor[1] = g;
+        this.buttonSelectedColor[2] = b;
+    }
+    
+    public void setLabelSelectedColor(float r, float g, float b){
+        this.labelSelectedColor = new float[3];
+        this.labelSelectedColor[0] = r;
+        this.labelSelectedColor[1] = g;
+        this.labelSelectedColor[2] = b;
+    }
+
+    public boolean isToggleable() {
+        return toggleable;
+    }
+    
+    public void setToggleable(boolean toggleable) {
+        this.toggleable = toggleable;
+    }
+
     public String toString() {
         return "RIButton [" + getWidth() + "x" + getHeight() + ", "
-            + getLabel() + "," + "spacing: " + spacing
+            + getLabel() + ", " + "spacing: " + spacing
             + ", " + "corner: " + corner + ", " + "shapeOffset: " + labelZOffset + " ]";
     }
 }
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java
new file mode 100644
index 000000000..d3b1de827
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/SceneUIController.java
@@ -0,0 +1,245 @@
+package com.jogamp.opengl.test.junit.graph.demos.ui;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLRunnable;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
+import com.jogamp.newt.event.MouseEvent;
+import com.jogamp.newt.event.MouseListener;
+import com.jogamp.newt.opengl.GLWindow;
+
+public class SceneUIController implements GLEventListener{
+    private ArrayList<UIShape> shapes = new ArrayList<UIShape>();
+
+    private int count = 0;
+    private RegionRenderer renderer = null;
+    private RenderState rs = null;
+
+    private float[] translate = new float[3];
+    private float[] scale = new float[3];
+    private float[] rotation = new float[3];
+
+    private float[] sceneClearColor = new float[]{0,0,0,1};
+    
+    private int activeId = -1;
+    
+    private SBCMouseListener sbcMouseListener = null;
+    
+    private GLAutoDrawable cDrawable = null;
+
+    public SceneUIController() {
+
+    }
+    
+    public void setRenderer(RegionRenderer renderer, RenderState rs) {
+        this.renderer = renderer;
+        this.rs = rs;
+    }
+    
+    public SceneUIController(RegionRenderer renderer, RenderState rs) {
+        this.renderer = renderer;
+        this.rs = rs;
+    }
+    
+    public void attachInputListenerTo(GLWindow window) {
+        sbcMouseListener = new SBCMouseListener();;
+        window.addMouseListener(sbcMouseListener);
+    }
+
+    public ArrayList<UIShape> getShapes() {
+        return shapes;
+    }
+    public void addShape(UIShape b) {
+        shapes.add(b);
+        count++;
+    }
+
+    public void removeShape(UIShape b) {
+        boolean found = shapes.remove(b);
+        if(found) {
+            count--;
+        }
+    }
+    
+    public void init(GLAutoDrawable drawable) {
+        cDrawable = drawable;
+    }
+    public void display(GLAutoDrawable drawable) {
+        final int width = drawable.getWidth();
+        final int height = drawable.getHeight();
+        GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+        render(gl, width, height,false);
+    }
+   
+    public void dispose(GLAutoDrawable drawable) {
+        
+    }
+
+    public void reshape(GLAutoDrawable drawable, int x, int y, int width,
+            int height) {
+        GL2ES2 gl = drawable.getGL().getGL2ES2();
+        renderer.reshapePerspective(gl, 45.0f, width, height, 5f, 70.0f);        
+    }
+
+    public UIShape getShape(GLAutoDrawable drawable,int x, int y) {
+        final int width = drawable.getWidth();
+        final int height = drawable.getHeight();
+        GL2ES2 gl = drawable.getGL().getGL2ES2();
+
+        int index = checkSelection(gl, x, y, width, height);
+        if(index == -1)
+            return null;
+        return shapes.get(index);
+    }
+    
+    public UIShape getActiveUI() {
+        if(activeId == -1)
+            return null;
+        return shapes.get(activeId);
+    }
+    
+    public void release() {
+        activeId = -1;
+    }
+
+    private int checkSelection(GL2ES2 gl,int x, int y, int width, int height) {
+        gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, 4);
+        gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, 4);
+        gl.glClearColor(sceneClearColor[0], sceneClearColor[1], sceneClearColor[2], sceneClearColor[3]);
+        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+        render(gl, width, height,true);
+        ByteBuffer pixel = Buffers.newDirectByteBuffer(4);
+        pixel.order(ByteOrder.nativeOrder());
+        IntBuffer viewport = IntBuffer.allocate(4);
+        gl.glGetIntegerv(GL2ES2.GL_VIEWPORT, viewport);
+        gl.glReadPixels(x, viewport.get(3) - y, 1, 1, GL2ES2.GL_RGBA,
+                GL2ES2.GL_UNSIGNED_BYTE, pixel);
+
+        int qp = pixel.get(0) & 0xFF;
+        int index = Math.round(((qp/255.0f)*(count+2))-1);
+        if(index < 0 || index >= count)
+            return -1;
+        return index;
+    }
+
+    private void render(GL2ES2 gl, int width, int height, boolean select) {
+        renderer.reshapePerspective(null, 45.0f, width, height, 0.1f, 7000.0f);
+        
+        for(int index=0; index < count;index++){
+            if(select) {
+                float color= index+1;
+                renderer.setColorStatic(gl, color/(count+2), color/(count+2), color/(count+2));
+            }
+            float[] s = shapes.get(index).getScale();
+            float[] p = shapes.get(index).getPosition();
+            renderer.resetModelview(null);
+            renderer.translate(null, translate[0]+p[0], translate[1]+p[1], translate[2]+p[2]);
+            renderer.scale(gl, s[0], s[1], s[2]);
+            renderer.rotate(gl, rotation[0], 1, 0, 0);
+            renderer.rotate(gl, rotation[1], 0, 1, 0);
+            renderer.rotate(gl, rotation[2], 0, 0, 1);
+            
+            shapes.get(index).render(gl, rs, renderer,select);
+            renderer.rotate(gl, -rotation[0], 1, 0, 0);
+            renderer.rotate(gl, -rotation[1], 0, 1, 0);
+            renderer.rotate(gl, -rotation[2], 0, 0, 1);
+        }
+    }
+
+    public void setTranslate(float x, float y, float z) {
+        this.translate[0] = x;
+        this.translate[1] = y;
+        this.translate[2] = z;
+    }
+
+    public void setScale(float x, float y, float z) {
+        this.scale[0] = x;
+        this.scale[1] = y;
+        this.scale[2] = z;
+    }
+
+    public void setRotation(float x, float y, float z) {
+        this.rotation[0] = x;
+        this.rotation[1] = y;
+        this.rotation[2] = z;
+    }
+    public float[] getSceneClearColor() {
+        return sceneClearColor;
+    }
+
+    public void setSceneClearColor(float r, float g, float b, float a) {
+        this.sceneClearColor[0] = r;
+        this.sceneClearColor[1] = g;
+        this.sceneClearColor[2] = b;
+        this.sceneClearColor[3] = a;
+    }
+    
+    private class SBCMouseListener implements MouseListener {
+        int lx = 0;
+        int ly = 0;
+        boolean selection = false;
+        int mouseX = -1;
+        int mouseY = -1;
+        
+        public void mouseClicked(MouseEvent e) {
+            UIShape uiShape = getActiveUI();
+            if(uiShape != null){
+                uiShape.onClick();
+            }
+        }
+
+        public void mousePressed(MouseEvent e) {
+            selection = true;
+            mouseX = e.getX();
+            mouseY = e.getY();
+            
+            GLRunnable runnable = new GLRunnable() {
+                public boolean run(GLAutoDrawable drawable) {
+                    UIShape s = getShape(drawable, mouseX, mouseY);
+                    if(null != s) {
+                        activeId = getShapes().indexOf(s);
+                    }
+                    else {
+                        activeId = -1;
+                    }
+                    return false;
+                }
+            };
+            cDrawable.invoke(true, runnable);
+            
+            UIShape uiShape = getActiveUI();
+            
+            if(uiShape != null) {
+                uiShape.setPressed(true);
+                uiShape.onPressed();
+            }
+        }
+
+        public void mouseReleased(MouseEvent e) { 
+            UIShape uiShape = getActiveUI();
+            if(uiShape != null){
+                uiShape.setPressed(false);
+                uiShape.onRelease();
+            }
+        }
+
+        public void mouseMoved(MouseEvent e) { }
+        public void mouseEntered(MouseEvent e) { }
+        public void mouseExited(MouseEvent e) { }
+        public void mouseDragged(MouseEvent e) { }
+        public void mouseWheelMoved(MouseEvent e) { }
+        
+    }
+}
\ No newline at end of file
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java
index 2ae7d1f30..6768eccca 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIGLListener01.java
@@ -51,7 +51,16 @@ public class UIGLListener01 extends UIListenerBase01 {
         super(RegionRenderer.create(rs, 0), debug, trace);
         setMatrix(-20, 00, 0f, -50);
         final Font font = FontFactory.get(FontFactory.UBUNTU).getDefault();
-        button = new RIButton(SVertex.factory(), font, "Click me!", 4f, 3f);
+        button = new RIButton(SVertex.factory(), font, "Click me!", 4f, 3f){
+            public void onClick() {
+            }
+            public void onPressed() {
+            }
+            public void onRelease() {
+            }
+            
+        };
+        button.setPosition(2,1,0);
         /** Button defaults !
             button.setLabelColor(1.0f,1.0f,1.0f);
             button.setButtonColor(0.6f,0.6f,0.6f);
@@ -99,7 +108,7 @@ public class UIGLListener01 extends UIListenerBase01 {
         
         regionRenderer.setColorStatic(gl, bColor[0], bColor[1], bColor[2]);
         regionRenderer.draw(gl, regionButton.getRegion(gl, rs, 0), getPosition(), 0);
-         
+//        regionRenderer.translate(gl, button.getPosition()[0], button.getPosition()[1], button.getPosition()[2]);
         regionRenderer.setColorStatic(gl, lColor[0], lColor[1], lColor[2]);
         regionRenderer.draw(gl, regionLabel.getRegion(gl, rs, 0), getPosition(), 0);
     }        
diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
index 7e3d26775..ebed0f7aa 100644
--- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
+++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/UIShape.java
@@ -27,7 +27,11 @@
  */
 package com.jogamp.opengl.test.junit.graph.demos.ui;
 
+import javax.media.opengl.GL2ES2;
+
 import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.graph.curve.opengl.RegionRenderer;
+import com.jogamp.graph.curve.opengl.RenderState;
 import com.jogamp.graph.geom.AABBox;
 import com.jogamp.graph.geom.Vertex;
 import com.jogamp.graph.geom.Vertex.Factory;
@@ -38,6 +42,8 @@ public abstract class UIShape {
     
     protected static final int DIRTY_SHAPE  = 1 << 0 ;    
     protected int dirty = DIRTY_SHAPE;
+    
+    private boolean down = false;
 
     public UIShape(Factory<? extends Vertex> factory) {
         this.vertexFactory = factory;
@@ -48,6 +54,39 @@ public abstract class UIShape {
         clearImpl();
         shape.clear();
     }
+    
+    public abstract void render(GL2ES2 gl, RenderState rs, RegionRenderer renderer, boolean selection);
+    
+    protected boolean positionDirty = false;
+    
+    private float[] position = new float[]{0,0,0};
+    private float[] scale = new float[]{1.0f,1.0f,1.0f};
+    public void setScale(float x, float y, float z){
+        scale[0] = x;
+        scale[1] = y;
+        scale[2] = z;
+    }
+    
+    public void setPosition(float x, float y, float z) {
+        this.position[0] = x;
+        this.position[1] = y;
+        this.position[2] = z;
+        positionDirty = true;
+    }
+    
+    private void updatePosition () {
+        float minX = shape.getBounds().getLow()[0];
+        float minY = shape.getBounds().getLow()[1];
+        float minZ = shape.getBounds().getLow()[2];
+        System.out.println("Position was: " + (position[0]) + " " + (position[1]) + " " + (position[2]));
+        System.out.println("Position became: " + (position[0] - minX) + " " + (position[1] - minY) + " " + (position[2] - minZ));
+        setPosition(position[0] - minX, position[1] - minY, position[2] - minZ);
+        positionDirty = false;
+    }
+    
+    public float[] getScale() { return scale; }   
+    public float[] getPosition() { return position; }
+    
     protected abstract void clearImpl();
     
     protected abstract void createShape();
@@ -56,6 +95,9 @@ public abstract class UIShape {
         if( isShapeDirty() ) {
             shape.clear();
             createShape();
+            if(positionDirty){
+                updatePosition();
+            }
             dirty &= ~DIRTY_SHAPE;
             return true;
         }
@@ -73,4 +115,16 @@ public abstract class UIShape {
     public boolean isShapeDirty() {
         return 0 != ( dirty & DIRTY_SHAPE ) ;
     }    
+    
+    public void setPressed(boolean b) {
+        this.down  = b;
+    }
+    
+    public boolean isPressed() {
+        return this.down;
+    }
+    
+    public abstract void onClick();
+    public abstract void onPressed();
+    public abstract void onRelease();
 }
-- 
cgit v1.2.3