Package org.mt4j.components.visibleComponents.widgets

Source Code of org.mt4j.components.visibleComponents.widgets.MTSlider

/***********************************************************************
* 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.visibleComponents.widgets;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import org.mt4j.components.TransformSpace;
import org.mt4j.components.visibleComponents.StyleInfo;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.components.visibleComponents.shapes.MTEllipse;
import org.mt4j.components.visibleComponents.shapes.MTRectangle;
import org.mt4j.components.visibleComponents.shapes.MTRoundRectangle;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.MTGestureEvent;
import org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapEvent;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor;
import org.mt4j.util.MTColor;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.Vector3D;

import processing.core.PApplet;
import processing.core.PImage;

/**
* A slider widget. Allows to select a value between the specified minnimum and maximum values.
* To recieve notice of a value change we can add a propertyChangeListener object to it.
* <br>NOTE: The silder has to be created horizontally but can afterwards be rotated freely.
* To get a vertical slider, just rotate the slider locally around 90 degrees.
*
* @author Christopher Ruff
*/
public class MTSlider extends MTRectangle {
 
  /** The horizontal. */
  private boolean horizontal;
 
  /** The outer shape. */
  private AbstractShape outerShape;
 
  /** The slider. */
  private AbstractShape knob;

  /** The max value. */
  private float maxValue;
 
  /** The min value. */
  private float minValue;
 
  /** The value range. */
  private float valueRange;

  /** The x. */
  private float x;
 
  /** The y. */
  private float y;
 
  /** The inner padding. */
  private float innerPadding;
 
  /** The property change support. */
  private PropertyChangeSupport propertyChangeSupport;
 
  private PApplet app;
 
  //TODO display a MTLine for the knob to slide on?
 

  /**
   * Instantiates a new mT slider.
   *
   * @param _x the _x
   * @param _y the _y
   * @param width the width
   * @param height the height
   * @param minValue the min value
   * @param maxValue the max value
   * @param applet the applet
   */
  public MTSlider(float _x, float _y, float width, float height, float minValue, float maxValue, PApplet applet) {
    super(_x, _y, width, height, applet);
    this.app = applet;
   
    this.propertyChangeSupport = new PropertyChangeSupport(this);
   
    if (minValue> maxValue){
      System.err.println("Minimum value is bigger than the maximum value in " + this);
    }
   
    this.x = _x;
    this.y = _y;
    this.minValue = minValue;
    this.maxValue = maxValue;
    this.valueRange = maxValue-minValue;
   
    this.setNoFill(true);
    this.setNoStroke(true);
    this.setPickable(false);
   
    float knobWidth;
    float knobHeight;
   
    horizontal = true; //FIXME only horizontal is implemented at the moment
    if (horizontal){
      innerPadding = 2;
      knobHeight = height - 2*innerPadding;
      //Check if we can make the knob as broad as the slider height so we get a circle knob
      //But if the height is too big the knob width would exceed the sliders width
      if (height >= width){ //We check if slider height > slider width => not good!
        knobWidth = (width - 2*innerPadding)*0.4f;
//        if (knobWidth >= 2*innerPadding) //FIXME REMOVE?? why is that here..
//          knobWidth = 1.5f;
//        while (knobWidth >= 2*innerPadding){
//          knobWidth *= 0.9f;
//        }
      }else{
        knobWidth = knobHeight;
      }
    }else{
      innerPadding = 5;
      knobWidth = width - 2*innerPadding;
      knobHeight = knobWidth;
    }
   
//    outerShape = new MTRectangle(x,y, width, height, applet);
    outerShape = new MTRoundRectangle(x,y,0, width, height, knobWidth/2f + innerPadding, knobHeight/2f  + innerPadding, applet);
    outerShape.unregisterAllInputProcessors();
    //When we click on the outershape move the knob in that direction a certain step
    outerShape.registerInputProcessor(new TapProcessor(applet, 35));
    outerShape.addGestureListener(TapProcessor.class, new IGestureEventListener() {
      public boolean processGestureEvent(MTGestureEvent ge) {
        TapEvent te = (TapEvent)ge;
        switch (te.getTapID()) {
        case TapEvent.BUTTON_CLICKED:
          Vector3D screenPos = te.getLocationOnScreen();
          Vector3D intersection = outerShape.getIntersectionGlobal(Tools3D.getCameraPickRay(app, outerShape, screenPos.x, screenPos.y));
          if (intersection != null){
            //Get the intersection point into knob local relative space
            Vector3D localClickPos = knob.globalToLocal(intersection);
            Vector3D knobCenterLocal = knob.getCenterPointLocal();
            float range = getValueRange();
            float step = range/5f; //Arbitrary step value
            float oldValue = getValue();
            if (localClickPos.x < knobCenterLocal.x){
              setValue(oldValue - step); //Move knob Left
            }else if (localClickPos.x > knobCenterLocal.x){
              setValue(oldValue + step)//Move knob Right
            }
          }
          break;
        }
        return false;
      }
    });
   
//    knob = new MTRectangle(x+innerOffset, y+innerOffset,   knobWidth, knobHeight, applet);
    knob = new MTEllipse(applet,  new Vector3D(0,0,0), knobWidth*0.5f, knobHeight*0.5f);
    knob.setFillColor(new MTColor(140, 140, 140, 255));
    AbstractComponentProcessor[] inputPs = knob.getInputProcessors();
    for (int i = 0; i < inputPs.length; i++) {
      AbstractComponentProcessor p = inputPs[i];
      if (!(p instanceof DragProcessor)){
        knob.unregisterInputProcessor(p);
      }
    }
    knob.removeAllGestureEventListeners(DragProcessor.class);
   
    outerShape.addChild(knob);
    this.addChild(outerShape);
   
    //TODO these have to be updated if knob or outershape are changed
//    final float knobWidthRelParent = knob.getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
//    final float knobHeightRelParent = knob.getHeightXY(TransformSpace.RELATIVE_TO_PARENT);
//    final float outerWidthLocal = outerShape.getWidthXY(TransformSpace.LOCAL);
   
    knob.addGestureListener(DragProcessor.class, new IGestureEventListener() {
      //@Override
      public boolean processGestureEvent(MTGestureEvent ge) {
        DragEvent de = (DragEvent)ge;
        Vector3D dir = new Vector3D(de.getTranslationVect());
        //Transform the global direction vector into knob local coordiante space
        dir.transformDirectionVector(knob.getGlobalInverseMatrix());
       
        float oldValue = getValue();
       
        if (horizontal){
          knob.translate(new Vector3D(dir.x,0,0), TransformSpace.LOCAL);
          float knobWidthRelParent = knob.getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
          float knobHeightRelParent = knob.getHeightXY(TransformSpace.RELATIVE_TO_PARENT);
          float outerWidthLocal = outerShape.getWidthXY(TransformSpace.LOCAL);
          
          Vector3D knobCenterRelToParent = knob.getCenterPointRelativeToParent();
          //Cap the movement at both ends of the slider
          if( (knobCenterRelToParent.x + knobWidthRelParent*0.5f) > (x + outerWidthLocal - innerPadding) ){
            //FIXME we could insetead just set the new value with setValue()
//            System.out.println("OUT OF BOUNDS RIGHT ->");
            Vector3D pos = new Vector3D( x + outerWidthLocal - innerPadding -knobWidthRelParent*0.5f, y + knobHeightRelParent*0.5f + innerPadding, 0);
//            pos.transform(outerShape.getGlobalMatrix());
//            knob.setPositionGlobal(pos);
            pos.transform(outerShape.getLocalMatrix());
            knob.setPositionRelativeToParent(pos);
          }else if( (knobCenterRelToParent.x - knobWidthRelParent*0.5f ) < (x + innerPadding) ){
//            System.out.println("OUT OF BOUNDS LEFT <-");
            Vector3D pos = new Vector3D( x + knobWidthRelParent*0.5f  + innerPadding, y + knobHeightRelParent*0.5f + innerPadding, 0);
//            pos.transform(outerShape.getGlobalMatrix());
//            knob.setPositionGlobal(pos);
            pos.transform(outerShape.getLocalMatrix());
            knob.setPositionRelativeToParent(pos);
          }
         
//          System.out.println("Slider value: " + getValue());
         
          //Fire property change event
          if (propertyChangeSupport.hasListeners("value")){
            propertyChangeSupport.firePropertyChange("value", oldValue, getValue())
          }
        }else{
          knob.translate(new Vector3D(0,dir.y,0), TransformSpace.LOCAL);
        }
        return false;
      }
    });
   
    //Default - Sets the current value to be the middle between min and max value (needed or knob may not appear correctly)
    this.setValue((minValue+maxValue)/2f);
  }
 
  /**
   * Gets the value.
   *
   * @return the value
   */
  public float getValue(){
    float outerShapeWidthLocal = outerShape.getWidthXY(TransformSpace.LOCAL);
    float knobWidthRelParent = knob.getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
   
    float leftMostPossibleKnobPosX = x + innerPadding + knobWidthRelParent * 0.5f;
    float rightMostPossibleKnobPosX = x + outerShapeWidthLocal - innerPadding - knobWidthRelParent * 0.5f;
   
    //in outershape local coords
    float slideableArea = rightMostPossibleKnobPosX-leftMostPossibleKnobPosX;
    float knobPosX = knob.getCenterPointRelativeToParent().x;
    float sliderAreaToValueAreaRatio = valueRange/slideableArea;
   
    float knobCurr = knobPosX-leftMostPossibleKnobPosX;
    float valueCurr = minValue +  knobCurr * sliderAreaToValueAreaRatio;
   
    valueCurr = ToolsMath.clamp(valueCurr, minValue, maxValue);
    /*
    System.out.println("sliderCurr: " + sliderCurr);
    System.out.println("ValueCurr: " + valueCurrr);
    */
    /*//Show begin and end point of slider works only unrotated
    MTEllipse e1 = new MTEllipse(this.getRenderer(), new Vector3D(leftMostPossibleSliderPosX, y + slider.getHeightXY(TransformSpace.RELATIVE_TO_PARENT)*0.5f),5,5);
    this.getRoot().addChild(e1);
    MTEllipse e2 = new MTEllipse(this.getRenderer(), new Vector3D(rightMostPossibleSliderPosX, y + slider.getHeightXY(TransformSpace.RELATIVE_TO_PARENT)*0.5f),5,5);
    this.getRoot().addChild(e2);
    */
    return valueCurr;
  }
 
  /**
   * Sets the value.
   *
   * @param value the new value
   */
  public void setValue(float value){
    if (value > maxValue){
      value = maxValue;
    }else if (value < minValue){
      value = minValue;
    }
   
    float oldValue = this.getValue();
   
    float outerShapeWidthLocal = outerShape.getWidthXY(TransformSpace.LOCAL);
    float knobWidthRelParent = knob.getWidthXY(TransformSpace.RELATIVE_TO_PARENT);
    float leftMostPossibleknobPosX = x + innerPadding + knobWidthRelParent * 0.5f;
    float rightMostPossibleknobPosX = x + outerShapeWidthLocal - innerPadding - knobWidthRelParent * 0.5f;
   
    //in outershape local coords
    float slideableRange = rightMostPossibleknobPosX-leftMostPossibleknobPosX;
    float valueRangeToSliderValueRange = slideableRange/valueRange;
   
    float valueOffsetFromMinValue = Math.abs(value - minValue);
//    float knobValue = minValue +  value*valueAreaToSliderValueArea;
    float knobAdvanceFromLeftValue =  valueOffsetFromMinValue * valueRangeToSliderValueRange;
   
    Vector3D pos = new Vector3D( x + innerPadding + knobWidthRelParent*0.5f + knobAdvanceFromLeftValue , y + knob.getHeightXY(TransformSpace.RELATIVE_TO_PARENT)*0.5f + innerPadding, 0);
//    pos.transform(outerShape.getGlobalMatrix());
//    knob.setPositionGlobal(pos);
    pos.transform(outerShape.getLocalMatrix());
    knob.setPositionRelativeToParent(pos);
   
//    System.out.println("slidervalue: " + sliderAdvanceFromLeftValue);
   
    //Fire property change event
    if (propertyChangeSupport.hasListeners("value")){
      this.propertyChangeSupport.firePropertyChange("value", oldValue, this.getValue())
    }
  }
 
  /**
   * Sets the value range.
   *
   * @param min the min
   * @param max the max
   */
  public void setValueRange(float min, float max){
    if (minValue > maxValue){
      System.err.println("Minimum value is bigger than the maximum value in " + this);
    }
   
    float oldValue = this.getValue();
    float oldMin = this.minValue;
    float oldValueRange = valueRange;
   
    this.minValue = min;
    this.maxValue = max;
    this.valueRange = maxValue-minValue;
   
    //Keeping the relative slider andvancement
    float newValue = minValue + (valueRange * ((oldValue-oldMin)/oldValueRange));
    this.setValue(newValue);
//    this.setCurrentValue(minValue);
   
    //Fire property change event
    if (propertyChangeSupport.hasListeners("valueRange")){
      this.propertyChangeSupport.firePropertyChange("valueRange", oldValueRange, valueRange);
    }
  }

 
  /**
   * Gets the value range.
   *
   * @return the value range
   */
  public float getValueRange(){
    return this.valueRange;
  }
 
 
  public float getMaxValue() {
    return this.maxValue;
  }

  public float getMinValue() {
    return this.minValue;
  }

  /**
   * Gets the outer shape.
   *
   * @return the outer shape
   */
  public AbstractShape getOuterShape() {
    return outerShape;
  }

  /**
   * Gets the knob.
   *
   * @return the slider
   */
  public AbstractShape getKnob() {
    return knob;
  }

 
  /**
   * Adds the property change listener.
   * The slider supports listening to the following properties changes.
   * <li>"value"
   * <li>"valueRange"
   *
   * @param propertyName the property name
   * @param listener the listener
   */
  public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
    propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
  }

  /**
   * Gets the property change listeners.
   *
   * @param propertyName the property name
   *
   * @return the property change listeners
   */
  public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
    return propertyChangeSupport.getPropertyChangeListeners(propertyName);
  }

  /**
   * Removes the property change listener.
   *
   * @param propertyName the property name
   * @param listener the listener
   */
  public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
    propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
  }
 
 
  // DELEGATE APPEARANCE TO OUTERSHAPE SINCE THE SLIDER ITSELF ISNT DISPLAYED!
  @Override
  public void setFillColor(MTColor color) {
    super.setFillColor(color);
    this.getOuterShape().setFillColor(color);
  }
  @Override
  public void setStrokeColor(MTColor strokeColor) {
    super.setStrokeColor(strokeColor);
    this.getOuterShape().setStrokeColor(strokeColor);
  }
  @Override
  public void setStyleInfo(StyleInfo styleInfo) {
    super.setStyleInfo(styleInfo);
    this.getOuterShape().setStyleInfo(styleInfo);
  }
  @Override
  public void setTexture(PImage newTexImage) {
//    super.setTexture(newTexImage);
    this.getOuterShape().setTexture(newTexImage);
  }
 

}
TOP

Related Classes of org.mt4j.components.visibleComponents.widgets.MTSlider

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.