1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-06-02 00:20:49 +00:00
plantuml/src/net/sourceforge/plantuml/timingdiagram/TimingDiagram.java

384 lines
13 KiB
Java
Raw Normal View History

2017-02-01 18:55:51 +00:00
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
2019-01-16 18:34:41 +00:00
* (C) Copyright 2009-2020, Arnaud Roques
2017-02-01 18:55:51 +00:00
*
* Project Info: http://plantuml.com
*
2017-03-15 19:13:31 +00:00
* If you like this project or if you find it useful, you can support us at:
*
* http://plantuml.com/patreon (only 1$ per month!)
* http://plantuml.com/paypal
*
2017-02-01 18:55:51 +00:00
* 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.
*
*
* Original Author: Arnaud Roques
*
*/
package net.sourceforge.plantuml.timingdiagram;
import java.awt.geom.Dimension2D;
2017-10-07 09:46:53 +00:00
import java.awt.geom.Rectangle2D;
2017-02-01 18:55:51 +00:00
import java.io.IOException;
import java.io.OutputStream;
2019-03-01 22:16:29 +00:00
import java.math.BigDecimal;
2017-02-01 18:55:51 +00:00
import java.util.ArrayList;
2019-03-01 22:16:29 +00:00
import java.util.HashMap;
2017-02-01 18:55:51 +00:00
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
2017-10-07 09:46:53 +00:00
import net.sourceforge.plantuml.AnnotatedWorker;
import net.sourceforge.plantuml.Dimension2DDouble;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.FileFormatOption;
2017-10-07 09:46:53 +00:00
import net.sourceforge.plantuml.ISkinParam;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.UmlDiagramType;
2019-03-01 22:16:29 +00:00
import net.sourceforge.plantuml.command.CommandExecutionResult;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.core.ImageData;
2020-03-03 22:29:34 +00:00
import net.sourceforge.plantuml.cucadiagram.Display;
2017-10-07 09:46:53 +00:00
import net.sourceforge.plantuml.graphic.InnerStrategy;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.graphic.StringBounder;
2017-10-07 09:46:53 +00:00
import net.sourceforge.plantuml.graphic.TextBlock;
2020-03-03 22:29:34 +00:00
import net.sourceforge.plantuml.graphic.color.Colors;
2017-10-07 09:46:53 +00:00
import net.sourceforge.plantuml.svek.TextBlockBackcolored;
2020-03-18 10:50:02 +00:00
import net.sourceforge.plantuml.timingdiagram.graphic.IntricatedPoint;
import net.sourceforge.plantuml.timingdiagram.graphic.TimeArrow;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.ugraphic.ImageBuilder;
2017-11-20 16:10:36 +00:00
import net.sourceforge.plantuml.ugraphic.MinMax;
2017-02-01 18:55:51 +00:00
import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.ULine;
import net.sourceforge.plantuml.ugraphic.UStroke;
import net.sourceforge.plantuml.ugraphic.UTranslate;
2020-03-18 10:50:02 +00:00
import net.sourceforge.plantuml.ugraphic.color.HColor;
import net.sourceforge.plantuml.ugraphic.color.HColorUtils;
2017-02-01 18:55:51 +00:00
2019-03-01 22:16:29 +00:00
public class TimingDiagram extends UmlDiagram implements Clocks {
2017-02-01 18:55:51 +00:00
2020-03-03 22:29:34 +00:00
public static final double marginX1 = 5;
private final double marginX2 = 5;
2020-04-19 16:04:39 +00:00
private final Map<String, TimeTick> codes = new HashMap<String, TimeTick>();
2017-02-01 18:55:51 +00:00
private final Map<String, Player> players = new LinkedHashMap<String, Player>();
2019-03-01 22:16:29 +00:00
private final Map<String, PlayerClock> clocks = new HashMap<String, PlayerClock>();
2017-02-01 18:55:51 +00:00
private final List<TimeMessage> messages = new ArrayList<TimeMessage>();
2020-03-03 22:29:34 +00:00
private final List<Highlight> highlights = new ArrayList<Highlight>();
2017-02-01 18:55:51 +00:00
private final TimingRuler ruler = new TimingRuler(getSkinParam());
private TimeTick now;
2017-02-15 21:34:36 +00:00
private Player lastPlayer;
2019-03-29 22:14:07 +00:00
private boolean drawTimeAxis = true;
2020-04-26 18:31:41 +00:00
private boolean compactByDefault = false;
2017-02-01 18:55:51 +00:00
public DiagramDescription getDescription() {
2017-03-12 17:22:02 +00:00
return new DiagramDescription("(Timing Diagram)");
2017-02-01 18:55:51 +00:00
}
@Override
public UmlDiagramType getUmlDiagramType() {
return UmlDiagramType.TIMING;
}
@Override
protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption)
throws IOException {
final double dpiFactor = 1;
final double margin = 10;
final ImageBuilder imageBuilder = new ImageBuilder(getSkinParam(), dpiFactor,
fileFormatOption.isWithMetadata() ? getMetadata() : null, getWarningOrError(), margin, margin,
getAnimation());
2017-10-07 09:46:53 +00:00
TextBlock result = getTextBlock();
final ISkinParam skinParam = getSkinParam();
2018-11-26 18:46:22 +00:00
result = new AnnotatedWorker(this, skinParam, fileFormatOption.getDefaultStringBounder()).addAdd(result);
2017-10-07 09:46:53 +00:00
imageBuilder.setUDrawable(result);
2017-02-01 18:55:51 +00:00
2017-04-19 18:30:16 +00:00
return imageBuilder.writeImageTOBEMOVED(fileFormatOption, seed(), os);
2017-02-01 18:55:51 +00:00
}
2017-10-07 09:46:53 +00:00
private TextBlockBackcolored getTextBlock() {
return new TextBlockBackcolored() {
public void drawU(UGraphic ug) {
drawInternal(ug);
}
public Rectangle2D getInnerPosition(String member, StringBounder stringBounder, InnerStrategy strategy) {
return null;
}
public Dimension2D calculateDimension(StringBounder stringBounder) {
2020-03-18 10:50:02 +00:00
final double withBeforeRuler = getPart1MaxWidth(stringBounder);
2017-10-07 09:46:53 +00:00
final double totalWith = withBeforeRuler + ruler.getWidth() + marginX1 + marginX2;
2020-03-03 22:29:34 +00:00
return new Dimension2DDouble(totalWith, getHeightTotal(stringBounder));
2017-10-07 09:46:53 +00:00
}
2017-11-20 16:10:36 +00:00
public MinMax getMinMax(StringBounder stringBounder) {
throw new UnsupportedOperationException();
}
2020-03-18 10:50:02 +00:00
public HColor getBackcolor() {
2017-10-07 09:46:53 +00:00
return null;
}
};
}
2017-02-01 18:55:51 +00:00
private void drawInternal(UGraphic ug) {
2019-08-26 17:07:21 +00:00
ruler.ensureNotEmpty();
2017-02-15 21:34:36 +00:00
final StringBounder stringBounder = ug.getStringBounder();
2020-04-26 18:31:41 +00:00
final double part1MaxWidth = getPart1MaxWidth(stringBounder);
final UTranslate widthPart1 = UTranslate.dx(part1MaxWidth);
if (compactByDefault == false) {
drawBorder(ug);
}
2020-03-18 10:50:02 +00:00
ug = ug.apply(UTranslate.dx(marginX1));
2017-02-01 18:55:51 +00:00
2020-04-26 18:31:41 +00:00
drawHighlightsBack(ug.apply(widthPart1));
ruler.draw0(ug.apply(widthPart1), getHeightInner(stringBounder));
boolean first = true;
2020-03-03 22:29:34 +00:00
2017-02-01 18:55:51 +00:00
for (Player player : players.values()) {
2020-04-26 18:31:41 +00:00
final UGraphic ugPlayer = ug.apply(getUTranslateForPlayer(player, stringBounder));
final double caption = getHeightForCaptions(stringBounder);
if (first) {
if (player.isCompact() == false) {
drawHorizontalSeparator(ugPlayer);
}
player.getPart1(part1MaxWidth, caption).drawU(ugPlayer);
player.getPart2().drawU(ugPlayer.apply(widthPart1).apply(UTranslate.dy(caption)));
} else {
if (player.isCompact() == false) {
drawHorizontalSeparator(ugPlayer.apply(UTranslate.dy(caption)));
}
player.getPart1(part1MaxWidth, 0).drawU(ugPlayer.apply(UTranslate.dy(caption)));
player.getPart2().drawU(ugPlayer.apply(widthPart1).apply(UTranslate.dy(caption)));
}
first = false;
2017-02-01 18:55:51 +00:00
}
2020-04-26 18:31:41 +00:00
ug = ug.apply(widthPart1);
2019-03-29 22:14:07 +00:00
if (this.drawTimeAxis) {
2020-03-03 22:29:34 +00:00
ruler.drawTimeAxis(ug.apply(getLastTranslate(stringBounder)));
2019-03-29 22:14:07 +00:00
}
2017-02-01 18:55:51 +00:00
for (TimeMessage timeMessage : messages) {
drawMessages(ug, timeMessage);
}
2020-03-03 22:29:34 +00:00
drawHighlightsLines(ug);
2017-02-01 18:55:51 +00:00
}
2020-03-03 22:29:34 +00:00
private void drawHorizontalSeparator(UGraphic ug) {
final StringBounder stringBounder = ug.getStringBounder();
2020-04-19 16:04:39 +00:00
ug = ug.apply(HColorUtils.BLACK);
2020-03-03 22:29:34 +00:00
ug = ug.apply(getBorderStroke());
2020-03-18 10:50:02 +00:00
ug = ug.apply(UTranslate.dx(-marginX1));
ug.draw(ULine.hline(getWidthTotal(stringBounder)));
2020-03-03 22:29:34 +00:00
}
2017-02-15 21:34:36 +00:00
2020-03-03 22:29:34 +00:00
private void drawBorder(UGraphic ug) {
final StringBounder stringBounder = ug.getStringBounder();
2020-03-18 10:50:02 +00:00
final ULine border = ULine.vline(getLastTranslate(stringBounder).getDy());
2020-04-19 16:04:39 +00:00
ug = ug.apply(HColorUtils.BLACK).apply(getBorderStroke());
2020-03-03 22:29:34 +00:00
ug.draw(border);
2020-03-18 10:50:02 +00:00
ug.apply(UTranslate.dx(getWidthTotal(stringBounder))).draw(border);
2020-03-03 22:29:34 +00:00
}
private UStroke getBorderStroke() {
return new UStroke(1.7);
}
private UTranslate getLastTranslate(final StringBounder stringBounder) {
2020-04-26 18:31:41 +00:00
return getUTranslateForPlayer(null, stringBounder).compose(UTranslate.dy(getHeightForCaptions(stringBounder)));
2020-03-03 22:29:34 +00:00
}
private void drawHighlightsBack(UGraphic ug) {
final double height = getHeightInner(ug.getStringBounder());
for (Highlight highlight : highlights) {
highlight.drawHighlightsBack(ug, ruler, height);
2017-02-15 21:34:36 +00:00
}
}
2020-03-03 22:29:34 +00:00
private void drawHighlightsLines(UGraphic ug) {
final double height = getHeightInner(ug.getStringBounder());
for (Highlight highlight : highlights) {
highlight.drawHighlightsLines(ug, ruler, height);
final double start = ruler.getPosInPixel(highlight.getTickFrom());
highlight.getCaption(getSkinParam()).drawU(ug.apply(new UTranslate(start + 3, 2)));
}
}
private double getHeightTotal(StringBounder stringBounder) {
return getHeightInner(stringBounder) + ruler.getHeight(stringBounder);
}
private double getHeightInner(StringBounder stringBounder) {
return getLastTranslate(stringBounder).getDy();
}
2020-04-26 18:31:41 +00:00
private double getHeightForCaptions(StringBounder stringBounder) {
2020-03-03 22:29:34 +00:00
double result = 0;
for (Highlight highlight : highlights) {
final TextBlock caption = highlight.getCaption(getSkinParam());
result = Math.max(result, caption.calculateDimension(stringBounder).getHeight());
}
return result;
}
private double getWidthTotal(final StringBounder stringBounder) {
2020-03-18 10:50:02 +00:00
return getPart1MaxWidth(stringBounder) + ruler.getWidth() + marginX1 + marginX2;
2020-03-03 22:29:34 +00:00
}
2020-03-18 10:50:02 +00:00
private double getPart1MaxWidth(StringBounder stringBounder) {
2019-03-29 22:14:07 +00:00
double width = 0;
for (Player player : players.values()) {
2020-04-26 18:31:41 +00:00
width = Math.max(width, player.getPart1(0, 0).calculateDimension(stringBounder).getWidth());
2019-03-29 22:14:07 +00:00
}
return width;
}
2017-02-01 18:55:51 +00:00
private void drawMessages(UGraphic ug, TimeMessage message) {
final Player player1 = message.getPlayer1();
final Player player2 = message.getPlayer2();
2020-04-26 18:31:41 +00:00
final StringBounder stringBounder = ug.getStringBounder();
final UTranslate translate1 = getUTranslateForPlayer(player1, stringBounder)
.compose(UTranslate.dy(getHeightForCaptions(stringBounder)));
final UTranslate translate2 = getUTranslateForPlayer(player2, stringBounder)
.compose(UTranslate.dy(getHeightForCaptions(stringBounder)));
2017-02-01 18:55:51 +00:00
2020-04-26 18:31:41 +00:00
final IntricatedPoint pt1 = player1.getTimeProjection(stringBounder, message.getTick1());
final IntricatedPoint pt2 = player2.getTimeProjection(stringBounder, message.getTick2());
2017-02-01 18:55:51 +00:00
2017-07-03 17:59:53 +00:00
if (pt1 == null || pt2 == null) {
return;
}
2017-02-01 18:55:51 +00:00
final TimeArrow timeArrow = TimeArrow.create(pt1.translated(translate1), pt2.translated(translate2),
2018-06-25 19:05:58 +00:00
message.getLabel(), getSkinParam(), message);
2017-02-01 18:55:51 +00:00
timeArrow.drawU(ug);
}
2020-04-26 18:31:41 +00:00
private UTranslate getUTranslateForPlayer(Player candidat, StringBounder stringBounder) {
2017-02-01 18:55:51 +00:00
double y = 0;
2020-03-03 22:29:34 +00:00
for (Player player : players.values()) {
if (candidat == player) {
2020-03-18 10:50:02 +00:00
return UTranslate.dy(y);
2020-03-03 22:29:34 +00:00
}
2020-04-26 18:31:41 +00:00
// if (y == 0) {
// y += getHeightHighlights(stringBounder);
// }
2020-03-18 10:50:02 +00:00
y += player.getFullHeight(stringBounder);
2020-03-03 22:29:34 +00:00
}
if (candidat == null) {
2020-03-18 10:50:02 +00:00
return UTranslate.dy(y);
2020-03-03 22:29:34 +00:00
}
throw new IllegalArgumentException();
}
2020-04-26 18:31:41 +00:00
public CommandExecutionResult createRobustConcise(String code, String full, TimingStyle type, boolean compact) {
final Player player = new PlayerRobustConcise(type, full, getSkinParam(), ruler, compactByDefault || compact);
2017-02-15 21:34:36 +00:00
players.put(code, player);
lastPlayer = player;
2019-03-01 22:16:29 +00:00
return CommandExecutionResult.ok();
}
2020-04-26 18:31:41 +00:00
public CommandExecutionResult createClock(String code, String full, int period, int pulse, boolean compact) {
final PlayerClock player = new PlayerClock(getSkinParam(), ruler, period, pulse, compactByDefault);
2019-03-01 22:16:29 +00:00
players.put(code, player);
clocks.put(code, player);
2019-08-26 17:07:21 +00:00
final TimeTick tick = new TimeTick(new BigDecimal(period), TimingFormat.DECIMAL);
2019-03-01 22:16:29 +00:00
ruler.addTime(tick);
return CommandExecutionResult.ok();
}
2020-04-26 18:31:41 +00:00
public CommandExecutionResult createBinary(String code, String full, boolean compact) {
final Player player = new PlayerBinary(full, getSkinParam(), ruler, compactByDefault);
2019-03-01 22:16:29 +00:00
players.put(code, player);
return CommandExecutionResult.ok();
2017-02-01 18:55:51 +00:00
}
2018-06-25 19:05:58 +00:00
public TimeMessage createTimeMessage(Player player1, TimeTick time1, Player player2, TimeTick time2, String label) {
2017-02-01 18:55:51 +00:00
final TimeMessage message = new TimeMessage(new TickInPlayer(player1, time1), new TickInPlayer(player2, time2),
label);
messages.add(message);
2018-06-25 19:05:58 +00:00
return message;
2017-02-01 18:55:51 +00:00
}
2020-04-19 16:04:39 +00:00
public void addTime(TimeTick time, String code) {
2017-02-01 18:55:51 +00:00
this.now = time;
ruler.addTime(time);
2020-04-19 16:04:39 +00:00
if (code != null) {
this.codes.put(code, time);
}
}
public TimeTick getCodeValue(String code) {
return codes.get(code);
2017-02-01 18:55:51 +00:00
}
2017-02-15 21:34:36 +00:00
public void updateNow(TimeTick time) {
this.now = time;
}
2017-02-01 18:55:51 +00:00
public Player getPlayer(String code) {
return players.get(code);
}
public TimeTick getNow() {
return now;
}
2019-03-01 22:16:29 +00:00
public TimeTick getClockValue(String clockName, int nb) {
final PlayerClock clock = clocks.get(clockName);
if (clock == null) {
return null;
}
2019-08-26 17:07:21 +00:00
return new TimeTick(new BigDecimal(nb * clock.getPeriod()), TimingFormat.DECIMAL);
2019-03-01 22:16:29 +00:00
}
2017-02-15 21:34:36 +00:00
public void setLastPlayer(Player player) {
this.lastPlayer = player;
}
public Player getLastPlayer() {
return lastPlayer;
}
2017-04-19 18:30:16 +00:00
public void scaleInPixels(long tick, long pixel) {
ruler.scaleInPixels(tick, pixel);
}
2019-03-29 22:14:07 +00:00
public CommandExecutionResult hideTimeAxis() {
this.drawTimeAxis = false;
return CommandExecutionResult.ok();
}
2020-03-03 22:29:34 +00:00
public CommandExecutionResult highlight(TimeTick tickFrom, TimeTick tickTo, Display caption, Colors colors) {
this.highlights.add(new Highlight(tickFrom, tickTo, caption, colors));
return CommandExecutionResult.ok();
}
2020-04-26 18:31:41 +00:00
public void goCompactMode() {
this.compactByDefault = true;
}
2017-02-01 18:55:51 +00:00
}