Package org.mt4j.components

Source Code of org.mt4j.components.MTCanvas$Position

/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.components;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;

import javax.swing.Timer;

import org.mt4j.components.clusters.Cluster;
import org.mt4j.components.clusters.ClusterManager;
import org.mt4j.components.interfaces.IMTComponent3D;
import org.mt4j.input.IHitTestInfoProvider;
import org.mt4j.input.inputData.MTInputEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor;
import org.mt4j.util.camera.Icamera;
import org.mt4j.util.math.Matrix;

import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PGraphics3D;

/**
* MTCanvas is the root node of the component hierarchy of a MT4j scene.
* To make a mt4j component visible and interactable you have to add it to
* the scene's canvas or to a component which is already attached to the canvas.
* The canvas then recursivly updates and draws all attached components each frame.
*
* @author Christopher Ruff
*/
public class MTCanvas extends MTComponent implements IHitTestInfoProvider{
 
  /** The cluster manager. */
  private ClusterManager clusterManager;
 
  /** The last time hit test. */
  private long lastTimeHitTest;
 
  /** The cache time delta. */
  private long cacheTimeDelta;
 
  /** The cache clear time. */
  private int cacheClearTime;
 
  /** The position to component. */
  private HashMap<Position, IMTComponent3D> positionToComponent;
 
  /** The timer. */
  private Timer timer;
 
  /** The use hit test cache. */
  private boolean useHitTestCache;
 
  /** The frustum culling switch. */
  private boolean frustumCulling;
 
  private int culledObjects = 0;

  private long lastUpdateTime;
 
 

 
 
  /**
   * The Constructor.
   *
   * @param pApplet the applet
   * @param attachedCamera the attached camera
   */
  public MTCanvas(PApplet pApplet, Icamera attachedCamera) {
    this(pApplet, "unnamed MT Canvas", attachedCamera);
  }

  /**
   * The Constructor.
   *
   * @param pApplet the applet
   * @param name the name
   * @param attachedCamera the attached camera
   */
  public MTCanvas(PApplet pApplet, String name, Icamera attachedCamera) {
    super(pApplet, name, attachedCamera);
    //Cache settings
    lastTimeHitTest = 0;
    cacheTimeDelta = 100;
    cacheClearTime = 20000;
    useHitTestCache = true;
   
    lastUpdateTime = 0;
   
    clusterManager = new ClusterManager(pApplet, this);
   
    positionToComponent = new HashMap<Position, IMTComponent3D>();
   
    //Schedule a Timer task to clear the object cache so it wont
    //get filled infinitely
    timer = new Timer(cacheClearTime, new ActionListener(){
      public void actionPerformed(ActionEvent arg0) {
//        System.out.println("Hit test chache entries: " + positionToComponent.size() + " ... cleared!");
        positionToComponent.clear();
      }
    });
//    timer.start(); //FIXME TEST
    this.useHitTestCache = false; //FIXME TEST DISABLE HIT CACHE
   
//    this.setCollidable(false);
   
    this.setGestureAllowance(RotateProcessor.class, false);
    this.setGestureAllowance(ScaleProcessor.class, false);
    this.setGestureAllowance(TapProcessor.class, false);
    this.setGestureAllowance(DragProcessor.class, false);
   
    this.setPickable(false);
   
    //Frustum culling default
    frustumCulling = false;
  }
 
  @Override
  protected void destroyComponent() {
    super.destroyComponent();
   
    if (this.timer != null && timer.isRunning()){
      timer.stop();
    }
   
    if (positionToComponent != null){
      positionToComponent.clear();
    }
  }
 

  /**
   * Method for asking the canvas whether and which object is at the specified
   * screen position.
   * <p>
   * IMPORTANT: this method returns the MTCanvas instance if no other object is hit.
   * This means that the MTCanvas instance acts like the background => Gestures that
   * are supposed to be performed on the background have to check if they hit the canvas.
   * And the gestureevents should then have the canvas as their targetComponent!
   * Also, you have to be careful in other gestures, as even when you dont hit an object, you will
   * get the mtcanvas returned as the hit component - not null!
   * <p>Note: if the hit component is part of a cluster, the cluster is returned!
   *
   * @param x the screen x coordinate
   * @param y the screen y coordinate
   *
   * @return the object at that position or this MTCanvas instance if no component was hit
   */
  public IMTComponent3D getComponentAt(float x, float y) {
    IMTComponent3D closest3DComp = null;
    try{
      long now = System.currentTimeMillis();
      if (useHitTestCache){
        if (now - lastTimeHitTest > cacheTimeDelta){ //If the time since last check surpassed => do new hit-test!
          //Benchmark the picking
//          long a = System.nanoTime();
          closest3DComp = this.pick(x, y).getNearestPickResult();
          //Benchmark the picking
//          long b = System.nanoTime();
//          System.out.println("Time for picking the scene: " + (b-a));
          /*
          for (MTBaseComponent c : pickResult.getPickList())
            System.out.println(c.getName());
          if (closest3DComp != null)
            System.out.println("Using: " + closest3DComp.getName());
          */
          if (closest3DComp == null){
            closest3DComp = this;
          }
          positionToComponent.put(new Position(x,y), closest3DComp);
        }else{
          //Check whats in the cache
          IMTComponent3D cachedComp = positionToComponent.get(new Position(x,y));
          if (cachedComp != null){ //Use cached obj
            closest3DComp = cachedComp;
            positionToComponent.put(new Position(x,y), closest3DComp);
          }else{
            closest3DComp = this.pick(x, y).getNearestPickResult();
            if (closest3DComp == null){
              closest3DComp = this;
            }
            positionToComponent.put(new Position(x,y), closest3DComp);
          }
        }
      }else{//IF no hittest cache is being used
        closest3DComp = this.pick(x, y).getNearestPickResult();
        if (closest3DComp == null){
          closest3DComp = this;
        }
      }
      lastTimeHitTest = now;
     
  //    /*//TODO anders machen..z.b. geclusterte comps einfach als kinder von
      //�bergeordnetem clusterpoly machen? aber mit clusterPoly.setComposite(TRUE);
      //Clusterpoly pickable machen damit das hier nicht gebraucht wird?
      Cluster sel = this.getClusterManager().getCluster(closest3DComp);
        if (sel != null){
          closest3DComp = sel;
        }
  //     */
       
//        //FIXME TEST for stencil clipped scene windows -> we have to return the scenes canvas for some gestures!
//        if (closest3DComp != null && closest3DComp instanceof mtClipSceneWindow)
     
    }catch(Exception e){
      System.err.println("Error while trying to pick an object: ");
      e.printStackTrace();
    }
    /*
    if (closest3DComp != null)
      System.out.println("Picked: '" + closest3DComp.getName() + "' at pos (" + x + "," + y + ")");
    else
      System.out.println("Picked: '" + closest3DComp + "' at pos (" + x + "," + y + ")");
    */
    return closest3DComp;
  }
 
 
  /* (non-Javadoc)
   * @see com.jMT.input.IHitTestInfoProvider#isBackGroundAt(float, float)
   */
  public boolean isBackGroundAt(float x, float y) {
    return this.getComponentAt(x, y).equals(this);
  }
 
 
  //FIXME TEST 
  @Override
  public void updateComponent(long timeDelta) {
    super.updateComponent(timeDelta);
    this.lastUpdateTime = timeDelta;
  }
  //FIXME TEST
  private boolean calledFromDrawComponent = false;
  //FIXME TEST
//  /*
//   * Actually this canvases drawComponent method should be called
//   * ever because the canvas should not be added as a child to any component.
//   * Anyway - this code will still make it possible to use it as a child of other components
//   * (non-Javadoc)
//   * @see org.mt4j.components.MTComponent#drawComponent(processing.core.PGraphics)
//   */
//  @Override
//  public void drawComponent(PGraphics g) { //FIXME this would draw the canvas 2 times..
//    super.drawComponent(g);
//   
//    //Call the canvases scenes draw method to also draw
//    //stuff defined in an overrriden scenes draw method
//    if (this.getRenderer() instanceof MTApplication){
//      MTApplication app = (MTApplication)this.getRenderer();
//      Iscene[] scenes = app.getScenes();
//      for (int i = 0; i < scenes.length; i++) {
//        Iscene iscene = scenes[i];
//        if (iscene instanceof AbstractScene){
//          AbstractScene as = (AbstractScene)iscene;
//          if (as.getCanvas().equals(this)){
//            this.calledFromDrawComponent = true;
////            this.drawAndUpdateCanvas(g, this.lastUpdateTime);
//            as.drawAndUpdate(g, this.lastUpdateTime);
//            this.calledFromDrawComponent = false;
//          }
//        }
//      }
//    }
//   
////    this.calledFromDrawComponent = true;
////    this.drawAndUpdateCanvas(g, this.lastUpdateTime);
////    this.calledFromDrawComponent = false;
//  }
 
 
  /**
   * Updates and then draws every visible object in the canvas.
   * First calls the <code>updateComponent(long timeDelta)</code> method. Then
   * the <code>drawComponent()</code> method of each object in the scene graph.
   * Also handles the setting of cameras attached to the objects.
   * @param graphics
   *
   * @param updateTime the time passed since the last update (in ms)
   */
  public void drawAndUpdateCanvas(PGraphics graphics, long updateTime){
    this.culledObjects = 0;
   
    //FIXME THIS IS A HACK! WE SHOULD REPLACE CLUSTERS WITH NORMAL COMPONENTS INSTEAD!
    //Update cluster components
    Cluster[] clusters = getClusterManager().getClusters();
    for (int i = 0; i < clusters.length; i++) {
      Cluster cluster = clusters[i];
      cluster.updateComponent(updateTime);
    }
   
    this.drawUpdateRecursive(this, updateTime, graphics);
//    System.out.println("Culled objects: " + culledObjects);
  }

 
  /**
   * Draw the whole canvas update recursive.
   *
   * @param currentcomp the currentcomp
   * @param updateTime the update time
   * @param graphics the renderer
   */
  private void drawUpdateRecursive(MTComponent currentcomp, long updateTime, PGraphics graphics){
    if (currentcomp.isVisible()){
      //Update current component
      currentcomp.updateComponent(updateTime);
     
      if (currentcomp.getAttachedCamera() != null){
        //Saves transformations up to this object
        graphics.pushMatrix();
       
        //Resets the modelview completely with a new camera matrix
        currentcomp.getAttachedCamera().update();
       
        if (currentcomp.getParent() != null){
          //Applies all transforms up to this components parent
          //because the new camera wiped out all previous transforms
          Matrix m = currentcomp.getParent().getGlobalMatrix();
          PGraphics3D pgraphics3D = (PGraphics3D)graphics;
          pgraphics3D.modelview.apply(
              m.m00, m.m01, m.m02,  m.m03,
              m.m10, m.m11, m.m12,  m.m13,
              m.m20, m.m21, m.m22,  m.m23,
              m.m30, m.m31, m.m32,  m.m33
          );
        }
       
        //Apply local transform etc
        currentcomp.preDraw(graphics);
       
        //Check visibility with camera frustum
        if (frustumCulling){
          if (currentcomp.isContainedIn(currentcomp.getViewingCamera().getFrustum())){
            if (!this.calledFromDrawComponent){ //FIXME TEST
            // DRAW THE COMPONENT  \\
            currentcomp.drawComponent(graphics);
            }
          }else{
            culledObjects++;
            //System.out.println("Not visible: " + currentcomp.getName());
          }
        }else{
          if (!this.calledFromDrawComponent){ //FIXME TEST
          // DRAW THE COMPONENT  \\
          currentcomp.drawComponent(graphics);
          }
        }
       
        currentcomp.postDraw(graphics);

        //Draw Children
        for (MTComponent child : currentcomp.getChildList())
          drawUpdateRecursive(child, updateTime, graphics);

        currentcomp.postDrawChildren(graphics);
       
        //Restores the transforms of the previous camera etc
        graphics.popMatrix();
      }else{//If no custom camera was set
        //TODO in abstactvisiblecomp wird outine �ber gradients und clips
        //gezeichnet obwohl hier invisble war! FIXME!
        //evtl applymatrix unapply in eigene methode? dann nur das ausf�hren, kein pre/post draw!
       
        //TODO vater an kinder listener -> resize - new geometry -> resize own
       
        currentcomp.preDraw(graphics);
       
        if (frustumCulling){
          //Check visibility with camera frustum
          if (currentcomp.isContainedIn(currentcomp.getViewingCamera().getFrustum())){
            // DRAW THE COMPONENT  \\
            currentcomp.drawComponent(graphics);
          }else{
            culledObjects++;
            //System.out.println("Not visible: " + currentcomp.getName());
          }
        }else{
          // DRAW THE COMPONENT  \\
          currentcomp.drawComponent(graphics);
        }
       
        currentcomp.postDraw(graphics);
         
        for (MTComponent child : currentcomp.getChildList())
          drawUpdateRecursive(child, updateTime, graphics);
       
        currentcomp.postDrawChildren(graphics);
      }
    }//if visible end
  }

 
  /* (non-Javadoc)
   * @see org.mt4j.components.MTComponent#processInputEvent(org.mt4j.input.inputData.MTInputEvent)
   */
  @Override
  public boolean processInputEvent(MTInputEvent inEvt) {
    //TODO not very elegant - better approach??
    if (inEvt.hasTarget()){
      if (!inEvt.getTargetComponent().equals(this)){ //Avoid recursion
        //Send directed event to the target component
        return inEvt.getTargetComponent().processInputEvent(inEvt);
      }
    }
   
//    return true;  //this.handleEvent
    //handle in superclass
   
    //The MTCanvas get events targeted at him AND events that have no target!
    return super.processInputEvent(inEvt);
  }
 
   
 
  /**
   * Gets the cluster manager.
   *
   * @return the cluster manager
   */
  public ClusterManager getClusterManager() {
    return clusterManager;
  }


  /**
   * Sets the cluster manager.
   *
   * @param selectionManager the new cluster manager
   */
  public void setClusterManager(ClusterManager selectionManager) {
    this.clusterManager = selectionManager;
  }


  /**
   * Gets the cache time delta.
   *
   * @return the cache time delta
   */
  public long getCacheTimeDelta() {
    return cacheTimeDelta;
  }

  /**
   * If repeated calls to getObjectAt(float x, float y) in MTCanvas class
   * are called during the provided cacheTimeDelta, the Canvas looks into his
   * cache instead of querying all objects again
   * Default value is: 80.
   *
   * @param cacheTimeDelta the cache time delta
   */
  public void setCacheTimeDelta(long cacheTimeDelta) {
    this.cacheTimeDelta = cacheTimeDelta;
  }

  /**
   * Checks if is use hit test cache.
   *
   * @return true, if is use hit test cache
   */
  public boolean isUseHitTestCache() {
    return useHitTestCache;
  }

 
  /**
   * The canvas can be set to look into a hit test cache if
   * repeated calls to getComponentAt() with the same coordinates
   * during a short period of time are made.
   * This period of time can be set with
   * <code>setCacheTimeDelta(long cacheTimeDelta)</code>
   * <p>
   * This is useful for example when a click is made many gestureanalyzers
   * call getObjectAt() almost concurrently.
   *
   * @param useHitTestCache the use hit test cache
   */
  public void setUseHitTestCache(boolean useHitTestCache) {
    if (useHitTestCache && !timer.isRunning())
      timer.start();
    else if (!useHitTestCache && timer.isRunning())
      timer.stop();
   
    this.useHitTestCache = useHitTestCache;
  }


  /**
   * Gets the cache clear time.
   *
   * @return the cache clear time
   */
  public int getCacheClearTime() {
    return cacheClearTime;
  }

  /**
   * Sets the time intervals in ms in which the canvas clears its hit test cache
   * Default value is: 20000 ms
   * <p>
   * This is important to prevent the hit test cache from growing indefinitely.
   *
   * @param cacheClearTime the cache clear time
   */
  public void setCacheClearTime(int cacheClearTime) {
    timer.setDelay(cacheClearTime);
    this.cacheClearTime = cacheClearTime;
  }


  public boolean isFrustumCulling() {
    return frustumCulling;
  }

  public void setFrustumCulling(boolean frustumCulling) {
    this.frustumCulling = frustumCulling;
  }
 
 
 

 
 
  /**
   * Class used for the pickobject cache.
   */
  private class Position{
   
    /** The y. */
    float x,y;
   
    /**
     * Instantiates a new position.
     *
     * @param x the x
     * @param y the y
     */
    public Position(float x, float y){
      this.x = x;
      this.y = y;
    }
   
    /**
     * Gets the x.
     *
     * @return the x
     */
    public float getX() {return x;}
   
    /**
     * Sets the x.
     *
     * @param x the new x
     */
    public void setX(float x) {this.x = x;}
   
    /**
     * Gets the y.
     *
     * @return the y
     */
    public float getY() {return y;}
   
    /**
     * Sets the y.
     *
     * @param y the new y
     */
    public void setY(float y) {this.y = y;}
   
    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object arg0) {
      return (arg0 instanceof Position && ((Position)arg0).getX() == this.getX() && ((Position)arg0).getY() == this.getY());
    }
   
    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
      return ((int)x+(int)y);
    }
  }
}
TOP

Related Classes of org.mt4j.components.MTCanvas$Position

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.