org.stathissideris.ascii2image.graphics.DiagramShape Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml-gplv2 Show documentation
Show all versions of plantuml-gplv2 Show documentation
PlantUML is a component that allows to quickly write diagrams from text.
// 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 GNU General Public License V2.
*
* THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
* LICENSE ("AGREEMENT"). [GNU General Public License V2]
*
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/old-licenses/gpl-2.0.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 GPL v2 license.
*
* The generated images can then be used without any reference to the GPL v2 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;
}
}