Commit 6308a0f2 authored by Benedikt Kleinmeier's avatar Benedikt Kleinmeier

Added new scenario element "AbsorbingArea" to package "org.vadere.state" and included it into GUI.

New scenario element "AbsorbingArea" is not handled in:
- AbsorbingAreaController (this class is missing completely at the moment).
- MigrationAssistant (must add node '"absorbingAreas" : [ ]' to topography tree).
- DefaultConfig (to be able to change color).
- TikzGenerator (must use "topography.getAbsorbingAreas()" to iterate over the new scenario element).
parent 5fa69f1f
......@@ -306,6 +306,7 @@ 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.btnErase.tooltip=Eraser
......
......@@ -298,6 +298,7 @@ TopographyCreator.btnCutTopography.tooltip=Szenario ausschneiden
TopographyCreator.btnInsertPedestrian.tooltip=Fu\u00dfg\u00e4nger
TopographyCreator.btnInsertObstacle.tooltip=Hindernis
TopographyCreator.btnInsertTarget.tooltip=Ziel
TopographyCreator.btnInsertAbsorbingArea.tooltip=Absorbierender Bereich
TopographyCreator.btnTopographyBound.tooltip=Topographie Grenze
TopographyCreator.btnTranslation.tooltip=Topographie verschieben
TopographyCreator.btnElementTranslation.tooltip=Elemente der Topography verschieben
......
......@@ -102,6 +102,9 @@ public abstract class DefaultModel<T extends DefaultConfig> extends Observable i
case TARGET:
c = getConfig().getTargetColor();
break;
case ABSORBING_AREA:
c = Color.RED;
break;
default:
c = Color.RED;
}
......
......@@ -12,14 +12,7 @@ import org.vadere.gui.topographycreator.control.AttributeModifier;
import org.vadere.state.attributes.scenario.AttributesAgent;
import org.vadere.state.attributes.scenario.AttributesCar;
import org.vadere.state.attributes.scenario.AttributesTopography;
import org.vadere.state.scenario.Obstacle;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.scenario.Source;
import org.vadere.state.scenario.Stairs;
import org.vadere.state.scenario.Target;
import org.vadere.state.scenario.Teleporter;
import org.vadere.state.scenario.Topography;
import org.vadere.state.scenario.*;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VShape;
......@@ -42,6 +35,7 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
private LinkedList<Stairs> stairs;
private LinkedList<Source> sources;
private LinkedList<Target> targets;
private LinkedList<AbsorbingArea> absorbingAreas;
private Teleporter teleporter;
private LinkedList<ScenarioElement> topographyElements;
private AttributesTopography attributes;
......@@ -57,6 +51,7 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
stairs = new LinkedList<>();
sources = new LinkedList<>();
targets = new LinkedList<>();
absorbingAreas = new LinkedList<>();
topographyElements = new LinkedList<>();
attributes = new AttributesTopography();
}
......@@ -77,6 +72,7 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
}
sources = new LinkedList<>(topography.getSources());
targets = new LinkedList<>(topography.getTargets());
absorbingAreas = new LinkedList<>(topography.getAbsorbingAreas());
teleporter = topography.getTeleporter();
} catch (SecurityException | IllegalArgumentException e) {
e.printStackTrace();
......@@ -90,6 +86,7 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
topographyElements.addAll(pedestrians);
topographyElements.addAll(sources);
topographyElements.addAll(targets);
topographyElements.addAll(absorbingAreas);
}
/**
......@@ -133,6 +130,9 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
for (Target target : targets)
topography.addTarget(target);
for (AbsorbingArea absorbingArea : absorbingAreas)
topography.addAbsorbingArea(absorbingArea);
for (AgentWrapper pedestrian : pedestrians)
topography.addInitialElement(pedestrian.getAgentInitialStore());
......@@ -167,6 +167,8 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
return pedestrians.remove(element);
case TARGET:
return targets.remove(element);
case ABSORBING_AREA:
return absorbingAreas.remove(element);
case SOURCE:
return sources.remove(element);
default:
......@@ -222,12 +224,23 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
this.targets.add(target);
}
public void addAbsorbingArea(final AbsorbingArea absorbingArea) {
this.topographyElements.add(absorbingArea);
this.absorbingAreas.add(absorbingArea);
}
public Target removeLastTarget() {
Target target = targets.removeLast();
topographyElements.remove(target);
return target;
}
public AbsorbingArea removeLastAbsorbingArea() {
AbsorbingArea absorbingArea = absorbingAreas.removeLast();
topographyElements.remove(absorbingArea);
return absorbingArea;
}
public Source removeLastSource() {
Source source = sources.removeLast();
topographyElements.remove(source);
......@@ -274,6 +287,10 @@ public class TopographyBuilder implements Iterable<ScenarioElement> {
return targets.iterator();
}
public Iterator<AbsorbingArea> getAbsorbingAreaIterator() {
return absorbingAreas.iterator();
}
public Iterator<Source> getSourceIterator() {
return sources.iterator();
}
......
......@@ -196,6 +196,9 @@ public class TopographyCreatorModel extends DefaultModel implements IDrawPanelMo
case TARGET:
element = topographyBuilder.removeLastTarget();
break;
case ABSORBING_AREA:
element = topographyBuilder.removeLastAbsorbingArea();
break;
case TELEPORTER:
element = topographyBuilder.removeTeleporter();
break;
......@@ -294,6 +297,9 @@ public class TopographyCreatorModel extends DefaultModel implements IDrawPanelMo
case TARGET:
topographyBuilder.addTarget((org.vadere.state.scenario.Target) shape);
break;
case ABSORBING_AREA:
topographyBuilder.addAbsorbingArea((org.vadere.state.scenario.AbsorbingArea) shape);
break;
case TELEPORTER:
topographyBuilder.setTeleporter((org.vadere.state.scenario.Teleporter) shape);
break;
......
package org.vadere.gui.topographycreator.model;
import org.vadere.state.attributes.Attributes;
import org.vadere.state.attributes.scenario.AttributesObstacle;
import org.vadere.state.attributes.scenario.AttributesSource;
import org.vadere.state.attributes.scenario.AttributesStairs;
import org.vadere.state.attributes.scenario.AttributesTarget;
import org.vadere.state.attributes.scenario.*;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.types.ScenarioElementType;
import org.vadere.util.geometry.shapes.Vector2D;
......@@ -35,6 +32,8 @@ public class TopographyElementFactory {
return new org.vadere.state.scenario.Source(new AttributesSource(-1, shape));
case TARGET:
return new org.vadere.state.scenario.Target(new AttributesTarget(shape));
case ABSORBING_AREA:
return new org.vadere.state.scenario.AbsorbingArea(new AttributesAbsorbingArea(shape));
case PEDESTRIAN:
return new AgentWrapper(((VCircle) shape).getCenter());
default:
......@@ -51,6 +50,8 @@ public class TopographyElementFactory {
return new org.vadere.state.scenario.Source((AttributesSource) attributes);
} else if (attributes instanceof AttributesTarget) {
return new org.vadere.state.scenario.Target((AttributesTarget) attributes);
} else if (attributes instanceof AttributesAbsorbingArea) {
return new org.vadere.state.scenario.AbsorbingArea((AttributesAbsorbingArea) attributes);
} else {
throw new IllegalArgumentException("unsupported Attributes.");
}
......
......@@ -37,6 +37,7 @@ public class TopographyCreatorRenderer extends DefaultRenderer {
renderers[ScenarioElementType.PEDESTRIAN.ordinal()] = this::renderFilledShape;
renderers[ScenarioElementType.SOURCE.ordinal()] = this::renderFilledShape;
renderers[ScenarioElementType.TARGET.ordinal()] = this::renderFilledShape;
renderers[ScenarioElementType.ABSORBING_AREA.ordinal()] = this::renderFilledShape;
renderers[ScenarioElementType.STAIRS.ordinal()] = this::renderStair;
renderers[ScenarioElementType.TELEPORTER.ordinal()] = this::renderFilledShape;
renderers[ScenarioElementType.CAR.ordinal()] = this::renderFilledShape;
......
......@@ -229,6 +229,10 @@ public class TopographyWindow extends JPanel {
TopographyAction switchToTargetAction = new ActionSwitchCategory("switch to targets", panelModel,
ScenarioElementType.TARGET, selectRectangleAction);
/* switch category to absorbing areas action */
TopographyAction switchToAbsorbingAreaAction = new ActionSwitchCategory("switch to absorbing areas", panelModel,
ScenarioElementType.ABSORBING_AREA, selectRectangleAction);
/* switch category to stairs action */
TopographyAction switchToStairsAction = new ActionSwitchCategory("switch to stairs", panelModel,
ScenarioElementType.STAIRS, selectRectangleAction);
......@@ -255,6 +259,7 @@ public class TopographyWindow extends JPanel {
List<Action> obstacleAndTargetDrawModes = new ArrayList<>();
List<Action> sourceDrawModes = new ArrayList<>();
List<Action> absorbingAreaDrawModes = new ArrayList<>();
obstacleAndTargetDrawModes.add(rectangle);
obstacleAndTargetDrawModes.add(pen);
......@@ -265,6 +270,10 @@ public class TopographyWindow extends JPanel {
sourceDrawModes.add(pen2);
sourceDrawModes.add(dot);
absorbingAreaDrawModes.add(rectangle);
absorbingAreaDrawModes.add(pen);
absorbingAreaDrawModes.add(pen2);
/* open obstacle paint method dialog action */
JButton obsButton = new JButton();
TopographyAction openObstacleDialog = new ActionOpenDrawOptionMenu(
......@@ -278,6 +287,12 @@ public class TopographyWindow extends JPanel {
.getResource("/icons/target_icon.png")), panelModel, switchToTargetAction, targetButton,
obstacleAndTargetDrawModes);
/* open absorbing area paint method dialog action */
JButton absorbingAreaButton = new JButton();
TopographyAction openAbsorbingAreaDialog = new ActionOpenDrawOptionMenu("AbsorbingArea", new ImageIcon(Resources.class
.getResource("/icons/emergency_exit.png")), panelModel, switchToAbsorbingAreaAction, absorbingAreaButton,
absorbingAreaDrawModes);
/* open stairs paint method dialog action */
JButton stairsButton = new JButton();
TopographyAction openStairsDialog = new ActionOpenDrawOptionMenu("Stairs", new ImageIcon(Resources.class
......@@ -345,6 +360,8 @@ public class TopographyWindow extends JPanel {
toolbar.addSeparator(new Dimension(5, 50));
addActionToToolbar(toolbar, openObstacleDialog, "TopographyCreator.btnInsertObstacle.tooltip",
obsButton);
addActionToToolbar(toolbar, openAbsorbingAreaDialog, "TopographyCreator.btnInsertAbsorbingArea.tooltip",
absorbingAreaButton);
addActionToToolbar(toolbar, closeDialogAction, "TopographyCreator.btnInsertPedestrian.tooltip");
addActionToToolbar(toolbar, openStairsDialog, "TopographyCreator.btnInsertStairs.tooltip",
stairsButton);
......
......@@ -164,6 +164,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
......@@ -164,6 +164,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
......@@ -164,6 +164,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
......@@ -164,6 +164,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
......@@ -152,6 +152,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
......@@ -164,6 +164,7 @@
"startingWithRedLight" : false,
"nextSpeed" : -1.0
} ],
"absorbingAreas" : [ ],
"sources" : [ {
"id" : 1,
"shape" : {
......
package org.vadere.state.attributes.scenario;
import org.vadere.state.attributes.AttributesEmbedShape;
import org.vadere.util.geometry.shapes.VShape;
/**
* Attributes of an absorbing area, used by "AbsorbingAreaController" during simulation.
*/
public class AttributesAbsorbingArea extends AttributesEmbedShape {
// Variables
private int id = ID_NOT_SET;
/**
* Shape and position.
*/
private VShape shape;
/**
* Waiting time in seconds on this area before absorbing the element.
*/
private double waitingTime = 0;
/**
* Within this distance, pedestrians have reached the absorbing area.
*/
private double deletionDistance = 0.1;
// Constructors
public AttributesAbsorbingArea() {
}
public AttributesAbsorbingArea(final VShape shape) {
this.shape = shape;
}
public AttributesAbsorbingArea(final VShape shape, final int id) {
this.shape = shape;
this.id = id;
}
public AttributesAbsorbingArea(final VShape shape, final int id, double waitingTime, double deletionDistance) {
this.shape = shape;
this.id = id;
this.waitingTime = waitingTime;
this.deletionDistance = deletionDistance;
}
// Getters
public int getId() {
return id;
}
@Override
public VShape getShape() {
return shape;
}
public double getWaitingTime() {
return waitingTime;
}
public double getDeletionDistance() {
return deletionDistance;
}
// Setters
public void setId(int id) {
checkSealed();
this.id = id;
}
@Override
public void setShape(VShape shape) {
this.shape = shape;
}
public void setWaitingTime(double waitingTime) {
checkSealed();
this.waitingTime = waitingTime;
}
public void setDeletionDistance(double deletionDistance) {
checkSealed();
this.deletionDistance = deletionDistance;
}
}
package org.vadere.state.scenario;
import org.vadere.state.attributes.Attributes;
import org.vadere.state.attributes.scenario.AttributesAbsorbingArea;
import org.vadere.state.attributes.scenario.AttributesTarget;
import org.vadere.state.types.ScenarioElementType;
import org.vadere.util.geometry.shapes.VShape;
import java.util.*;
/**
* An area with an arbitrary shape that absorbs agents event if they have not reached their target.
* This can be useful when agents run away from targets (e.g., after a bang event).
*/
public class AbsorbingArea extends ScenarioElement implements Comparable<AbsorbingArea> {
// Variables
private AttributesAbsorbingArea attributes;
private final Map<Integer, Double> enteringTimes;
/**
* Collection of listeners - unordered because it's order is not predictable
* (at least not for clients).
*/
private final Collection<AbsorbingAreaListener> absorbingAreaListeners = new LinkedList<>();
// Constructors
public AbsorbingArea(AttributesAbsorbingArea attributes) {
this(attributes, new HashMap<>());
}
public AbsorbingArea(AttributesAbsorbingArea attributes, Map<Integer, Double> enteringTimes) {
this.attributes = attributes;
this.enteringTimes = enteringTimes;
}
// Getters
public double getWaitingTime() {
return attributes.getWaitingTime();
}
public Map<Integer, Double> getEnteringTimes() {
return enteringTimes;
}
@Override
public int getId() {
return attributes.getId();
}
@Override
public VShape getShape() {
return attributes.getShape();
}
@Override
public ScenarioElementType getType() {
return ScenarioElementType.ABSORBING_AREA;
}
@Override
public AttributesAbsorbingArea getAttributes() {
return attributes;
}
// Setters
@Override
public void setShape(VShape newShape) {
attributes.setShape(newShape);
}
@Override
public void setAttributes(Attributes attributes) {
this.attributes = (AttributesAbsorbingArea) attributes;
}
// Other Methods
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof AbsorbingArea)) {
return false;
}
AbsorbingArea other = (AbsorbingArea) obj;
if (attributes == null) {
if (other.attributes != null) {
return false;
}
} else if (!attributes.equals(other.attributes)) {
return false;
}
return true;
}
@Override
public int compareTo(AbsorbingArea otherTarget) {
return this.getId() - otherTarget.getId();
}
/** Models can register a target listener. */
public void addListener(AbsorbingAreaListener listener) {
absorbingAreaListeners.add(listener);
}
public boolean removeListener(TargetListener listener) {
return absorbingAreaListeners.remove(listener);
}
/** Returns an unmodifiable collection. */
public Collection<AbsorbingAreaListener> getAbsorbingAreaListeners() {
return Collections.unmodifiableCollection(absorbingAreaListeners);
}
@Override
public AbsorbingArea clone() {
return new AbsorbingArea((AttributesAbsorbingArea) attributes.clone());
}
}
package org.vadere.state.scenario;
public interface AbsorbingAreaListener {
void reachedAbsorbingArea(Target target, Agent agent);
}
......@@ -2,6 +2,7 @@ package org.vadere.state.scenario;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.commons.math3.analysis.function.Abs;
import org.jetbrains.annotations.NotNull;
import org.vadere.state.attributes.Attributes;
import org.vadere.state.attributes.scenario.AttributesAgent;
......@@ -61,6 +62,11 @@ public class Topography implements DynamicElementMover{
* iteration between frames.
*/
private final LinkedList<Target> targets;
/**
* AbsorbingAreas of scenario by id. Tree maps ensures same update order during
* iteration between frames.
*/
private final LinkedList<AbsorbingArea> absorbingAreas;
/**
* List of obstacles used as a boundary for the whole topography.
......@@ -106,6 +112,7 @@ public class Topography implements DynamicElementMover{
stairs = new LinkedList<>();
sources = new LinkedList<>();
targets = new LinkedList<>();
absorbingAreas = new LinkedList<>();
boundaryObstacles = new LinkedList<>();
allScenarioElements.add(obstacles);
......@@ -151,6 +158,16 @@ public class Topography implements DynamicElementMover{
return null;
}
public AbsorbingArea getAbsorbingArea(int targetId) {
for (AbsorbingArea absorbingArea : this.absorbingAreas) {
if (absorbingArea.getId() == targetId) {
return absorbingArea;
}
}
return null;
}
public double distanceToObstacle(@NotNull IPoint point) {
return this.obstacleDistanceFunction.apply(point);
}
......@@ -167,6 +184,14 @@ public class Topography implements DynamicElementMover{
return getTargets().stream().filter(t -> t.getId() == targetId).anyMatch(targetPredicate);
}
public boolean containsAbsorbingArea(final Predicate<AbsorbingArea> absorbingAreaPredicate) {
return getAbsorbingAreas().stream().anyMatch(absorbingAreaPredicate);
}
public boolean containsAbsorbingArea(final Predicate<AbsorbingArea> absorbingAreaPredicate, final int absorbingAreaId) {
return getAbsorbingAreas().stream().filter(t -> t.getId() == absorbingAreaId).anyMatch(absorbingAreaPredicate);
}
/**
* Returns a list containing Targets with the specific id. This list may be empty.
*/
......@@ -182,6 +207,14 @@ public class Topography implements DynamicElementMover{
.toList())));
}
public Map<Integer, List<VShape>> getAbsorbingAreaShapes() {
return getAbsorbingAreas().stream()
.collect(Collectors
.groupingBy(absorbingArea -> absorbingArea.getId(), Collectors
.mapping(t -> t.getShape(), Collectors
.toList())));
}
@SuppressWarnings("unchecked")
private <T extends DynamicElement, TAttributes extends AttributesDynamicElement> DynamicElementContainer<T> getContainer(
Class<? extends T> elementType) {
......@@ -250,6 +283,10 @@ public class Topography implements DynamicElementMover{
return targets;
}
public List<AbsorbingArea> getAbsorbingAreas() {
return absorbingAreas;
}
public List<Obstacle> getObstacles() {
return obstacles;
}
......@@ -278,6 +315,10 @@ public class Topography implements DynamicElementMover{
this.targets.add(target);
}
public void addAbsorbingArea(AbsorbingArea absorbingArea) {
this.absorbingAreas.add(absorbingArea);
}
public void addObstacle(Obstacle obstacle) {
this.obstacles.add(obstacle);
}
......@@ -399,6 +440,9 @@ public class Topography implements DynamicElementMover{
for (Target target : getTargets()) {
s.addTarget(target.clone());
}
<