All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.stathissideris.ascii2image.graphics.DiagramShape Maven / Gradle / Ivy

There is a newer version: 1.2024.8
Show newest version
// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
 * |
 * |      PlantUML : a free UML diagram generator
 * |
 * +=======================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 *
 * If you like this project or if you find it useful, you can support us at:
 *
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/liberapay (only 1€ per month!)
 * https://plantuml.com/paypal
 *
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the Eclipse Public License.
 *
 * THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
 * LICENSE ("AGREEMENT"). [Eclipse Public License - v 1.0]
 *
 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
 * RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
 *
 * You may obtain a copy of the License at
 *
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * PlantUML can occasionally display sponsored or advertising messages. Those
 * messages are usually generated on welcome or error images and never on
 * functional diagrams.
 * See https://plantuml.com/professional if you want to remove them
 *
 * Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
 * are owned by the author of their corresponding sources code (that is, their
 * textual description in PlantUML language). Those images are not covered by
 * this EPL license.
 *
 * The generated images can then be used without any reference to the EPL license.
 * It is not even necessary to stipulate that they have been generated with PlantUML,
 * although this will be appreciated by the PlantUML team.
 *
 * There is an exception : if the textual description in PlantUML language is also covered
 * by any license, then the generated images are logically covered
 * by the very same license.
 *
 * This is the IGY distribution (Install GraphViz by Yourself).
 * You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
 * (see https://plantuml.com/graphviz-dot )
 *
 * Icons provided by OpenIconic :  https://useiconic.com/open
 * Archimate sprites provided by Archi :  http://www.archimatetool.com
 * Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
 * Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
 * ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
 * ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
 * CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
 * Brotli (c) by the Brotli Authors https://github.com/google/brotli
 * Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
 * Twemoji (c) by Twitter at https://twemoji.twitter.com/
 *
 */
package org.stathissideris.ascii2image.graphics;

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

import org.stathissideris.ascii2image.text.TextGrid;

/**
 * 
 * @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;

	protected int type = TYPE_SIMPLE;

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

	protected ArrayList points = new ArrayList();

	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) {
		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);
		}

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

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

		if(getType() == TYPE_TRAPEZOID && points.size() == 4){
			return makeTrapezoidPath(diagram, 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:
	 * 
	 *     Cell
	 *    +-----+
	 *    |  A  |C                 B
	 *    |  *--*------------------*
	 *    |     |
	 *    +-----+
	 *
* * @param pointInCell * @param otherPoint * @param diagram * @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: * *
	 *     Cell
	 *    +-----+
	 *    |  A  |                  B
	 *  C *--*--+------------------*
	 *    |     |
	 *    +-----+
	 * 
* * @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, boolean inverted) { if(points.size() != 4) return null; Rectangle bounds = makeIntoPath().getBounds(); float offset = 0.7f * diagram.getCellWidth(); // fixed slope 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) { 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 = diagram.getCellWidth() / 2; 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy