net.sourceforge.plantuml.posimo.DotPath Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml Show documentation
Show all versions of plantuml Show documentation
PlantUML is a component that allows to quickly write :
* sequence diagram,
* use case diagram,
* class diagram,
* activity diagram,
* component diagram,
* state diagram
* object diagram
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2013, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 4236 $
*
*/
package net.sourceforge.plantuml.posimo;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import net.sourceforge.plantuml.EnsureVisible;
import net.sourceforge.plantuml.asciiart.BasicCharArea;
import net.sourceforge.plantuml.eps.EpsGraphics;
import net.sourceforge.plantuml.svek.ClusterPosition;
import net.sourceforge.plantuml.svek.MinFinder;
import net.sourceforge.plantuml.svek.PointAndAngle;
import net.sourceforge.plantuml.svek.PointDirected;
import net.sourceforge.plantuml.ugraphic.UPath;
import net.sourceforge.plantuml.ugraphic.USegmentType;
import net.sourceforge.plantuml.ugraphic.UShape;
public class DotPath implements UShape, Moveable {
static class TriPoints {
public TriPoints(String p1, String p2, String p, double deltaY) {
final StringTokenizer st1 = new StringTokenizer(p1, ",");
x1 = Double.parseDouble(st1.nextToken());
y1 = Double.parseDouble(st1.nextToken()) + deltaY;
final StringTokenizer st2 = new StringTokenizer(p2, ",");
x2 = Double.parseDouble(st2.nextToken());
y2 = Double.parseDouble(st2.nextToken()) + deltaY;
final StringTokenizer st = new StringTokenizer(p, ",");
x = Double.parseDouble(st.nextToken());
y = Double.parseDouble(st.nextToken()) + deltaY;
}
private final double x1;
private final double y1;
private final double x2;
private final double y2;
private final double x;
private final double y;
// @Override
// public String toString() {
// return "[" + x1 + "," + y1 + " " + x2 + "," + y2 + " " + x + "," + y
// + "]";
// }
}
private final List beziers = new ArrayList();
public DotPath() {
this(new ArrayList());
}
public DotPath(DotPath other) {
this(new ArrayList());
for (CubicCurve2D.Double c : other.beziers) {
this.beziers.add(new CubicCurve2D.Double(c.x1, c.y1, c.ctrlx1, c.ctrly1, c.ctrlx2, c.ctrly2, c.x2, c.y2));
}
}
private DotPath(List beziers) {
this.beziers.addAll(beziers);
}
public DotPath(String init, double deltaY) {
if (init.startsWith("M") == false) {
throw new IllegalArgumentException();
}
final int posC = init.indexOf("C");
if (posC == -1) {
throw new IllegalArgumentException();
}
final StringTokenizer st = new StringTokenizer(init.substring(1, posC), ",");
final double startX = Double.parseDouble(st.nextToken());
final double startY = Double.parseDouble(st.nextToken()) + deltaY;
final StringTokenizer st2 = new StringTokenizer(init.substring(posC + 1), " ");
final List triPoints = new ArrayList();
while (st2.hasMoreTokens()) {
final String p1 = st2.nextToken();
final String p2 = st2.nextToken();
final String p = st2.nextToken();
triPoints.add(new TriPoints(p1, p2, p, deltaY));
}
double x = startX;
double y = startY;
for (TriPoints p : triPoints) {
final CubicCurve2D.Double bezier = new CubicCurve2D.Double(x, y, p.x1, p.y1, p.x2, p.y2, p.x, p.y);
beziers.add(bezier);
x = p.x;
y = p.y;
}
// this.print = triPoints.toString();
}
// private final String print;
public Point2D getStartPoint() {
return beziers.get(0).getP1();
}
public PointAndAngle getMiddle() {
Point2D result = null;
double angle = 0;
for (CubicCurve2D.Double bez : beziers) {
final CubicCurve2D.Double left = new CubicCurve2D.Double();
final CubicCurve2D.Double right = new CubicCurve2D.Double();
bez.subdivide(left, right);
final Point2D p1 = left.getP1();
final Point2D p2 = left.getP2();
final Point2D p3 = right.getP1();
final Point2D p4 = right.getP2();
if (result == null || getCost(p1) < getCost(result)) {
result = p1;
angle = BezierUtils.getStartingAngle(left);
}
if (getCost(p2) < getCost(result)) {
result = p2;
angle = BezierUtils.getEndingAngle(left);
}
if (getCost(p3) < getCost(result)) {
result = p3;
angle = BezierUtils.getStartingAngle(right);
}
if (getCost(p4) < getCost(result)) {
result = p4;
angle = BezierUtils.getEndingAngle(right);
}
}
return new PointAndAngle(result, angle);
}
private double getCost(Point2D pt) {
final Point2D start = getStartPoint();
final Point2D end = getEndPoint();
return pt.distanceSq(start) + pt.distanceSq(end);
}
public void forceStartPoint(double x, double y) {
beziers.get(0).x1 = x;
beziers.get(0).y1 = y;
beziers.get(0).ctrlx1 = x;
beziers.get(0).ctrly1 = y;
}
public Point2D getEndPoint() {
return beziers.get(beziers.size() - 1).getP2();
}
public void forceEndPoint(double x, double y) {
beziers.get(beziers.size() - 1).x2 = x;
beziers.get(beziers.size() - 1).y2 = y;
beziers.get(beziers.size() - 1).ctrlx2 = x;
beziers.get(beziers.size() - 1).ctrly2 = y;
}
public MinFinder getMinMax() {
final MinFinder result = new MinFinder();
for (CubicCurve2D.Double c : beziers) {
result.manage(c.x1, c.y1);
result.manage(c.x2, c.y2);
result.manage(c.ctrlx1, c.ctrly1);
result.manage(c.ctrlx2, c.ctrly2);
}
return result;
}
public double getMinDist(Point2D ref) {
double result = Double.MAX_VALUE;
for (CubicCurve2D.Double c : beziers) {
final double d1 = ref.distance(c.x1, c.y1);
if (d1 < result) {
result = d1;
}
final double d2 = ref.distance(c.x2, c.y2);
if (d2 < result) {
result = d2;
}
final double d3 = ref.distance(c.ctrlx1, c.ctrly1);
if (d3 < result) {
result = d3;
}
final double d4 = ref.distance(c.ctrlx2, c.ctrly2);
if (d4 < result) {
result = d4;
}
}
return result;
}
public Line2D getEndTangeante() {
final CubicCurve2D.Double last = beziers.get(beziers.size() - 1);
double dx = last.x2 - last.ctrlx2;
double dy = last.y2 - last.ctrly2;
if (dx == 0 && dy == 0) {
dx = last.x2 - last.x1;
dy = last.y2 - last.y1;
}
return new Line2D.Double(last.x2, last.y2, last.x2 + dx, last.y2 + dy);
}
public double getEndAngle() {
final Line2D tan = getEndTangeante();
final double theta1 = Math.atan2(tan.getY2() - tan.getY1(), tan.getX2() - tan.getX1());
return theta1;
}
public double getStartAngle() {
final Line2D tan = getStartTangeante();
final double theta1 = Math.atan2(tan.getY2() - tan.getY1(), tan.getX2() - tan.getX1());
return theta1;
}
public Line2D getStartTangeante() {
final CubicCurve2D.Double first = beziers.get(0);
double dx = first.ctrlx1 - first.x1;
double dy = first.ctrly1 - first.y1;
if (dx == 0 && dy == 0) {
dx = first.x2 - first.x1;
dy = first.y2 - first.y1;
}
return new Line2D.Double(first.x1, first.y1, first.x1 + dx, first.y1 + dy);
}
public DotPath addBefore(CubicCurve2D.Double before) {
final List copy = new ArrayList(beziers);
copy.add(0, before);
return new DotPath(copy);
}
public DotPath addBefore(DotPath other) {
final List copy = new ArrayList(beziers);
copy.addAll(0, other.beziers);
return new DotPath(copy);
}
public DotPath addAfter(CubicCurve2D.Double after) {
final List copy = new ArrayList(beziers);
copy.add(after);
return new DotPath(copy);
}
public DotPath addAfter(DotPath other) {
final List copy = new ArrayList(beziers);
copy.addAll(other.beziers);
return new DotPath(copy);
}
public Map somePoints() {
final Map result = new HashMap();
for (CubicCurve2D.Double bez : beziers) {
final CubicCurve2D.Double left = new CubicCurve2D.Double();
final CubicCurve2D.Double right = new CubicCurve2D.Double();
bez.subdivide(left, right);
result.put(left.getP1(), BezierUtils.getStartingAngle(left));
result.put(left.getP2(), BezierUtils.getEndingAngle(left));
result.put(right.getP1(), BezierUtils.getStartingAngle(right));
result.put(right.getP2(), BezierUtils.getEndingAngle(right));
}
return result;
}
public PointDirected getIntersection(ClusterPosition position) {
for (CubicCurve2D.Double bez : beziers) {
final PointDirected result = position.getIntersection(bez);
if (result != null) {
return result;
}
}
return null;
}
// public void drawOld(Graphics2D g2d, double x, double y) {
// for (CubicCurve2D.Double bez : beziers) {
// bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y +
// bez.ctrly1, x + bez.ctrlx2, y
// + bez.ctrly2, x + bez.x2, y + bez.y2);
// g2d.draw(bez);
// }
// }
//
public void draw(Graphics2D g2d, double x, double y) {
final GeneralPath p = new GeneralPath();
for (CubicCurve2D.Double bez : beziers) {
bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
+ bez.ctrly2, x + bez.x2, y + bez.y2);
p.append(bez, true);
}
g2d.draw(p);
}
public void manageEnsureVisible(double x, double y, EnsureVisible visible) {
for (CubicCurve2D.Double bez : beziers) {
visible.ensureVisible(x + bez.x1, y + bez.y1);
visible.ensureVisible(x + bez.x2, y + bez.y2);
}
}
public void drawOk(EpsGraphics eps, double x, double y) {
// boolean first = true;
for (CubicCurve2D.Double bez : beziers) {
bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
+ bez.ctrly2, x + bez.x2, y + bez.y2);
eps.epsLine(bez.x1, bez.y1, bez.x2, bez.y2);
}
}
public void draw(EpsGraphics eps, double x, double y) {
eps.newpathDot();
final boolean dashed = false;
boolean first = true;
for (CubicCurve2D.Double bez : beziers) {
bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
+ bez.ctrly2, x + bez.x2, y + bez.y2);
if (first) {
eps.movetoNoMacro(bez.x1, bez.y1);
first = dashed;
}
eps.curvetoNoMacro(bez.ctrlx1, bez.ctrly1, bez.ctrlx2, bez.ctrly2, bez.x2, bez.y2);
}
eps.closepathDot();
}
public UPath toUPath() {
final UPath result = new UPath();
boolean start = true;
for (CubicCurve2D.Double bez : beziers) {
if (start) {
result.add(new double[] { bez.x1, bez.y1 }, USegmentType.SEG_MOVETO);
start = false;
}
result.add(new double[] { bez.ctrlx1, bez.ctrly1, bez.ctrlx2, bez.ctrly2, bez.x2, bez.y2 },
USegmentType.SEG_CUBICTO);
}
return result;
}
public Point2D getFrontierIntersection(Shape shape, Rectangle2D... notIn) {
final List all = new ArrayList(beziers);
for (int i = 0; i < 8; i++) {
for (CubicCurve2D.Double immutable : all) {
if (contains(immutable, notIn)) {
continue;
}
final CubicCurve2D.Double bez = new CubicCurve2D.Double();
bez.setCurve(immutable);
if (BezierUtils.isCutting(bez, shape)) {
while (BezierUtils.dist(bez) > 1.0) {
BezierUtils.shorten(bez, shape);
}
final Point2D.Double result = new Point2D.Double((bez.x1 + bez.x2) / 2, (bez.y1 + bez.y2) / 2);
if (contains(result, notIn) == false) {
return result;
}
}
}
cutAllCubic(all);
}
throw new IllegalArgumentException("shape=" + shape);
}
private void cutAllCubic(List all) {
final List tmp = new ArrayList(all);
all.clear();
for (CubicCurve2D.Double bez : tmp) {
final CubicCurve2D.Double left = new CubicCurve2D.Double();
final CubicCurve2D.Double right = new CubicCurve2D.Double();
bez.subdivide(left, right);
all.add(left);
all.add(right);
}
}
static private boolean contains(Point2D.Double point, Rectangle2D... rects) {
for (Rectangle2D r : rects) {
if (r.contains(point)) {
return true;
}
}
return false;
}
static private boolean contains(CubicCurve2D.Double cubic, Rectangle2D... rects) {
for (Rectangle2D r : rects) {
if (r.contains(cubic.getP1()) && r.contains(cubic.getP2())) {
return true;
}
}
return false;
}
public DotPath manageRect(Rectangle2D start, Rectangle2D end) {
final List list = new ArrayList(this.beziers);
while (true) {
if (BezierUtils.isCutting(list.get(0), start) == false) {
throw new IllegalStateException();
}
if (BezierUtils.dist(list.get(0)) <= 1.0) {
break;
}
final CubicCurve2D.Double left = new CubicCurve2D.Double();
final CubicCurve2D.Double right = new CubicCurve2D.Double();
list.get(0).subdivide(left, right);
list.set(0, left);
list.add(1, right);
if (BezierUtils.isCutting(list.get(1), start)) {
list.remove(0);
}
}
return new DotPath(list);
}
public Point2D getFrontierIntersection(Positionable p) {
return getFrontierIntersection(PositionableUtils.convert(p));
}
public void draw(BasicCharArea area, double pixelXPerChar, double pixelYPerChar) {
for (CubicCurve2D.Double bez : beziers) {
if (bez.x1 == bez.x2) {
area.drawVLine('|', (int) (bez.x1 / pixelXPerChar), (int) (bez.y1 / pixelYPerChar),
(int) (bez.y2 / pixelYPerChar));
} else if (bez.y1 == bez.y2) {
area.drawHLine('-', (int) (bez.y1 / pixelYPerChar), (int) (bez.x1 / pixelXPerChar),
(int) (bez.x2 / pixelXPerChar));
} /*
* else { throw new UnsupportedOperationException("bez=" + toString(bez)); }
*/
}
}
static String toString(CubicCurve2D.Double c) {
return "(" + c.x1 + "," + c.y1 + ") " + "(" + c.ctrlx1 + "," + c.ctrly1 + ") " + "(" + c.ctrlx2 + ","
+ c.ctrly2 + ") " + "(" + c.x2 + "," + c.y2 + ") ";
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (CubicCurve2D.Double c : beziers) {
sb.append(toString(c));
}
return sb.toString();
}
public static CubicCurve2D.Double reverse(CubicCurve2D curv) {
return new CubicCurve2D.Double(curv.getX2(), curv.getY2(), curv.getCtrlX2(), curv.getCtrlY2(),
curv.getCtrlX1(), curv.getCtrlY1(), curv.getX1(), curv.getY1());
}
public DotPath reverse() {
final List reverse = new ArrayList(beziers);
Collections.reverse(reverse);
final List copy = new ArrayList();
for (CubicCurve2D.Double cub : reverse) {
copy.add(reverse(cub));
}
return new DotPath(copy);
}
public void moveSvek(double deltaX, double deltaY) {
for (int i = 0; i < beziers.size(); i++) {
final CubicCurve2D.Double c = beziers.get(i);
beziers.set(i, new CubicCurve2D.Double(c.x1 + deltaX, c.y1 + deltaY, c.ctrlx1 + deltaX, c.ctrly1 + deltaY,
c.ctrlx2 + deltaX, c.ctrly2 + deltaY, c.x2 + deltaX, c.y2 + deltaY));
}
}
public final List getBeziers() {
return Collections.unmodifiableList(beziers);
}
}