TaskDrawRegistryData.java
/* ========================================================================
* 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/paypal
*
* 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.project.data;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.project.core.Resource;
import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.core.TaskGroup;
import net.sourceforge.plantuml.project.core.TaskImpl;
import net.sourceforge.plantuml.project.core.TaskSeparator;
import net.sourceforge.plantuml.project.draw.FingerPrint;
import net.sourceforge.plantuml.project.draw.ResourceDraw;
import net.sourceforge.plantuml.project.draw.ResourceDrawNumbers;
import net.sourceforge.plantuml.project.draw.TaskDraw;
import net.sourceforge.plantuml.project.draw.TaskDrawDiamond;
import net.sourceforge.plantuml.project.draw.TaskDrawGroup;
import net.sourceforge.plantuml.project.draw.TaskDrawRegular;
import net.sourceforge.plantuml.project.draw.TaskDrawSeparator;
import net.sourceforge.plantuml.project.draw.header.TimeHeader;
import net.sourceforge.plantuml.project.time.TimePoint;
import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.real.Real;
import net.sourceforge.plantuml.real.RealOrigin;
import net.sourceforge.plantuml.real.RealUtils;
import net.sourceforge.plantuml.style.ISkinParam;
/**
* Value object containing the mapping from Task to TaskDraw.
*/
public class TaskDrawRegistryData {
private final RealOrigin origin = RealUtils.createOrigin();
private final Map<Task, TaskDraw> draws = new LinkedHashMap<>();
private double totalHeightWithoutFooter;
public double getTotalHeightWithoutFooter() {
return totalHeightWithoutFooter;
}
public RealOrigin getOrigin() {
return origin;
}
public TaskDraw getTaskDraw(Task task) {
return draws.get(task);
}
// Mutators
public void putTaskDraw(Task task, TaskDraw draw) {
draws.put(task, draw);
}
// Internal access
private double computeBottomY(StringBounder stringBounder) {
double result = 0;
for (TaskDraw td : draws.values())
result = Math.max(result, td.getY(stringBounder).getCurrentValue() + td.getHeightMax(stringBounder));
return result;
}
private void resolveNoteOverlaps(StringBounder stringBounder) {
final List<TaskDraw> notes = new ArrayList<>();
for (TaskDraw td : draws.values()) {
final FingerPrint taskPrint = td.getFingerPrint(stringBounder);
final FingerPrint fingerPrintNote = td.getFingerPrintNote(stringBounder);
if (td.getTrueRow() == null)
for (TaskDraw note : notes) {
final FingerPrint otherNote = note.getFingerPrintNote(stringBounder);
final double deltaY = otherNote.overlap(taskPrint);
if (deltaY > 0) {
final Real bottom = note.getY(stringBounder).addAtLeast(note.getHeightMax(stringBounder));
td.getY(stringBounder).ensureBiggerThan(bottom);
getOrigin().compileNow();
}
}
if (fingerPrintNote != null)
notes.add(td);
}
}
private TaskDraw createRegularTaskDraw(GanttModelData modelData, TimeBoundsData timeBounds,
TimelineStyleData timelineStyle, TimeScale timeScale, Real y, final Task task, final String display) {
final boolean oddEnd;
final boolean oddStart;
final TimePoint startForDrawing = timeBounds.getStartForDrawing(task);
final TimePoint endForDrawing = timeBounds.getEndForDrawing(task);
if (timeBounds.getPrintStart() != null) {
oddStart = TimePoint.ofStartOfDay(timeBounds.getMinDay()).compareTo(startForDrawing) == 0;
oddEnd = TimePoint.ofStartOfDay(timeBounds.getMaxDay().plusDays(1)).compareTo(endForDrawing) == 0;
} else {
oddStart = false;
oddEnd = false;
}
return new TaskDrawRegular(timelineStyle.getColorSet(), timeScale, y, display, startForDrawing, endForDrawing,
oddStart, oddEnd, timelineStyle.getSkinParam(), task, this, modelData.getConstraintsForTask(task),
task.getStyleBuilder());
}
public void buildTaskAndResourceDraws(StringBounder stringBounder, TimeHeader timeHeader, GanttModelData modelData,
DisplayConfigData displayConfig, TimeBoundsData timeBounds, TimelineStyleData timelineStyle) {
final TimeScale timeScale = timeHeader.getTimeScale();
final double headerTotalHeight = timeHeader.getFullHeaderHeight(stringBounder);
final ISkinParam skinParam = timelineStyle.getSkinParam();
Real y = getOrigin().addFixed(headerTotalHeight);
for (Task task : modelData.getTasks()) {
final TaskDraw draw;
if (task instanceof TaskSeparator) {
final TaskSeparator taskSeparator = (TaskSeparator) task;
draw = new TaskDrawSeparator(taskSeparator.getName(), timeScale, y, timeBounds.getMinDay(),
timeBounds.getMaxDay(), task.getStyleBuilder(), skinParam);
} else if (task instanceof TaskGroup) {
final TaskGroup taskGroup = (TaskGroup) task;
draw = new TaskDrawGroup(timelineStyle.getColorSet(), timeScale, y, taskGroup.getCode().getDisplay(),
timeBounds.getStartForDrawing(taskGroup), timeBounds.getEndForDrawing(taskGroup), task, this,
task.getStyleBuilder(), skinParam);
} else {
final TaskImpl taskImpl = (TaskImpl) task;
final String taskLabel = displayConfig.isHideResourceName() ? taskImpl.getCode().getDisplay()
: taskImpl.getPrettyDisplay();
if (taskImpl.isDiamond())
draw = new TaskDrawDiamond(timelineStyle.getColorSet(), timeScale, y, taskLabel,
timeBounds.getStartForDrawing(taskImpl), taskImpl, this, task.getStyleBuilder(), skinParam);
else
draw = createRegularTaskDraw(modelData, timeBounds, timelineStyle, timeScale, y, taskImpl,
taskLabel);
draw.setColorsAndCompletion(taskImpl.getColors(), taskImpl.getCompletion(), taskImpl.getUrl(),
taskImpl.getNote(), taskImpl.getNoteStereotype());
}
if (task.getRow() == null)
y = y.addAtLeast(draw.getFullHeightTask(stringBounder));
putTaskDraw(task, draw);
}
getOrigin().compileNow();
resolveNoteOverlaps(stringBounder);
buildResourceDraws(stringBounder, timeScale, modelData, displayConfig, timeBounds, timelineStyle,
headerTotalHeight);
}
private void buildResourceDraws(StringBounder stringBounder, final TimeScale timeScale, GanttModelData modelData,
DisplayConfigData displayConfig, TimeBoundsData timeBounds, TimelineStyleData timelineStyle,
final double fullHeaderHeight) {
double yy = computeBottomY(stringBounder);
if (yy == 0) {
yy = fullHeaderHeight;
} else if (displayConfig.isHideResourceFootbox() == false)
for (Resource res : modelData.getResources()) {
final ResourceDraw draw = new ResourceDrawNumbers(modelData, timelineStyle, res, timeScale, yy,
TimePoint.ofStartOfDay(timeBounds.getMinDay()),
TimePoint.ofEndOfDayMinusOneSecond(timeBounds.getMaxDay()));
res.setTaskDraw(draw);
yy += draw.getHeight(stringBounder);
}
totalHeightWithoutFooter = yy;
}
}