blob: 84f1472ee25c07d8d8bb599803744feb82cd15f6 [file] [log] [blame]
/*********************************************************************************
* Copyright (c) 2020 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.amalthea.visualizations.standard.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.app4mc.amalthea.model.Label;
import org.eclipse.app4mc.amalthea.model.LabelAccess;
import org.eclipse.app4mc.amalthea.model.Runnable;
import org.eclipse.app4mc.amalthea.model.SWModel;
import org.eclipse.app4mc.amalthea.model.util.SoftwareUtil;
public final class RunnableHelper {
private RunnableHelper() {
// empty private default constructor for helper class
}
/**
* Typically textures with bigger dimensions than 8192 x 8192 can't even be
* processed by modern graphic cards. Since even that is to much in some cases,
* we specify an even smaller value.
*/
public static final double CANVAS_MAX = 6000;
/**
* Calculates a grid out of the given number of items.
*
* @param count The number of items.
* @return A grid definition where the first entry in the array is the number of
* columns, the second is the number of rows.
*/
public static double[] calculateGrid(int count) {
// special handling for 3
if (count == 3) {
return new double[] {3, 1};
}
double sqrt = Math.sqrt(count);
double columns = Math.ceil(sqrt);
double rows = Math.max(1, Math.floor(sqrt));
double cells = columns * rows;
if (cells < count) {
rows += 1;
}
return new double[] {columns, rows};
}
/**
* Calculate the minimum grid cell width based on the minimum preferred
* dimensions of the given painter.
*
* @param grid The grid definition.
* @param painter The painter that should be rendered in the grid cells.
* @return The minimum cell dimensions where the first entry in the array is the
* cell width, the second entry is the cell height.
*/
public static double[] calculateMinimumCellDimensions(double[] grid, List<RunnablePainter> painter) {
double minWidth = 0;
double minHeight = 0;
int count = 0;
RunnablePainter p = null;
for (int row = 0; row < grid[1]; row++) {
for (int column = 0; column < grid[0]; column++) {
if (count >= painter.size()) {
break;
}
p = painter.get(count);
minWidth = Math.max(minWidth, p.getPreferredMinimumWidth());
minHeight = Math.max(minHeight, p.getPreferredMinimumHeight());
count++;
}
}
return new double[] {minWidth, minHeight};
}
/**
* Find all {@link Runnable} to which the given {@link Runnable} has
* dependencies in terms of Label access.
*
* @param swModel The {@link SWModel} needed to access all available
* {@link Runnable}.
* @param runnable The {@link Runnable} for which the dependencies
* should be resolved.
* @param collectTransitive <code>false</code> if only the {@link Runnable}
* should be collected that directly write to the given
* {@link Label}, <code>true</code> if the read
* dependencies of the found {@link Runnable} should be
* further evaluated.
* @return List of {@link Runnable} to which the given {@link Runnable} is
* dependent.
*/
public static List<Runnable> getRunnableDependencies(SWModel swModel, Runnable runnable, boolean collectTransitive) {
List<Label> readItems = SoftwareUtil.getReadLabelAccessList(runnable, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
List<Label> writeItems = SoftwareUtil.getWriteLabelAccessList(runnable, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
// find all Runnables that write to labels that are read by the given Runnable
List<Runnable> dependencies = new ArrayList<>();
collectReadDependencies(swModel, readItems, dependencies, collectTransitive);
Collections.reverse(dependencies);
// first remove the starting point if it was added to the read dependencies
dependencies.remove(runnable);
// now add it at the correct position
dependencies.add(runnable);
// find all Runnables that read from labels that are written by the given Runnable
collectWriteDependencies(swModel, writeItems, dependencies, collectTransitive);
return dependencies;
}
/**
* Collect all {@link Runnable} that write to the given {@link Label}s.
*
* @param swModel The {@link SWModel} needed to access all available
* {@link Runnable}.
* @param readItems The {@link Label}s to check for.
* @param collected The collection of {@link Runnable} that are already
* collected.
* @param collectTransitive <code>false</code> if only the {@link Runnable}
* should be collected that directly write to the given
* {@link Label}, <code>true</code> if the read
* dependencies of the found {@link Runnable} should be
* further evaluated.
*/
public static void collectReadDependencies(SWModel swModel, List<Label> readItems, List<Runnable> collected, boolean collectTransitive) {
// find all Runnables that write to labels that are read by the given Runnable
List<Runnable> directReadDependency = swModel.getRunnables().stream()
.filter(r -> {
List<Label> wItems = SoftwareUtil.getWriteLabelAccessList(r, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
for (Label l : readItems) {
if (wItems.contains(l)) {
return true;
}
}
return false;
})
.filter(item -> !collected.contains(item))
.collect(Collectors.toList());
collected.addAll(directReadDependency);
if (collectTransitive) {
directReadDependency.forEach(dependency -> {
List<Label> read = SoftwareUtil.getReadLabelAccessList(dependency, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
collectReadDependencies(swModel, read, collected, collectTransitive);
});
}
}
/**
* Collect all {@link Runnable} that read from the given {@link Label}s.
*
* @param swModel The {@link SWModel} needed to access all available
* {@link Runnable}.
* @param writeItems The {@link Label}s to check for.
* @param collected The collection of {@link Runnable} that are already
* collected.
* @param collectTransitive <code>false</code> if only the {@link Runnable}
* should be collected that directly write to the given
* {@link Label}, <code>true</code> if the read
* dependencies of the found {@link Runnable} should be
* further evaluated.
*/
public static void collectWriteDependencies(SWModel swModel, List<Label> writeItems, List<Runnable> collected, boolean collectTransitive) {
// find all Runnables that read from labels that are written by the given Runnable
List<Runnable> directWriteDependency = swModel.getRunnables().stream()
.filter(r -> {
List<Label> rItems = SoftwareUtil.getReadLabelAccessList(r, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
for (Label l : writeItems) {
if (rItems.contains(l)) {
return true;
}
}
return false;
})
.filter(item -> !collected.contains(item))
.collect(Collectors.toList());
collected.addAll(directWriteDependency);
if (collectTransitive) {
directWriteDependency.forEach(dependency -> {
List<Label> write = SoftwareUtil.getWriteLabelAccessList(dependency, null).stream()
.map(LabelAccess::getData)
.collect(Collectors.toList());
collectWriteDependencies(swModel, write, collected, collectTransitive);
});
}
}
/**
* Calculates the maximum scale factor to be used to avoid texture overflows on
* rendering.
*
* @param painter The list of {@link RunnablePainter} needed for calculation of
* the grid and the minimum cell dimensions.
*/
public static void setMaxScaleFactor(List<RunnablePainter> painter) {
setMaxScaleFactor(painter, RunnableHelper.calculateGrid(painter.size()));
}
/**
* Calculates the maximum scale factor to be used to avoid texture overflows on
* rendering.
*
* @param painter The list of {@link RunnablePainter} needed for calculation of
* the minimum cell dimensions.
* @param grid The grid definition needed for calculation of the minimum cell
* dimensions.
*/
public static void setMaxScaleFactor(List<RunnablePainter> painter, double[] grid) {
double[] minDim = RunnableHelper.calculateMinimumCellDimensions(grid, painter);
// calculate the maximum scaling factor
// because textures with bigger dimensions than 8192 x 8192 can't even be processed by modern graphic cards
double max = Math.max(grid[0] * minDim[0], grid[1] * minDim[1]);
double factor = CANVAS_MAX / max;
painter.forEach(p -> p.setMaxScaleFactor(factor));
}
}