Package org.stathissideris.ascii2image.graphics

Source Code of org.stathissideris.ascii2image.graphics.DiagramShape

/**
* ditaa - Diagrams Through Ascii Art
*
* Copyright (C) 2004-2011 Efstathios Sideris
*
* ditaa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* ditaa 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ditaa.  If not, see <http://www.gnu.org/licenses/>.
*  
*/
package org.stathissideris.ascii2image.graphics;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.stathissideris.ascii2image.core.RenderingOptions;
import org.stathissideris.ascii2image.text.*;

/**
*
* @author Efstathios Sideris
*/
public class DiagramShape extends DiagramComponent {
 
  private static final boolean DEBUG = false;
 
  public static final int TYPE_SIMPLE = 0;
  public static final int TYPE_ARROWHEAD = 1;
  public static final int TYPE_POINT_MARKER = 2;
  public static final int TYPE_DOCUMENT = 3;
  public static final int TYPE_STORAGE = 4;
  public static final int TYPE_IO = 5;
  public static final int TYPE_DECISION = 6;
  public static final int TYPE_MANUAL_OPERATION = 7; // upside-down trapezoid
  public static final int TYPE_TRAPEZOID = 8; // rightside-up trapezoid
  public static final int TYPE_ELLIPSE = 9;
  public static final int TYPE_CUSTOM = 9999;

  /** The slope of side lines on trapezoids (mo, tr) and parallelograms (io). */
  public static final float SHAPE_SLOPE = 8;

  protected int type = TYPE_SIMPLE;

  private Color fillColor = null;
  private Color strokeColor = Color.black;
 
  private boolean isClosed = false;
  private boolean isStrokeDashed = false;

  protected ArrayList<ShapePoint> points = new ArrayList<ShapePoint>();

  CustomShapeDefinition definition = null;

  public static void main(String[] args) {
  }

  public static DiagramShape createArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if(!grid.isArrowhead(cell)) return null;
    if(grid.isNorthArrowhead(cell)) return createNorthArrowhead(grid, cell, cellXSize, cellYSize);
    if(grid.isSouthArrowhead(cell)) return createSouthArrowhead(grid, cell, cellXSize, cellYSize);
    if(grid.isWestArrowhead(cell)) return createWestArrowhead(grid, cell, cellXSize, cellYSize);
    if(grid.isEastArrowhead(cell)) return createEastArrowhead(grid, cell, cellXSize, cellYSize);
    return null;
  }

  private static DiagramShape createNorthArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if(!grid.isNorthArrowhead(cell)) return null;
    DiagramShape shape = new DiagramShape();
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMidX(cell,cellXSize),
      Diagram.getCellMinY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMinX(cell,cellXSize),
      Diagram.getCellMaxY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMaxX(cell,cellXSize),
      Diagram.getCellMaxY(cell,cellYSize)));
    shape.setClosed(true);
    shape.setFillColor(Color.black);
    shape.setStrokeColor(Color.black);
    shape.setType(TYPE_ARROWHEAD);
    return shape;
  }

  private static DiagramShape createSouthArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if(!grid.isSouthArrowhead(cell)) return null;
    DiagramShape shape = new DiagramShape();
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMinX(cell,cellXSize),
      Diagram.getCellMinY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMidX(cell,cellXSize),
      Diagram.getCellMaxY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMaxX(cell,cellXSize),
      Diagram.getCellMinY(cell,cellYSize)));
    shape.setClosed(true);
    shape.setFillColor(Color.black);
    shape.setStrokeColor(Color.black);
    shape.setType(TYPE_ARROWHEAD);
    return shape;
  }

  private static DiagramShape createWestArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if(!grid.isWestArrowhead(cell)) return null;
    DiagramShape shape = new DiagramShape();
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMaxX(cell,cellXSize),
      Diagram.getCellMinY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMinX(cell,cellXSize),
      Diagram.getCellMidY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMaxX(cell,cellXSize),
      Diagram.getCellMaxY(cell,cellYSize)));
    shape.setClosed(true);
    shape.setFillColor(Color.black);
    shape.setStrokeColor(Color.black);
    shape.setType(TYPE_ARROWHEAD);
    return shape;
  }
 
  private static DiagramShape createEastArrowhead(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if(!grid.isEastArrowhead(cell)) return null;
    DiagramShape shape = new DiagramShape();
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMinX(cell,cellXSize),
      Diagram.getCellMinY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMaxX(cell,cellXSize),
      Diagram.getCellMidY(cell,cellYSize)));
    shape.addToPoints(new ShapePoint(
      Diagram.getCellMinX(cell,cellXSize),
      Diagram.getCellMaxY(cell,cellYSize)));
    shape.setClosed(true);
    shape.setFillColor(Color.black);
    shape.setStrokeColor(Color.black);
    shape.setType(TYPE_ARROWHEAD);
    return shape;
  }

  public static DiagramShape createSmallLine(TextGrid grid, TextGrid.Cell cell, int cellXSize, int cellYSize) {
    if (grid.isLine(cell)) {
      DiagramShape shape = new DiagramShape();
      if (grid.isHorizontalLine(cell)) {
        shape.addToPoints(
          new ShapePoint(
            cell.x * cellXSize,
            cell.y * cellYSize + cellYSize / 2));
        shape.addToPoints(
          new ShapePoint(
            cell.x * cellXSize + cellXSize - 1,
            cell.y * cellYSize + cellYSize / 2));
      } else if (grid.isVerticalLine(cell)) {
        shape.addToPoints(
          new ShapePoint(
            cell.x * cellXSize + cellXSize / 2,
            cell.y * cellYSize));
        shape.addToPoints(
          new ShapePoint(
            cell.x * cellXSize + cellXSize / 2,
            cell.y * cellYSize + cellYSize - 1));
      }
     
      //the -1 above, make a difference: the second point
      //should not fall into the next cell, because this
      //results in a failure of a proper end-of-line
      //plotting correction
      return shape;
    }
    return null;
  }

  public void addToPoints(ShapePoint point){
    points.add(point);
  }
 
  public Iterator getPointsIterator(){
    return points.iterator();
  }
 
  public void scale(float factor){
    Iterator it = getPointsIterator();
    while(it.hasNext()){
      ShapePoint point = (ShapePoint) it.next();
      point.x *= factor;
      point.y *= factor;
    }
  }
 
  public boolean isEmpty(){
    return points.isEmpty();
  }
 
  public boolean isFilled(){
    return (fillColor != null);
  }
 
  public void setIsNotFilled(){
    fillColor = null;
  }
 
  public boolean isPointLinesEnd(ShapePoint point){
    if(isClosed()) return false; //no line-ends in closed shapes!
    if(point == points.get(0)) return true;
    if(point == points.get(points.size() - 1)) return true;
    return false;
  }
 
  //TODO: method in development: isRectangle()
  public boolean isRectangle(){
    if(points.size() != 4) return false;
    ShapePoint p1 = (ShapePoint) points.get(0);
    ShapePoint p2 = (ShapePoint) points.get(1);
    ShapePoint p3 = (ShapePoint) points.get(2);
    ShapePoint p4 = (ShapePoint) points.get(3);
    if(p1.isInLineWith(p2)
      && p2.isInLineWith(p3)
      && p3.isInLineWith(p4)
      && p4.isInLineWith(p1)) return true;
    return false;
  }
 
  /**
   * Crude way to determine which of the two shapes is smaller,
   * based just on their bounding boxes. Used in markup
   * assignment precendence.
   *
   * @param other
   * @return
   */
  public boolean isSmallerThan(DiagramShape other){
    Rectangle bounds = getBounds();
    Rectangle otherBounds = other.getBounds();
   
    int area = bounds.height * bounds.width;
    int otherArea = otherBounds.height * otherBounds.width;
   
    if(area < otherArea) {
      return true;
    }
    return false;
  }
 
  /**
   * @return
   */
  public Color getFillColor() {
    return fillColor;
  }

  /**
   * @return
   */
  public Color getStrokeColor() {
    return strokeColor;
  }

  /**
   * @param color
   */
  public void setFillColor(Color color) {
    fillColor = color;
  }

  /**
   * @param color
   */
  public void setStrokeColor(Color color) {
    strokeColor = color;
  }

  /**
   * @return
   */
  public boolean isClosed() {
    return isClosed;
  }

  /**
   * @param b
   */
  public void setClosed(boolean b) {
    isClosed = b;
  }

  public void printDebug(){
    System.out.print("DiagramShape: ");
    System.out.println(points.size()+" points");
  }

  /**
   * @return
   */
  public ArrayList getPoints() {
    return points;
  }

  public ShapePoint getPoint(int i) {
    return (ShapePoint) points.get(i);
  }

  public void setPoint(int i, ShapePoint point) {
    points.set(i, point);
  }


  public boolean equals(Object object){
    DiagramShape shape = null;
    if(!(object instanceof DiagramShape)) { return false; }
    else shape = (DiagramShape) object;
    if(getPoints().size() != shape.getPoints().size()) return false;
   
    if(DEBUG) System.out.println("comparing shapes:");
   
    if(DEBUG) System.out.println("points1: ");
    HashMap points1 = new HashMap();
    Iterator it = getPointsIterator();
    while(it.hasNext()){
      ShapePoint point = (ShapePoint) it.next();
      points1.put( ""+((int) point.x)+","+((int) point.y), null);
      if(DEBUG) System.out.println(((int) point.x)+", "+((int) point.y));
    }
   
    if(DEBUG) System.out.println("points2: ");
    HashMap points2 = new HashMap();
    it = shape.getPointsIterator();
    while(it.hasNext()){
      ShapePoint point = (ShapePoint) it.next();
      points2.put( ""+((int) point.x)+","+((int) point.y), null);
      if(DEBUG) System.out.println(((int) point.x)+", "+((int) point.y));
    }
   
    it = points1.keySet().iterator();
    while(it.hasNext()){
      String key = (String) it.next();
      if(!points2.containsKey(key)) {
        if (DEBUG)
          System.out.println("\tare not equal");
        return false;
      }
    }
    if (DEBUG)
      System.out.println("\tare equal");
    return true;
  }

  public GeneralPath makeIntoPath() {
    int size = getPoints().size();
   
    if(size < 2) return null;
   
    GeneralPath path = new GeneralPath();
    ShapePoint point = (ShapePoint) getPoints().get(0);
    path.moveTo((int) point.x, (int) point.y);
    for(int i = 1; i < size; i++){
      point = (ShapePoint) getPoints().get(i);
      path.lineTo((int) point.x, (int) point.y);
    }
    if(isClosed() && size > 2){
      path.closePath();
    }
    return path;
  }

  public GeneralPath makeMarkerPath(Diagram diagram){
    if(points.size() != 1) return null;
    ShapePoint center = (ShapePoint) this.getPoint(0);
    float diameter =
      (float) 0.7 * Math.min(diagram.getCellWidth(), diagram.getCellHeight());
    return new GeneralPath(new Ellipse2D.Float(
      center.x - diameter/2,
      center.y - diameter/2,
      diameter,
      diameter));
  }

  public Rectangle getBounds(){
    Rectangle bounds = makeIntoPath().getBounds();
    return bounds;
  }
 
  public GeneralPath makeIntoRenderPath(Diagram diagram, RenderingOptions options) {
    int size = getPoints().size();
   
    if(getType() == TYPE_POINT_MARKER){
      return makeMarkerPath(diagram);
    }
   
    if(getType() == TYPE_DOCUMENT && points.size() == 4){
      return makeDocumentPath(diagram);
    }

    if(getType() == TYPE_STORAGE && points.size() == 4){
      return makeStoragePath(diagram);
    }

    if(getType() == TYPE_IO && points.size() == 4){
      return makeIOPath(diagram, options);
    }

    if(getType() == TYPE_DECISION && points.size() == 4){
      return makeDecisionPath(diagram);
    }

    if(getType() == TYPE_MANUAL_OPERATION && points.size() == 4){
      return makeTrapezoidPath(diagram, options, true);
    }

    if(getType() == TYPE_TRAPEZOID && points.size() == 4){
      return makeTrapezoidPath(diagram, options, false);
    }

    if(getType() == TYPE_ELLIPSE && points.size() == 4){
      return makeEllipsePath(diagram);
    }

    if(size < 2) return null;

    GeneralPath path = new GeneralPath();
    ShapePoint point = (ShapePoint) getPoints().get(0);
    TextGrid.Cell cell = diagram.getCellFor(point);
    //path.moveTo((int) point.x, (int) point.y);
    ShapePoint previous = (ShapePoint) getPoints().get(size - 1);
    ShapePoint next = (ShapePoint) getPoints().get(1);
    ShapePoint entryPoint;
    ShapePoint exitPoint;
   
    if(point.getType() == ShapePoint.TYPE_NORMAL){
      //if(isClosed()){
        path.moveTo((int) point.x, (int) point.y);
      /*} else {
        ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(point, next, diagram);
        path.moveTo((int) projectionPoint.x, (int) projectionPoint.y);
      }*/
    } else if(point.getType() == ShapePoint.TYPE_ROUND){
      entryPoint = getCellEdgePointBetween(point, previous, diagram);
      exitPoint = getCellEdgePointBetween(point, next, diagram);
      path.moveTo(entryPoint.x, entryPoint.y);
      path.quadTo(point.x, point.y, exitPoint.x, exitPoint.y);     
    }

    for(int i = 1; i < size; i++){
      previous = point;
      point = (ShapePoint) getPoints().get(i);
      if(i < size - 1)
        next = (ShapePoint) getPoints().get(i + 1);
      else next = (ShapePoint) getPoints().get(0);

      cell = diagram.getCellFor(point);

      if(point.getType() == ShapePoint.TYPE_NORMAL)
        //if(!isPointLinesEnd(point))
          path.lineTo((int) point.x, (int) point.y);
        /*else { //it is line's end, so we plot it at the projected intersection of the line with the cell's edge
          ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(point, previous, diagram);
          path.lineTo((int) projectionPoint.x, (int) projectionPoint.y);
        }*/
      else if(point.getType() == ShapePoint.TYPE_ROUND){
        entryPoint = getCellEdgePointBetween(point, previous, diagram);
        exitPoint = getCellEdgePointBetween(point, next, diagram);

        path.lineTo(entryPoint.x, entryPoint.y);
        path.quadTo(point.x, point.y, exitPoint.x, exitPoint.y);
        //if(!isPointLinesEnd(next)){
          if(next.getType() == ShapePoint.TYPE_NORMAL)
            path.lineTo(next.x, next.y);
          else if(next.getType() == ShapePoint.TYPE_ROUND){
            entryPoint = getCellEdgePointBetween(next, point, diagram);
            path.lineTo(entryPoint.x, entryPoint.y);         
          }
        /*} else {
          entryPoint = getCellEdgeProjectionPointBetween(next, point, diagram);
          path.lineTo(entryPoint.x, entryPoint.y);                   
        }*/
      }
    }
    //TODO: this shouldn't be needed, but it is!
    if(isClosed() && size > 2){
      path.closePath();
    }
    return path;
  }
 
  public ArrayList getEdges(){
    ArrayList edges = new ArrayList();
    if(this.points.size() == 1) return edges;
    int noOfPoints = points.size();
    for(int i = 0; i < noOfPoints - 1; i++){
      ShapePoint startPoint = (ShapePoint) points.get(i);
      ShapePoint endPoint = (ShapePoint) points.get(i + 1);
      ShapeEdge edge = new ShapeEdge(startPoint, endPoint, this);
      edges.add(edge);
    }
    //if it is closed return edge that connects the
    //last point to the first
    if(this.isClosed()){
      ShapePoint firstPoint = (ShapePoint) points.get(0);
      ShapePoint lastPoint = (ShapePoint) points.get(points.size() - 1);
      ShapeEdge edge = new ShapeEdge(lastPoint, firstPoint, this);
      edges.add(edge);
    }
    return edges;
  }

  /**
   * Finds the point that represents the intersection between the cell edge
   * that contains pointInCell and the line connecting pointInCell and
   * otherPoint.
   *
     * Returns C, if A is point in cell and B is otherPoint:
   * <pre>
   *     Cell
   *    +-----+
   *    |  A  |C                 B
   *    |  *--*------------------*
   *    |     |
   *    +-----+
   *</pre>
   *
   * @param pointInCell
   * @param otherPoint
   * @return
   */
  public ShapePoint getCellEdgePointBetween(ShapePoint pointInCell, ShapePoint otherPoint, Diagram diagram){
    if(pointInCell == null || otherPoint == null || diagram == null)
      throw new IllegalArgumentException("None of the parameters can be null");
    if(pointInCell.equals(otherPoint))
      throw new IllegalArgumentException("The two points cannot be the same");

    ShapePoint result = null;
    TextGrid.Cell cell = diagram.getCellFor(pointInCell);
   
    if(cell == null)
      throw new RuntimeException("Upexpected error, cannot find cell corresponding to point "+pointInCell+" for diagram "+diagram);
   
    if(otherPoint.isNorthOf(pointInCell))
      result = new ShapePoint(pointInCell.x,
                    diagram.getCellMinY(cell));
    else if(otherPoint.isSouthOf(pointInCell))
      result = new ShapePoint(pointInCell.x,
                    diagram.getCellMaxY(cell));
    else if(otherPoint.isWestOf(pointInCell))
      result = new ShapePoint(diagram.getCellMinX(cell),
                    pointInCell.y);
    else if(otherPoint.isEastOf(pointInCell))
      result = new ShapePoint(diagram.getCellMaxX(cell),
                    pointInCell.y);
   
    if(result == null)
      throw new RuntimeException("Upexpected error, cannot find cell edge point for points "+pointInCell+" and "+otherPoint+" for diagram "+diagram);

   
    return result;
  }


  /**
   *
   * Returns C, if A is point in cell and B is otherPoint:
   *
   * <pre>
   *     Cell
   *    +-----+
   *    |  A  |                  B
   *  C *--*--+------------------*
   *    |     |
   *    +-----+
   * </pre>
   *
   * @param pointInCell
   * @param otherPoint
   * @param diagram
   * @return
   */

  public ShapePoint getCellEdgeProjectionPointBetween(ShapePoint pointInCell, ShapePoint otherPoint, Diagram diagram){
    if(pointInCell == null || otherPoint == null || diagram == null)
      throw new IllegalArgumentException("None of the parameters can be null");
    if(pointInCell.equals(otherPoint))
      throw new IllegalArgumentException("The two points cannot be the same: "+pointInCell+" and "+otherPoint+" passed");

    ShapePoint result = null;
    TextGrid.Cell cell = diagram.getCellFor(pointInCell);
   
    if(cell == null)
      throw new RuntimeException("Upexpected error, cannot find cell corresponding to point "+pointInCell+" for diagram "+diagram);
   
    if(otherPoint.isNorthOf(pointInCell))
      result = new ShapePoint(pointInCell.x,
                    diagram.getCellMaxY(cell));
    else if(otherPoint.isSouthOf(pointInCell))
      result = new ShapePoint(pointInCell.x,
                    diagram.getCellMinY(cell));
    else if(otherPoint.isWestOf(pointInCell))
      result = new ShapePoint(diagram.getCellMaxX(cell),
                    pointInCell.y);
    else if(otherPoint.isEastOf(pointInCell))
      result = new ShapePoint(diagram.getCellMinX(cell),
                    pointInCell.y);
   
    if(result == null)
      throw new RuntimeException("Upexpected error, cannot find cell edge point for points "+pointInCell+" and "+otherPoint+" for diagram "+diagram);

   
    return result;
  }

  public boolean contains(ShapePoint point){
    GeneralPath path = makeIntoPath();
    if(path != null) return path.contains(point);
    return false;
  }

  public boolean contains(Rectangle2D rect){
    GeneralPath path = makeIntoPath();
    if(path != null) return path.contains(rect);
    return false;
  }

  public boolean intersects(Rectangle2D rect){
    GeneralPath path = makeIntoPath();
    if(path != null) return path.intersects(rect);
    return false;
  }
 
  public boolean dropsShadow(){
    return (isClosed()
        && getType() != DiagramShape.TYPE_ARROWHEAD
        && getType() != DiagramShape.TYPE_POINT_MARKER
        && !isStrokeDashed());
  }

  /**
   * @return
   */
  public int getType() {
    return type;
  }

  /**
   * @param i
   */
  public void setType(int i) {
    type = i;
  }
 
  public void moveEndsToCellEdges(TextGrid grid, Diagram diagram){
    if(isClosed()) return;
   
    ShapePoint linesEnd = (ShapePoint) points.get(0);
    ShapePoint nextPoint = (ShapePoint) points.get(1);

    ShapePoint projectionPoint = getCellEdgeProjectionPointBetween(linesEnd, nextPoint, diagram);
   
    linesEnd.moveTo(projectionPoint);

    linesEnd = (ShapePoint) points.get(points.size() - 1);
    nextPoint = (ShapePoint) points.get(points.size() - 2);

    projectionPoint = getCellEdgeProjectionPointBetween(linesEnd, nextPoint, diagram);
   
    linesEnd.moveTo(projectionPoint);
  }
 
  public void connectEndsToAnchors(TextGrid grid, Diagram diagram){
    if(isClosed()) return;

    ShapePoint linesEnd;
    ShapePoint nextPoint;

    linesEnd = (ShapePoint) points.get(0);
    nextPoint = (ShapePoint) points.get(1);
   
    connectEndToAnchors(grid, diagram, nextPoint, linesEnd);
   
    linesEnd = (ShapePoint) points.get(points.size() - 1);
    nextPoint = (ShapePoint) points.get(points.size() - 2);
   
    connectEndToAnchors(grid, diagram, nextPoint, linesEnd);
   
  }
 
 
  //TODO: improve connect Ends To Arrowheads to take direction into account
  private void connectEndToAnchors(
      TextGrid grid,
      Diagram diagram,
      ShapePoint nextPoint,
      ShapePoint linesEnd){

    if(isClosed()) return;

    TextGrid.Cell anchorCell;
    anchorCell = getPossibleAnchorCell(linesEnd, nextPoint, diagram);
   
    if(grid.isArrowhead(anchorCell)){
      linesEnd.x = diagram.getCellMidX(anchorCell);
      linesEnd.y = diagram.getCellMidY(anchorCell);
      linesEnd.setLocked(true);
    } else if (grid.isCorner(anchorCell) || grid.isIntersection(anchorCell)){
      linesEnd.x = diagram.getCellMidX(anchorCell);
      linesEnd.y = diagram.getCellMidY(anchorCell);
      linesEnd.setLocked(true);
    }
  }

  /**
   * Given the end of a line, the next point and a Diagram, it
   * returns the cell that may contain intersections or arrowheads
   * to which the line's end should be connected
   *
   * @param linesEnd
   * @param nextPoint
   * @param diagram
   * @return
   */
  private static TextGrid.Cell getPossibleAnchorCell(
      ShapePoint linesEnd,
      ShapePoint nextPoint,
      Diagram diagram
      ){
    ShapePoint cellPoint = null;

    if(nextPoint.isNorthOf(linesEnd))
      cellPoint = new ShapePoint(linesEnd.x, linesEnd.y + diagram.getCellHeight());
    if(nextPoint.isSouthOf(linesEnd))
      cellPoint = new ShapePoint(linesEnd.x, linesEnd.y - diagram.getCellHeight());
    if(nextPoint.isWestOf(linesEnd))
      cellPoint = new ShapePoint(linesEnd.x + diagram.getCellWidth(), linesEnd.y);
    if(nextPoint.isEastOf(linesEnd))
      cellPoint = new ShapePoint(linesEnd.x - diagram.getCellWidth(), linesEnd.y);

    return diagram.getCellFor(cellPoint);
  }

 
  public String toString(){
    String s = "DiagramShape, "+points.size()+" points: ";
    Iterator it = getPointsIterator();
    while(it.hasNext()){
      ShapePoint point = (ShapePoint) it.next();
      s += point;
      if(it.hasNext()) s += " ";
    }
    return s;
  }

  /**
   * @return
   */
  public boolean isStrokeDashed() {
    return isStrokeDashed;
  }

  /**
   * @param b
   */
  public void setStrokeDashed(boolean b) {
    isStrokeDashed = b;
  }

  private GeneralPath makeStoragePath(Diagram diagram) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY());
    ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY());
    ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY());
    ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY());
   
    ShapePoint pointMidTop = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMinY());
    ShapePoint pointMidBottom = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY());
 
    float diameterX = bounds.width;
    float diameterY = 0.75f * diagram.getCellHeight();
 
    //control point offset X, and Y
    float cpOffsetX = bounds.width / 6;
    float cpOffsetYTop = diagram.getCellHeight() / 2;
    float cpOffsetYBottom = 10 * diagram.getCellHeight() / 14;
    //float cpOffsetYBottom = cpOffsetYTop;
 
    GeneralPath path = new GeneralPath();
 
    //top of cylinder
    path.moveTo(point1.x, point1.y);
    path.curveTo(
      point1.x + cpOffsetX, point1.y + cpOffsetYTop,
      point2.x - cpOffsetX, point2.y + cpOffsetYTop,
      point2.x, point2.y
      );
    path.curveTo(
      point2.x - cpOffsetX, point2.y - cpOffsetYTop,
      point1.x + cpOffsetX, point1.y - cpOffsetYTop,
      point1.x, point1.y
      );
 
    //side of cylinder
    path.moveTo(point1.x, point1.y);
    path.lineTo(point4.x, point4.y);
   
    path.curveTo(
      point4.x + cpOffsetX, point4.y + cpOffsetYBottom,
      point3.x - cpOffsetX, point3.y + cpOffsetYBottom,
      point3.x, point3.y
      );
 
    path.lineTo(point2.x, point2.y);   
   
    return path;
  }

  private GeneralPath makeDocumentPath(Diagram diagram) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY());
    ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY());
    ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY());
    ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY());
   
    ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY());
   
    GeneralPath path = new GeneralPath();
    path.moveTo(point1.x, point1.y);
    path.lineTo(point2.x, point2.y);
    path.lineTo(point3.x, point3.y);
 
    //int controlDX = diagram.getCellWidth();
    //int controlDY = diagram.getCellHeight() / 2;
   
    int controlDX = bounds.width / 6;
    int controlDY = bounds.height / 8;
   
    path.quadTo(pointMid.x + controlDX, pointMid.y - controlDY, pointMid.x, pointMid.y);
    path.quadTo(pointMid.x - controlDX, pointMid.y + controlDY, point4.x, point4.y);
    path.closePath();
   
    return path;
  }

  // to draw a circle with 4 Bezier curves, set the control points at this ratio of
  // the radius above & below the side points
  // thanks to G. Adam Stanislav, http://whizkidtech.redprince.net/bezier/circle/
  private static final float KAPPA = 4f * ((float) Math.sqrt(2) - 1) / 3f;

  private GeneralPath makeEllipsePath(Diagram diagram) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    float xOff = (float) bounds.getWidth() * 0.5f * KAPPA;
    float yOff = (float) bounds.getHeight() * 0.5f * KAPPA;
    ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getCenterY());

    ShapePoint left = new ShapePoint((float)bounds.getMinX(), (float)pointMid.getY());
    ShapePoint right = new ShapePoint((float)bounds.getMaxX(), (float)pointMid.getY());
    ShapePoint top = new ShapePoint((float)pointMid.getX(), (float)bounds.getMinY());
    ShapePoint bottom = new ShapePoint((float)pointMid.getX(), (float)bounds.getMaxY());

    GeneralPath path = new GeneralPath();
    path.moveTo(top.x, top.y);
    path.curveTo(top.x + xOff, top.y, right.x, right.y - yOff, right.x, right.y);
    path.curveTo(right.x, right.y + yOff, bottom.x + xOff, bottom.y, bottom.x, bottom.y);
    path.curveTo(bottom.x - xOff, bottom.y, left.x, left.y + yOff, left.x, left.y);
    path.curveTo(left.x, left.y - yOff, top.x - xOff, top.y, top.x, top.y);
    path.closePath();

    return path;
  }

  private GeneralPath makeTrapezoidPath(Diagram diagram, RenderingOptions options, boolean inverted) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    float offset = options.isFixedSlope() ? bounds.height / SHAPE_SLOPE : diagram.getCellWidth() * 0.5f;
    if (inverted) offset = -offset;
    ShapePoint ul = new ShapePoint((float)bounds.getMinX() + offset, (float)bounds.getMinY());
    ShapePoint ur = new ShapePoint((float)bounds.getMaxX() - offset, (float)bounds.getMinY());
    ShapePoint br = new ShapePoint((float)bounds.getMaxX() + offset, (float)bounds.getMaxY());
    ShapePoint bl = new ShapePoint((float)bounds.getMinX() - offset, (float)bounds.getMaxY());

    ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getMaxY());

    GeneralPath path = new GeneralPath();
    path.moveTo(ul.x, ul.y);
    path.lineTo(ur.x, ur.y);
    path.lineTo(br.x, br.y);
    path.lineTo(bl.x, bl.y);
    path.closePath();

    return path;
  }

  private GeneralPath makeDecisionPath(Diagram diagram) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    ShapePoint pointMid = new ShapePoint((float)bounds.getCenterX(), (float)bounds.getCenterY());
    ShapePoint left = new ShapePoint((float)bounds.getMinX(), (float)pointMid.getY());
    ShapePoint right = new ShapePoint((float)bounds.getMaxX(), (float)pointMid.getY());
    ShapePoint top = new ShapePoint((float)pointMid.getX(), (float)bounds.getMinY());
    ShapePoint bottom = new ShapePoint((float)pointMid.getX(), (float)bounds.getMaxY());

    GeneralPath path = new GeneralPath();
    path.moveTo(left.x, left.y);
    path.lineTo(top.x, top.y);
    path.lineTo(right.x, right.y);
    path.lineTo(bottom.x, bottom.y);

    path.closePath();

    return path;
  }

  private GeneralPath makeIOPath(Diagram diagram, RenderingOptions options) {
    if(points.size() != 4) return null;
    Rectangle bounds = makeIntoPath().getBounds();
    ShapePoint point1 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMinY());
    ShapePoint point2 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMinY());
    ShapePoint point3 = new ShapePoint((float)bounds.getMaxX(), (float)bounds.getMaxY());
    ShapePoint point4 = new ShapePoint((float)bounds.getMinX(), (float)bounds.getMaxY());
 
        float offset = options.isFixedSlope() ? bounds.height / SHAPE_SLOPE : diagram.getCellWidth() * 0.5f;
   
    GeneralPath path = new GeneralPath();
    path.moveTo(point1.x + offset, point1.y);
    path.lineTo(point2.x + offset, point2.y);
    path.lineTo(point3.x - offset, point3.y);
    path.lineTo(point4.x - offset, point4.y);
    path.closePath();
   
    return path;
  }

  public CustomShapeDefinition getDefinition() {
    return definition;
  }

  public void setDefinition(CustomShapeDefinition definition) {
    this.definition = definition;
  }

  /**
   * See http://mathworld.wolfram.com/PolygonArea.html
   *
   * @return the overall area of the shape
   */
  public double calculateArea() {
    if(points.size() == 0) return 0;
   
    double area = 0;
   
    for(int i = 0; i < points.size() - 1; i++){
      ShapePoint point1 = points.get(i);
      ShapePoint point2 = points.get(i + 1);
      area += point1.x * point2.y;
      area -= point2.x * point1.y;
    }
    ShapePoint point1 = points.get(points.size() - 1);
    ShapePoint point2 = points.get(0);
    area += point1.x * point2.y;
    area -= point2.x * point1.y;
   
    return Math.abs(area / 2);
  }
 
}
TOP

Related Classes of org.stathissideris.ascii2image.graphics.DiagramShape

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.