Commit 5c092be5 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck

Merge branch 'develop' into jsonTransform

parents 9e34daec ac78f73f
......@@ -8,7 +8,34 @@ unit_tests_with_coverage:
#
# Watch out: OpenCL code cannot be run on virtual machine. Therefore,
# skip this tests.
#
# The comma-separated list of tests after "-Dtest=<list>" is skipped.
# All other tests are executed normally.
#
# Watch out: on some CI instances the exclamation mark must be escaped by a
# backslash. Otherwise, it is interpreted by the executing shell as special
# command.
#
# Following syntaxes allow you to skip whole classes and single methods:
#
# #Exclude one test class
# mvn test -Dtest=!LegacyTest
# # Exclude one test method
# mvn verify -Dtest=!LegacyTest#testFoo
# # Exclude two test methods
# mvn verify -Dtest=!LegacyTest#testFoo+testBar
# # Exclude a package with a wildcard (*)
# mvn test -Dtest=!com.mycompany.app.Legacy*
script:
- mvn clean
- mvn test -Dtest=\!TestConvolution
\ No newline at end of file
- mvn test -Dtest=!TestConvolution,!TestBitonicSort,!TestCLLinkedList
- python3 Tools/collect_line_and_branch_coverage.py
run_scenario_files:
stage: deploy
script:
- mvn clean
- mvn package -Dmaven.test.skip=true
- python3 Tools/run_vadere_console_with_all_scenario_files.py
# Extract line and branch coverage (in percentage) from HTML coverage reports
# which are created by Maven's jacoco plugin.
# Use top-level pom.xml to search in correct subdirectories.
#
# Wach out: call this script from root directory of project. E.g.
#
# python Tools/my_script.py
import xml.etree.ElementTree as ET
import os
import re
def get_modules_from_pom_file(filename="pom.xml"):
"""Return a list of submodules which where found in passed "pom.xml"."""
xml_name_spaces = {
"default": "http://maven.apache.org/POM/4.0.0"
}
xml_tree = ET.parse(filename)
xml_root = xml_tree.getroot()
exclude_list = ["./VadereAnnotation", "./VadereGui"]
modules = xml_root.findall("default:modules/default:module", xml_name_spaces)
module_names = [module.text for module in modules if module.text not in exclude_list]
return module_names
def extract_line_and_branch_coverage(module_names):
"""Return a dictionary which maps module name to line and branch coverage tuple."""
module_to_coverage = dict()
default_coverage_file = "target/site/coverage-reports/index.html"
for module in module_names:
coverage_path = os.path.join(module, default_coverage_file)
with open(coverage_path, "r") as file:
coverage_report = file.read()
regex_pattern = re.compile(r"Total.*?([0-9]{1,3})%.*?([0-9]{1,3})%")
match = regex_pattern.search(coverage_report)
if match:
line_coverage = float(match.group(1))
branch_coverage = float(match.group(2))
module_to_coverage[module] = (line_coverage, branch_coverage)
else:
raise Exception("Coverage data not found for module: {}".format(module))
return module_to_coverage
def print_averaged_line_coverage(coverage_data):
"""GitLab CI tools read out the stdout output of the build process. Therefore, print coverage info to stdout."""
total_modules = len(coverage_data)
line_coverage_data = [line_coverage for (line_coverage, branch_coverage) in coverage_data.values()]
branch_coverage_data = [branch_coverage for (line_coverage, branch_coverage) in coverage_data.values()]
summed_line_coverage_data = sum(line_coverage_data)
summed_branch_coverage_data = sum(branch_coverage_data)
# By default, GitLab CI parses only integers.
averaged_line_coverage = int(round(summed_line_coverage_data / total_modules, 0))
averaged_branch_coverage = int(round(summed_branch_coverage_data / total_modules, 0))
print("Line Coverage: Total {}%".format(averaged_line_coverage))
print("Branch Coverage: {}%".format(averaged_branch_coverage))
if __name__ == "__main__":
module_names = get_modules_from_pom_file()
module_to_coverage = extract_line_and_branch_coverage(module_names)
print_averaged_line_coverage(module_to_coverage)
# Use "vadere-console.jar", which is created by "mvn package", to run all
# scenario files under "VadereModelTests" subdirectory.
#
# Note: script contains some print statements so that progress can be tracked
# a little bit
# Wach out: call this script from root directory of project. E.g.
#
# python Tools/my_script.py
import fnmatch
import os
import re
import shutil
import subprocess
def find_scenario_files(path="VadereModelTests"):
scenario_search_pattern = "*.scenario"
scenario_files = []
exclude_patterns = ["TESTOVM"]
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, scenario_search_pattern):
scenario_path = os.path.join(root, filename)
for exclude_pattern in exclude_patterns:
regex_pattern = re.compile(exclude_pattern)
match = regex_pattern.search(scenario_path)
if match is None:
scenario_files.append(scenario_path)
print("Total scenario files: {}".format(len(scenario_files)))
print("Exclude patterns: {}".format(exclude_patterns))
return scenario_files
def run_scenario_files_with_vadere_console(scenario_files, vadere_console="VadereGui/target/vadere-console.jar", scenario_timeout_in_sec=60):
output_dir = "output"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
total_scenario_files = len(scenario_files)
for i, scenario_file in enumerate(scenario_files):
print("Running scenario file ({}/{}): {}".format(i + 1, total_scenario_files, scenario_file))
# Use timout feature, check return value and capture stdout/stderr to a PIPE (use completed_process.stdout to get it).
completed_process = subprocess.run(args=["java", "-jar", vadere_console, scenario_file, output_dir],
timeout=scenario_timeout_in_sec,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print("Finished scenario file: {}".format(scenario_file))
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
if __name__ == "__main__":
scenario_files = find_scenario_files()
run_scenario_files_with_vadere_console(scenario_files)
......@@ -220,7 +220,7 @@ PostVis.chbShowStairs.text=Treppen anzeigen
PostVis.btnSnapshot.tooltip=Snapshot
PostVis.btnPNGSnapshot.tooltip=PNG-Snapshot
PostVis.btnSVGSnapshot.tooltip=SVG-Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ-Snapshot
PostVis.menuFile.title=Datei
PostVis.menuSettings.title=Einstellungen
PostVis.menuRecentFiles.title=K\u00FCrzlich verwendete Dateien
......
......@@ -150,20 +150,20 @@ public class OnlineVisualisationWindow extends JPanel implements Observer {
};
ActionGeneratePNG generatePNG = new ActionGeneratePNG(
"generatePNG",
Messages.getString("PostVis.btnPNGSnapshot.tooltip"),
resources.getIcon("camera_png.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
ActionGenerateSVG generateSVG = new ActionGenerateSVG(
"generateSVG",
Messages.getString("PostVis.btnSVGSnapshot.tooltip"),
resources.getIcon("camera_svg.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
ActionGenerateTikz generateTikz = new ActionGenerateTikz(
"generateTikz",
Messages.getString("PostVis.btnTikZSnapshot.tooltip"),
resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
......
......@@ -100,10 +100,20 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
});
}
public void init(final Map<Step, List<Agent>> agentsByStep, final Scenario vadere, final String projectPath) {
logger.info("start init postvis model");
init(vadere, projectPath);
/**
* Initialize the {@link PostvisualizationModel}.
*
* @param agentsByStep the trajectory information: a list of agent (their position, target, group...) sorted by the time step.
* @param scenario the scenario which was used to produce the output the PostVis will display.
* @param projectPath the path to the project.
*/
public void init(final Map<Step, List<Agent>> agentsByStep, final Scenario scenario, final String projectPath) {
logger.info("start the initialization of the PostvisualizationModel.");
init(scenario, projectPath);
this.agentsByStep = agentsByStep;
trajectories = new HashMap<>();
// to have fast access to the key values.
Map<Integer, Step> map = agentsByStep
.keySet().stream()
.sorted(stepComparator)
......@@ -118,21 +128,22 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
for (int stepNumber = 1; stepNumber <= optLastStep.get().getStepNumber(); stepNumber++) {
if (map.containsKey(stepNumber)) {
steps.add(map.get(stepNumber));
for(Agent agent : agentsByStep.get(map.get(stepNumber))) {
if(!trajectories.containsKey(agent.getId())) {
trajectories.put(agent.getId(), new Trajectory(agent.getId()));
}
trajectories.get(agent.getId()).addStep(map.get(stepNumber), agent);
}
} else {
steps.add(new Step(stepNumber));
}
}
}
this.trajectories = agentsByStep
.entrySet()
.stream()
.flatMap(entry -> entry.getValue().stream())
.map(ped -> ped.getId())
.distinct()
.map(id -> new Trajectory(agentsByStep, id))
.collect(Collectors.toMap(t -> t.getPedestrianId(), t -> t));
for(Trajectory trajectory : trajectories.values()) {
trajectory.fill();
}
this.step = !steps.isEmpty() ? steps.get(0) : null;
logger.info("finished init postvis model");
......@@ -300,7 +311,7 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
private double getSimTimeInSec(final Step step) {
return step.getSimTimeInSec()
.orElse(step.getStepNumber() * vadere.getScenarioStore().attributesSimulation.getSimTimeStepLength());
.orElse(step.getStepNumber() * vadere.getScenarioStore().getAttributesSimulation().getSimTimeStepLength());
}
public synchronized void setPotentialFieldContainer(final PotentialFieldContainer container) {
......
......@@ -2,6 +2,7 @@ package org.vadere.gui.postvisualization.utils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.model.SimulationModel;
import org.vadere.gui.components.view.SimulationRenderer;
......@@ -19,20 +20,20 @@ import static java.awt.geom.PathIterator.*;
* Convert the (Java) scenario description into a TikZ representation.
*
* Usually, each (Java) scenario element is represented as a path via
*
* @see PathIterator This PathSeparator must be converted into its TikZ representation.
* @see PathIterator This PathSeparator must be converted into its TikZ
* representation.
*
* For example, traversing a Java path with PathIterator returns two segments:
*
*
* segment type= 0 (SEG_MOVETO) with coords: [1.0, 1.0, 0.0, 0.0, 0.0, 0.0] segment type= 3
* (SEG_LINETO) with coords: [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]
* segment type = 0 (SEG_MOVETO) with coords: [1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
* segment type = 3 (SEG_LINETO) with coords: [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]
*
* This must be transformed to TikZ:
*
* (1.0,1.0) to (2.0,2.0)
*
* The TikZGenerator should also respect the GUI settings (e.g., enabled elements, colors etc.).
* The TikZGenerator should also respect the GUI settings (e.g., enabled
* elements, colors etc.).
*/
public class TikzGenerator {
......@@ -163,22 +164,51 @@ public class TikzGenerator {
}
// TODO: maybe, draw also trajectories.
if (config.isShowPedestrians()) {
generatedCode += "% Agents\n";
for (Agent agent : model.getAgents()) {
String agentTextPattern = "\\fill[AgentColor] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
// Do not draw agents as path for performance reasons. Usually, agents have a circular shape.
// generatedCode += String.format("\\fill[AgentColor] %s\n", generatePathForScenarioElement(agent));
}
} else {
generatedCode += "% Agents (not enabled in config)\n";
}
return generatedCode;
if (config.isShowPedestrians()) {
generatedCode += "% Agents\n";
generatedCode += drawAgents(config);
} else {
generatedCode = "% Agents (not enabled in config)\n";
}
return generatedCode;
}
private String generatePathForScenarioElement(ScenarioElement element) {
@NotNull
private String drawAgents(DefaultSimulationConfig config) {
String generatedCode = "";
for (Agent agent : model.getAgents()) {
if (model.getConfig().isShowGroups()) {
try {
Pedestrian pedestrian = (Pedestrian) agent;
Color pedestrianColor = renderer.getAgentRender().getColor(pedestrian);
Shape pedestrianShape = renderer.getAgentRender().getShape(pedestrian);
String colorString = String.format("{rgb,255:red,%d; green,%d; blue,%d}", pedestrianColor.getRed(), pedestrianColor.getGreen(), pedestrianColor.getBlue());
generatedCode += String.format("\\fill[fill=%s] %s\n", colorString, generatePathForShape(pedestrianShape));
} catch (ClassCastException cce) {
logger.error("Error casting to Pedestrian");
cce.printStackTrace();
// TODO: render agent as circle!
}
} else {
String agentTextPattern = "\\fill[AgentColor] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
}
if (model.isElementSelected() && model.getSelectedElement().equals(agent)) {
String agentTextPattern = "\\draw[magenta] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
}
// Do not draw agents as path for performance reasons. Usually, agents have a circular shape.
// generatedCode += String.format("\\fill[AgentColor] %s\n", generatePathForScenarioElement(agent));
}
return generatedCode;
}
private String generatePathForScenarioElement(ScenarioElement element) {
String generatedPath = "";
AffineTransform noTransformation = new AffineTransform();
......@@ -195,6 +225,23 @@ public class TikzGenerator {
return generatedPath;
}
private String generatePathForShape(Shape shape) {
String generatedPath = "";
AffineTransform noTransformation = new AffineTransform();
PathIterator pathIterator = shape.getPathIterator(noTransformation);
while (!pathIterator.isDone()) {
float[] coords = new float[6];
int type = pathIterator.currentSegment(coords);
generatedPath += convertJavaToTikzPath(type, coords);
pathIterator.next();
}
return generatedPath;
}
private String convertJavaToTikzPath(int type, float[] coords) {
if (type < SEG_MOVETO || type > SEG_CLOSE) {
throw new IllegalStateException(String.format("Cannot process path segment type: %d (coordinates: %s)", type, Arrays.toString(coords)));
......
......@@ -253,12 +253,13 @@ public class PostvisualizationWindow extends JPanel implements Observer {
renderer);
ActionVisualization svgImg = new ActionGenerateSVG(Messages.getString("PostVis.btnSVGSnapshot.tooltip"), resources.getIcon("camera_svg.png", iconWidth, iconHeight),
renderer);
ActionVisualization tikzImg = new ActionGenerateTikz("tikz_snapshot", resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
ActionVisualization tikzImg = new ActionGenerateTikz(Messages.getString("PostVis.btnTikZSnapshot.tooltip"), resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
renderer);
// add new ImageGenerator Action ...
imgOptions.add(pngImg);
imgOptions.add(svgImg);
imgOptions.add(tikzImg);
// add Action to List ....
ActionVisualizationMenu imgDialog = new ActionVisualizationMenu(
......
......@@ -173,7 +173,7 @@ public class TextView extends JPanel implements IJsonView {
switch (attributeType) {
case MODEL:
ModelDefinition modelDefinition = JsonConverter.deserializeModelDefinition(json);
currentScenario.getScenarioStore().mainModel = modelDefinition.getMainModel();
currentScenario.getScenarioStore().setMainModel(modelDefinition.getMainModel());
currentScenario.setAttributesModel(modelDefinition.getAttributesList());
break;
case SIMULATION:
......@@ -220,7 +220,7 @@ public class TextView extends JPanel implements IJsonView {
switch (attributeType) {
case MODEL:
txtrTextfiletextarea.setText(StateJsonConverter.serializeMainModelAttributesModelBundle(
scenario.getModelAttributes(), scenario.getScenarioStore().mainModel));
scenario.getModelAttributes(), scenario.getScenarioStore().getMainModel()));
break;
case SIMULATION:
txtrTextfiletextarea
......
......@@ -60,7 +60,7 @@ public class AgentRender implements Renderer {
return new Color(Color.HSBtoRGB(hue, 1f, 0.75f));
}
private Color getColor(Pedestrian ped) {
public Color getColor(Pedestrian ped) {
if (ped.getGroupIds().isEmpty()) {
return defaultColor;
}
......@@ -74,7 +74,7 @@ public class AgentRender implements Renderer {
return c;
}
private VShape getShape(Pedestrian ped) {
public VShape getShape(Pedestrian ped) {
if (ped.getGroupIds().isEmpty()) {
return ped.getShape();
} else if (ped.getGroupIds().getFirst() == 1) {
......
......@@ -31,7 +31,6 @@ public class GroupSourceController extends SourceController {
if (!isSourceFinished(simTimeInSec)) {
if (simTimeInSec >= timeOfNextEvent || !groupsToSpawn.isEmpty()) {
determineNumberOfSpawnsAndNextEvent(simTimeInSec);
LinkedList<List<VPoint>> spawnGroups = new LinkedList<>();
if (sourceAttributes.isSpawnAtRandomPositions()) {
......@@ -40,7 +39,7 @@ public class GroupSourceController extends SourceController {
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextFreeGroup(groupSize, random, getDynElementsAtSource());
if (newGroup != null) {
if (newGroup.size() > 0) {
// add immediately to Scenario to update DynElementsAtSource
addElementToScenario(newGroup);
iter.remove();
......@@ -52,7 +51,12 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, random);
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, random, getDynElementsAtSource());
if (newGroup.size() == 0)
throw new RuntimeException("Cannot spawn new Group. Source " + source.getId() + " is set " +
"to useFreeSpaceOnly == false but no space is left to spawn group without exactly" +
"overlapping with neighbours which can cause numerical problems. Use useFreeSpaceOnly == true (default)" +
"to queue groups.");
addElementToScenario(newGroup);
iter.remove();
}
......@@ -77,7 +81,12 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize);
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, getDynElementsAtSource());
if (newGroup == null)
throw new RuntimeException("Cannot spawn new Group. Source " + source.getId() + " is set " +
"to useFreeSpaceOnly == false but no space is left to spawn group without exactly" +
"overlapping with neighbours which can cause numerical problems. Use useFreeSpaceOnly == true (default)" +
"to queue groups.");
addElementToScenario(newGroup);
iter.remove();
}
......
......@@ -69,7 +69,7 @@ public class Simulation {
this.name = name;
this.mainModel = mainModel;
this.scenarioStore = scenarioStore;
this.attributesSimulation = scenarioStore.attributesSimulation;
this.attributesSimulation = scenarioStore.getAttributesSimulation();
this.attributesAgent = scenarioStore.getTopography().getAttributesPedestrian();
this.sourceControllers = new LinkedList<>();
this.targetControllers = new LinkedList<>();
......
......@@ -9,8 +9,6 @@ import org.vadere.util.geometry.shapes.VPoint;
import java.util.LinkedList;
import java.util.Random;
import jdk.nashorn.internal.ir.annotations.Ignore;
public class SingleSourceController extends SourceController {
private int numberToSpawn;
......@@ -36,9 +34,7 @@ public class SingleSourceController extends SourceController {
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
} else {
for (int i = 0; i < numberToSpawn; i++) {
spawnPoints.add(spawnArray.getNextRandomSpawnPoint(random));
}
spawnPoints = spawnArray.getNextRandomSpawnPoints(numberToSpawn, random, getDynElementsAtSource());
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
}
......@@ -50,9 +46,7 @@ public class SingleSourceController extends SourceController {
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
} else {
for (int i = 0; i < numberToSpawn; i++) {
spawnPoints.add(spawnArray.getNextSpawnPoint());
}
spawnPoints = spawnArray.getNextSpawnPoints(numberToSpawn, getDynElementsAtSource());
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
}
......
......@@ -61,9 +61,9 @@ public class ScenarioBuilder {
// TODO: duplicated code
if(AttributesSimulation.class == clazz){
builder = new AttributesBuilder<>((E)store.attributesSimulation);
builder = new AttributesBuilder<>((E)store.getAttributesSimulation());
builder.setField(fieldName, value);
store.attributesSimulation = (AttributesSimulation) builder.build();
store.setAttributesSimulation((AttributesSimulation) builder.build());
}
else if(AttributesAgent.class == clazz){
builder = new AttributesBuilder<>((E) store.getTopography().getAttributesPedestrian());
......@@ -84,8 +84,8 @@ public class ScenarioBuilder {
else {
builder = new AttributesBuilder<>(store.getAttributes(clazz));
builder.setField(fieldName, value);
store.attributesList.removeIf(attributes -> attributes.getClass() == clazz);
store.attributesList.add(builder.build());
store.removeAttributesIf(attributes -> attributes.getClass() == clazz);
store.addAttributes(builder.build());
}
}
......
......@@ -69,8 +69,9 @@ public class VadereConsole {
new ScenarioRun(scenario, null).run();
}
} catch (IOException e) {
} catch (Exception e) {
logger.error(e);