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

net.sourceforge.plantuml.activitydiagram3.ftile.Worm 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 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 net.sourceforge.plantuml.activitydiagram3.ftile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

import net.sourceforge.plantuml.decoration.HtmlColorAndStyle;
import net.sourceforge.plantuml.decoration.LinkStyle;
import net.sourceforge.plantuml.klimt.Arrows;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.compress.CompressionMode;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.geom.MinMax;
import net.sourceforge.plantuml.klimt.geom.XLine2D;
import net.sourceforge.plantuml.klimt.geom.XPoint2D;
import net.sourceforge.plantuml.klimt.shape.ULine;
import net.sourceforge.plantuml.klimt.shape.UPolygon;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.utils.Direction;

public class Worm implements Iterable {

	private final List points = new ArrayList<>();
	private final Style style;
	private final Arrows arrows;

	public Worm(Style style, Arrows arrows) {
		this.style = style;
		this.arrows = arrows;
	}

	public boolean isPureHorizontal() {
		return points.size() == 2 && points.get(0).getY() == points.get(1).getY();
	}

	private boolean ignoreForCompression;

	public Worm cloneEmpty() {
		final Worm result = new Worm(style, arrows);
		result.ignoreForCompression = this.ignoreForCompression;
		return result;
	}

	public final void setIgnoreForCompression() {
		if (points.size() > 0)
			throw new IllegalStateException();

		this.ignoreForCompression = true;
	}

	public void drawInternalOneColor(UPolygon startDecoration, UGraphic ug, HtmlColorAndStyle colorAndStyle,
			double strokeValue, Direction emphasizeDirection, UPolygon endDecoration) {
		final HColor arrowColor = Objects.requireNonNull(colorAndStyle.getArrowColor());
		final LinkStyle linkStyle = colorAndStyle.getStyle();
		if (linkStyle.isInvisible())
			return;

		ug = ug.apply(arrowColor);
		ug = ug.apply(arrowColor.bg());
		if (linkStyle.isNormal())
			ug = ug.apply(style.getStroke());
		else
			ug = ug.apply(linkStyle.goThickness(strokeValue).getStroke3());

		boolean drawn = false;
		for (int i = 0; i < points.size() - 1; i++) {
			final XPoint2D p1 = points.get(i);
			final XPoint2D p2 = points.get(i + 1);
			final XLine2D line = XLine2D.line(p1, p2);
			if (drawn == false && emphasizeDirection != null && Direction.fromVector(p1, p2) == emphasizeDirection) {
				drawLine(ug, line, emphasizeDirection);
				drawn = true;
			} else {
				drawLine(ug, line, null);
			}
		}

		final HColor arrowHeadColor = colorAndStyle.getArrowHeadColor();
		if (arrowHeadColor == null)
			throw new IllegalStateException();

//		if (arrowHeadColor == null || arrowHeadColor.equals(HColorUtils.transparent())) {
//			return;
//		}
		ug = ug.apply(arrowHeadColor);
		ug = ug.apply(arrowHeadColor.bg());

		if (startDecoration != null) {
			ug = ug.apply(UStroke.withThickness(1.5));
			final XPoint2D start = points.get(0);
			if (ignoreForCompression)
				startDecoration.setCompressionMode(CompressionMode.ON_X);

			ug.apply(UTranslate.point(start)).apply(UStroke.simple()).draw(startDecoration);
		}
		if (endDecoration != null) {
			ug = ug.apply(UStroke.withThickness(1.5));
			final XPoint2D end = points.get(points.size() - 1);
			if (ignoreForCompression)
				endDecoration.setCompressionMode(CompressionMode.ON_X);

			ug.apply(UTranslate.point(end)).apply(UStroke.simple()).draw(endDecoration);
		}
	}

	private void drawLine(UGraphic ug, XLine2D line, Direction direction) {
		drawLine(ug, line.getX1(), line.getY1(), line.getX2(), line.getY2(), direction);
	}

	private void drawLine(UGraphic ug, double x1, double y1, double x2, double y2, Direction direction) {
		ug = ug.apply(new UTranslate(x1, y1));
		if (direction != null)
			ug.apply(new UTranslate((x2 - x1) / 2, (y2 - y1) / 2)).draw(arrows.asTo(direction));

		ug.draw(new ULine(x2 - x1, y2 - y1));
	}

	public Worm move(double dx, double dy) {
		final Worm result = new Worm(style, arrows);
		for (XPoint2D pt : points)
			result.addPoint(pt.getX() + dx, pt.getY() + dy);

		return result;
	}

	public Worm moveFirstPoint(UTranslate move) {
		final double dx = move.getDx();
		final double dy = move.getDy();
		if (dx != 0 && dy != 0)
			throw new IllegalArgumentException("move=" + move);

		final Worm result = new Worm(style, arrows);
		double x0 = this.points.get(0).getX();
		double y0 = this.points.get(0).getY();
		double x1 = this.points.get(1).getX();
		double y1 = this.points.get(1).getY();

		if (dx != 0 && x0 == x1)
			x1 += dx;

		if (dy != 0 && y0 == y1)
			y1 += dy;

		x0 += dx;
		y0 += dy;

		result.addPoint(x0, y0);
		result.addPoint(x1, y1);
		for (int i = 2; i < this.points.size(); i++)
			result.addPoint(this.points.get(i));

		return result;
	}

	public Worm moveLastPoint(UTranslate move) {
		final double dx = move.getDx();
		final double dy = move.getDy();
		if (dx != 0 && dy != 0)
			throw new IllegalArgumentException("move=" + move);

		final Worm result = new Worm(style, arrows);
		double x8 = this.points.get(this.points.size() - 2).getX();
		double y8 = this.points.get(this.points.size() - 2).getY();
		double x9 = this.points.get(this.points.size() - 1).getX();
		double y9 = this.points.get(this.points.size() - 1).getY();

		if (dx != 0 && x8 == x9)
			x8 += dx;

		if (dy != 0 && y8 == y9)
			y8 += dy;

		x9 += dx;
		y9 += dy;

		for (int i = 0; i < this.points.size() - 2; i++)
			result.addPoint(this.points.get(i));

		result.addPoint(x8, y8);
		result.addPoint(x9, y9);
		return result;
	}

	@Override
	public String toString() {
		final StringBuilder result = new StringBuilder();
		for (int i = 0; i < points.size() - 1; i++)
			result.append(getDirectionAtPoint(i)).append(" ");

		return result + points.toString();
	}

	public void addPoint(double x, double y) {
		if (Double.isNaN(x))
			throw new IllegalArgumentException();

		if (Double.isNaN(y))
			throw new IllegalArgumentException();

		if (points.size() > 0) {
			final XPoint2D last = getLast();
			if (last.getX() == x && last.getY() == y)
				return;

		}
		this.points.add(new XPoint2D(x, y));
	}

	public void addPoint(XPoint2D pt) {
		this.addPoint(pt.getX(), pt.getY());
	}

	public Worm translate(UTranslate translate) {
		return move(translate.getDx(), translate.getDy());
	}

	SnakeDirection getDirection() {
		if (points.size() < 2)
			throw new IllegalStateException();

		return SnakeDirection.getDirection(points.get(0), points.get(1));
	}

	String getDirectionsCode() {
		final StringBuilder result = new StringBuilder();
		for (int i = 0; i < points.size() - 1; i++) {
			final Direction direction = Direction.fromVector(points.get(i), points.get(i + 1));
			result.append(direction.getShortCode());
		}
		return result.toString();
	}

	private List getPatternAt(int i) {
		return Arrays.asList(getDirectionAtPoint(i), getDirectionAtPoint(i + 1), getDirectionAtPoint(i + 2),
				getDirectionAtPoint(i + 3));
	}

	private boolean isForwardAndBackwardAt(int i) {
		return getDirectionAtPoint(i) == getDirectionAtPoint(i + 1).getInv();
	}

	private Direction getDirectionAtPoint(int i) {
		return Direction.fromVector(points.get(i), points.get(i + 1));
	}

	public Iterator iterator() {
		return Collections.unmodifiableCollection(points).iterator();
	}

	public boolean doesHorizontalCross(MinMax area) {
		for (int i = 0; i < points.size() - 1; i++) {
			final XPoint2D pt1 = get(i);
			final XPoint2D pt2 = get(i + 1);
			if (pt1.getY() == pt2.getY() && area.doesHorizontalCross(pt1, pt2))
				return true;

		}
		return false;
	}

	public int size() {
		return this.points.size();
	}

	public XPoint2D get(int i) {
		return this.points.get(i);
	}

	public void addAll(Worm other) {
		this.points.addAll(other.points);
	}

	public void remove(int i) {
		this.points.remove(i);
	}

	public void add(int i, XPoint2D pt) {
		this.points.add(i, pt);
	}

	public XPoint2D getFirst() {
		return points.get(0);
	}

	public XPoint2D getLast() {
		return points.get(points.size() - 1);
	}

	public double getMinX() {
		double result = points.get(0).getX();
		for (XPoint2D pt : points)
			result = Math.min(result, pt.getX());
		return result;
	}

	public double getMaxY() {
		double result = points.get(0).getY();
		for (XPoint2D pt : points)
			result = Math.max(result, pt.getY());
		return result;
	}

	public Worm merge(Worm other, MergeStrategy merge) {
		if (Snake.same(this.getLast(), other.getFirst()) == false)
			throw new IllegalArgumentException();

		final Worm result = new Worm(style, arrows);
		result.points.addAll(this.points);
		result.points.addAll(other.points);
		result.mergeMe(merge);
		return result;
	}

	private void mergeMe(MergeStrategy merge) {
		boolean change = false;
		do {
			change = false;
			change = change || removeNullVector();
			change = change || removeRedondantDirection();
			change = change || removePattern1();
			change = change || removePattern2();
			change = change || removePattern3();
			change = change || removePattern4();
			change = change || removePattern5();
			change = change || removePattern6();
			change = change || removePattern7();
			if (merge == MergeStrategy.FULL)
				change = change || removePattern8();

		} while (change);
	}

	private boolean removeNullVector() {
		for (int i = 0; i < points.size() - 1; i++) {
			final Direction dir = getDirectionAtPoint(i);
			if (dir == null) {
				points.remove(i);
				return true;
			}
		}
		return false;
	}

	private boolean removeRedondantDirection() {
		for (int i = 0; i < points.size() - 2; i++) {
			final Direction dir1 = getDirectionAtPoint(i);
			final Direction dir2 = getDirectionAtPoint(i + 1);
			if (dir1 == dir2) {
				points.remove(i + 1);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern1() {
		for (int i = 0; i < points.size() - 5; i++) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.DOWN, Direction.LEFT, Direction.DOWN, Direction.RIGHT).equals(patternAt)
					|| Arrays.asList(Direction.DOWN, Direction.RIGHT, Direction.DOWN, Direction.LEFT)
							.equals(patternAt)) {
				final XPoint2D newPoint = new XPoint2D(points.get(i + 1).x, points.get(i + 3).y);
				points.remove(i + 3);
				points.remove(i + 2);
				points.remove(i + 1);
				points.add(i + 1, newPoint);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern7() {
		if (points.size() > 4) {
			final int i = 0;
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.RIGHT, Direction.DOWN, Direction.LEFT, Direction.DOWN).equals(patternAt)
					&& points.get(i + 3).x > points.get(i).x) {
				final XPoint2D newPoint = new XPoint2D(points.get(i + 3).x, points.get(i).y);
				points.remove(i + 2);
				points.remove(i + 1);
				points.add(i + 1, newPoint);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern2() {
		for (int i = 0; i < points.size() - 5; i++) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.RIGHT, Direction.DOWN, Direction.RIGHT, Direction.UP).equals(patternAt)
					|| Arrays.asList(Direction.LEFT, Direction.DOWN, Direction.LEFT, Direction.UP).equals(patternAt)) {
				final XPoint2D newPoint = new XPoint2D(points.get(i + 3).x, points.get(i + 1).y);
				points.remove(i + 3);
				points.remove(i + 2);
				points.remove(i + 1);
				points.add(i + 1, newPoint);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern3() {
		for (int i = 0; i < points.size() - 4; i++) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.DOWN, Direction.RIGHT, Direction.DOWN, Direction.RIGHT).equals(patternAt)
					|| Arrays.asList(Direction.DOWN, Direction.LEFT, Direction.DOWN, Direction.LEFT)
							.equals(patternAt)) {
				final XPoint2D newPoint = new XPoint2D(points.get(i + 1).x, points.get(i + 3).y);
				points.remove(i + 3);
				points.remove(i + 2);
				points.remove(i + 1);
				points.add(i + 1, newPoint);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern4() {
		final int i = points.size() - 5;
		if (i >= 0) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.DOWN, Direction.LEFT, Direction.DOWN, Direction.RIGHT).equals(patternAt)) {
				final XPoint2D p1 = points.get(i + 1);
				final XPoint2D p4 = points.get(i + 4);
				if (p4.x > p1.x) {
					final XPoint2D newPoint = new XPoint2D(points.get(i + 1).x, points.get(i + 3).y);
					points.remove(i + 3);
					points.remove(i + 2);
					points.remove(i + 1);
					points.add(i + 1, newPoint);
					return true;
				}
			}
		}
		return false;
	}

	private boolean removePattern5() {
		final int i = points.size() - 5;
		if (i >= 0) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.DOWN, Direction.RIGHT, Direction.DOWN, Direction.LEFT).equals(patternAt)) {
				final XPoint2D p1 = points.get(i + 1);
				final XPoint2D p4 = points.get(i + 4);
				if (p4.x + 4 < p1.x) {
					final XPoint2D newPoint = new XPoint2D(points.get(i + 1).x, points.get(i + 3).y);
					points.remove(i + 3);
					points.remove(i + 2);
					points.remove(i + 1);
					points.add(i + 1, newPoint);
					return true;
				}
			}
		}
		return false;
	}

	private boolean removePattern6() {
		for (int i = 0; i < points.size() - 2; i++) {
			if (isForwardAndBackwardAt(i)) {
				points.remove(i + 1);
				return true;
			}
		}
		return false;
	}

	private boolean removePattern8() {
		for (int i = 0; i < points.size() - 4; i++) {
			final List patternAt = getPatternAt(i);
			if (Arrays.asList(Direction.LEFT, Direction.DOWN, Direction.LEFT, Direction.DOWN).equals(patternAt)
					|| Arrays.asList(Direction.RIGHT, Direction.DOWN, Direction.RIGHT, Direction.DOWN)
							.equals(patternAt)) {
				final XPoint2D newPoint = new XPoint2D(points.get(i + 3).x, points.get(i + 1).y);
				points.remove(i + 3);
				points.remove(i + 2);
				points.remove(i + 1);
				points.add(i + 1, newPoint);
				return true;
			}
		}
		return false;
	}

	public Style getStyle() {
		return style;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy