Package org.freeplane.view.swing.map.link

Source Code of org.freeplane.view.swing.map.link.ConnectorView

/*
*  Freeplane - mind map editor
*  Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
*  This file is modified by Dimitry Polivaev in 2008.
*
*  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 2 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.freeplane.view.swing.map.link;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;

import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.ColorUtils;
import org.freeplane.features.link.ArrowType;
import org.freeplane.features.link.ConnectorModel;
import org.freeplane.features.link.LinkController;
import org.freeplane.features.link.NodeLinkModel;
import org.freeplane.features.mode.ModeController;
import org.freeplane.view.swing.map.MapView;
import org.freeplane.view.swing.map.NodeView;

/**
* This class represents a ArrowLink around a node.
*/
public class ConnectorView extends AConnectorView{
        private static final int LOOP_INCLINE_OFFSET = 45;
  private static final int NORMAL_LENGTH = 50;
  private static final float[] DOTTED_DASH = new float[] { 4, 7};
  static final Stroke DEF_STROKE = new BasicStroke(1);
  private static final int LABEL_GAP = 4;
  private static final double PRECISION = 2;
  private Shape arrowLinkCurve;
  private Rectangle sourceTextRectangle;
  private Rectangle middleTextRectangle;
  private Rectangle targetTextRectangle;
  final private Color textColor;
  final private Color color;
  final private BasicStroke stroke;
  final private Color bgColor;
  /* Note, that source and target are nodeviews and not nodemodels!. */
  public ConnectorView(final ConnectorModel connectorModel, final NodeView source, final NodeView target, Color bgColor) {
    super(connectorModel, source, target);
    final LinkController linkController = LinkController.getController(getModeController());
    textColor = linkController.getColor(connectorModel);
    this.bgColor =bgColor;
    final int alpha = linkController.getAlpha(connectorModel);
    color =  ColorUtils.createColor(textColor, alpha);

    final int width = linkController.getWidth(connectorModel);
    if (!isSourceVisible() || !isTargetVisible()) {
      stroke = new BasicStroke(width);
    }
    else{
      stroke = UITools.createStroke(width, linkController.getDash(connectorModel));
    }

  }

  public float[] zoomDash(float[] dash) {
    float[] result = dash.clone();
      final double zoom = getZoom();
      for(float f : result){
        f *= zoom;
      }
      return result;
    }

  /**
   */
  private Point calcInclination(final NodeView node, final int dellength) {
    return new Point(dellength, 0);
  }

  /* (non-Javadoc)
   * @see org.freeplane.view.swing.map.link.ILinkView#detectCollision(java.awt.Point, boolean)
   */
  public boolean detectCollision(final Point p, final boolean selectedOnly) {
    if (selectedOnly && (source == null || !source.isSelected()) && (target == null || !target.isSelected())) {
      return false;
    }
    if (arrowLinkCurve == null) {
      return false;
    }
    return new CollisionDetector().detectCollision(p, arrowLinkCurve);
  }

  private Rectangle drawEndPointText(final Graphics2D g, final String text, final Point endPoint, final Point controlPoint) {
    if (text == null || text.equals("")) {
      return null;
    }
    final TextPainter textPainter = new TextPainter(g, text);
    final int textWidth = textPainter.getTextWidth();
    final int textHeight = textPainter.getTextHeight();
    final int x;
    if (controlPoint.x > endPoint.x) {
      x = endPoint.x - textWidth - LABEL_GAP;
    }
    else {
      x = endPoint.x  + LABEL_GAP;
    }
    final int y;
    if (controlPoint.y > endPoint.y) {
      y = endPoint.y  + LABEL_GAP;
    }
    else {
      y = endPoint.y - textHeight - LABEL_GAP;
    }
    textPainter.draw(x, y, textColor, bgColor);
    return new Rectangle(x, y, textWidth, textHeight);
  }
 
  private Rectangle drawMiddleLabel(final Graphics2D g, final String text, final Point centerPoint) {
    if (text == null || text.equals("")) {
      return null;
    }
    final TextPainter textPainter = new TextPainter(g, text);
    final int textWidth = textPainter.getTextWidth();
    final int x = centerPoint.x - textWidth / 2;
    final int textHeight = textPainter.getTextHeight();
    int y = centerPoint.y - textHeight/2;
    textPainter.draw(x, y, textColor, bgColor);
    return new Rectangle(x, y, textWidth, textHeight);
  }

  Shape getArrowLinkCurve() {
    return arrowLinkCurve;
  }

  NodeLinkModel getArrowLinkModel() {
    return connectorModel;
  }

  private Point getCenterPoint() {
    if (arrowLinkCurve == null) {
      return null;
    }
    final double halfLength = getHalfLength();
    final PathIterator pathIterator = arrowLinkCurve.getPathIterator(new AffineTransform(), PRECISION);
    double lastCoords[] = new double[6];
    pathIterator.currentSegment(lastCoords);
    double length = 0;
    for (;;) {
      pathIterator.next();
      final double nextCoords[] = new double[6];
      if (pathIterator.isDone() || PathIterator.SEG_CLOSE == pathIterator.currentSegment(nextCoords)) {
        break;
      }
      final double dx = nextCoords[0] - lastCoords[0];
      final double dy = nextCoords[1] - lastCoords[1];
      final double dr = Math.sqrt(dx * dx + dy * dy);
      length += dr;
      if (length >= halfLength) {
        final double k;
        if (dr < 1) {
          k = 0.5;
        }
        else {
          k = (length - halfLength) / dr;
        }
        return new Point((int) Math.rint(nextCoords[0] - k * dx), (int) Math.rint(nextCoords[1] - k * dy));
      }
      lastCoords = nextCoords;
    }
    throw new RuntimeException("center point not found");
  }

  private double getHalfLength() {
    final PathIterator pathIterator = arrowLinkCurve.getPathIterator(new AffineTransform(), PRECISION);
    double lastCoords[] = new double[6];
    pathIterator.currentSegment(lastCoords);
    double length = 0;
    for (;;) {
      pathIterator.next();
      final double nextCoords[] = new double[6];
      if (pathIterator.isDone() || PathIterator.SEG_CLOSE == pathIterator.currentSegment(nextCoords)) {
        break;
      }
      final double dx = nextCoords[0] - lastCoords[0];
      final double dy = nextCoords[1] - lastCoords[1];
      length += Math.sqrt(dx * dx + dy * dy);
      lastCoords = nextCoords;
    }
    return length / 2;
  }

  private ModeController getModeController() {
    NodeView nodeView = source;
    if (source == null) {
      nodeView = target;
    }
    final MapView mapView = nodeView.getMap();
    return mapView.getModeController();
  }

  /* (non-Javadoc)
   * @see org.freeplane.view.swing.map.link.ILinkView#getModel()
   */
  public ConnectorModel getModel() {
    return connectorModel;
  }


  /**
   * Computes the intersection between two lines. The calculated point is approximate,
   * since integers are used. If you need a more precise result, use doubles
   * everywhere.
   * (c) 2007 Alexander Hristov. Use Freely (LGPL license). http://www.ahristov.com
   *
   * @param x1 Point 1 of Line 1
   * @param y1 Point 1 of Line 1
   * @param x2 Point 2 of Line 1
   * @param y2 Point 2 of Line 1
   * @param x3 Point 1 of Line 2
   * @param y3 Point 1 of Line 2
   * @param x4 Point 2 of Line 2
   * @param y4 Point 2 of Line 2
   * @return Point where the segments intersect, or null if they don't
   */
  Point intersection(final double x1, final double y1, final double x2, final double y2, final double x3,
                     final double y3, final double x4, final double y4) {
    final double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
    if (d == 0) {
      return null;
    }
    final int xi = (int) (((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d);
    final int yi = (int) (((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d);
    if (xi + 2 < Math.min(x1, x2) || xi - 2 > Math.max(x1, x2)) {
      return null;
    }
    return new Point(xi, yi);
  }

  /**
   * Computes the unitary normal vector of a segment
   * @param x1 Starting point of the segment
   * @param y1 Starting point of the segment
   * @param x2 Ending point of the segment
   * @param y2 Ending point of the segment
   * @return
   */
  Point2D.Double normal(final double x1, final double y1, final double x2, final double y2) {
    double nx, ny;
    if (x1 == x2) {
      nx = Math.signum(y2 - y1);
      ny = 0;
    }
    else {
      final double f = (y2 - y1) / (x2 - x1);
      nx = f * Math.signum(x2 - x1) / Math.sqrt(1 + f * f);
      ny = -1 * Math.signum(x2 - x1) / Math.sqrt(1 + f * f);
    }
    return new Point2D.Double(nx, ny);
  }

  /* (non-Javadoc)
   * @see org.freeplane.view.swing.map.link.ILinkView#paint(java.awt.Graphics)
   */
  public void paint(final Graphics graphics) {
        final boolean selfLink = getSource() == getTarget();
    if (!isSourceVisible() && !isTargetVisible()) {
      return;
    }
    Point startPoint = null, endPoint = null, startPoint2 = null, endPoint2 = null;
    boolean targetIsLeft = false;
    boolean sourceIsLeft = false;
    final Graphics2D g = (Graphics2D) graphics.create();
    final Color oldColor = g.getColor();
    g.setColor(color);
    /* set stroke. */
    g.setStroke(stroke);
    if (isSourceVisible()) {
      startPoint = source.getLinkPoint(connectorModel.getStartInclination());
      sourceIsLeft = source.isLeft();
    }
    if (isTargetVisible()) {
      endPoint = target.getLinkPoint(connectorModel.getEndInclination());
      targetIsLeft = target.isLeft();
    }
    if (connectorModel.getEndInclination() == null || connectorModel.getStartInclination() == null) {
      final int dellength = isSourceVisible() && isTargetVisible() ? Math.max(40, (int)(startPoint.distance(endPoint) / getZoom())) : 40;
      if (isSourceVisible() && connectorModel.getStartInclination() == null) {
        final Point incl = calcInclination(source, dellength);
        connectorModel.setStartInclination(incl);
        startPoint = source.getLinkPoint(connectorModel.getStartInclination());
      }
      if (isTargetVisible() && connectorModel.getEndInclination() == null) {
        final Point incl = calcInclination(target, dellength);
        incl.y = -incl.y;
        if (selfLink) {
          fixInclineIfLoopNode(incl);
        }
        connectorModel.setEndInclination(incl);
        endPoint = target.getLinkPoint(connectorModel.getEndInclination());
      }
    }
    if (startPoint != null) {
      startPoint2 = new Point(startPoint);
      Point startInclination = connectorModel.getStartInclination();
      if(endPoint == null){
        normalizeLength(NORMAL_LENGTH, startInclination);
      }
      startPoint2.translate(((sourceIsLeft) ? -1 : 1) * getMap().getZoomed(startInclination.x),
        getMap().getZoomed(startInclination.y));

    }
    if (endPoint != null) {
      endPoint2 = new Point(endPoint);
      Point endInclination = connectorModel.getEndInclination();
      if(startPoint == null){
        normalizeLength(NORMAL_LENGTH, endInclination);
      }
      endPoint2.translate(((targetIsLeft) ? -1 : 1) * getMap().getZoomed(endInclination.x), getMap()
        .getZoomed(endInclination.y));
    }
    paintCurve(g, startPoint, startPoint2, endPoint2, endPoint);
    drawLabels(g, startPoint, startPoint2, endPoint2, endPoint);
    g.setColor(oldColor);
  }

  private void normalizeLength(int normalLength, Point startInclination) {
    double k = normalLength / Math.sqrt(startInclination.x * startInclination.x + startInclination.y * startInclination.y);
    startInclination.x *= k;
    startInclination.y *= k;
  }

  private Shape createLine(Point p1, Point p2) {
      return new Line2D.Float(p1, p2);
    }

  private Shape createLinearPath(Point startPoint, Point startPoint2, Point endPoint2, Point endPoint) {
      final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 4);
      generalPath.moveTo(startPoint.x, startPoint.y);
      generalPath.lineTo(startPoint2.x, startPoint2.y);
      generalPath.lineTo(endPoint2.x, endPoint2.y);
      generalPath.lineTo(endPoint.x, endPoint.y);
    return generalPath;
    }

  private void paintCurve(final Graphics2D g, Point startPoint, Point startPoint2, Point endPoint2, Point endPoint) {
    final boolean selfLink = getSource() == getTarget();
    final boolean isLine = ConnectorModel.Shape.LINE.equals(connectorModel.getShape());
    if (startPoint != null && endPoint != null) {
      if(isLine) {
                            if (selfLink) {
        arrowLinkCurve = createLine(startPoint, startPoint2);
                            }
                            else {
        arrowLinkCurve = createLine(startPoint, endPoint);
                            }
                        }
                        else if (ConnectorModel.Shape.LINEAR_PATH.equals(connectorModel.getShape()))
                            arrowLinkCurve = createLinearPath(startPoint, startPoint2, endPoint2, endPoint);
                        else
                            arrowLinkCurve = createCubicCurve2D(startPoint, startPoint2, endPoint2, endPoint);
    }
    else
      arrowLinkCurve = null;
      if (arrowLinkCurve != null) {
      g.draw(arrowLinkCurve);
    }
    if (isSourceVisible() && !connectorModel.getStartArrow().equals(ArrowType.NONE)) {
      if(!selfLink && isLine && endPoint != null)
        paintArrow(g, endPoint, startPoint);
      else
        paintArrow(g, startPoint2, startPoint);
    }
    if (isTargetVisible() && !connectorModel.getEndArrow().equals(ArrowType.NONE)) {
      if(isLine && startPoint != null) {
                            if (selfLink)
        paintArrow(g, startPoint, startPoint2);
                            else
        paintArrow(g, startPoint, endPoint);
                        }
      else
      paintArrow(g, endPoint2, endPoint);
    }
    if (connectorModel.getShowControlPointsFlag()) {
      g.setColor(textColor);
      g.setStroke(new BasicStroke(stroke.getLineWidth(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, DOTTED_DASH, 0));
    }
    if (connectorModel.getShowControlPointsFlag() || !isSourceVisible() || !isTargetVisible()) {
      if (startPoint != null) {
        g.drawLine(startPoint.x, startPoint.y, startPoint2.x, startPoint2.y);
        drawCircle(g, startPoint2, source.getZoomedFoldingSymbolHalfWidth());
          if (arrowLinkCurve == null) {
            arrowLinkCurve = createLine(startPoint, startPoint2);
        }
      }
      if (endPoint != null && !(selfLink && isLine)) {
        g.drawLine(endPoint.x, endPoint.y, endPoint2.x, endPoint2.y);
        drawCircle(g, endPoint2, target.getZoomedFoldingSymbolHalfWidth());
          if (arrowLinkCurve == null) {
            arrowLinkCurve = createLine(endPoint, endPoint2);
        }
      }
    }
    }

  private void drawCircle(Graphics2D g, Point p, int hw) {
    g.setStroke(DEF_STROKE);
    g.fillOval(p.x - hw, p.y - hw, hw*2, hw*2);
    }

  private void paintArrow(final Graphics2D g, Point from, Point to) {
      paintArrow(from, to, g, getZoom() * 10);
    }

  private void drawLabels(final Graphics2D g, Point startPoint, Point startPoint2, Point endPoint2, Point endPoint) {
      final String sourceLabel = connectorModel.getSourceLabel();
    final String middleLabel = connectorModel.getMiddleLabel();
    final String targetLabel = connectorModel.getTargetLabel();
    if (sourceLabel == null && middleLabel == null && targetLabel == null) {
      return;
    }

    final Font oldFont = g.getFont();
    final String fontFamily = connectorModel.getLabelFontFamily();
        final int fontSize = Math.round (connectorModel.getLabelFontSize() * UITools.FONT_SCALE_FACTOR);
        final Font linksFont = new Font(fontFamily, 0, getZoomed(fontSize));
        g.setFont(linksFont);

    if (startPoint != null) {
      sourceTextRectangle = drawEndPointText(g, sourceLabel, startPoint, startPoint2);
      if (endPoint == null) {
        middleTextRectangle = drawEndPointText(g, middleLabel, startPoint2, startPoint);
      }
    }
    if (endPoint != null) {
      targetTextRectangle = drawEndPointText(g, targetLabel, endPoint, endPoint2);
      if (startPoint == null) {
        middleTextRectangle = drawEndPointText(g, middleLabel, endPoint2, endPoint);
      }
    }
                if (startPoint != null && endPoint != null) {
                    middleTextRectangle = drawMiddleLabel(g, middleLabel, getCenterPoint());
    }
    g.setFont(oldFont);
    }

  private CubicCurve2D createCubicCurve2D(Point startPoint, Point startPoint2, Point endPoint2, Point endPoint) {
      final CubicCurve2D arrowLinkCurve = new CubicCurve2D.Double();
    if (startPoint != null && endPoint != null) {
      arrowLinkCurve.setCurve(startPoint, startPoint2, endPoint2, endPoint);
    }
    else if (startPoint != null) {
      arrowLinkCurve.setCurve(startPoint, startPoint2, startPoint, startPoint2);
    }
    else if (endPoint != null) {
      arrowLinkCurve.setCurve(endPoint, endPoint2, endPoint, endPoint2);
    }
      return arrowLinkCurve;
    }

  /* (non-Javadoc)
   * @see org.freeplane.view.swing.map.link.ILinkView#increaseBounds(java.awt.Rectangle)
   */
  public void increaseBounds(final Rectangle innerBounds) {
    final Shape arrowLinkCurve = getArrowLinkCurve();
    if (arrowLinkCurve == null) {
      return;
    }
    final Rectangle arrowViewBigBounds = arrowLinkCurve.getBounds();
    if (!innerBounds.contains(arrowViewBigBounds)) {
      final Rectangle arrowViewBounds = PathBBox.getBBox(arrowLinkCurve).getBounds();
      innerBounds.add(arrowViewBounds);
    }
    increaseBounds(innerBounds, sourceTextRectangle);
    increaseBounds(innerBounds, middleTextRectangle);
    increaseBounds(innerBounds, targetTextRectangle);
  }

  private void increaseBounds(Rectangle innerBounds, Rectangle rect) {
      if (rect != null)
                innerBounds.add(rect);
        }

        private void fixInclineIfLoopNode(Point endIncline) {
            if (endIncline.y < 0) {
                endIncline.y -= LOOP_INCLINE_OFFSET;
            }
            else {
                endIncline.y += LOOP_INCLINE_OFFSET;
            }
        }
}
TOP

Related Classes of org.freeplane.view.swing.map.link.ConnectorView

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.