The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit a345e775 authored by Daniel Lehmberg's avatar Daniel Lehmberg
Browse files

Merge branch 'master' into...

Merge branch 'master' into 241-new-flag-to-compute-metric-for-the-quality-of-the-stepcircleoptimizer
parents 18aaa1f8 d6a4b916
......@@ -45,6 +45,7 @@ VadereModelCalibration/**/output
VadereModelTests/**/legacy
VadereUtils/output/**
VadereModelTests/*_private
**/current_commit_hash.txt
# Operating system files
.DS_Store
......@@ -54,3 +55,7 @@ VadereModelTests/*_private
**/output/
**/legacy/
**/*_private/
# Vadere Cache
**/__cache__
**/vadere-server-output
\ No newline at end of file
......@@ -25,6 +25,7 @@ variables:
VADERE_DEPLOYMENT_BASE_URL: "http://www.vadere.org/builds"
VADERE_PACKAGE_NAME_BRANCHES: "vadere.${CI_COMMIT_REF_NAME}.${CI_RUNNER_TAGS}.zip"
VADERE_PACKAGE_NAME_RELEASES: "vadere.${CI_COMMIT_TAG}.${CI_RUNNER_TAGS}.zip"
EIKMESH_PACKAGE_NAME_BRANCHES: "eikmesh.${CI_COMMIT_REF_NAME}.${CI_RUNNER_TAGS}.zip"
# Stage Definitions
# Watch out: integration tests and the seed tests run after deployment, because
......@@ -82,6 +83,8 @@ stages:
- mvn -Dmaven.test.skip=true package
- python3 -m zipfile -c ${VADERE_PACKAGE_NAME_BRANCHES} CHANGELOG.md README.md VadereModelTests/ VadereGui/target/vadere-gui.jar VadereSimulator/target/vadere-console.jar
- scp ${VADERE_PACKAGE_NAME_BRANCHES} di49mur@webdev-mwn.lrz.de:~/webserver/htdocs/builds/master/${VADERE_PACKAGE_NAME_BRANCHES}
- python3 -m zipfile -c ${EIKMESH_PACKAGE_NAME_BRANCHES} VadereMeshing/README.md VadereMeshing/target/meshing-0.1-SNAPSHOT.jar
- scp ${EIKMESH_PACKAGE_NAME_BRANCHES} di49mur@webdev-mwn.lrz.de:~/webserver/htdocs/builds/master/eikmesh/${EIKMESH_PACKAGE_NAME_BRANCHES}
only:
refs:
- master
......
......@@ -6,6 +6,37 @@
### Added
### Changed
## v1.2 (2019-07-13)
### Added
- VadereServer:
- Introducing TraCI server implementation for Vadere to allow remote control
of Vaderes simulation loop.
- VadereManager/target/vadere-server.jar will open a TCP socket and waits
for connection request.
- FloorField Caching:
- CellGrid based floorfields can be loaded from a persisted cache file.
- Added attributes:
- `cacheType: [NO_CACHE|TXT_CACHE|BIN_CACHE]`
- `cacheDir: ""` relative path
- Cache files will be saved in a `__cache__` directory beside (sibling) the
scenario file. With `cacheDir` it is possible to create some structure within
the cache directory. Important: If `cacheDir` is an absolute path the cache
file will not be placed in `__cache__`.
- A TXT_CACHE type will save the CellGrid in a human readable form (CSV) and
BIN_CACHE will use a simple binary format for better performance and space
reasons.
- TikzGenerator:
- add configuration to show all traces of pedestrians, even if they
left the simulation. Config: PostVis -> `Show all Trajectories on Snapshot`
- add named coordinate for each scenario element. The coordinate represents the
centroid point of the polygon and can be used as a point for relative placement
of labels.
- introduced id for reference: Source `src_XXX`, Target `trg_XXX`, AbsorbingArea `absorb_XXX`
Obstacels `obs_XXX`, Stairs `str_XXX`, MeasurementArea `mrmtA_XXX`
- single step mode in GUI: Allows the user to step through the simulation one
step at a time to identify bugs.
- simplify obstacles (with undo support): Merge multiple obstacles based on the
......@@ -24,6 +55,11 @@
### Changed
- version migration ensures that new attributes in the scenario file will be added
with default values if no specific actions are defined in a Transformation class.
- TikzGenerator: style information for pedestrians are moved to dedicated style
classes to simplify changes in generated tikz files.
## v1.0 (2019-06-13)
### Added
......
......@@ -55,7 +55,7 @@ With the following steps, you can run a simulation with one of the built-in exam
- Start Vadere
- Click *Project* > *Open*
- Choose `vadere.project` of one of the test projects, e.g. [TestOSM](https://gitlab.lrz.de/vadere/vadere/tree/master/VadereModelTests/TestOSM) and click *open*
- Select tahe scenario on the left and press *run selected scenario*
- Select the scenario on the left and press *run selected scenario*
## Build from Source
......
package org.vadere.annotation.traci.client;
import com.google.auto.service.AutoService;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypeException;
import javax.tools.JavaFileObject;
@SupportedAnnotationTypes("org.vadere.annotation.traci.client.TraCIApi")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@AutoService(Processor.class)
public class ClientAnnotationProcessor extends AbstractProcessor {
private StringBuilder apiMembers;
private StringBuilder apiInit;
private StringBuilder apiMapping;
private StringBuilder apiAbstract;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
apiMembers = new StringBuilder();
apiInit = new StringBuilder();
apiMapping = new StringBuilder();
apiAbstract = new StringBuilder();
// see SupportedAnnotationTypes (here only TraCIApi)
for (TypeElement annotation : annotations) {
Set<? extends Element> annotatedElements =
roundEnv.getElementsAnnotatedWith(annotation)
.stream()
.filter(e -> e.getKind().isClass()
&& !e.getKind().equals(ElementKind.ENUM)
&& !e.getModifiers().contains(Modifier.ABSTRACT)
)
.collect(Collectors.toSet());
if (annotatedElements.isEmpty())
continue;
for (Element annotatedElement : annotatedElements) {
try {
writeApiClass(annotatedElement);
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
TypeElement element = processingEnv.getElementUtils().
getTypeElement("org.vadere.manager.client.AbstractTestClient");
if (element == null)
writeAbstractTestClient();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
protected void writeAbstractTestClient() throws IOException {
JavaFileObject jFile = processingEnv.getFiler().createSourceFile("AbstractTestClient");
try (PrintWriter writer = new PrintWriter(jFile.openWriter())){
writer.append("package org.vadere.manager.client; ").println();
writer.println();
writer.append("import org.vadere.manager.TraCISocket;").println();
writer.append("import org.vadere.manager.client.ConsoleReader;").println();
writer.append("import java.io.IOException;").println();
writer.println();
writer.append("public abstract class AbstractTestClient {").println();
writer.append(apiMembers.toString()).println();
writer.append("\tpublic AbstractTestClient() { }").println();
writer.println();
writer.append("\tpublic void init(TraCISocket socket, ConsoleReader consoleReader){").println();
writer.append(apiInit.toString());
writer.println();
writer.append(apiMapping.toString());
writer.append("\t}").println();
writer.println();
writer.append(apiAbstract.toString());
writer.println();
writer.append("}").println();
}
}
protected void writeApiClass(Element apiClass) throws IOException {
TraCiApiWrapper traCIApi = new TraCiApiWrapper(apiClass);
JavaFileObject jFile = processingEnv.getFiler().createSourceFile(traCIApi.name);
apiMembers.append(String.format("\tprotected %s.%s %s;\n", traCIApi.packageName, traCIApi.name, traCIApi.name.toLowerCase()));
apiInit.append(String.format("\t\t%s = new %s.%s(socket);\n", traCIApi.name.toLowerCase(), traCIApi.packageName, traCIApi.name));
try (PrintWriter writer = new PrintWriter(jFile.openWriter())){
writer.append("package ").append(traCIApi.packageName).append(";").println();
writer.println();
for (String anImport : traCIApi.imports) {
writer.append("import ").append(anImport).append(";").println();
}
writer.append("import ").append(traCIApi.cmdEnum).append(";").println();
writer.append("import ").append(traCIApi.varEnum).append(";").println();
writer.println();
// start API class
writer.append("public class ").append(traCIApi.name).append(" extends ")
.append(traCIApi.extendedClassName).append(" {").println();
// constructor
writer.append("\tpublic ").append(traCIApi.name).append("(TraCISocket socket) {").println();
writer.append("\t\tsuper(socket, \"").append(traCIApi.name).append("\");").println();
writer.append("\t}").println();
writer.println();
for (Element element : apiClass.getEnclosedElements()) {
List<? extends AnnotationMirror> anMirrors = element.getAnnotationMirrors();
if (anMirrors != null){
for (AnnotationMirror anMirror : anMirrors) {
String anName = anMirror.getAnnotationType().asElement().getSimpleName().toString();
String singeAn = traCIApi.singleAnnotation
.substring(traCIApi.singleAnnotation.lastIndexOf(".") + 1).trim();
if (anName.equals(singeAn)){
ApiHandler apiHandler = new ApiHandler(traCIApi, element, anMirror);
apiMapping.append(String.format("\t\tconsoleReader.addCommand(\"%s.%s\", \"\", this::%s_%s);\n", traCIApi.name.toLowerCase(), apiHandler.name, traCIApi.name.toLowerCase(), apiHandler.name));
apiAbstract.append(String.format("\t\tabstract public void %s_%s (String args[]) throws IOException;\n",traCIApi.name.toLowerCase(), apiHandler.name));
switch (apiHandler.apiType){
case "GET":
writeGET(writer, apiHandler);
break;
case "SET":
writeSET(writer, apiHandler);
break;
}
}
}
}
}
writer.append("}").println(); // end API class
}
}
protected void writeGET(PrintWriter writer, ApiHandler apiHandler){
if (apiHandler.ignoreElementId){
writer.append("\tpublic TraCIGetResponse ").append(apiHandler.name).append("() throws IOException {").println();
writer.append("\t\tTraCIPacket p = TraCIGetCommand.build(").append(apiHandler.cmd).append(", ").append(apiHandler.varId).append(", \"-1\");").println();
}
else {
writer.append("\tpublic TraCIGetResponse ").append(apiHandler.name).append("(String elementID) throws IOException {").println();
writer.append("\t\tTraCIPacket p = TraCIGetCommand.build(").append(apiHandler.cmd).append(", ").append(apiHandler.varId).append(", elementID);").println();
}
writer.append("\n\t\tsocket.sendExact(p);\n").println();
writer.append("\t\treturn (TraCIGetResponse) socket.receiveResponse();").println();
writer.append("\t}").println();
writer.println();
}
protected void writeSET(PrintWriter writer, ApiHandler apiHandler){
writer.append("\tpublic TraCIResponse ").append(apiHandler.name).append("(String elementId, ").append(apiHandler.dataTypeStr).append(" data) throws IOException {").println();
writer.append("\t\tTraCIPacket p = TraCISetCommand.build(")
.append(apiHandler.cmd).append(", elementId, ").append(apiHandler.varId).append(", ").append(apiHandler.varType).append(", data);").println();
writer.append("\n\t\tsocket.sendExact(p);\n").println();
writer.append("\t\treturn socket.receiveResponse();").println();
writer.append("\t}").println();
writer.println();
}
class TraCiApiWrapper {
String name;
String singleAnnotation;
String multipleAnnotation;
String cmdEnum;
String varEnum;
String packageName;
String[] imports;
String extendedClassName;
TraCiApiWrapper(Element apiClass){
TraCIApi traCIApi = apiClass.getAnnotation(TraCIApi.class);
name = traCIApi.name();
packageName = traCIApi.packageName();
imports = traCIApi.imports();
extendedClassName = traCIApi.extendedClassName();
try {
singleAnnotation = traCIApi.singleAnnotation().getCanonicalName();
} catch (MirroredTypeException e){
singleAnnotation = e.getTypeMirror().toString();
}
try {
multipleAnnotation = traCIApi.multipleAnnotation().getCanonicalName();
} catch (MirroredTypeException e){
multipleAnnotation = e.getTypeMirror().toString();
}
try {
cmdEnum = traCIApi.cmdEnum().getCanonicalName();
} catch (MirroredTypeException e){
cmdEnum = e.getTypeMirror().toString();
}
try {
varEnum = traCIApi.varEnum().getCanonicalName();
} catch (MirroredTypeException e){
varEnum = e.getTypeMirror().toString();
}
}
}
class ApiHandler {
String cmd;
String varId;
String varType;
String name;
String dataTypeStr;
boolean ignoreElementId;
String apiType; //SET, GET, SUB
public ApiHandler(TraCiApiWrapper traCIApi, Element method, AnnotationMirror annotationMirror){
ignoreElementId = false; //default
dataTypeStr = "";
String cmdPrefix = traCIApi.cmdEnum;
cmdPrefix = cmdPrefix.substring(cmdPrefix.lastIndexOf('.') + 1).trim();
String varPrefix = traCIApi.varEnum;
varPrefix = varPrefix.substring(varPrefix.lastIndexOf('.') + 1).trim();
Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap = annotationMirror.getElementValues();
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : valueMap.entrySet()) {
String key = entry.getKey().getSimpleName().toString();
Object value = entry.getValue().getValue();
switch (key){
case "cmd":
this.cmd = cmdPrefix+ "." + value.toString();
this.apiType = value.toString().substring(0, 3);
break;
case "var":
this.varId = varPrefix + "." + value.toString() + ".id";
this.varType = varPrefix + "." + value.toString() + ".type";
break;
case "name":
this.name = (String) value;
break;
case "ignoreElementId":
this.ignoreElementId = (boolean) value;
break;
case "dataTypeStr":
this.dataTypeStr = value.toString();
}
}
}
@Override
public String toString() {
return "ApiHandler{" +
"cmd='" + cmd + '\'' +
", varId='" + varId + '\'' +
", varType='" + varType + '\'' +
", name='" + name + '\'' +
", ignoreElementId=" + ignoreElementId +
", apiType='" + apiType + '\'' +
'}';
}
}
}
package org.vadere.annotation.traci.client;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface TraCIApi {
String packageName() default "org.vadere.manager.client.traci";
String[] imports() default {
"org.vadere.manager.client.traci.TraCIClientApi",
"org.vadere.manager.TraCISocket",
"org.vadere.manager.traci.commands.TraCIGetCommand",
"org.vadere.manager.traci.commands.TraCISetCommand",
"org.vadere.manager.traci.respons.TraCIGetResponse",
"org.vadere.manager.traci.writer.TraCIPacket",
"org.vadere.manager.traci.respons.TraCIResponse",
"java.io.IOException",
"java.util.ArrayList"
};
String extendedClassName() default "TraCIClientApi";
String name();
Class singleAnnotation();
Class multipleAnnotation();
Class cmdEnum();
Class varEnum();
}
......@@ -303,9 +303,10 @@ ProjectView.btnCancel=Cancel
ProjectView.btnExpertCSV=Export as csv
ProjectView.label.simResults=Simulation Results
TopographyCreator.btnGeneratePoly.tooltip=Generate a Planar Straight-Line Graph (PSLG)
TopographyCreator.btnMergeObstacles.tooltip=Merge Overlapping Obstacles into One Polygon
TopographyCreator.btnMinimizeTopography.tooltip=Select Viewport area
TopographyCreator.btnMaximizeTopography.tooltip=Maximize Viewport area
TopographyCreator.btnMaximizeTopography.tooltip=Maximize Viewport Area
TopographyCreator.btnQuickSave.tooltip=Quicksave
TopographyCreator.btnNewTopography.tooltip=New Scenario
TopographyCreator.btnScroll.tooltip=Scroll Around
......@@ -317,14 +318,14 @@ TopographyCreator.btnCutTopography.tooltip=Cut Scenario
TopographyCreator.btnInsertPedestrian.tooltip=Pedestrian
TopographyCreator.btnTopographyBound.tooltip=Topography Bound
TopographyCreator.btnMergeWithConvexHull.tooltip=Merge With Convex Hull
TopographyCreator.btnTranslation.tooltip=Translate topography
TopographyCreator.btnElementTranslation.tooltip=Translate topography elements
TopographyCreator.btnTranslation.tooltip=Translate Topography
TopographyCreator.btnElementTranslation.tooltip=Translate Topography Elements
TopographyCreator.btnInsertObstacle.tooltip=Obstacle
TopographyCreator.btnInsertTarget.tooltip=Target
TopographyCreator.btnInsertAbsorbingArea.tooltip=Absorbing Area
TopographyCreator.btnInsertSource.tooltip=Source
TopographyCreator.btnInsertStairs.tooltip=Stairs
TopographyCreator.btnInsertMeasurementArea.tooltip=MeasurmentArea
TopographyCreator.btnInsertMeasurementArea.tooltip=Measurment Area
TopographyCreator.btnErase.tooltip=Eraser
TopographyCreator.btnConvexHull.label=Convex Hull
TopographyCreator.btnSimplePolygon.label=Simple Polygon
......
......@@ -297,6 +297,7 @@ ProjectView.btnCancel=Abbrechen
ProjectView.btnDrawVoronoiDiagram.tooltip=Voronoi-Diagramm zeichnen und anzeigen
ProjectView.btnDrawMesh.tooltip=Mesh zeichnen und anzeigen
TopographyCreator.btnGeneratePoly.tooltip=Generiere einen PSLG
TopographyCreator.btnMergeObstacles.tooltip=\u00dcberlappende Hindernisse zusammenf\u00fchren
TopographyCreator.btnMinimizeTopography.tooltip=Select Viewport area
TopographyCreator.btnMaximizeTopography.tooltip=Anzeigebereich maximieren
......
package org.vadere.gui.components.control.simulation;
package org.vadere.gui.components.control;
import org.apache.commons.configuration2.Configuration;
import org.vadere.gui.components.control.simulation.ActionGeneratePNG;
import org.vadere.gui.components.model.DefaultModel;
import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.model.SimulationModel;
import org.vadere.gui.components.utils.Messages;
import org.vadere.gui.components.utils.Resources;
import org.vadere.gui.components.view.SimulationRenderer;
import org.vadere.gui.postvisualization.PostVisualisation;
import org.vadere.meshing.mesh.impl.PSLG;
import org.vadere.meshing.utils.io.poly.PSLGGenerator;
import org.vadere.state.scenario.Obstacle;
import org.vadere.util.config.VadereConfig;
import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.logging.Logger;
......@@ -21,30 +22,29 @@ import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import javax.swing.*;
public class ActionGeneratePoly extends AbstractAction {
private static Logger logger = Logger.getLogger(ActionGeneratePNG.class);
private static Resources resources = Resources.getInstance("global");
private final SimulationModel<? extends DefaultSimulationConfig> model;
private static final Configuration CONFIG = VadereConfig.getConfig();
private final DefaultModel<? extends DefaultSimulationConfig> model;
public ActionGeneratePoly(final String name, Icon icon, final SimulationRenderer renderer,
final SimulationModel<? extends DefaultSimulationConfig> model) {
public ActionGeneratePoly(final String name, Icon icon, final DefaultModel<? extends DefaultSimulationConfig> model) {
super(name, icon);
this.model = model;
}
@Override
public void actionPerformed(final ActionEvent e) {
JFileChooser fileChooser = new JFileChooser(Preferences.userNodeForPackage(PostVisualisation.class).get("SettingsDialog.snapshotDirectory.path", "."));
JFileChooser fileChooser = new JFileChooser(CONFIG.getString("SettingsDialog.snapshotDirectory.path"));
Date todaysDate = new java.util.Date();
SimpleDateFormat formatter = new SimpleDateFormat(resources.getProperty("SettingsDialog.dataFormat"));
SimpleDateFormat formatter = new SimpleDateFormat(CONFIG.getString("SettingsDialog.dataFormat"));
String formattedDate = formatter.format(todaysDate);
......@@ -67,17 +67,27 @@ public class ActionGeneratePoly extends AbstractAction {
List<Obstacle> obstacles = new ArrayList<>(model.getTopography().getObstacles());
obstacles.removeAll(model.getTopography().getBoundaryObstacles());
String polyString = PSLGGenerator.toPSLG(
new VPolygon(bound),
obstacles.stream()
List<VPolygon> obsShapes = obstacles.stream()
.map(obs -> obs.getShape())
.map(shape -> new VPolygon(shape))
.collect(Collectors.toList()));
.collect(Collectors.toList());
// this computes the union of intersecting obstacles.
obsShapes = PSLG.constructHoles(obsShapes);
// this will help to construct a valid non-rectangular bound.
List<VPolygon> polygons = PSLG.constructBound(new VPolygon(bound), obsShapes);
String polyString = PSLGGenerator.toPSLG(
polygons.get(0),
polygons.size() > 1 ? polygons.subList(1, polygons.size()) : Collections.emptyList());
try {
outputFile.createNewFile();
Writer out = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8");
out.write(polyString);
out.flush();
VadereConfig.getConfig().setProperty("SettingsDialog.snapshotDirectory.path", outputFile.getParentFile().getAbsolutePath());
logger.info("generate new Poly.file: " + outputFile.getAbsolutePath());
} catch (IOException e1) {
logger.error(e1.getMessage());
......
......@@ -47,7 +47,6 @@ public class ActionGeneratePNG extends AbstractAction implements IRendererChange
SimpleDateFormat formatter = new SimpleDateFormat(CONFIG.getString("SettingsDialog.dataFormat"));
String formattedDate = formatter.format(todaysDate);
File outputFile = new File(Messages.getString("FileDialog.filenamePrefix") + formattedDate + ".png");
fileChooser.setSelectedFile(outputFile);
......@@ -62,6 +61,7 @@ public class ActionGeneratePNG extends AbstractAction implements IRendererChange
try {
ImageIO.write(bi, "png", outputFile);
VadereConfig.getConfig().setProperty("SettingsDialog.snapshotDirectory.path", outputFile.getParentFile().getAbsolutePath());
logger.info("generate new png: " + outputFile.getAbsolutePath());
} catch (IOException e1) {
logger.error(e1.getMessage());
......
......@@ -47,6 +47,7 @@ public class ActionGenerateSVG extends AbstractAction implements IRendererChange
outputFile = fileChooser.getSelectedFile().toString().endsWith(".svg") ? fileChooser.getSelectedFile()
: new File(fileChooser.getSelectedFile().toString() + ".svg");
svgGenerator.generateSVG(outputFile);
VadereConfig.getConfig().setProperty("SettingsDialog.snapshotDirectory.path", outputFile.getParentFile().getAbsolutePath());
}
}
......
......@@ -51,6 +51,7 @@ public class ActionGenerateTikz extends AbstractAction implements IRendererChang
: new File(fileChooser.getSelectedFile().toString() + ".tex");
tikzGenerator.generateTikz(outputFile);
VadereConfig.getConfig().setProperty("SettingsDialog.snapshotDirectory.path", outputFile.