2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit c5e9ad8e authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

add poly files to further test EikMesh, fix issue #257.

parent dd62dfb7
Pipeline #137533 passed with stages
in 118 minutes and 23 seconds
......@@ -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());
......
package org.vadere.gui.components.control.simulation;
import org.apache.commons.configuration2.Configuration;
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,6 +24,7 @@ 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;
......@@ -30,7 +34,7 @@ import javax.swing.*;
public class ActionGeneratePoly extends AbstractAction {
private static Logger logger = Logger.getLogger(ActionGeneratePNG.class);
private static Resources resources = Resources.getInstance("global");
private static final Configuration CONFIG = VadereConfig.getConfig();
private final SimulationModel<? extends DefaultSimulationConfig> model;
public ActionGeneratePoly(final String name, Icon icon, final SimulationRenderer renderer,
......@@ -41,10 +45,10 @@ public class ActionGeneratePoly extends AbstractAction {
@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 +71,27 @@ public class ActionGeneratePoly extends AbstractAction {
List<Obstacle> obstacles = new ArrayList<>(model.getTopography().getObstacles());
obstacles.removeAll(model.getTopography().getBoundaryObstacles());
List<VPolygon> obsShapes = obstacles.stream()
.map(obs -> obs.getShape())
.map(shape -> new VPolygon(shape))
.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(
new VPolygon(bound),
obstacles.stream()
.map(obs -> obs.getShape())
.map(shape -> new VPolygon(shape))
.collect(Collectors.toList()));
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,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.getParentFile().getAbsolutePath());
}
}
......
......@@ -31,6 +31,7 @@ public class MovRecorder implements IRecorder {
private Rectangle2D.Double imageSize;
private Rectangle2D.Double viewport;
private boolean finished;
private File outputFile;
public MovRecorder(final PostvisualizationRenderer renderer) {
this.model = renderer.getModel();
......@@ -76,6 +77,7 @@ public class MovRecorder implements IRecorder {
try {
this.finished = true;
enc.finish();
VadereConfig.getConfig().setProperty("SettingsDialog.snapshotDirectory.path", outputFile.getParentFile().getAbsolutePath());
} catch (IndexOutOfBoundsException error) {
logger.debug("Nothing recorded! " + error.getMessage());
throw error;
......@@ -108,7 +110,7 @@ public class MovRecorder implements IRecorder {
SimpleDateFormat formatter = new SimpleDateFormat(CONFIG.getString("SettingsDialog.dataFormat"));
String formattedDate = formatter.format(todaysDate);
JFileChooser fileChooser = new JFileChooser(VadereConfig.getConfig().getString("SettingsDialog.snapshotDirectory.path", "."));
File outputFile = new File(Messages.getString("FileDialog.filenamePrefix") + formattedDate + ".mov");
outputFile = new File(Messages.getString("FileDialog.filenamePrefix") + formattedDate + ".mov");
fileChooser.setSelectedFile(outputFile);
int returnVal = fileChooser.showDialog(null, "Save");
......
......@@ -120,7 +120,7 @@ class DataProcessingView extends JPanel implements IJsonView {
private JMenu processorsMenu = new JMenu();
private TextView buildExpertView() {
TextView panel = new TextView("/" + IOUtils.OUTPUT_DIR, "default_directory_outputprocessors", AttributeType.OUTPUTPROCESSOR);
TextView panel = new TextView("ProjectView.defaultDirectoryOutputProcessors", AttributeType.OUTPUTPROCESSOR);
JMenuBar processorsMenuBar = new JMenuBar();
processorsMenu = new JMenu(Messages.getString("Tab.Model.loadTemplateMenu.title"));
processorsMenu.setEnabled(isEditable);
......
......@@ -106,13 +106,13 @@ public class ScenarioPanel extends JPanel implements IProjectChangeListener, Pro
//Tab
attributesSimulationView =
new TextView("/attributes", "ProjectView.defaultDirectoryAttributes", AttributeType.SIMULATION);
new TextView("ProjectView.defaultDirectoryAttributes", AttributeType.SIMULATION);
attributesSimulationView.setScenarioChecker(model);
tabbedPane.addTab(Messages.getString("Tab.Simulation.title"), attributesSimulationView);
//Tab
attributesModelView = new TextView("/attributes", "ProjectView.defaultDirectoryAttributes", AttributeType.MODEL);
attributesModelView = new TextView("ProjectView.defaultDirectoryAttributes", AttributeType.MODEL);
attributesModelView.setScenarioChecker(model);
JMenuBar presetMenuBar = new JMenuBar();
......@@ -192,11 +192,11 @@ public class ScenarioPanel extends JPanel implements IProjectChangeListener, Pro
attributesModelView.getPanelTop().add(presetMenuBar, 0); // the 0 puts it at the leftmost position instead of the rightmost
tabbedPane.addTab(Messages.getString("Tab.Model.title"), attributesModelView);
topographyFileView = new TextView("/scenarios", "ProjectView.defaultDirectoryScenarios", AttributeType.TOPOGRAPHY);
topographyFileView = new TextView("ProjectView.defaultDirectoryScenarios", AttributeType.TOPOGRAPHY);
topographyFileView.setScenarioChecker(model);
tabbedPane.addTab(Messages.getString("Tab.Topography.title"), topographyFileView);
eventFileView = new TextView("/attributes", "ProjectView.defaultDirectoryAttributes", AttributeType.EVENT);
eventFileView = new TextView( "ProjectView.defaultDirectoryAttributes", AttributeType.EVENT);
eventFileView.isEditable(true);
tabbedPane.addTab(Messages.getString("Tab.Event.title"), eventFileView);
......
......@@ -2,9 +2,11 @@ package org.vadere.gui.projectview.view;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.configuration2.Configuration;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.jetbrains.annotations.NotNull;
import org.vadere.gui.components.utils.Messages;
import org.vadere.gui.components.utils.Resources;
import org.vadere.gui.projectview.VadereApplication;
......@@ -17,12 +19,14 @@ import org.vadere.state.events.json.EventInfoStore;
import org.vadere.state.events.presettings.EventPresettings;
import org.vadere.state.scenario.Topography;
import org.vadere.state.util.StateJsonConverter;
import org.vadere.util.config.VadereConfig;
import org.vadere.util.io.IOUtils;
import org.vadere.util.logging.Logger;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.prefs.Preferences;
......@@ -41,11 +45,12 @@ import javax.swing.filechooser.FileNameExtensionFilter;
public class TextView extends JPanel implements IJsonView {
private static Logger logger = Logger.getLogger(TextView.class);
private static final Configuration CONFIG = VadereConfig.getConfig();
private AttributeType attributeType;
private String default_folder;
private String default_resource;
private JPanel panelTop = new JPanel();
private static final long serialVersionUID = 3975758744810301970L;
......@@ -64,14 +69,15 @@ public class TextView extends JPanel implements IJsonView {
private ActionListener saveToFileActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String path = IOUtils.chooseJSONFileSave(Messages.getString("TextFileView.btnSaveToFile.text"),
Preferences.userNodeForPackage(VadereApplication.class).get(default_resource, default_folder));
String path = IOUtils.chooseJSONFileSave(Messages.getString("TextFileView.btnSaveToFile.text"), CONFIG.getString(default_resource));
if (path == null)
return;
try {
IOUtils.writeTextFile(path.endsWith(".json") ? path : path + ".json", txtrTextfiletextarea.getText());
File file = new File(path);
VadereConfig.getConfig().setProperty(default_resource, file.getParentFile().getAbsolutePath());
} catch (IOException e1) {
IOUtils.errorBox(e1.getLocalizedMessage(), Messages.getString("SaveFileErrorMessage.title"));
logger.error(e1);
......@@ -83,15 +89,15 @@ public class TextView extends JPanel implements IJsonView {
@Override
public void actionPerformed(ActionEvent arg0) {
FileFilter filter = new FileNameExtensionFilter("JSON file", "json");
String path = IOUtils.chooseFile("Choose file...",
Preferences.userNodeForPackage(VadereApplication.class).get(default_resource, default_folder),
filter);
String path = IOUtils.chooseFile(Messages.getString("ChooseFile.text"), CONFIG.getString(default_resource), filter);
if (path == null)
return;
try {
String content = IOUtils.readTextFile(path);
File file = new File(path);
VadereConfig.getConfig().setProperty(default_resource, file.getParentFile().getAbsolutePath());
txtrTextfiletextarea.setText(content);
} catch (IOException e) {
logger.error("could not load from file: " + e.getMessage());
......@@ -102,8 +108,7 @@ public class TextView extends JPanel implements IJsonView {
/**
* Create the panel.
*/
public TextView(String default_folder, String default_resource, final AttributeType attributeType) {
this.default_folder = default_folder;
public TextView(@NotNull final String default_resource, final AttributeType attributeType) {
this.default_resource = default_resource;
this.attributeType = attributeType;
setLayout(new BorderLayout(0, 0));
......
package org.vadere.gui.projectview.view;
import org.apache.commons.configuration2.Configuration;
import org.vadere.gui.components.utils.Messages;
import org.vadere.gui.projectview.VadereApplication;
import org.vadere.util.config.VadereConfig;
import org.vadere.util.io.IOUtils;
import org.vadere.util.logging.Logger;
......@@ -14,6 +16,7 @@ import java.util.prefs.Preferences;
public class VDialogManager {
private static final Configuration CONFIG = VadereConfig.getConfig();
private static Logger logger = Logger.getLogger(VDialogManager.class);
private static final FileFilter PROJECT_FILTER = new FileNameExtensionFilter("Vadere Project", "project");
......@@ -34,12 +37,11 @@ public class VDialogManager {
}
public static String loadProjectDialog() {
return IOUtils.chooseFile(Messages.getString("LoadProjectText"),
getDefaultDirectory(), PROJECT_FILTER);
return IOUtils.chooseFile(Messages.getString("LoadProjectText"), getDefaultDirectory(), PROJECT_FILTER);
}
private static String getDefaultDirectory() {
return Preferences.userNodeForPackage(VadereApplication.class).get("ProjectView.defaultDirectory", "/projects");
return CONFIG.getString("ProjectView.defaultDirectory");
}
public static int showConfirmDialogWithBodyAndTextArea(String title, String body, String textAreaContent,
......
# nVertices dimension nAttributes boundaryMarker
21 2 0 0
19 2 0 0
# vertexId x y
1 0.500000 0.500000
2 0.326351 11.349890
3 50.295924 42.506680
4 10.068317 15.240938
5 50.129121 5.936819
6 49.500000 49.500000
7 49.500000 0.500000
8 7.711478 10.030659
9 0.463889 45.907735
10 0.500000 49.500000
11 32.129121 5.936819
12 29.943119 0.478289
13 3.888684 20.251412
14 0.463889 42.907735
15 1.463889 42.907735
16 23.463889 45.907735
17 0.326351 -0.650110
18 15.459378 12.808810
19 20.931872 10.544835
20 29.943119 -1.521711
21 31.295924 42.506680
1 10.068317 15.240938
2 0.500000 12.473684
3 49.500000 49.500000
4 49.500000 0.500000
5 7.711478 10.030659
6 0.500000 45.907735
7 0.500000 42.907735
8 32.129121 5.936819
9 0.500000 49.500000
10 3.888684 20.251412
11 1.463889 42.907735
12 1.821300 11.082842
13 23.463889 45.907735
14 49.500000 5.936819
15 15.459378 12.808810
16 20.931872 10.544835
17 29.892589 0.500000
18 49.500000 42.506680
19 31.295924 42.506680
#
# nSegments boundaryMarker
21 0
19 0
# lineId vertexId1 vertexId2
1 7 6
2 6 10
3 10 1
4 1 7
5 3 21
6 21 19
7 19 11
8 11 5
9 5 3
10 20 12
11 12 8
12 8 2
13 2 17
14 17 20
15 13 4
16 4 18
17 18 16
18 16 9
19 9 14
20 14 15
21 15 13
1 4 14
2 14 8
3 8 16
4 16 19
5 19 18
6 18 3
7 3 9
8 9 6
9 6 13
10 13 15
11 15 1
12 1 10
13 10 11
14 11 7
15 7 2
16 2 12
17 12 5
18 5 17
19 17 4
#
# nHoles
3
# vertexId x y (of a vertex which lies inside the hole)
1 37.999061 23.296979
2 11.703993 3.490417
3 11.505979 32.412715
\ No newline at end of file
0
# vertexId x y (of a vertex which lies inside the hole)
\ No newline at end of file
......@@ -25,9 +25,22 @@ import java.util.stream.Collectors;
/**
* The Weiler-Atherton-Algorithm (https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm)
* merges a set of polygons. Note this merging does not support holes that is if there is a hole it will be
* filled by the merging algorithm. Two polygons will be merged if they overlap. Co-linear and duplicated points,
* i.e. useless points, will be removed. If polygon A contains polygon B, the result will be equals to polygon A.
* enables boolean operations on polygons, i.e.:
* <ul>
* <li>
* INTERSECTION of polygon A (subject) and B (clipper).
* </li>
* <li>
* UNION of polygon A (subject) and B (clipper).
* </li>
* <li>
* SUBTRACTION of polygon B (subject) from polygon A (clipper).
* </li>
* </ul>
* Note that holes are not supported, i.e. if UNION produces a polygon with holes this will be filled.
* If SUBTRACTION produces a hole an error will be thrown since {@link VPolygon} do not support holes.
* Furthermore, it is assumed that each {@link VPolygon} which is involved is a simple non-self intersecting
* polygon. Co-linear and duplicated points, i.e. useless points, will be removed.
*
* @author Benedikt Zoennchen
*/
......@@ -39,6 +52,13 @@ public class WeilerAtherton {
private static final String propNameInside = "inside";
private static final String propNameTwin = "twin";
private enum Operation {
INTERSECTION,
UNION,
SUBTRACTION
}
/**
* The default constructor.
*
......@@ -287,6 +307,31 @@ public class WeilerAtherton {
return newPolygons;
}
public Optional<VPolygon> subtraction() {
List<VPolygon> newPolygons = new ArrayList<>();
newPolygons.addAll(polygons);
if(polygons.size() == 0) {
return Optional.empty();
}
else if(polygons.size() == 1) {
return Optional.of(polygons.get(0));
}
else {
VPolygon subject = polygons.get(0);
List<VPolygon> clippers = polygons.subList(1, polygons.size());
for(VPolygon clipper : clippers) {
Optional<VPolygon> result = subtraction(subject, clipper);
if(result.isPresent()) {
subject = result.get();
} else {
return Optional.empty();
}
}
return Optional.of(subject);
}
}
/**
* Executes the Weiler-Atherton-Algorithm for all of its polygons.
*
......@@ -330,13 +375,19 @@ public class WeilerAtherton {
private List<VPolygon> construct(
@NotNull final VPolygon subjectCandidat,
@NotNull final VPolygon clippingCandidat,
final boolean cap) {
final Operation operation) {
//Predicate<WeilerPoint> startEdgeCondition = cap ? p -> p.isInside() : p -> !p.isInside();
VPolygon subject = GeometryUtils.isCCW(subjectCandidat) ? subjectCandidat : subjectCandidat.revertOrder();
VPolygon clipping = GeometryUtils.isCCW(clippingCandidat) ? clippingCandidat : clippingCandidat.revertOrder();
VPolygon clipping;
switch (operation) {
case SUBTRACTION: clipping = GeometryUtils.isCCW(clippingCandidat) ? clippingCandidat.revertOrder() : clippingCandidat; break;
case UNION:
case INTERSECTION:
default: {
clipping = GeometryUtils.isCCW(clippingCandidat) ? clippingCandidat : clippingCandidat.revertOrder(); break;
}
}
PMesh subjectMesh = new PMesh();
PMesh clippingMesh = new PMesh();
......@@ -371,7 +422,13 @@ public class WeilerAtherton {
List<VPolygon> polygons = new ArrayList<>();
PMesh mesh = subjectMesh;
Set<PHalfEdge> intersectionSet = cap ? subjectEnteringEdges : subjectExitingEdges;
Set<PHalfEdge> intersectionSet;
switch (operation) {
case INTERSECTION: intersectionSet = subjectEnteringEdges; break;
case UNION:
case SUBTRACTION:
default: intersectionSet = subjectExitingEdges; break;
};
// cup will preserve the polyons.
if(intersectionSet.isEmpty()) {
......@@ -383,27 +440,34 @@ public class WeilerAtherton {
.streamPoints(clippingFace)
.allMatch(p -> contains(subject, p));
if(cap) {
if(subInClip) {
polygons.add(subjectCandidat);
}
else if(clipInSub) {
polygons.add(clippingCandidat);
}
}
else {
if(subInClip) {
polygons.add(clippingCandidat);
}
else if(clipInSub) {
polygons.add(subjectCandidat);
}
else {
polygons.add(subjectCandidat);
polygons.add(clippingCandidat);
}
switch (operation) {
case INTERSECTION: {
if(subInClip) {
polygons.add(subjectCandidat);
}
else if(clipInSub) {
polygons.add(clippingCandidat);
}
} break;
case UNION: {
if(subInClip) {
polygons.add(clippingCandidat);
}
else if(clipInSub) {
polygons.add(subjectCandidat);
}
else {
polygons.add(subjectCandidat);
polygons.add(clippingCandidat);
}
} break;
case SUBTRACTION:
default: {
if(clipInSub) {
throw new IllegalArgumentException("subtracting a polygon which is contained in its counterpart will produce a polygon with a hole which is not supported.");
}
} break;
}
return polygons;
}
......@@ -558,7 +622,7 @@ public class WeilerAtherton {
}
public List<VPolygon> cap(@NotNull final VPolygon subjectCandidat, @NotNull final VPolygon clippingCandidat) {
return construct(subjectCandidat, clippingCandidat, true);
return construct(subjectCandidat, clippingCandidat, Operation.INTERSECTION);
}
//private PVertex<VPoint, Object, Object> getTwin(@NotNull final PVertex<VPoint, Object, Object> v, PMesh<VPoint, Object, >)
......@@ -571,6 +635,18 @@ public class WeilerAtherton {
* @return a pair of polygon where the second element is null if the polygons got merged. This is not the case if there do not overlap.
*/
public List<VPolygon> cup(@NotNull final VPolygon subjectCandidat, @NotNull final VPolygon clippingCandidat) {
return construct(subjectCandidat, clippingCandidat, false);
return construct(subjectCandidat, clippingCandidat, Operation.UNION);
}
public Optional<VPolygon> subtraction(@NotNull final VPolygon subjectCandidat, @NotNull final VPolygon clippingCandidat) {
List<VPolygon> result = construct(subjectCandidat, clippingCandidat, Operation.SUBTRACTION);
assert result.size() >= 1;
if(result.size() == 1) {
return Optional.of(result.get(0));
} else if(result.isEmpty()) {
return Optional.empty();
} else{
throw new IllegalStateException("subtraction of two polygons should never produce more than one polygon.");
}
}
}