Package org.rascalmpl.library.vis.figure

Source Code of org.rascalmpl.library.vis.figure.Figure

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Bert Lisser - Bert.Lisser@cwi.nl (CWI)
*   * Paul Klint - Paul.Klint@cwi.nl - CWI
*   * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.library.vis.figure;



import static org.rascalmpl.library.vis.properties.Properties.ASPECT_RATIO;
import static org.rascalmpl.library.vis.properties.Properties.FILL_COLOR;
import static org.rascalmpl.library.vis.properties.Properties.FONT;
import static org.rascalmpl.library.vis.properties.Properties.FONT_BOLD;
import static org.rascalmpl.library.vis.properties.Properties.FONT_COLOR;
import static org.rascalmpl.library.vis.properties.Properties.FONT_ITALIC;
import static org.rascalmpl.library.vis.properties.Properties.FONT_SIZE;
import static org.rascalmpl.library.vis.properties.Properties.HALIGN;
import static org.rascalmpl.library.vis.properties.Properties.HGROW;
import static org.rascalmpl.library.vis.properties.Properties.HRESIZABLE;
import static org.rascalmpl.library.vis.properties.Properties.HSHADOWPOS;
import static org.rascalmpl.library.vis.properties.Properties.LINE_COLOR;
import static org.rascalmpl.library.vis.properties.Properties.LINE_STYLE;
import static org.rascalmpl.library.vis.properties.Properties.LINE_WIDTH;
import static org.rascalmpl.library.vis.properties.Properties.SHADOW;
import static org.rascalmpl.library.vis.properties.Properties.SHADOW_COLOR;
import static org.rascalmpl.library.vis.properties.Properties.VALIGN;
import static org.rascalmpl.library.vis.properties.Properties.VGROW;
import static org.rascalmpl.library.vis.properties.Properties.VRESIZABLE;
import static org.rascalmpl.library.vis.properties.Properties.VSHADOWPOS;
import static org.rascalmpl.library.vis.properties.TwoDProperties.ALIGN;
import static org.rascalmpl.library.vis.properties.TwoDProperties.MIRROR;
import static org.rascalmpl.library.vis.properties.TwoDProperties.SIZE;
import static org.rascalmpl.library.vis.util.vector.Dimension.HOR_VER;
import static org.rascalmpl.library.vis.util.vector.Dimension.X;
import static org.rascalmpl.library.vis.util.vector.Dimension.Y;

import java.util.List;

import org.eclipse.imp.pdb.facts.IBool;
import org.eclipse.imp.pdb.facts.IMap;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.eclipse.imp.pdb.facts.type.Type;
import org.eclipse.imp.pdb.facts.type.TypeFactory;
import org.rascalmpl.library.vis.figure.interaction.MouseOver;
import org.rascalmpl.library.vis.graphics.FontStyle;
import org.rascalmpl.library.vis.graphics.GraphicsContext;
import org.rascalmpl.library.vis.properties.Properties;
import org.rascalmpl.library.vis.properties.PropertyManager;
import org.rascalmpl.library.vis.swt.ICallbackEnv;
import org.rascalmpl.library.vis.swt.IFigureConstructionEnv;
import org.rascalmpl.library.vis.swt.SWTFontsAndColors;
import org.rascalmpl.library.vis.swt.applet.IHasSWTElement;
import org.rascalmpl.library.vis.util.NameResolver;
import org.rascalmpl.library.vis.util.vector.BoundingBox;
import org.rascalmpl.library.vis.util.vector.Coordinate;
import org.rascalmpl.library.vis.util.vector.Dimension;
import org.rascalmpl.library.vis.util.vector.Rectangle;
import org.rascalmpl.library.vis.util.vector.TransformMatrix;
import org.rascalmpl.library.vis.util.vector.TwoDimensional;
import org.rascalmpl.values.ValueFactoryFactory;


/**
* Figures are the foundation of Rascal visualization. They are based on a
* bounding box. The bounding box defines the maximal dimensions
* of the element.
*
* @author paulk
*/

public abstract class Figure implements Comparable<Figure> {
  private static final IValueFactory VF = ValueFactoryFactory.getValueFactory();
 
  public static enum ResizeMode{
    RESIZE, ZOOM;
  }
 
  public static enum RecomputeStatus{
    DID_NOT_CHANGE,
    CHANGED;
  }
  public static final Figure[] childless = new Figure[0];
  public static final Type[] noTypes = new Type[0];
  public static final IValue[] noArgs = new IValue[0];
 
  public static int sequencer = 0; // to impose arbitrary ordering on figures
  @SuppressWarnings("unused")
  private static final boolean debug = false;
 
  public int sequenceNr;
  public RecomputeStatus status;
  public Figure[] children;

  public boolean mouseOver; // set externally, only set when relevant (see MouseAndKeyboardHandler)
  public PropertyManager prop;
  public BoundingBox minSize;
  public BoundingBox size;
  public Coordinate localLocation; // the location of the left, top corner of this figure relative to the parent location
  public Coordinate globalLocation; //
  public TwoDimensional<Boolean> resizable;

  public Figure(PropertyManager properties) {
    this.prop = properties;
    minSize = new BoundingBox();
    size = new BoundingBox();
    localLocation = new Coordinate();
    globalLocation = new Coordinate();
    resizable = new TwoDimensional<Boolean>(true, true);
    sequenceNr = sequencer;
    sequencer++;
  }
 
  public final void registerIds(IFigureConstructionEnv env,NameResolver resolver){
    resolver.register(this);
    registerMore(env,resolver);
    setChildren(env,resolver);
    for(Figure child : children){
      child.registerIds(env,resolver);
    }
  }
 
  void registerMore(IFigureConstructionEnv env, NameResolver resolver) {}

  public void setChildren(IFigureConstructionEnv env, NameResolver resolver) {}
 
  public final void registerConverts(NameResolver resolver){
    prop.registerMeasures(resolver);
    for(Figure child : children){
      child.registerConverts(resolver);
    }
  }
 
  /*
   * There are three major phases in the processing of Figures:
   * - init: compute minimal size
   * - resize: compute actual size
   * - draw: draw it.
   */
 
  /* PHASE: init
   *
   * Down:
   * - initialize (computefigure, register controls and other stuff) (*)
   * - registerNames (defined by id property).
   * Up:
   * - register measures
   * - compute min size (*)
   * - finalize (*)
   *
   * (*) : extension point for subclasses.
   */
 
  /**
   *
   * @param env    the callback environment for computing stuff when initializing
   * @param resolver  the name resolver
   * @param swtSeen
   * @param visible
   * @param overlaps  the current set of overlaps (figure not abiding to strict inclusive layout), add overlapping figures here
   * @param zparent   the parent in the zorder tree, this is a partial view of the figure tree describing only the swt elements and hence their z order
   * @return
   */
  public final boolean init(IFigureConstructionEnv env,NameResolver resolver, MouseOver mparent, boolean swtSeen, boolean visible){
    resizable.set(prop.getBool(HRESIZABLE), prop.getBool(VRESIZABLE));
    initElem(env, mparent, swtSeen, visible, resolver);
    swtSeen = initChildren(env, resolver, mparent, swtSeen, visible);
    swtSeen = swtSeen || containsSWTElement();
    computeMinSize();
    adjustMinSize();
    finalize(true);
    return swtSeen;
  }

  public boolean containsSWTElement() {
    return false;
  }

  public boolean initChildren(IFigureConstructionEnv env,
      NameResolver resolver, MouseOver mparent, boolean swtSeen, boolean visible) {
    boolean swtSeenResult = false;
    for(int i = 0; i < children.length ; i++){
      boolean here =  children[i].init(env, resolver,mparent, swtSeen, visible);
      swtSeenResult = swtSeenResult || here;
    }
    return swtSeenResult;
  }

  public void initElem(IFigureConstructionEnv env, MouseOver mparent, boolean swtSeen, boolean visible, NameResolver resolver){}
 
  public abstract void computeMinSize() ;
 
  public void finalize(boolean needsRecompute){}
 
  /*
   * PHASE: resize
   *
   * Down:
   * - resize *
   *     - Size has been set by parent.
   *     - Set size of all children and their locallocations (globalocations are set automatically in the step below).
   * - register the zorder elements (SWT related) (*)
   * - set the global location of children (transform locallocation to globallocation)
   * Up:
   * - onResizeUp (do some stuff if the clients wants to) (*)
   *
   * (*) : extension point for subclasses.
   */
  public final void resize(Rectangle view,TransformMatrix transform){
    adjustSizeAndLocation();
    resizeElement(view);
    resizeChildren(view, transform);
    onResizeUp();
  }

  public void resizeChildren(Rectangle view, TransformMatrix transform) {
    for(Figure child : children){
      for(Dimension d : HOR_VER){
        if(prop.get2DBool(d, MIRROR)){
          child.localLocation.set(d,size.get(d) - (child.localLocation.get(d) + child.size.get(d)));
        }
      }
      child.globalLocation.set(globalLocation);
      child.resize(view,transform);
    }
  }
 
  public abstract void resizeElement(Rectangle view) ;

  public void applyTransformation(TransformMatrix transform) {}
  public void reverseTransformation(TransformMatrix transform) {}
  public void onResizeUp() { }

  public void updateGlobalLocation(){
   
    for(Figure child : children){
      child.globalLocation.set(globalLocation);
      child.globalLocation.add(child.localLocation);
      child.updateGlobalLocation();
    }
  }
 
  /*
   * PHASE: draw
   *
   * Down:
   * - beforeDraw (optional preprocessing) (*)
   * - applyProperties
   * - drawElement (draw the current figure)
   * - drawChildren (draw its children).
   * Up: there is no up phase.
   *
   * (*) : extension point for subclasses.
   *
   */
 
  public final void draw(Coordinate zoom, GraphicsContext gc,Rectangle part, List<IHasSWTElement> visibleSWTElements) {
    // TODO: iets met rotate en transformaties
//    Coordinate offset = new Coordinate();
//    double oldZoomX, oldZoomY;
//    oldZoomX = zoom.getX();
//    oldZoomY = zoom.getY();
//    for(Dimension d : HOR_VER){
//      if(!prop.get2DBool(d, ZOOMABLE)){
//        offset.set(d, size.get(d) * (zoom.get(d) - 1.0) * prop.get2DReal(d, ALIGN) );
//        zoom.set(d, 1.0);
//      }
//    }
//    if(zoom.getX() != zoom.getY() && prop.isSet(ASPECT_RATIO)){
//      double minZoom = Math.min(zoom.getX(),zoom.getY());
//      zoom.set(minZoom,minZoom);
//    }
//    gc.translate(offset.getX(), offset.getY());
   
    beforeDraw(zoom);
    applyProperties(gc);
    drawElement(gc, visibleSWTElements);
    drawChildren(zoom, gc, part, visibleSWTElements);
    // gc.translate(-offset.getX(), -offset.getY());
    //zoom.set(oldZoomX, oldZoomY);
  }

  public void drawChildren(Coordinate zoom, GraphicsContext gc,
      Rectangle part, List<IHasSWTElement> visibleSWTElements) {
    for(Figure f : children){
      //if(f.overlapsWith(part)){
        Rectangle npart = f.isContainedIn(part) ? null : part;
        f.draw(zoom, gc, npart, visibleSWTElements);
      //}
    }
  }

  public void beforeDraw(Coordinate zoom) {}

  public void drawElement(GraphicsContext gc, List<IHasSWTElement> visibleSWTElements){}
 

  public boolean widthDependsOnHeight(){
    return false;
  }

  public Dimension getMajorDimension(){
    return X;
  }
 
  /**
   * Give a figure the opportunity to remove allocated components, etc.
   */
  public void destroy(IFigureConstructionEnv env) {
   
    for(Figure child : children){
      child.destroy(env);
    }
    destroyElement(env);
  }
 
  public void destroyElement(IFigureConstructionEnv env) { }
 
  public void hide(IFigureConstructionEnv env) {
    for(Figure child : children){
      child.hide(env);
    }
    hideElement(env);
  }
 
  public void hideElement(IFigureConstructionEnv env) {
    destroyElement(env);
  }

  public void getFiguresUnderMouse(Coordinate c,List<Figure> result){
    if(!mouseInside(c)){
      return;
    }
    for(Figure child : children){
      child.getFiguresUnderMouse(c, result); // TODO: overlap
    }
    if(handlesInput()){
      result.add(this);
    }
  }

  public boolean handlesInput(){
    return prop.hasHandlerProperties();
  }
 

  private void adjustMinSize() {
    if(prop.isSet(ASPECT_RATIO)){
      double ar = prop.getReal(ASPECT_RATIO);
      minSize.setMax(X, minSize.getY() * ar);
      minSize.setMax(Y, minSize.getX() / ar);
    }
    for(Dimension d : HOR_VER){
      minSize.setMax(d,prop.get2DReal(d, SIZE));
    }
  }

  public void adjustSizeAndLocation(){
    for(Dimension d : HOR_VER){
      if(!resizable.get(d)){
        localLocation.add(d, (size.get(d) - minSize.get(d)) * prop.get2DReal(d, ALIGN) );
        size.set(d,minSize.get(d));
      }
    }
    if(prop.isSet(ASPECT_RATIO)){
      double ar = prop.getReal(Properties.ASPECT_RATIO);
      double car = size.getX() / size.getY();
      if(car > ar) { // too wide
        double newWidth = size.getY() * ar;
        localLocation.add(X, (size.getX() - newWidth) * prop.getReal(HALIGN));
        size.setX(newWidth);
      } else if( car < ar){ // too tall
        double newHeight = size.getX() / ar;
        localLocation.add(Y, (size.getY() - newHeight) * prop.getReal(VALIGN));
        size.setY(newHeight);
      }
    }
    globalLocation.add(localLocation);
  }
 

  /*
   * Compare two Figures using an arbitrary ordering
   *
   * @see java.lang.Comparable#compareTo(java.lang.Object)
   */
  public int compareTo(Figure o) {
    return sequenceNr - o.sequenceNr;
  }

  public BoundingBox getMinViewingSize() {
    BoundingBox minViewSize = new BoundingBox();
    minViewSize.set(minSize.getX() / prop.getReal(HGROW), minSize.getY() /prop.getReal(VGROW) );
    return minViewSize;
  }
 
  public boolean overlapsWith(Rectangle r){
    return r == null || r.overlapsWith(globalLocation, size);
  }
 
  public boolean isContainedIn(Rectangle r){
    return  r == null || r.contains(globalLocation,size);
  }
 
  public Rectangle getRectangle(){
    return new Rectangle(globalLocation, size);
  }
 
  public Rectangle getRectangleIncludingOuterLines(){
    double hlw = 0.5 * prop.getReal(LINE_WIDTH);
    return new Rectangle(globalLocation.getX() - hlw, globalLocation.getY() -hlw , size.getX() + 2*hlw, size.getY() + 2*hlw);
  }
 
  public boolean executeKeyHandlers(ICallbackEnv env,IValue keySym, boolean keyDown, IMap modifiers){
    Type[] types = {keySym.getType(),modifiers.getType()};
    IValue[] args = {keySym,modifiers};
    if(keyDown) return executeHandlerProperty(env,Properties.ON_KEY_DOWN,types,args);
    else  return executeHandlerProperty(env,Properties.ON_KEY_UP,types,args);
  }

  public void executeMouseMoveHandlers(ICallbackEnv env, boolean enter) {
    Type[] types = {};
    IValue[] args = {};
    if(enter) executeHandlerProperty(env,Properties.ON_MOUSE_OVER,types,args);
    else executeHandlerProperty(env,Properties.ON_MOUSE_OFF,types,args);
  }


  public boolean mouseInside(Coordinate c) {
    return c.getX() >= globalLocation.getX() && c.getX() <= globalLocation.getX() + size.getX()
        && c.getY() >= globalLocation.getY() && c.getY() <= globalLocation.getY()+ size.getY();
  }

  public boolean executeOnClick(ICallbackEnv env, int button, IMap modifiers, boolean down) {
    Type[] types = {TypeFactory.getInstance().integerType(),modifiers.getType()};
    IValue[] args = {VF.integer(button),modifiers};
    if(down) return executeHandlerProperty(env, Properties.ON_MOUSE_DOWN, types, args);
    else return executeHandlerProperty(env, Properties.ON_MOUSE_UP, types, args);
  }
 
  // returns if the event is captured (i.e. not propagated further)
  public boolean executeHandlerProperty(ICallbackEnv env, Properties property, Type[] types, IValue[] args){
    if(prop.isSet(property)){
      IValue v = prop.executeHandler(env, property, types, args);
      if(v instanceof IBool){
        return ((IBool)v).getValue();
      } else {
        return false;
      }
    } else {
      return false; // event not consumed, so not captured
    }
  }
 

  public void applyProperties(GraphicsContext gc) {
    gc.fill(prop.getColor(FILL_COLOR));
    gc.stroke(prop.getColor(LINE_COLOR));
    gc.strokeWeight(prop.getReal(LINE_WIDTH));
    gc.strokeStyle(prop.getStr(LINE_STYLE));
    boolean shadow = prop.getBool(SHADOW);
    gc.setShadow(shadow);
    if (shadow) {
      gc.setShadowColor(prop.getColor(SHADOW_COLOR));
      gc.setShadowLeft(prop.getReal(HSHADOWPOS));
      gc.setShadowTop(prop.getReal(VSHADOWPOS));
    }

    gc.setFont(prop.getStr(FONT), prop.getInt(FONT_SIZE),
        FontStyle.getFontStyles(prop.getBool(FONT_BOLD), prop.getBool(FONT_ITALIC)));
    gc.font(prop.getColor(FONT_COLOR));
  }
 
  public double getTextAscent(){
    return SWTFontsAndColors.textAscent(
        prop.getStr(FONT),
        prop.getInt(FONT_SIZE),FontStyle.getFontStyles(prop.getBool(FONT_BOLD), prop.getBool(FONT_ITALIC)));
  }
 
  public double getTextDescent(){
    return SWTFontsAndColors.textDescent(
        prop.getStr(FONT),
        prop.getInt(FONT_SIZE),FontStyle.getFontStyles(prop.getBool(FONT_BOLD), prop.getBool(FONT_ITALIC)));
  }
 
  public double getTextHeight(){
    return getTextAscent() + getTextDescent();
  }
 
  public double getTextWidth(String s){
    return SWTFontsAndColors.textWidth(s,
        prop.getStr(FONT),
        prop.getInt(FONT_SIZE),FontStyle.getFontStyles(prop.getBool(FONT_BOLD), prop.getBool(FONT_ITALIC)));
  }
 
 
  /**
   * Draw an arrow from an external position (fromX, fromY) directed to the
   * center (X,Y) of the current figure. The arrow is placed at At the
   * intersection with the border of the current figure and it is
   * appropriately rotated.
   * @param X
   *            X of center of current figure
   * @param Y
   *            Y of center of current figure
   * @param fromX
   *            X of center of figure from which connection is to be drawn
   * @param fromY
   *            Y of center of figure from which connection is to be drawn
   * @param toArrow
   *            the figure to be used as arrow (the upperside will act as arrow head)
   */
  public void connectArrowFrom(double X, double Y, double fromX, double fromY,
      Figure toArrow, GraphicsContext gc, List<IHasSWTElement> visibleSWTElements ) {
 
    System.out.printf("Niet gespecialiseerd %s!!\n",this);
    for(Dimension d : HOR_VER){
      toArrow.minSize.set(d,toArrow.prop.get2DReal(d, SIZE));
    }
    toArrow.size.set(toArrow.minSize);
    toArrow.globalLocation.set(0,0);
    toArrow.localLocation.set(0,0);
    toArrow.resize(null, new TransformMatrix());

    if (fromX == X)
      fromX += 0.00001;
    double s = (fromY - Y) / (fromX -X);

    double rotd = Math.toDegrees(Math.atan(s));

    double IX;
    double IY;
    double h2 = minSize.getY() / 2;
    double w2 = minSize.getX() / 2;

    if ((-h2 <= s * w2) && (s * w2 <= h2)) {
      if (fromX > X) {   // right
        IX = X + w2;
        IY = Y + s * w2;
        rotd += (fromY < Y ? -90 : -90);
      } else {       // left
        IX = X - w2;
        IY = Y - s * w2;
        rotd += 90;
      }
    } else {
      if (fromY > Y) {   // bottom
        IX = X + h2 / s;
        IY = Y + h2;
        rotd += (fromX < X ? 90 : -90);
      } else {       // top
        IX = X - h2 / s;
        IY = Y - h2;
        rotd += (fromX < X ? 90 : -90);
      }
    }
   
    gc.pushMatrix();
    gc.translate(IX , IY);

    gc.rotate(rotd);
    gc.translate(-toArrow.size.getX()/2.0,0);
    toArrow.applyProperties(gc);
    toArrow.drawElement(gc,visibleSWTElements);
    gc.popMatrix();
  }
 
  public Coordinate getGlobalCenter(){
    return new Coordinate(globalLocation.getX() + size.getX()/2, globalLocation.getY() + size.getY()/2);
  }
 
  public Coordinate getLocalCenter(){
    return new Coordinate(localLocation.getX() + size.getX()/2, localLocation.getY() + size.getY()/2);
  }
 
 
}
TOP

Related Classes of org.rascalmpl.library.vis.figure.Figure

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.