Package ketUI.panel

Source Code of ketUI.panel.GraphDisplay

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* 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 ketUI.panel;

import geom.Offset;

import java.util.*;
import java.awt.Stroke;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;

import geom.Position;
import ket.*;
import ket.display.*;
import ket.display.box.BorderedBox;
import ket.display.box.Box;
import ket.display.box.BoxFactory;
import ket.display.box.BoxText;
import ket.display.box.BoxTools;
import ket.math.*;
import ketUI.Document;

/**
* Represent the current equation as a graph.
*/
public class GraphDisplay implements Display {

  static final int ICON_SIZE = 7;
  static final Random random = new Random();

  public static final double LABEL_SCALE_FACTOR = 0.4; // fractional label scale factor


  public static final Stroke ROOT_STROKE = new BasicStroke(1.7f,
      BasicStroke.CAP_SQUARE,
      BasicStroke.JOIN_MITER,
      10.0f,
      new float[] {1.0f},
      0.0f);

  public static final Stroke LINE_STROKE = new BasicStroke(1.0f,
      BasicStroke.CAP_SQUARE,
      BasicStroke.JOIN_MITER,
      10.0f,
      new float[] {2.0f, 2.0f},
      0.0f);

  static final boolean TREE_MAP   = false;
  static final boolean ANIMATE    = false;
  static final int     SAMPLE_MAX = 10000;

  /**
   * When the equation is drawn, record its box from which the arguments
   * associated with a mouse click may be determined.
   */
  final Document document;
  final MathCollection mathCollection;

  Box equationBox;
  Position actualEquationTopLeft;
  Map<Label, Vector<Node>> groups;
  Vector<Node> nodes;
  Map<Node, Vector<Position>> cloud;
  boolean repaint;
  //- double edgeLength = 65.0;

  public GraphDisplay(Document document, MathCollection mathCollection) {
    this.document = document;
    this.mathCollection = mathCollection;
    groups = new TreeMap<Label, Vector<Node>>();
    nodes = new Vector<Node>();
    cloud = new IdentityHashMap<Node, Vector<Position>>();
    repaint = false;
  }

  public double getEdgeLength() {
    return 65.0 * document.getBoxFontSize() / 25.0;
  }
 
  @Override
  public Box findDeepestBox(Position p) {
    return null;
  }

  public Node findNearestNode(Position p, boolean anywhere) {
    if (nodes==null) {
      return null;
    }
    Node closest = null;
    double best = Double.MAX_VALUE;
    for (Node node : nodes) { // Can be empty.
      if (node.actualX==null || node.actualY==null) {
        // Has not yet been drawn.
        continue;
      }
      double dx = node.actualX - p.x;
      double dy = node.actualY - p.y;
      double norm = Math.sqrt(dx*dx + dy*dy);
      if (norm < best) {
        closest = node;
        best = norm;
      }
    }

    if (anywhere || closest==null) {
      return closest;
    }

    // Otherwise find the next two closest family members in order
    // to produce a smooth halo around the edges.
    Node nextClosest = null;
    double nextBest = Double.MAX_VALUE;
    for (Node node : nodes) { // Can be empty.
      if (node.actualX==null || node.actualY==null) {
        continue;
      }
      if (node==closest) {
        continue;
      }
      double dx = node.actualX - p.x;
      double dy = node.actualY - p.y;
      double norm = Math.sqrt(dx*dx + dy*dy);
      if (norm<nextBest && node.family(closest)) {
        nextClosest = node;
        nextBest = norm;
      }
    }
    if (nextClosest==null) {
      return closest;
    }
    int power = 2;
    double val = 1.0/Math.pow(best, power) + 1.0/Math.pow(nextBest, power);
    double scale = 1.0/Math.pow(getEdgeLength(), power);
    if (7.0*scale<val) {
      return closest;
    } else {
      return null;
    }
  }

  @Override
  public Argument findDeepestArgument(Position p) {
    Node closest = findNearestNode(p, true);
    return closest!=null ? closest.argument : null;
  }

  @Override
  public Vector<Integer> findVisibleEquationIndices() {
    Vector<Integer> indices = new Vector<Integer>();
    if (nodes==null) return indices;
    for (Node n : nodes) {
      Integer index = n.getEquationIndex();
      if (index!=null) {
        indices.add(index);
      }
    }
    return indices;
  }

  @Override
  public Equation pointToEquation(Position p) {
    if (equationBox!=null) {
      Argument argument = equationBox.getArgument();
      if (argument==null) {
        return null;
      }
      return argument.getEquation();
    } else {
      Argument argument = findDeepestArgument(p);
      return argument!=null ? argument.getEquation() : null;
    }
  }


  @Override
  public boolean generateBoxes(Graphics2D g2D, ColourScheme colourScheme, int fontSize, Offset panelRectangle) {
    Equation equation = mathCollection.getCursor().getEquation();
    if (equation.isText()) {
      generateText(g2D, colourScheme, fontSize, panelRectangle, equation);
      return true;
    }

    equationBox = null; // Clear past text.
    cloud = new IdentityHashMap<Node, Vector<Position>>();
    TreeMap<Argument, Node> ids = new TreeMap<Argument, Node>();
    ArgumentVector v = new ArgumentVector(equation.getVisibleRoot(), ArgumentVector.INCLUDE_ROOT|ArgumentVector.EXCLUDE_HIDDEN_CHILDREN);
    nodes = new Vector<Node>();
    TreeSet<String> names =  new TreeSet<String>();
    repaint = TREE_MAP;
    for (Argument a : v) {
      double x = 0.0;
      double y = 0.0;
      Branch branch = a.getParentBranch();
      Node parent = branch!=null ? ids.get(branch) : null;
      Node n = new Node(a, parent, colourScheme, getEdgeLength());
      names.add(n.name);
      ids.put(a, n);
      nodes.add(n);
    }

    for (Node node : nodes) {
      Offset original = new Offset(node.x, node.y);
      Offset rotate = original.rotate(Math.PI/12.0)// 15 deg
      node.x = rotate.width;
      node.y = rotate.height;
    }

    relax(panelRectangle);
    moveToCentreOfMass();

    groups = new TreeMap<Label, Vector<Node>>();
    for (String string : names) {
      Vector<Node> value = new Vector<Node>();
      double sumX = 0.0;
      double sumY = 0.0;
      for (Node n : nodes) { // group by name
        if (string.equals(n.name)) {
          value.add(n);
          sumX += n.x;
          sumY += n.y;
        }
      }
      groups.put(
        new Label(
          string,
          sumX/nodes.size(),
          sumY/nodes.size()),
        value);
    }

    shiftLabels(panelRectangle);
    relaxLabels();
    return true;
  }

  public void generateText(Graphics2D g2D, ColourScheme colourScheme, int fontSize, Offset panelRectangle, Equation equation) {
    equationBox = equation.toBox(colourScheme);
    repaint = false;
    equationBox.setupInnerRectangle(fontSize);
    double minimumHeight = equationBox.getInnerRectangle().height;
    double shapeWidth = panelRectangle.width - KetPanel.BORDER_OFFSET.width;
    Offset windowWithoutLabel = new Offset(shapeWidth, minimumHeight);
    equationBox.setupOuterRectangle(windowWithoutLabel);
    double panelHeightOfNextBox = KetPanel.TOP_BORDER_SIZE;
    actualEquationTopLeft = new Position(KetPanel.LEFT_BORDER_SIZE, panelHeightOfNextBox);
  }

  public void shiftLabels(Offset panelRectangle) {
    long rand = 1;
    //! groups = new TreeMap<Label, Vector<Node>>();
    for (Map.Entry<Label, Vector<Node>> entry : groups.entrySet()) {
      //! Vector<Node> value = new Vector<Node>();
      double sumX = 0.0;
      double sumY = 0.0;
      for (Node n : entry.getValue()) {
        sumX += n.x;
        sumY += n.y;
      }
      // Ellipse
      double beta = Math.atan2(sumY, sumX);
      double a = LABEL_SCALE_FACTOR*panelRectangle.width;
      double b = LABEL_SCALE_FACTOR*panelRectangle.height;
      double denA = b*Math.cos(beta);
      double denB = a*Math.sin(beta);
      // Repeatedly consistent random number generation (using the linear congruential method).
      rand = (LCM_A * rand + LCM_C) % LCM_M;
      double r = a * b * (0.8 + 0.2 * (rand*1.0/LCM_M)) / Math.sqrt(denA*denA + denB*denB);
      entry.getKey().x = r * Math.cos(beta);
      entry.getKey().y = r * Math.sin(beta);
    }
  }

  public static final long LCM_A = 16807;
  public static final long LCM_M = Integer.MAX_VALUE;
  public static final long LCM_C = 0;

  // used:?
  public static int sign(double x) {
    return x<0.0 ? -1 : +1;
  }
  public static int abs(int x) {
    return x<0 ? -x : +x;
  }
  public static double sq(double x) {
    return x*x;
  }

  public void relax(Offset panelRectangle) {
    /*
    for (Node node : nodes) { //D Random noise to check stability and response rate.
      node.x += (Math.random() - 0.5)/2.0;
      node.y += (Math.random() - 0.5)/2.0;
    }*/
    Offset[] diff = new Offset[nodes.size()];
    int N = 100;

    double edgeLength = getEdgeLength();
    for (int q=0; q<N; q++) {
      for (int i=0; i<nodes.size(); i++) {
        Node m = nodes.get(i);
        double forceX = 0.0;
        double forceY = 0.0;

        double scale = 1.0E-4;
        forceX = scale * sign(m.x) * m.x * m.x;
        forceY = scale * sign(m.y) * m.y * m.y;

        for (int j=0; j<nodes.size(); j++) {
          Node n = nodes.get(j);
          Offset r = m.inversePower(n);
          forceX += r.width;
          forceY += r.height;
        }
        Offset tension = m.tension(edgeLength);
        diff[i] = new Offset(tension.width + forceX/nodes.size(), tension.height + forceY/nodes.size());
      }
      /* DEBUG INFO
      if (q==0 || q==N-1) {
        double sum = 0.0;
        for (Offset offset : diff) {
          sum += offset.length();
        }
        Ket.out.println("sum = " + sum);
      }
      */
    }
    for (int i=0; i<nodes.size(); i++) {
      Node node = nodes.get(i);
      node.x += bound(diff[i].width);
      node.y += bound(diff[i].height);
    }
  }

  public void relaxLabels() {
    Vector<Label> labels = new Vector<Label>(groups.keySet());
    Offset[] diff = new Offset[labels.size()];
    int N = 100;
    for (int q=0; q<N; q++) {
      for (int i=0; i<labels.size(); i++) {
        diff[i] = new Offset(0.0, 0.0);
        Label m = labels.get(i);
        if (groups.get(m).size()<2) {
          continue;
        }
        double forceX = 0.0;
        double forceY = 0.0;
        for (int j=0; j<labels.size(); j++) {
          if (groups.get(m).size()<2) {
            continue;
          }
          Label n = labels.get(j);
          Offset r = m.inversePower(n);
          forceX += r.width;
          forceY += r.height;
        }
        diff[i].width += forceX;
        diff[i].height += forceY;
      }
      if (q==0 || q==N-1) {
        double sum = 0.0;
        for (Offset offset : diff) {
          sum += offset.length();
        }
        //! Ket.out.println("sum = " + sum);
      }
    }
    for (int i=0; i<labels.size(); i++) {
      Label label = labels.get(i);
      label.x += bound(diff[i].width);
      label.y += bound(diff[i].height);
    }
  }

  public double bound(double x) {
    double range = 2.0E3;
    return Math.min(Math.max(x, -range), range);
  }

  public Offset calcCentreOfMass() {
    double sumX = 0.0;
    double sumY = 0.0;
    for (Node n : nodes) {
      sumX += n.x;
      sumY += n.y;
    }
    return new Offset(sumX/nodes.size(), sumY/nodes.size());
  }

  public void moveToCentreOfMass() {
    Offset centreOfMass = calcCentreOfMass();
    for (Node n : nodes) {
      n.x -= centreOfMass.width;
      n.y -= centreOfMass.height;
    }
    // Shift the labels
    for (Label label : groups.keySet()) {
      label.x -= centreOfMass.width;
      label.y -= centreOfMass.height;
    }
  }


  /**
   * Draw a single equation in the centre of the screen or text to the
   * top left.  In either case a label is also displayed.
   */
  @Override
  public void paint(Graphics2D g2D, ColourScheme colourScheme, int fontSize, Offset panelRectangle) {
    if (equationBox!=null) {
      // (Just in case) If ket panel is painted before boxes have been created in generate boxes.
      // g2D.setColor(colourScheme.getBackgroundColour());
      equationBox.paint(g2D, actualEquationTopLeft, colourScheme);
      return;
    }
    double centreX = KetPanel.LEFT_BORDER_SIZE + panelRectangle.width/2.0;
    double centreY = KetPanel.TOP_BORDER_SIZE + panelRectangle.height/2.0;

    if (ANIMATE) {
      relax(panelRectangle);
      relaxLabels();
      moveToCentreOfMass();
      shiftLabels(panelRectangle);
    }

    drawTreeMap(g2D, panelRectangle);
    if ( ! TREE_MAP ) {
      drawEdges(g2D, centreX, centreY, colourScheme);
    }
    drawLabelLines(g2D, centreX, centreY, colourScheme);
    drawLabelText(g2D, centreX, centreY, colourScheme);
    drawNodeSymbols(g2D, centreX, centreY, colourScheme);
    drawSimpleFunctionIcons(g2D, centreX, centreY, colourScheme);
  }

  // --- <NEW BIT> ---


  public double norm(Position a, Position b) {
    double dx = b.x - a.x;
    double dy = b.y - a.y;
    return Math.sqrt(dx*dx + dy*dy);
  }

  static final double MARGIN = 5.0;
  public boolean lt(double s, double u, double v) {
    return s+MARGIN<u  && s+MARGIN<v;
  }

  public boolean isDIn(double ab, double ac, double ad, double bc, double bd, double cd) {
    return lt(cd, ac, bc) && lt(ad, ab, ac) && lt(bd, ab, bc);
  }

  public Position contains(Position a, Position b, Position c, Position d) {
    double ab = norm(a, b);
    double ac = norm(a, c);
    double ad = norm(a, d);
    double bc = norm(b, c);
    double bd = norm(b, d);
    double cd = norm(c, d);
    if (isDIn(bc, bd, ab, cd, ac, ad)) {
      return a;
    } else if (isDIn(cd, ac, bc, ad, bd, ab)) {
      return b;
    } else if (isDIn(ad, bd, cd, ab, ac, bc)) {
      return c;
    } else if (isDIn(ab, ac, ad, bc, bd, cd)) {
      return d;
    } else {
      return null;
    }
  }

  // --- </NEW BIT> ---


  public void drawTreeMap(Graphics g2D, Offset panelRectangle) {
    if ( ! TREE_MAP ) {
      return;
    }
    for (int q=0; q<100; q++) { // was 100
      Position p = new Position(Math.random()*panelRectangle.width, Math.random()*panelRectangle.height);
      Node nearest = findNearestNode(p, false);
      if (nearest==null) continue;
      if (random.nextInt(1+nearest.depth)!=0) continue;
      if (!cloud.containsKey(nearest)) {
        cloud.put(nearest, new Vector<Position>());
      }
      if (cloud.get(nearest).size()<SAMPLE_MAX) {
        cloud.get(nearest).add(p);
      } else {
        repaint = false;
      }
    }
    // Draw background cloud.
    int i=0;
    for (Map.Entry<Node, Vector<Position>> entry : cloud.entrySet()) {
      i += 1;
      float hue = i*1.0f/cloud.size();
      float saturation = Math.min(1.0f, 1.0f/entry.getKey().depth);
      float brightness = 1.0f - Math.min(1.0f, 1.0f/entry.getKey().depth)/3.0f;
      g2D.setColor(Color.getHSBColor(hue, saturation, brightness));
      Vector<Position> points = entry.getValue(); //- cloud.get(entry.getKey());
      for (Position p : points) {
        g2D.fillRect((int) p.x-1, (int) p.y-1, 3, 3); // square
        //| g2D.fillRect((int) p.x, (int) p.y, 1, 1); // dot
      }
    }
  }

  public void drawEdges(Graphics2D g2D, double centreX, double centreY, ColourScheme colourScheme) {
    Stroke oldStroke = g2D.getStroke();
    g2D.setColor(colourScheme.getBorderColour());
    for (Node n : nodes) {
      double x = centreX + n.x;
      double y = centreY + n.y;
      if (n.parent!=null) {
        if (n.parent.parent==null) { // This is a line to 'root'.
          g2D.setStroke(ROOT_STROKE);
        }
        double px = centreX + n.parent.x;
        double py = centreY + n.parent.y;
        g2D.drawLine((int) px, (int) py, (int) x, (int) y);
        if (n.parent.parent==null) {
          g2D.setStroke(oldStroke);
        }
      }
    }
  }

  public void drawLabelLines(Graphics2D g2D, double centreX, double centreY, ColourScheme colourScheme) {
    // TODO: Use colourScheme.
    Stroke oldStroke = g2D.getStroke();
    for (Map.Entry<Label, Vector<Node>> entry : groups.entrySet()) {
      if (entry.getValue().firstElement().isSimpleFunction()) {
        continue;
      } else if (entry.getValue().size()<2) {
        continue;
      }
      boolean stack = Math.atan(Math.abs(entry.getKey().y/entry.getKey().x)) < Math.PI/4.0;
      double xEdge = centreX + entry.getKey().x;
      double yEdge = centreY + entry.getKey().y;

      Double min = stack ? yEdge : xEdge;
      Double max = stack ? yEdge : xEdge;

      for (Node node : entry.getValue()) {
        double x2 = centreX + node.x;
        double y2 = centreY + node.y;
        double dx = Math.abs(x2-xEdge);
        double dy = Math.abs(y2-yEdge);
        if (stack) {
          min = Math.min(min, y2);
          max = Math.max(max, y2);
          g2D.setStroke(LINE_STROKE);
          g2D.setColor(colourScheme.getBorderColour()); //<
          g2D.drawLine((int) xEdge, (int) y2, (int) x2, (int) y2); // const y
          g2D.setStroke(oldStroke);
          g2D.setColor(colourScheme.getBackgroundColour());
          g2D.drawLine((int) xEdge+1, (int) y2+1, (int) x2+1, (int) y2+1); // const y
        } else {
          min = Math.min(min, x2);
          max = Math.max(max, x2);
          g2D.setStroke(LINE_STROKE);
          g2D.setColor(colourScheme.getBorderColour()); //<
          g2D.drawLine((int) x2, (int) yEdge, (int) x2, (int) y2); // const x
          g2D.setStroke(oldStroke);
          g2D.setColor(colourScheme.getBackgroundColour());
          g2D.drawLine((int) x2+1, (int) yEdge+1, (int) x2+1, (int) y2+1); // const x
        }
      }
      if (min!=null && max!=null) {
        int iMin = (int) ((double) min);
        int iMax = (int) ((double) max);
        if (stack) {
          g2D.setStroke(LINE_STROKE);
          g2D.setColor(colourScheme.getBorderColour()); //<
          g2D.drawLine((int) xEdge, iMin, (int) xEdge, iMax);
          g2D.setStroke(oldStroke);
          g2D.setColor(colourScheme.getBackgroundColour());
          g2D.drawLine((int) xEdge+1, iMin+1, (int) xEdge+1, iMax+1);
        } else {
          g2D.setStroke(LINE_STROKE);
          g2D.setColor(colourScheme.getBorderColour()); //<
          g2D.drawLine(iMin, (int) yEdge, iMax, (int) yEdge);
          g2D.setStroke(oldStroke);
          g2D.setColor(colourScheme.getBackgroundColour());
          g2D.drawLine(iMin+1, (int) yEdge+1, iMax+1, (int) yEdge+1);
        }
      }
    }
  }

  public void drawLabelText(Graphics2D g2D, double centreX, double centreY, ColourScheme colourScheme) {
    for (Map.Entry<Label, Vector<Node>> entry : groups.entrySet()) {
      Label label;
      boolean highlight;
      boolean background;
      int size = entry.getValue().size();
      if (entry.getValue().firstElement().isSimpleFunction()) {
        continue;
      } else if (size==1) {
        label = entry.getValue().firstElement();
        highlight = entry.getValue().firstElement().highlight;
        background = true;
      } else {
        label = entry.getKey();
        highlight = false;
        background = false;
      }

      double large = 50.0; // bigger than box's yet-to-be-determined width and height.
      Offset bounds = new Offset(2.0*large, 2.0*large);

      Color borderColour = colourScheme.getBorderColour();
      BoxText box = new BoxText(null, " "+label.name+" " , Box.RIGHT_ALIGN|Box.BOTTOM_ALIGN);
      label.actualX = centreX + label.x - large;
      label.actualY = centreY + label.y - large;

      BoxText content = new BoxText(null, " " + label.name + " " , Box.RIGHT_ALIGN|Box.BOTTOM_ALIGN);
      Color colour = colourScheme.getBorderColour(); // This doesn't appear appear to be used.
      Box border = new BorderedBox(null, content, Box.X_CENTRE_ALIGN|Box.Y_CENTRE_ALIGN, highlight, colourScheme);

      Position centre = new Position(label.actualX, label.actualY);
      border.setupAndPaint(g2D, colourScheme, Box.DEFAULT_BOX_FONT_SIZE, centre, new Offset(2*large, 2*large), background);
    }
  }

  public void drawNodeSymbols(Graphics2D g2D, double centreX, double centreY, ColourScheme colourScheme) {
    // Draw node symbols.
    for (Map.Entry<Label, Vector<Node>> entry : groups.entrySet()) {
      if ( !entry.getValue().firstElement().isSimpleFunction() && entry.getValue().size()==1) {
        // Skip multiple arguments that are not simple functions.
        continue;
      }
      for (Node n : entry.getValue()) {
        double x = centreX + n.x;
        double y = centreY + n.y;
        n.actualX = x;
        n.actualY = y;
        //if (TREE_MAP) {
        //  continue;
        //}
        if (n.parent==null) { // root   
          g2D.setColor(n.colour);
          g2D.fillOval(-(int) (1.6*ICON_SIZE) + (int) x, -(int) (1.6*ICON_SIZE) + (int) y, (int) (2*1.6*ICON_SIZE), (int) (2*1.6*ICON_SIZE));
          g2D.setColor(colourScheme.getBackgroundColour());
          g2D.fillOval(-ICON_SIZE + (int) x, -ICON_SIZE + (int) y, 2*ICON_SIZE, 2*ICON_SIZE);
          g2D.setColor(colourScheme.getBorderColour());
          g2D.drawOval(-(int) (1.6*ICON_SIZE) + (int) x, -(int) (1.6*ICON_SIZE) + (int) y, (int) (2*1.6*ICON_SIZE), (int) (2*1.6*ICON_SIZE));
          g2D.drawOval(-ICON_SIZE + (int) x, -ICON_SIZE + (int) y, 2*ICON_SIZE, 2*ICON_SIZE);
        } else {
          g2D.setColor(colourScheme.getBorderColour());
          if (n.leaf) {
            g2D.setColor(n.colour);
            g2D.fillRect((int) (x-ICON_SIZE), (int) (y-ICON_SIZE), 2*ICON_SIZE, 2*ICON_SIZE);
            g2D.setColor(colourScheme.getBorderColour());
            g2D.drawRect((int) (x-ICON_SIZE), (int) (y-ICON_SIZE), 2*ICON_SIZE, 2*ICON_SIZE);
          } else {
            g2D.setColor(n.colour);
            g2D.fillOval(-ICON_SIZE + (int) x, -ICON_SIZE + (int) y, 2*ICON_SIZE, 2*ICON_SIZE);
            g2D.setColor(colourScheme.getBorderColour());
            g2D.drawOval(-ICON_SIZE + (int) x, -ICON_SIZE + (int) y, 2*ICON_SIZE, 2*ICON_SIZE);
          }
        }
      }
    }
  }

  public void drawSimpleFunctionIcons(Graphics2D g2D, double centreX, double centreY, ColourScheme colourScheme) {
    Stroke oldStroke = g2D.getStroke();
    for (Map.Entry<Label, Vector<Node>> entry : groups.entrySet()) {
      for (Node n : entry.getValue()) {
        double x = centreX + n.x;
        double y = centreY + n.y;
        if (TREE_MAP) {
          g2D.setColor(n.colour);
        } else {
          g2D.setColor(colourScheme.getBorderColour());
        }
        if (entry.getValue().firstElement().isSimpleFunction()) {
          g2D.setStroke(ROOT_STROKE);
          double scale = 1.0 / Math.sqrt(2.0);
          Function f = entry.getValue().firstElement().argument.getFunction();
          if (f==Function.ADD) {
            g2D.drawLine(-ICON_SIZE+(int) x, (int) y, ICON_SIZE+(int) x, (int) y);
            g2D.drawLine((int) x, -ICON_SIZE+(int) y, (int) x, ICON_SIZE+(int) y);
          } else if (f==Function.MINUS) {
            g2D.drawLine(-ICON_SIZE+(int) x, (int) y, ICON_SIZE+(int) x, (int) y);
          } else if (f==Function.TIMES) {
            g2D.drawLine((int) (-scale*ICON_SIZE+x), (int) (-scale*ICON_SIZE+y), (int) (scale*ICON_SIZE+x), (int) (scale*ICON_SIZE+y));
            g2D.drawLine((int) (-scale*ICON_SIZE+x), (int) (scale*ICON_SIZE+y), (int) (scale*ICON_SIZE+x), (int) (-scale*ICON_SIZE+y));
          } else if (f==Function.FRACTION) {
            g2D.drawLine((int) (-scale*ICON_SIZE+x), (int) (scale*ICON_SIZE+y), (int) (scale*ICON_SIZE+x), (int) (-scale*ICON_SIZE+y));
          } else if (f==Function.POWER) {
            // remove second scale
            g2D.drawLine((int) (-scale*ICON_SIZE + x), (int) y, (int) x, (int) (-scale*ICON_SIZE + y));
            g2D.drawLine((int) (scale*ICON_SIZE + x), (int) y, (int) x, (int) (-scale*ICON_SIZE + y)); // remove second scale
          } else if (f==Function.EQUALS) {
            g2D.drawLine((int) (-scale*ICON_SIZE+x), (int) (-0.4*ICON_SIZE+y), (int) (scale*ICON_SIZE+x), (int) (-0.4*ICON_SIZE+y));
            g2D.drawLine((int) (-scale*ICON_SIZE+x), (int) (0.4*ICON_SIZE+y), (int) (scale*ICON_SIZE+x), (int) (0.4*ICON_SIZE+y));
          }
          g2D.setStroke(oldStroke);
        }
      }
    }
  }

  @Override
  public boolean isArgumentVisible(Argument argument) {
    if (equationBox==null) {
      return false;
    }
    return equationBox.containsArgument(argument);
  }

  /*+
  private int getFontSize() {
    // WARNING: The panel decoration scales more slowely than large equations.
    return (document.getBoxFontSize() + 2*Box.DEFAULT_BOX_FONT_SIZE) / 3;
  }*/


  @Override
  public boolean requiresRepaint() {
    // return true;
    // return false;
    return repaint || ANIMATE;
  }

  @Override
  public void noteChange(Graphics2D g2D, ColourScheme colourScheme, Argument before, Equation afterEquation, int fontSize, Offset panelRectangle) {
    // Do nothing?
  }
}
TOP

Related Classes of ketUI.panel.GraphDisplay

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.