ArrowView.java

package algorithmen.umleditor;

import java.awt.*;

/**
 * View eines UmlArrow.
 * Zuständig für die graphische Darstellung eines Pfeils.
 */
public class ArrowView extends View implements Observer {
  
  protected View start;
  protected View end;
  
  protected int tipWidth;  // Breite der Ofeilspitze
  protected int tipLength;  // L?nge der Pfeilspitze
  protected int topOffset;  // Abstand des (horiz.) Pfeils von der oberen Linie
  protected Polygon pol;
  // enth?lt Anfangspunkt, rechte Pfeilspitze, Endpunkte, linke Pfeilspitze
  protected Polygon pol3;  // Pfeilspitze, Teil von pol
  
  protected static BasicStroke normal;   // Zeichenstil für normale
  protected static BasicStroke dashed;   // und für gestrichelte Linien
  
  /**
   * Erzeugt eine Pfeilansicht zwischen den Objekten, die durch die Views
   * start und end repräsentiert werden.
   */
  public ArrowView(Arrow model, View start, View end) {
    super(model, new Point());    // Anfangspunkt muss erst berechnet werden
    this.start = start;
    this.end = end;
    tipWidth = 10;
    tipLength = 10;
    topOffset = 15;
    
    computeCoordinates();
    setupStrokes();
    
    // achte auf Änderungen der UmlObjekte
    start.addObserver(this);
    end.addObserver(this);
  }
  
  public Editor createEditor(UmlElement m, Frame p) {
    return new ArrowEditor((Arrow) m, p);
  }
  
  public Editor createEditor(UmlElement m, Dialog p) {
    return new ArrowEditor((Arrow) m, p);
  }
  
  /**
   * Problem: Arrow bezieht sich immer auf zwei UmlElement's,
   * es gibt daher kein Standard-Objekt.
   * Um dem Interface Gen?ge zu tun, werden einfach zwei Standard-Objekte
   * miterzeugt.
   */
  public UmlElement createStandardObject() {
    return new Arrow(new Class(), new Class());
  }
  
  /**
   * Pr?ft, ob der Punkt p im umschlie?enden Polygon des Objekts liegt.
   */
  public boolean contains(Point p) {
    return pol.contains(p);
  }
  
  public void draw(Graphics g) {
    Arrow a = (Arrow) model;
    ArrowType type = a.getType();
    Graphics2D g2 = (Graphics2D) g;
    
    computeCoordinates();
    // lazy initialisation
    // nötig, da Strokes nicht Serializable sind.
    if (normal == null) {
       setupStrokes();
    }
    
    // erst die Linie
    if (type.equals(ArrowType.IMPLEMENTATION) || type.equals(ArrowType.RELATION)) {
      g2.setStroke(dashed);
      g2.drawLine(pol.xpoints[0], pol.ypoints[0], pol.xpoints[2], pol.ypoints[2]);
      g2.setStroke(normal);
    } else {
      g2.drawLine(pol.xpoints[0], pol.ypoints[0], pol.xpoints[2], pol.ypoints[2]);
    }
    
    // dann die Spitze
    if (type.equals(ArrowType.REFERENCE) || type.equals(ArrowType.RELATION)) {
      g2.drawLine(pol.xpoints[2], pol.ypoints[2], pol.xpoints[1], pol.ypoints[1]);
      g2.drawLine(pol.xpoints[2], pol.ypoints[2], pol.xpoints[3], pol.ypoints[3]);
    } else if (!type.equals(ArrowType.ASSOCIATION) ) {
      g2.setPaint(Color.WHITE);
      g2.fill(pol3);
      g2.setPaint(Color.BLACK);
      g2.draw(pol3);
    } // ASSOCIATION hat keinen Pfeil
  }
  
  /**
   * Berechnet Punkte des Pfeils.
   */
  protected void computeCoordinates() {
    Arrow a = (Arrow) model;
    
    // 4 Punkte für den Pfeil, definieren auch das Polygon für Maus-Treffer
    int[] x = new int[4];
    int[] y = new int[4];
    pol = new Polygon(x, y, 4);
    
    // Dreieck für die Pfeilspitze bzw. Raute
    if (a.getType().equals(ArrowType.AGGREGATION)) {
      pol3 = new Polygon(x, y, 4);
    } else {
      pol3 = new Polygon(x, y, 3);
    }
    
    // berechne Start- und Endpunkt
    if (a.getType().isVertical()) {
      // Pfeile beginnen und enden auf der Mitte der oberen bzw. unteren
      // Begrenzung des Objekts
      pol.xpoints[0] = start.getPosition().x + start.getSize().width/2;
      pol.ypoints[0] = start.getPosition().y;
      pol.xpoints[2] = end.getPosition().x + end.getSize().width/2;
      pol.ypoints[2] = end.getPosition().y + end.getSize().height;
      
    } else {
      // Pfeile starten rechts oben und enden links oben
      pol.xpoints[0] = start.getPosition().x + start.getSize().width;
      pol.ypoints[0] = start.getPosition().y + topOffset;
      pol.xpoints[2] = end.getPosition().x;
      pol.ypoints[2] = end.getPosition().y + topOffset;
    }
    
    // berechne Enden der Pfeilspitze
    // e1 = Einheitsvektor in Pfeilrechtung
    // e2 = Einheitsvektor orthogonal dazu
    double e1x = pol.xpoints[2] - pol.xpoints[0];
    double e1y = pol.ypoints[2] - pol.ypoints[0];
    double eabs = Math.sqrt(e1x*e1x + e1y*e1y);
    e1x /= eabs;
    e1y /= eabs;
    double e2x = e1y;
    double e2y = -e1x;
    
    if (! (a.getType().equals(ArrowType.AGGREGATION) )){
      pol.xpoints[1] = (int)(pol.xpoints[2] + tipWidth*e2x - tipLength*e1x);
      pol.ypoints[1] = (int)(pol.ypoints[2] + tipWidth*e2y - tipLength*e1y);
      pol.xpoints[3] = (int)(pol.xpoints[2] - tipWidth*e2x - tipLength*e1x);
      pol.ypoints[3] = (int)(pol.ypoints[2] - tipWidth*e2y - tipLength*e1y);
      pol.invalidate();  // interne Caches von Polygon aktualisieren
      
      pol3.xpoints[0] = pol.xpoints[1];
      pol3.ypoints[0] = pol.ypoints[1];
      pol3.xpoints[1] = pol.xpoints[2];
      pol3.ypoints[1] = pol.ypoints[2];
      pol3.xpoints[2] = pol.xpoints[3];
      pol3.ypoints[2] = pol.ypoints[3];
      pol3.invalidate();
    } else {
      // Aggregation wird mit Raute am Anfang dargestellt
      pol.xpoints[1] = (int)(pol.xpoints[0] + tipWidth*e2x + tipLength*e1x);
      pol.ypoints[1] = (int)(pol.ypoints[0] + tipWidth*e2y + tipLength*e1y);
      pol.xpoints[3] = (int)(pol.xpoints[0] - tipWidth*e2x + tipLength*e1x);
      pol.ypoints[3] = (int)(pol.ypoints[0] - tipWidth*e2y + tipLength*e1y);
      pol.invalidate();  // interne Caches von Polygon aktualisieren
      
      pol3.xpoints[0] = pol.xpoints[0];
      pol3.ypoints[0] = pol.ypoints[0];
      pol3.xpoints[1] = pol.xpoints[1];
      pol3.ypoints[1] = pol.ypoints[1];
      pol3.xpoints[2] = (int)(pol.xpoints[0] + 2*tipLength*e1x);
      pol3.ypoints[2] = (int)(pol.ypoints[0] + 2*tipLength*e1y);
      pol3.xpoints[3] = pol.xpoints[3];
      pol3.ypoints[3] = pol.ypoints[3];
      pol3.invalidate();
    }
  }
    
  public void update()  {
     computeCoordinates();    
  }
   
  protected void setupStrokes() {
    float dash1[] = {10.0f};
    normal = new BasicStroke();
    dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
  }
    
}