Commit 408dba06 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen

implementation of the gui elements and actions to merge overlapping obstacles....

implementation of the gui elements and actions to merge overlapping obstacles. The algorithm sometimes still fail and the undo/redo actions are not jet implemented.
parent c2a8ce85
......@@ -277,6 +277,7 @@ ProjectView.btnSettings.tooltip=Settings
ProjectView.btnOk=OK
ProjectView.btnCancel=Cancel
TopographyCreator.btnMergeObstacles.tooltip=Merge Obstacles
TopographyCreator.btnMinimizeTopography.tooltip=Select Viewport area
TopographyCreator.btnMaximizeTopography.tooltip=Maximize Viewport area
TopographyCreator.btnQuickSave.tooltip=Quicksave
......
......@@ -275,6 +275,7 @@ ProjectView.btnSettings.tooltip=Einstellungen
ProjectView.btnOk=OK
ProjectView.btnCancel=Abbrechen
TopographyCreator.btnMergeObstacles.tooltip=Hindernisse zusammenf\u00fchren
TopographyCreator.btnMinimizeTopography.tooltip=Select Viewport area
TopographyCreator.btnMaximizeTopography.tooltip=Anzeigebereich maximieren
TopographyCreator.btnQuickSave.tooltip=Schnelles speichern
......
package org.vadere.gui.topographycreator.control;
import org.vadere.gui.topographycreator.model.IDrawPanelModel;
import org.vadere.gui.topographycreator.model.TopographyElementFactory;
import org.vadere.state.attributes.scenario.AttributesObstacle;
import org.vadere.state.scenario.Obstacle;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.types.ScenarioElementType;
import org.vadere.util.geometry.WeilerAtherton;
import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.undo.UndoableEdit;
import javax.swing.undo.UndoableEditSupport;
public class ActionMergeObstacles extends TopographyAction {
private final UndoableEditSupport undoSupport;
public ActionMergeObstacles(String name, ImageIcon icon, IDrawPanelModel panelModel,
UndoableEditSupport undoSupport) {
super(name, icon, panelModel);
this.undoSupport = undoSupport;
}
@Override
public void actionPerformed(ActionEvent e) {
List<Obstacle> obstacleList = getScenarioPanelModel().getTopography().getObstacles();
List<VPolygon> polygons = obstacleList.stream()
.map(obstacle -> obstacle.getShape())
.map(shape -> shape instanceof VRectangle ? new VPolygon(shape) : shape)
.filter(shape -> shape instanceof VPolygon)
.map(shape -> ((VPolygon)shape))
.collect(Collectors.toList());
WeilerAtherton weilerAtherton = new WeilerAtherton(polygons);
List<VPolygon> mergedPolygons = weilerAtherton.execute();
// remove polygon obstacles
getScenarioPanelModel().removeObstacleIf(obstacle ->
obstacle.getShape() instanceof VPolygon || obstacle.getShape() instanceof VRectangle);
// add merged obstacles
mergedPolygons
.stream()
.map(polygon -> new Obstacle(new AttributesObstacle(-1, polygon)))
.forEach(obstacle -> getScenarioPanelModel().addShape(obstacle));
/*ScenarioElementType type = getScenarioPanelModel().getCurrentType();
UndoableEdit edit = new EditDrawShape(getScenarioPanelModel(), type);
undoSupport.postEdit(edit);
IDrawPanelModel model = getScenarioPanelModel();
model.getCurrentType();
model.hideSelection();
ScenarioElement element = TopographyElementFactory.getInstance().createScenarioShape(model.getCurrentType(),
model.getSelectionShape());
model.addShape(element);
model.setSelectedElement(element);*/
getScenarioPanelModel().notifyObservers();
}
}
......@@ -5,11 +5,14 @@ import java.awt.Cursor;
import java.awt.Font;
import java.awt.Point;
import java.util.Observer;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.vadere.gui.components.control.IMode;
import org.vadere.gui.components.model.DefaultConfig;
import org.vadere.gui.components.model.IDefaultModel;
import org.vadere.simulator.projects.Scenario;
import org.vadere.state.scenario.Obstacle;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.scenario.Teleporter;
import org.vadere.state.scenario.Topography;
......@@ -172,4 +175,6 @@ public interface IDrawPanelModel<T extends DefaultConfig> extends IDefaultModel<
VShape translate(VPoint vector);
VShape translateElement(ScenarioElement elementToCopy, VPoint diff);
void removeObstacleIf(final @NotNull Predicate<Obstacle> predicate);
}
......@@ -4,7 +4,9 @@ import java.awt.geom.Rectangle2D;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.vadere.state.attributes.scenario.AttributesAgent;
import org.vadere.state.attributes.scenario.AttributesCar;
import org.vadere.state.attributes.scenario.AttributesTopography;
......@@ -266,6 +268,11 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
return pedestrians.iterator();
}
public void removeObstacleIf(@NotNull final Predicate<Obstacle> predicate) {
topographyElements.removeIf(scenarioElement -> scenarioElement instanceof Obstacle && predicate.test((Obstacle)scenarioElement));
obstacles.removeIf(predicate);
}
@Override
public Iterator<ScenarioElement> iterator() {
return topographyElements.iterator();
......
......@@ -7,12 +7,15 @@ import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observer;
import java.util.function.Predicate;
import org.jetbrains.annotations.NotNull;
import org.vadere.gui.components.control.IMode;
import org.vadere.gui.components.model.DefaultConfig;
import org.vadere.gui.components.model.DefaultModel;
import org.vadere.simulator.projects.Scenario;
import org.vadere.state.attributes.scenario.AttributesTopography;
import org.vadere.state.scenario.Obstacle;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.scenario.Teleporter;
import org.vadere.state.scenario.Topography;
......@@ -103,6 +106,11 @@ public class TopographyCreatorModel extends DefaultModel implements IDrawPanelMo
return topographyBuilder.build();
}
@Override
public double getBoundingBoxWidth() {
return topographyBuilder.getAttributes().getBoundingBoxWidth();
}
@Override
public void setTopography(final Topography topography) {
this.topographyBuilder = new TopographyBuilder(topography);
......@@ -336,6 +344,16 @@ public class TopographyCreatorModel extends DefaultModel implements IDrawPanelMo
return element.getShape().translatePrecise(alignToGrid(vector));
}
@Override
public void removeObstacleIf(@NotNull final Predicate predicate) {
if(selectedElement instanceof Obstacle) {
selectedElement = null;
}
topographyBuilder.removeObstacleIf(predicate);
setChanged();
}
@Override
public int getBoundId() {
return boundId;
......
......@@ -19,6 +19,7 @@ import org.vadere.gui.topographycreator.control.ActionCopyElement;
import org.vadere.gui.topographycreator.control.ActionDeleteElement;
import org.vadere.gui.topographycreator.control.ActionInsertCopiedElement;
import org.vadere.gui.topographycreator.control.ActionMaximizeSize;
import org.vadere.gui.topographycreator.control.ActionMergeObstacles;
import org.vadere.gui.topographycreator.control.ActionOpenDrawOptionMenu;
import org.vadere.gui.topographycreator.control.ActionQuickSaveTopography;
import org.vadere.gui.topographycreator.control.ActionRedo;
......@@ -160,6 +161,9 @@ public class TopographyWindow extends JPanel {
Action redoAction = new ActionRedo("redo", new ImageIcon(Resources.class
.getResource("/icons/redo_icon.png")), undoManager, basicAction);
Action mergeObstaclesAction = new ActionMergeObstacles("mergeObstacles", new ImageIcon(Resources.class
.getResource("/icons/merge.png")), panelModel, undoSupport);
FormLayout layout = new FormLayout("2dlu, default:grow(0.75), 2dlu, default:grow(0.25), 2dlu", // col
"2dlu, default, 2dlu, default, 2dlu, default, 2dlu"); // rows
thisPanel.setLayout(layout);
......@@ -326,6 +330,7 @@ public class TopographyWindow extends JPanel {
addActionToToolbar(toolbar, openStairsDialog, "TopographyCreator.btnInsertStairs.tooltip",
stairsButton);
toolbar.addSeparator(new Dimension(5, 50));
addActionToToolbar(toolbar, mergeObstaclesAction, "TopographyCreator.btnMergeObstacles.tooltip");
// addActionToToolbar(toolbar, scrollAction, "TopographyCreator.btnScroll.tooltip");
addActionToToolbar(toolbar, zoomInAction, "TopographyCreator.btnZoomIn.tooltip");
addActionToToolbar(toolbar, zoomOutAction, "TopographyCreator.btnZoomOut.tooltip");
......@@ -337,6 +342,7 @@ public class TopographyWindow extends JPanel {
addActionToToolbar(toolbar, selectCutAction, "TopographyCreator.btnCutTopography.tooltip");
addActionToToolbar(toolbar, resetScenarioAction, "TopographyCreator.btnNewTopography.tooltip");
addActionToToolbar(toolbar, saveScenarioAction, "TopographyCreator.btnQuickSave.tooltip");
toolbar.addSeparator(new Dimension(5, 50));
addActionToToolbar(toolbar, undoAction, "TopographyCreator.btnUndo.tooltip");
addActionToToolbar(toolbar, redoAction, "TopographyCreator.btnRedo.tooltip");
......
......@@ -22,8 +22,9 @@ public class WeilerAtherton {
private VPolygon subject;
private VPolygon clipping;
private boolean directionRevert = true;
private PMesh<WeilerPoint> subjectMesh;
private PMesh<WeilerPoint> clippingMesh;
private int i = 0;
private int j = 1;
private List<VPolygon> polygons;
@Nullable private WeilerPoint wp;
......@@ -64,16 +65,16 @@ public class WeilerAtherton {
}
}
public WeilerAtherton(@NotNull final VPolygon subject, @NotNull final VPolygon clipping) {
assert subject.isSimple() && clipping.isSimple();
this.subject = subject;
this.clipping = clipping;
subjectMesh = new PMesh<>((x,y) -> new WeilerPoint(new VPoint(x,y), false, false));
clippingMesh = new PMesh<>((x,y) -> new WeilerPoint(new VPoint(x,y), false, false));
public WeilerAtherton(@NotNull final List<VPolygon> polygons) {
this.polygons = polygons;
}
public Pair<PFace<WeilerPoint>, PFace<WeilerPoint>> constructIntersectionFaces() {
// phase (1)
public Pair<PFace<WeilerPoint>, PFace<WeilerPoint>> constructIntersectionFaces(
@NotNull final VPolygon subject,
@NotNull final PMesh<WeilerPoint> subjectMesh,
@NotNull final VPolygon clipping,
@NotNull final PMesh<WeilerPoint> clippingMesh) {
PFace<WeilerPoint> subjectFace = subjectMesh.toFace(subject.getPath()
.stream()
.map(p -> new WeilerPoint(p, false, clipping.contains(p)))
......@@ -90,14 +91,14 @@ public class WeilerAtherton {
PVertex<WeilerPoint> ip = null;
// compute intersections and add those to the two faces, this implementation is rather slow!
do {
do {
PHalfEdge<WeilerPoint> innerStart = clippingMesh.getEdge(clippingFace);
PHalfEdge<WeilerPoint> innerNext = innerStart;
do {
VLine l1 = subjectMesh.toLine(next);
VLine l2 = clippingMesh.toLine(innerNext);
if(GeometryUtils.intersectLineSegment(l1.getP1().getX(), l1.getP1().getY(), l1.getP2().getX(), l1.getP2().getY(), l2.getP1().getX(), l2.getP1().getY(), l2.getP2().getX(), l2.getP2().getY())) {
if (GeometryUtils.intersectLineSegment(l1.getP1().getX(), l1.getP1().getY(), l1.getP2().getX(), l1.getP2().getY(), l2.getP1().getX(), l2.getP1().getY(), l2.getP2().getX(), l2.getP2().getY())) {
VPoint intersectionPoint = GeometryUtils.intersectionPoint(l1.getP1().getX(), l1.getP1().getY(), l1.getP2().getX(), l1.getP2().getY(), l2.getP1().getX(), l2.getP1().getY(), l2.getP2().getX(), l2.getP2().getY());
WeilerPoint wp1 = new WeilerPoint(intersectionPoint, true, false);
WeilerPoint wp2 = new WeilerPoint(intersectionPoint, true, false);
......@@ -127,9 +128,55 @@ public class WeilerAtherton {
}
public List<VPolygon> execute() {
boolean merged = true;
List<VPolygon> newPolygons = new ArrayList<>();
newPolygons.addAll(polygons);
while (merged) {
int ii = -1;
int jj = -1;
merged = false;
Pair<VPolygon, VPolygon> mergeResult = null;
for(int i = 0; i < newPolygons.size(); i++) {
VPolygon first = newPolygons.get(i);
for(int j = i+1; j < newPolygons.size(); j++) {
VPolygon second = newPolygons.get(j);
mergeResult = merge(first, second);
// something got merged
if(mergeResult.getSecond() == null) {
merged = true;
ii = i;
jj = j;
break;
}
}
if(merged) {
break;
}
}
if(merged) {
newPolygons.remove(ii);
newPolygons.remove(jj-1);
newPolygons.add(mergeResult.getFirst());
}
}
return newPolygons;
}
public Pair<VPolygon, VPolygon> merge(@NotNull final VPolygon subject, @NotNull final VPolygon clipping) {
PMesh<WeilerPoint> subjectMesh = new PMesh<>((x,y) -> new WeilerPoint(new VPoint(x,y), false, false));
PMesh<WeilerPoint> clippingMesh = new PMesh<>((x,y) -> new WeilerPoint(new VPoint(x,y), false, false));
List<VPolygon> result = new ArrayList<>(2);
Pair<PFace<WeilerPoint>, PFace<WeilerPoint>> pair = constructIntersectionFaces();
Pair<PFace<WeilerPoint>, PFace<WeilerPoint>> pair = constructIntersectionFaces(subject, subjectMesh, clipping, clippingMesh);
PFace<WeilerPoint> subjectFace = pair.getFirst();
PFace<WeilerPoint> clippingFace = pair.getSecond();
......@@ -140,22 +187,22 @@ public class WeilerAtherton {
Optional<PHalfEdge<WeilerPoint>> optStartPointClip = clippingMesh.findAnyEdge(p -> !p.isIntersectionPoint() && !p.isInside());
// no point intersection and no point is contained in the other polygon => polygons do not overlap at all
if(!optStartPointSub.isPresent() && !optStartPointClip.isPresent() && !intersectionEdges.isPresent()) {
if(optStartPointSub.isPresent() && optStartPointClip.isPresent() && !intersectionEdges.isPresent()) {
result.add(subject);
result.add(clipping);
return result;
return Pair.create(subject, clipping);
}
// no point intersections and there is a point of the subject outside of the clipping => clipping is fully contained in subject
if(optStartPointSub.isPresent() && !intersectionEdges.isPresent()) {
result.add(subject);
return result;
return Pair.create(subject, null);
}
// no point intersections and there is a point of the clipping outside of the subject => subject is fully contained in clipping
if(optStartPointClip.isPresent() && !intersectionEdges.isPresent()) {
result.add(clipping);
return result;
return pair.create(clipping, null);
}
PHalfEdge<WeilerPoint> first = null;
......@@ -183,7 +230,6 @@ public class WeilerAtherton {
List<VPoint> points = new ArrayList<>();
do {
next = getNext(mesh, next, directionRevert && mesh == mesh2);
WeilerPoint wp = mesh.getPoint(next);
......@@ -200,7 +246,7 @@ public class WeilerAtherton {
}
while (!next.equals(first));
return result;
return Pair.create(GeometryUtils.toPolygon(points), null);
}
private PHalfEdge<WeilerPoint> getNext(@NotNull final PMesh<WeilerPoint> mesh, @NotNull final PHalfEdge<WeilerPoint> edge, boolean reverse) {
......
package org.vadere.util.geometry;
import com.google.common.collect.Iterables;
import org.apache.commons.math3.util.Pair;
import org.junit.Before;
import org.junit.Test;
......@@ -30,7 +28,11 @@ public class TestWeilerAtherton {
VPolygon poly1 = GeometryUtils.toPolygon(new VPoint(0, 0), new VPoint(1, 1), new VPoint(1, -1));
VPolygon poly2 = GeometryUtils.toPolygon(new VPoint(-0.01, 0), new VPoint(-1, 1), new VPoint(-1, -1));
WeilerAtherton weilerAtherton = new WeilerAtherton(poly1, poly2);
List<VPolygon> originalList = new ArrayList<>(2);
originalList.add(poly1);
originalList.add(poly2);
WeilerAtherton weilerAtherton = new WeilerAtherton(originalList);
List<VPolygon> polygonList = weilerAtherton.execute();
assertTrue(polygonList.contains(poly1));
......@@ -43,8 +45,15 @@ public class TestWeilerAtherton {
VPolygon poly1 = GeometryUtils.toPolygon(new VPoint(0, 0), new VPoint(1, 1), new VPoint(1, -1));
VPolygon poly2 = GeometryUtils.toPolygon(new VPoint(-0.01, 0), new VPoint(-1, 1), new VPoint(-1, -1));
WeilerAtherton weilerAtherton = new WeilerAtherton(poly1, poly2);
Pair<PFace<WeilerAtherton.WeilerPoint>, PFace<WeilerAtherton.WeilerPoint>> pair = weilerAtherton.constructIntersectionFaces();
List<VPolygon> originalList = new ArrayList<>(2);
originalList.add(poly1);
originalList.add(poly2);
WeilerAtherton weilerAtherton = new WeilerAtherton(originalList);
Pair<PFace<WeilerAtherton.WeilerPoint>, PFace<WeilerAtherton.WeilerPoint>> pair = weilerAtherton.constructIntersectionFaces(
poly1, new PMesh<>((x ,y) -> new WeilerAtherton.WeilerPoint(new VPoint(x,y), false, false)),
poly2, new PMesh<>((x ,y) -> new WeilerAtherton.WeilerPoint(new VPoint(x,y), false, false))
);
PFace<WeilerAtherton.WeilerPoint> face1 = pair.getFirst();
PFace<WeilerAtherton.WeilerPoint> face2 = pair.getSecond();
......@@ -68,8 +77,15 @@ public class TestWeilerAtherton {
VPolygon poly1 = GeometryUtils.toPolygon(new VPoint(0, 0), new VPoint(1, 1), new VPoint(1, -1));
VPolygon poly2 = GeometryUtils.toPolygon(new VPoint(0.3, 0), new VPoint(-1, 1), new VPoint(-1, -1));
WeilerAtherton weilerAtherton = new WeilerAtherton(poly1, poly2);
Pair<PFace<WeilerAtherton.WeilerPoint>, PFace<WeilerAtherton.WeilerPoint>> pair = weilerAtherton.constructIntersectionFaces();
List<VPolygon> originalList = new ArrayList<>(2);
originalList.add(poly1);
originalList.add(poly2);
WeilerAtherton weilerAtherton = new WeilerAtherton(originalList);
Pair<PFace<WeilerAtherton.WeilerPoint>, PFace<WeilerAtherton.WeilerPoint>> pair = weilerAtherton.constructIntersectionFaces(
poly1, new PMesh<>((x ,y) -> new WeilerAtherton.WeilerPoint(new VPoint(x,y), false, false)),
poly2, new PMesh<>((x ,y) -> new WeilerAtherton.WeilerPoint(new VPoint(x,y), false, false))
);
PFace<WeilerAtherton.WeilerPoint> face1 = pair.getFirst();
PFace<WeilerAtherton.WeilerPoint> face2 = pair.getSecond();
......@@ -79,7 +95,8 @@ public class TestWeilerAtherton {
assertEquals(5, mesh.streamPoints(face1).map(p -> new VPoint(p)).collect(Collectors.toSet()).size());
assertEquals(5, mesh.streamPoints(face2).map(p -> new VPoint(p)).collect(Collectors.toSet()).size());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment