net.sourceforge.plantuml.golem.TilesField 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 net.sourceforge.plantuml.golem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.HColors;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.geom.XPoint2D;
import net.sourceforge.plantuml.klimt.shape.AbstractTextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.ULine;
public class TilesField extends AbstractTextBlock implements TextBlock {
private int size = 1;
private final Tile root = new Tile(0);
private final Map positions = new HashMap();
private final List paths = new ArrayList<>();
public TilesField() {
positions.put(root, new Position(0, 0, 1, 1));
}
public Tile getRoot() {
return root;
}
public Tile createTile(Tile start, TileGeometry position) {
final Tile result = new Tile(size++);
final Position p = getFreePosition(start, position);
positions.put(result, p);
paths.add(buildPath(start.getArea(position), result.getArea(position.opposite())));
return result;
}
public void addPath(Tile start, Tile dest, TileGeometry startDirection) {
paths.add(buildPath(start.getArea(startDirection), dest.getArea(startDirection.opposite())));
}
private Path buildPath(TileArea tileArea1, TileArea tileArea2) {
if (isAdjoining(tileArea1, tileArea2)) {
return Path.build(tileArea1, tileArea2);
}
final Tile tile1 = tileArea1.getTile();
final Tile tile2 = tileArea2.getTile();
final Position pos1 = getPosition(tile1);
final Position pos2 = getPosition(tile2);
final TileGeometry geom1 = tileArea1.getGeometry();
final TileGeometry geom2 = tileArea2.getGeometry();
if (pos1.getYmin() == pos2.getYmin() && pos1.getYmax() == pos2.getYmax() && geom1 == TileGeometry.WEST
&& geom2 == TileGeometry.EAST) {
return Path.build(tileArea1, tileArea2);
}
throw new IllegalArgumentException();
}
private boolean isAdjoining(TileArea tileArea1, TileArea tileArea2) {
final Tile tile1 = tileArea1.getTile();
final Tile tile2 = tileArea2.getTile();
final Position pos1 = getPosition(tile1);
final Position pos2 = getPosition(tile2);
final TileGeometry geom1 = tileArea1.getGeometry();
final TileGeometry geom2 = tileArea2.getGeometry();
if (pos1.equals(pos2)) {
assert tile1 == tile2;
if (geom1 == geom2) {
throw new IllegalArgumentException();
}
return true;
}
if (geom1.equals(geom2.opposite()) == false) {
return false;
}
switch (geom1) {
case EAST:
return pos1.getYmin() == pos2.getYmin() && pos1.getYmax() == pos2.getYmax()
&& pos1.getXmax() + 1 == pos2.getXmin();
case WEST:
return pos1.getYmin() == pos2.getYmin() && pos1.getYmax() == pos2.getYmax()
&& pos1.getXmin() == pos2.getXmax() + 1;
case SOUTH:
return pos1.getXmin() == pos2.getXmin() && pos1.getXmax() == pos2.getXmax()
&& pos1.getYmax() + 1 == pos2.getYmin();
case NORTH:
return pos1.getXmin() == pos2.getXmin() && pos1.getXmax() == pos2.getXmax()
&& pos1.getYmin() == pos2.getYmax() + 1;
case CENTER:
return false;
default:
throw new IllegalStateException();
}
}
private Tile getTileAt(Position p) {
for (Map.Entry ent : positions.entrySet()) {
if (p.equals(ent.getValue())) {
return ent.getKey();
}
}
return null;
}
private Position getFreePosition(Tile start, TileGeometry position) {
final Position p = getPosition(start).move(position, 2);
while (isOccuped(p)) {
// p = p.move(TileGeometry.EAST, 2);
moveAllToEast(p);
}
return p;
}
private void moveAllToEast(Position startingPosition) {
final List toMove = new ArrayList<>();
for (Position p : positions.values()) {
if (p.getXmax() < startingPosition.getXmin()) {
continue;
}
if (p.getYmax() < startingPosition.getYmin()) {
continue;
}
toMove.add(p);
}
for (Position p : toMove) {
positions.put(getTileAt(p), p.move(TileGeometry.EAST, 2));
}
}
private boolean isOccuped(Position test) {
for (Position p : positions.values()) {
if (p.equals(test)) {
return true;
}
}
return false;
}
public Position getPosition(Tile tile) {
final Position result = Objects.requireNonNull(positions.get(tile));
return result;
}
private int getXmin() {
int result = Integer.MAX_VALUE;
for (Position p : positions.values()) {
final int v = p.getXmin();
if (v < result) {
result = v;
}
}
return result;
}
private int getYmin() {
int result = Integer.MAX_VALUE;
for (Position p : positions.values()) {
final int v = p.getYmin();
if (v < result) {
result = v;
}
}
return result;
}
private int getXmax() {
int result = Integer.MIN_VALUE;
for (Position p : positions.values()) {
final int v = p.getXmax();
if (v > result) {
result = v;
}
}
return result;
}
private int getYmax() {
int result = Integer.MIN_VALUE;
for (Position p : positions.values()) {
final int v = p.getYmax();
if (v > result) {
result = v;
}
}
return result;
}
public List getPaths() {
return Collections.unmodifiableList(paths);
}
// -----------
public void drawU(UGraphic ug) {
double x = 0;
double y = 0;
final int xmin = getXmin();
final int ymin = getYmin();
final XDimension2D dimSingle = root.calculateDimension(ug.getStringBounder());
x -= xmin * dimSingle.getWidth() / 2;
y -= ymin * dimSingle.getHeight() / 2;
for (Map.Entry ent : positions.entrySet()) {
final Position p = ent.getValue();
final Tile t = ent.getKey();
final double xt = p.getXmin() * dimSingle.getWidth() / 2;
final double yt = p.getYmin() * dimSingle.getHeight() / 2;
t.drawU(ug.apply(new UTranslate((x + xt), (y + yt))));
}
ug = ug.apply(HColors.RED);
for (Path p : paths) {
final TileArea start = p.getStart();
final TileArea dest = p.getDest();
final XPoint2D pstart = getPoint2D(dimSingle, start);
final XPoint2D pdest = getPoint2D(dimSingle, dest);
ug.apply(new UTranslate(x + pstart.getX(), y + pstart.getY()))
.draw(new ULine(pdest.getX() - pstart.getX(), pdest.getY() - pstart.getY()));
}
}
private XPoint2D getPoint2D(XDimension2D dimSingle, TileArea area) {
final Position p = getPosition(area.getTile());
double xt = p.getXmin() * dimSingle.getWidth() / 2;
double yt = p.getYmin() * dimSingle.getHeight() / 2;
xt += dimSingle.getWidth() / 2;
yt += dimSingle.getHeight() / 2;
final double coef = 0.33;
switch (area.getGeometry()) {
case NORTH:
yt -= dimSingle.getHeight() * coef;
break;
case SOUTH:
yt += dimSingle.getHeight() * coef;
break;
case EAST:
xt += dimSingle.getWidth() * coef;
break;
case WEST:
xt -= dimSingle.getWidth() * coef;
break;
default:
throw new IllegalStateException();
}
return new XPoint2D(xt, yt);
}
public XDimension2D calculateDimension(StringBounder stringBounder) {
final int xmin = getXmin();
final int xmax = getXmax();
final int ymin = getYmin();
final int ymax = getYmax();
final int width = (xmax - xmin) / 2 + 1;
final int height = (ymax - ymin) / 2 + 1;
final XDimension2D dimSingle = root.calculateDimension(stringBounder);
return new XDimension2D(width * dimSingle.getWidth(), height * dimSingle.getHeight());
}
}