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

net.sourceforge.plantuml.svek.Cluster 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.svek;

import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sourceforge.plantuml.ColorParam;
import net.sourceforge.plantuml.Dimension2DDouble;
import net.sourceforge.plantuml.FontParam;
import net.sourceforge.plantuml.ISkinParam;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.UmlDiagramType;
import net.sourceforge.plantuml.UniqueSequence;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.cucadiagram.EntityPosition;
import net.sourceforge.plantuml.cucadiagram.EntityUtils;
import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.IGroup;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.cucadiagram.MethodsOrFieldsArea;
import net.sourceforge.plantuml.cucadiagram.Stereotype;
import net.sourceforge.plantuml.cucadiagram.dot.DotData;
import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.graphic.HtmlColorTransparent;
import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.graphic.TextBlockEmpty;
import net.sourceforge.plantuml.graphic.TextBlockWidth;
import net.sourceforge.plantuml.posimo.Moveable;
import net.sourceforge.plantuml.skin.rose.Rose;
import net.sourceforge.plantuml.svek.image.EntityImageState;
import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
import net.sourceforge.plantuml.ugraphic.UChangeColor;
import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.ULine;
import net.sourceforge.plantuml.ugraphic.URectangle;
import net.sourceforge.plantuml.ugraphic.UStroke;
import net.sourceforge.plantuml.ugraphic.UTranslate;

public class Cluster implements Moveable {

	private final Cluster parent;
	private final IGroup group;
	private final List shapes = new ArrayList();
	private final List children = new ArrayList();
	private final int color;
	private final int colorTitle;
	private final ISkinParam skinParam;

	private int titleAndAttributeWidth;
	private int titleAndAttributeHeight;
	private TextBlock ztitle;
	private TextBlock zstereo;

	private double xTitle;
	private double yTitle;

	private double minX;
	private double minY;
	private double maxX;
	private double maxY;

	public void moveSvek(double deltaX, double deltaY) {
		this.xTitle += deltaX;
		this.minX += deltaX;
		this.maxX += deltaX;
		this.yTitle += deltaY;
		this.minY += deltaY;
		this.maxY += deltaY;

	}

	private boolean hasEntryOrExitPoint() {
		for (Shape sh : shapes) {
			if (sh.getEntityPosition() != EntityPosition.NORMAL) {
				return true;
			}
		}
		return false;
	}

	public Cluster(ColorSequence colorSequence, ISkinParam skinParam, IGroup root) {
		this(null, root, colorSequence, skinParam);
	}

	private Cluster(Cluster parent, IGroup group, ColorSequence colorSequence, ISkinParam skinParam) {
		if (group == null) {
			throw new IllegalStateException();
		}
		this.parent = parent;
		this.group = group;
		this.color = colorSequence.getValue();
		this.colorTitle = colorSequence.getValue();
		this.skinParam = skinParam;
	}

	@Override
	public String toString() {
		return super.toString() + " " + group;
	}

	public final Cluster getParent() {
		return parent;
	}

	public void addShape(Shape sh) {
		if (sh == null) {
			throw new IllegalArgumentException();
		}
		this.shapes.add(sh);
		sh.setCluster(this);
	}

	public final List getShapes() {
		return Collections.unmodifiableList(shapes);
	}

	private List getShapesOrderedTop(Collection lines) {
		final List firsts = new ArrayList();
		final Set tops = new HashSet();
		final Map shs = new HashMap();

		for (final Iterator it = shapes.iterator(); it.hasNext();) {
			final Shape sh = it.next();
			shs.put(sh.getUid(), sh);
			if (sh.isTop() && sh.getEntityPosition() == EntityPosition.NORMAL) {
				firsts.add(sh);
				tops.add(sh.getUid());
			}
		}

		for (Line l : lines) {
			if (tops.contains(l.getStartUid())) {
				final Shape sh = shs.get(l.getEndUid());
				if (sh != null && sh.getEntityPosition() == EntityPosition.NORMAL) {
					firsts.add(0, sh);
				}
			}

			if (l.isInverted()) {
				final Shape sh = shs.get(l.getStartUid());
				if (sh != null && sh.getEntityPosition() == EntityPosition.NORMAL) {
					firsts.add(0, sh);
				}
			}
		}

		return firsts;
	}

	private List getShapesEntryExit(EntityPosition position) {
		final List result = new ArrayList();

		for (final Iterator it = shapes.iterator(); it.hasNext();) {
			final Shape sh = it.next();
			if (sh.getEntityPosition() == position) {
				result.add(sh);
			}
		}
		return result;
	}

	private List getShapesOrderedWithoutTop(Collection lines) {
		final List all = new ArrayList(shapes);
		final Set tops = new HashSet();
		final Map shs = new HashMap();

		for (final Iterator it = all.iterator(); it.hasNext();) {
			final Shape sh = it.next();
			if (sh.getEntityPosition() != EntityPosition.NORMAL) {
				it.remove();
				continue;
			}
			shs.put(sh.getUid(), sh);
			if (sh.isTop()) {
				tops.add(sh.getUid());
				it.remove();
			}
		}

		for (Line l : lines) {
			if (tops.contains(l.getStartUid())) {
				final Shape sh = shs.get(l.getEndUid());
				if (sh != null) {
					all.remove(sh);
				}
			}

			if (l.isInverted()) {
				final Shape sh = shs.get(l.getStartUid());
				if (sh != null) {
					all.remove(sh);
				}
			}
		}

		return all;
	}

	public final List getChildren() {
		return Collections.unmodifiableList(children);
	}

	public Cluster createChild(IGroup g, int titleAndAttributeWidth, int titleAndAttributeHeight, TextBlock title,
			TextBlock stereo, ColorSequence colorSequence, ISkinParam skinParam) {
		final Cluster child = new Cluster(this, g, colorSequence, skinParam);
		child.titleAndAttributeWidth = titleAndAttributeWidth;
		child.titleAndAttributeHeight = titleAndAttributeHeight;
		child.ztitle = title;
		child.zstereo = stereo;
		this.children.add(child);
		return child;
	}

	public final IGroup getGroup() {
		return group;
	}

	public final int getTitleAndAttributeWidth() {
		return titleAndAttributeWidth;
	}

	public final int getTitleAndAttributeHeight() {
		return titleAndAttributeHeight;
	}

	public double getWidth() {
		return maxX - minX;
	}

	public double getMinX() {
		return minX;
	}

	public ClusterPosition getClusterPosition() {
		return new ClusterPosition(minX, minY, maxX, maxY);
	}

	public void setTitlePosition(double x, double y) {
		this.xTitle = x;
		this.yTitle = y;
	}

	public void drawU(UGraphic ug, HtmlColor borderColor, DotData dotData) {
		final Url url = group.getUrl99();
		if (url != null) {
			ug.startUrl(url);
		}
		try {
			if (hasEntryOrExitPoint()) {
				manageEntryExitPoint(dotData, ug.getStringBounder());
			}
			if (skinParam.useSwimlanes()) {
				drawSwinLinesState(ug, borderColor, dotData);
				return;
			}
			final boolean isState = dotData.getUmlDiagramType() == UmlDiagramType.STATE;
			if (isState) {
				drawUState(ug, borderColor, dotData);
				return;
			}
			PackageStyle style = group.getPackageStyle();
			if (style == null) {
				style = dotData.getSkinParam().getPackageStyle();
			}
			if (ztitle != null || zstereo != null) {
				final HtmlColor stateBack = getStateBackColor(getBackColor(), dotData.getSkinParam(),
						group.getStereotype() == null ? null : group.getStereotype().getLabel());
				final ClusterDecoration decoration = new ClusterDecoration(style, group.getUSymbol(), ztitle, zstereo,
						stateBack, minX, minY, maxX, maxY);
				decoration.drawU(ug, borderColor, dotData.getSkinParam().shadowing());
				return;
			}
			final URectangle rect = new URectangle(maxX - minX, maxY - minY);
			if (dotData.getSkinParam().shadowing()) {
				rect.setDeltaShadow(3.0);
			}
			final HtmlColor stateBack = getStateBackColor(getBackColor(), dotData.getSkinParam(),
					group.getStereotype() == null ? null : group.getStereotype().getLabel());
			ug = ug.apply(new UChangeBackColor(stateBack)).apply(new UChangeColor(borderColor));
			ug.apply(new UStroke(2)).apply(new UTranslate(minX, minY)).draw(rect);

		} finally {
			if (url != null) {
				ug.closeAction();
			}
		}

	}

	private void manageEntryExitPoint(DotData dotData, StringBounder stringBounder) {
		final Collection insides = new ArrayList();
		final List points = new ArrayList();
		for (Shape sh : shapes) {
			if (sh.getEntityPosition() == EntityPosition.NORMAL) {
				insides.add(sh.getClusterPosition());
			} else {
				points.add(sh.getClusterPosition().getPointCenter());
			}
		}
		for (Cluster in : children) {
			insides.add(in.getClusterPosition());
		}
		final FrontierCalculator frontierCalculator = new FrontierCalculator(getClusterPosition(), insides, points);
		if (titleAndAttributeHeight > 0 && titleAndAttributeWidth > 0) {
			frontierCalculator.ensureMinWidth(titleAndAttributeWidth + 10);
		}
		final ClusterPosition forced = frontierCalculator.getSuggestedPosition();
		xTitle += ((forced.getMinX() - minX) + (forced.getMaxX() - maxX)) / 2;
		minX = forced.getMinX();
		minY = forced.getMinY();
		maxX = forced.getMaxX();
		maxY = forced.getMaxY();
		yTitle = minY + IEntityImage.MARGIN;
		final double widthTitle = ztitle.calculateDimension(stringBounder).getWidth();
		xTitle = minX + ((maxX - minX - widthTitle) / 2);
	}

	private void drawSwinLinesState(UGraphic ug, HtmlColor borderColor, DotData dotData) {
		if (ztitle != null) {
			ztitle.drawU(ug.apply(new UTranslate(xTitle, 0)));
		}
		final ULine line = new ULine(0, maxY - minY);
		ug = ug.apply(new UChangeColor(borderColor));
		ug.apply(new UTranslate(minX, 0)).draw(line);
		ug.apply(new UTranslate(maxX, 0)).draw(line);

	}

	private HtmlColor getColor(DotData dotData, ColorParam colorParam, String stereo) {
		return new Rose().getHtmlColor(dotData.getSkinParam(), colorParam, stereo);
	}

	private void drawUState(UGraphic ug, HtmlColor borderColor, DotData dotData) {
		final Dimension2D total = new Dimension2DDouble(maxX - minX, maxY - minY);
		final double suppY;
		if (ztitle == null) {
			suppY = 0;
		} else {
			suppY = ztitle.calculateDimension(ug.getStringBounder()).getHeight() + IEntityImage.MARGIN
					+ IEntityImage.MARGIN_LINE;
		}

		HtmlColor stateBack = getBackColor();
		if (stateBack == null) {
			stateBack = getColor(dotData, ColorParam.stateBackground, group.getStereotype() == null ? null : group
					.getStereotype().getLabel());
		}
		final HtmlColor background = getColor(dotData, ColorParam.background, null);
		final TextBlockWidth attribute = getTextBlockAttribute(dotData);
		final double attributeHeight = attribute.calculateDimension(ug.getStringBounder()).getHeight();
		final RoundedContainer r = new RoundedContainer(total, suppY, attributeHeight
				+ (attributeHeight > 0 ? IEntityImage.MARGIN : 0), borderColor, stateBack, background);
		r.drawU(ug.apply(new UTranslate(minX, minY)), dotData.getSkinParam().shadowing());

		if (ztitle != null) {
			ztitle.drawU(ug.apply(new UTranslate(xTitle, yTitle)));
		}

		if (attributeHeight > 0) {
			attribute.asTextBlock(total.getWidth()).drawU(
					ug.apply(new UTranslate(minX + IEntityImage.MARGIN, minY + suppY + IEntityImage.MARGIN / 2.0)));
		}

		final Stereotype stereotype = group.getStereotype();
		final boolean withSymbol = stereotype != null && stereotype.isWithOOSymbol();
		if (withSymbol) {
			EntityImageState.drawSymbol(ug.apply(new UChangeColor(borderColor)), maxX, maxY);
		}

	}

	private TextBlockWidth getTextBlockAttribute(DotData dotData) {
		final TextBlockWidth attribute;
		final List members = group.getFieldsToDisplay();
		if (members.size() == 0) {
			attribute = new TextBlockEmpty();
		} else {
			attribute = new MethodsOrFieldsArea(members, FontParam.STATE_ATTRIBUTE, dotData.getSkinParam());
		}
		return attribute;
	}

	public void setPosition(double minX, double minY, double maxX, double maxY) {
		this.minX = minX;
		this.maxX = maxX;
		this.minY = minY;
		this.maxY = maxY;

	}

	private boolean isThereALinkFromOrToGroup(Collection lines) {
		for (Line line : lines) {
			if (line.isLinkFromOrToGroup(group)) {
				return true;
			}
		}
		return false;
	}

	public void printCluster1(StringBuilder sb, Collection lines) {
		for (Shape sh : getShapesOrderedTop(lines)) {
			sh.appendShape(sb);
		}
	}

	private List addProtection(List entries, double width) {
		final List result = new ArrayList();
		result.add(entries.get(0));
		for (int i = 1; i < entries.size(); i++) {
			result.add(new ShapePseudoImpl("psd" + UniqueSequence.getValue(), width, 5));
			result.add(entries.get(i));
		}
		return result;
	}

	private double getMaxWidthFromLabelForEntryExit(List entries, StringBounder stringBounder) {
		double result = -Double.MAX_VALUE;
		for (Shape shape : entries) {
			final double w = getMaxWidthFromLabelForEntryExit(shape, stringBounder);
			if (w > result) {
				result = w;
			}
		}
		return result;
	}

	private double getMaxWidthFromLabelForEntryExit(Shape shape, StringBounder stringBounder) {
		return shape.getMaxWidthFromLabelForEntryExit(stringBounder);
	}

	public void printClusterEntryExit(StringBuilder sb, StringBounder stringBounder) {
		// final List entries = getShapesEntryExit(EntityPosition.ENTRY_POINT);
		final List shapesEntryExitList = getShapesEntryExit(EntityPosition.ENTRY_POINT);
		final double maxWith = getMaxWidthFromLabelForEntryExit(shapesEntryExitList, stringBounder);
		final double naturalSpace = 70;
		final List entries;
		if (maxWith > naturalSpace) {
			entries = addProtection(shapesEntryExitList, maxWith - naturalSpace);
		} else {
			entries = shapesEntryExitList;
		}
		if (entries.size() > 0) {
			sb.append("{rank=source;");
			for (IShapePseudo sh : entries) {
				sb.append(sh.getUid() + ";");
			}
			sb.append("}");
			for (IShapePseudo sh : entries) {
				sh.appendShape(sb);
			}
		}
		final List exits = getShapesEntryExit(EntityPosition.EXIT_POINT);
		if (exits.size() > 0) {
			sb.append("{rank=sink;");
			for (Shape sh : exits) {
				sb.append(sh.getUid() + ";");
			}
			sb.append("}");
			for (Shape sh : exits) {
				sh.appendShape(sb);
			}
		}
	}

	public boolean printCluster2(StringBuilder sb, Collection lines, StringBounder stringBounder, DotMode dotMode) {
		// Log.println("Cluster::printCluster " + this);

		boolean added = false;
		for (Shape sh : getShapesOrderedWithoutTop(lines)) {
			sh.appendShape(sb);
			added = true;
		}

		if (dotMode != DotMode.NO_LEFT_RIGHT) {
			appendRankSame(sb, lines);
		}

		for (Cluster child : getChildren()) {
			child.printInternal(sb, lines, stringBounder, dotMode);
		}

		return added;
	}

	private void appendRankSame(StringBuilder sb, Collection lines) {
		for (String same : getRankSame(lines)) {
			sb.append(same);
			SvekUtils.println(sb);
		}
	}

	private Set getRankSame(Collection lines) {
		final Set rankSame = new HashSet();
		for (Line l : lines) {
			if (l.hasEntryPoint()) {
				continue;
			}
			final String startUid = l.getStartUid();
			final String endUid = l.getEndUid();
			if (isInCluster(startUid) && isInCluster(endUid)) {
				final String same = l.rankSame();
				if (same != null) {
					rankSame.add(same);
				}
			}
		}
		return rankSame;
	}

	public void fillRankMin(Set rankMin) {
		for (Shape sh : getShapes()) {
			if (sh.isTop()) {
				rankMin.add(sh.getUid());
			}
		}

		for (Cluster child : getChildren()) {
			child.fillRankMin(rankMin);
		}
	}

	private boolean isInCluster(String uid) {
		for (Shape sh : shapes) {
			if (sh.getUid().equals(uid)) {
				return true;
			}
		}
		return false;
	}

	public String getClusterId() {
		return "cluster" + color;
	}

	public static String getSpecialPointId(IEntity group) {
		return CENTER_ID + group.getUid();
	}

	public final static String CENTER_ID = "za";

	private boolean protection0() {
		if (skinParam.useSwimlanes()) {
			return false;
		}
		return true;
	}

	private boolean protection1() {
		if (skinParam.useSwimlanes()) {
			return false;
		}
		return true;
	}

	public String getMinPoint() {
		if (skinParam.useSwimlanes()) {
			return "minPoint" + color;
		}
		return null;
	}

	public String getMaxPoint() {
		if (skinParam.useSwimlanes()) {
			return "maxPoint" + color;
		}
		return null;
	}

	private String getSourceInPoint() {
		if (skinParam.useSwimlanes()) {
			return "sourceIn" + color;
		}
		return null;
	}

	private String getSinkInPoint() {
		if (skinParam.useSwimlanes()) {
			return "sinkIn" + color;
		}
		return null;
	}

	private void printInternal(StringBuilder sb, Collection lines, StringBounder stringBounder, DotMode dotMode) {
		final boolean thereALinkFromOrToGroup = isThereALinkFromOrToGroup(lines);
		if (thereALinkFromOrToGroup) {
			subgraphCluster(sb, "a");
		}
		final boolean hasEntryOrExitPoint = hasEntryOrExitPoint();
		if (hasEntryOrExitPoint) {
			for (Line line : lines) {
				if (line.isLinkFromOrToGroup(group)) {
					line.setProjectionCluster(this);
				}
			}
		}
		boolean protection0 = protection0();
		boolean protection1 = protection1();
		if (hasEntryOrExitPoint) {
			protection0 = false;
			protection1 = false;
		}
		if (protection0) {
			subgraphCluster(sb, "p0");
		}
		sb.append("subgraph " + getClusterId() + " {");
		sb.append("style=solid;");
		sb.append("color=\"" + StringUtils.getAsHtml(color) + "\";");

		final boolean isLabel = getTitleAndAttributeHeight() > 0 && getTitleAndAttributeWidth() > 0;
		final String label;
		if (isLabel) {
			final StringBuilder sblabel = new StringBuilder("<");
			Line.appendTable(sblabel, getTitleAndAttributeWidth(), getTitleAndAttributeHeight() - 5, colorTitle);
			sblabel.append(">");
			label = sblabel.toString();
		} else {
			label = "\"\"";
		}

		if (hasEntryOrExitPoint) {
			printClusterEntryExit(sb, stringBounder);
			subgraphCluster(sb, "ee", label);
		} else {
			sb.append("label=" + label + ";");
			SvekUtils.println(sb);
		}

		// if (hasEntryOrExitPoint) {
		// printClusterEntryExit(sb);
		// subgraphCluster(sb, "ee");
		// }

		if (thereALinkFromOrToGroup) {
			sb.append(getSpecialPointId(group) + " [shape=point,width=.01,label=\"\"];");
			subgraphCluster(sb, "i");
		}
		if (protection1) {
			subgraphCluster(sb, "p1");
		}
		if (skinParam.useSwimlanes()) {
			sb.append("{rank = source; ");
			sb.append(getSourceInPoint());
			sb.append(" [shape=point,width=.01,label=\"\"];");
			sb.append(getMinPoint() + "->" + getSourceInPoint() + "  [weight=999];");
			sb.append("}");
			SvekUtils.println(sb);
			sb.append("{rank = sink; ");
			sb.append(getSinkInPoint());
			sb.append(" [shape=point,width=.01,label=\"\"];");
			sb.append("}");
			sb.append(getSinkInPoint() + "->" + getMaxPoint() + "  [weight=999];");
			SvekUtils.println(sb);
		}
		SvekUtils.println(sb);
		printCluster1(sb, lines);
		final boolean added = printCluster2(sb, lines, stringBounder, dotMode);
		if (hasEntryOrExitPoint && added == false) {
			final String empty = "empty" + color;
			sb.append(empty + " [shape=point,width=.01,label=\"\"];");
		}
		sb.append("}");
		if (protection1) {
			sb.append("}");
		}
		if (thereALinkFromOrToGroup) {
			sb.append("}");
			sb.append("}");
		}
		if (hasEntryOrExitPoint) {
			sb.append("}");
		}
		if (protection0) {
			sb.append("}");
		}
		SvekUtils.println(sb);
	}

	private void subgraphCluster(StringBuilder sb, String id) {
		subgraphCluster(sb, id, "\"\"");
	}

	private void subgraphCluster(StringBuilder sb, String id, String label) {
		final String uid = getClusterId() + id;
		sb.append("subgraph " + uid + " {");
		sb.append("label=" + label + ";");
	}

	public int getColor() {
		return color;
	}

	public int getTitleColor() {
		return colorTitle;
	}

	private final HtmlColor getBackColor() {
		if (EntityUtils.groupRoot(group)) {
			return null;
		}
		final HtmlColor result = group.getSpecificBackColor();
		if (result != null) {
			return result;
		}
		if (parent == null) {
			return null;
		}
		return parent.getBackColor();
	}

	public boolean isClusterOf(IEntity ent) {
		if (ent.isGroup() == false) {
			return false;
		}
		return group == ent;
	}

	public static HtmlColor getStateBackColor(HtmlColor stateBack, ISkinParam skinParam, String stereotype) {
		if (stateBack == null) {
			stateBack = skinParam.getHtmlColor(ColorParam.packageBackground, stereotype, false);
		}
		if (stateBack == null) {
			stateBack = skinParam.getHtmlColor(ColorParam.background, stereotype, false);
		}
		if (stateBack == null /* || stateBack instanceof HtmlColorTransparent */) {
			stateBack = new HtmlColorTransparent();
		}
		return stateBack;
	}

	// public Point2D projection(double x, double y) {
	// final double v1 = Math.abs(minX - x);
	// final double v2 = Math.abs(maxX - x);
	// final double v3 = Math.abs(minY - y);
	// final double v4 = Math.abs(maxY - y);
	// if (v1 <= v2 && v1 <= v3 && v1 <= v4) {
	// return new Point2D.Double(minX, y);
	// }
	// if (v2 <= v1 && v2 <= v3 && v2 <= v4) {
	// return new Point2D.Double(maxX, y);
	// }
	// if (v3 <= v1 && v3 <= v2 && v3 <= v4) {
	// return new Point2D.Double(x, minY);
	// }
	// if (v4 <= v1 && v4 <= v1 && v4 <= v3) {
	// return new Point2D.Double(x, maxY);
	// }
	// throw new IllegalStateException();
	// }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy