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

net.sourceforge.plantuml.posimo.DotPath Maven / Gradle / Ivy

Go to download

PlantUML is a component that allows to quickly write : * sequence diagram, * use case diagram, * class diagram, * activity diagram, * component diagram, * state diagram * object diagram

There is a newer version: 8059
Show newest version
/* ========================================================================
 * 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);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy