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

Commit 3a6093c1 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck

refactor groupSpawn to use a polygon as source shape

parent 75c7c2b3
......@@ -59,6 +59,8 @@ public class AgentRender implements Renderer {
}
private void renderGroup(Pedestrian ped, Graphics2D g) {
g.setColor(Color.DARK_GRAY);
g.fill(ped.getShape());
g.setColor(getGroupColor(ped));
DefaultRenderer.fill(getShape(ped), g);
}
......
......@@ -5,7 +5,8 @@ import org.vadere.simulator.models.groups.GroupModel;
import org.vadere.state.attributes.scenario.AttributesDynamicElement;
import org.vadere.state.scenario.Source;
import org.vadere.state.scenario.Topography;
import org.vadere.state.util.SpawnArray;
import org.vadere.state.util.GroupSpawnArray;
import org.vadere.util.geometry.PointPositioned;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
......@@ -13,12 +14,13 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class GroupSourceController extends SourceController {
private final GroupModel groupModel;
private LinkedList<Integer> groupsToSpawn;
protected final SpawnArray spawnArray;
protected final GroupSpawnArray spawnArray;
public GroupSourceController(Topography scenario, Source source,
DynamicElementFactory dynamicElementFactory,
......@@ -30,8 +32,9 @@ public class GroupSourceController extends SourceController {
VRectangle elementBound = new VRectangle(dynamicElementFactory.getDynamicElementRequiredPlace(new VPoint(0,0)).getBounds2D());
this.spawnArray = new SpawnArray(new VRectangle(source.getShape().getBounds2D()),
new VRectangle(0, 0,elementBound.getWidth() , elementBound.getHeight() ));
this.spawnArray = new GroupSpawnArray(source.getShape(),
new VRectangle(0, 0,elementBound.getWidth() , elementBound.getHeight()),
dynamicElementFactory::getDynamicElementRequiredPlace);
}
......@@ -47,7 +50,14 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextFreeGroup(groupSize, random, getDynElementsAtSource());
List<VPoint> newGroup = spawnArray.getNextFreeGroup(
groupSize,
random,
getDynElementsAtSource().stream()
.map(PointPositioned::getPosition)
.map(dynamicElementFactory::getDynamicElementRequiredPlace)
.collect(Collectors.toList())
);
if (newGroup.size() > 0) {
// add immediately to Scenario to update DynElementsAtSource
addElementToScenario(newGroup);
......@@ -60,7 +70,14 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, random, getDynElementsAtSource());
List<VPoint> newGroup = spawnArray.getNextGroup(
groupSize,
random,
getDynElementsAtSource().stream()
.map(PointPositioned::getPosition)
.map(dynamicElementFactory::getDynamicElementRequiredPlace)
.collect(Collectors.toList())
);
if (newGroup.isEmpty())
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" +
......@@ -77,7 +94,13 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextFreeGroup(groupSize, getDynElementsAtSource());
List<VPoint> newGroup = spawnArray.getNextFreeGroup(
groupSize,
getDynElementsAtSource().stream()
.map(PointPositioned::getPosition)
.map(dynamicElementFactory::getDynamicElementRequiredPlace)
.collect(Collectors.toList())
);
if (newGroup != null && !newGroup.isEmpty()) {
// add immediately to Scenario to update DynElementsAtSource
addElementToScenario(newGroup);
......@@ -90,7 +113,13 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, getDynElementsAtSource());
List<VPoint> newGroup = spawnArray.getNextGroup(
groupSize,
getDynElementsAtSource().stream()
.map(PointPositioned::getPosition)
.map(dynamicElementFactory::getDynamicElementRequiredPlace)
.collect(Collectors.toList())
);
if (newGroup == null || newGroup.isEmpty())
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" +
......
package org.vadere.simulator.control;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.system.CallbackI;
import org.vadere.simulator.models.DynamicElementFactory;
import org.vadere.state.attributes.scenario.AttributesDynamicElement;
import org.vadere.state.scenario.Obstacle;
import org.vadere.state.scenario.Source;
import org.vadere.state.scenario.Topography;
import org.vadere.state.util.SingleSourceSpawnArray;
import org.vadere.state.util.SpawnArray;
import org.vadere.util.geometry.PointPositioned;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedList;
......@@ -22,7 +19,6 @@ import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class SingleSourceController extends SourceController {
......@@ -37,7 +33,6 @@ public class SingleSourceController extends SourceController {
AttributesDynamicElement attributesDynamicElement,
Random random) {
super(scenario, source, dynamicElementFactory, attributesDynamicElement, random);
this.dynamicElementFactory = dynamicElementFactory;
VRectangle elementBound = new VRectangle(dynamicElementFactory.getDynamicElementRequiredPlace(new VPoint(0,0)).getBounds2D());
this.spawnArray = new SingleSourceSpawnArray(source.getShape(),
new VRectangle(0, 0,elementBound.getWidth(), elementBound.getHeight()),
......@@ -106,7 +101,7 @@ public class SingleSourceController extends SourceController {
List<VPoint> positions = new ArrayList<>(numberToSpawn);
for(int i = 0; i < numberToSpawn; i++) {
Optional<VPoint> optPosition = getNextPosition(blockPedestrianShapes, spawnArray.getSpawnPoints());
Optional<VPoint> optPosition = getNextPosition(blockPedestrianShapes, spawnArray.getAllowedSpawnPoints());
if (optPosition.isPresent()) {
VPoint position = optPosition.get();
......
......@@ -2,7 +2,6 @@ package org.vadere.simulator.control;
import org.apache.commons.math3.distribution.RealDistribution;
import org.vadere.simulator.models.DynamicElementFactory;
import org.vadere.state.attributes.scenario.AttributesAgent;
import org.vadere.state.attributes.scenario.AttributesDynamicElement;
import org.vadere.state.attributes.scenario.AttributesSource;
import org.vadere.state.scenario.Agent;
......@@ -12,11 +11,8 @@ import org.vadere.state.scenario.DynamicElement;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.state.scenario.Source;
import org.vadere.state.scenario.Topography;
import org.vadere.state.util.SpawnArray;
import org.vadere.util.geometry.LinkedCellsGrid;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
......@@ -29,7 +25,7 @@ public abstract class SourceController {
// public static final double SPAWN_BUFFER_SIZE = 0.03;
protected final Source source;
private final DynamicElementFactory dynamicElementFactory;
protected final DynamicElementFactory dynamicElementFactory;
private final Topography topography;
protected final Random random;
......
package org.vadere.state.util;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Function;
public abstract class AbstractSpawnArray {
private static Logger logger = LogManager.getLogger(AbstractSpawnArray.class);
protected final VRectangle spawnElementBound;
protected final VRectangle bound;
// number of spawn elements in x and y Dimension.
protected int xDim;
protected int yDim;
protected final ArrayList<VPoint> allowedSpawnPoints;
protected VPoint firstSpawnPoint;
protected double eX, eY;
// map valid boundGrid coordinates to #allowedSpawnPoints ArrayList index.
protected HashMap<Integer,Integer> validSpawnPointMapInBoundShape;
protected Function<VPoint, VShape> shapeProducer;
public AbstractSpawnArray(final VShape boundShape, final VRectangle spawnElementBound,
Function<VPoint, VShape> shapeProducer) {
this.spawnElementBound = spawnElementBound;
this.bound = new VRectangle(boundShape.getBounds2D());
this.shapeProducer = shapeProducer;
xDim = (int) (bound.width / spawnElementBound.width);
yDim = (int) (bound.height / spawnElementBound.height);
if (xDim * yDim <= 0) {
xDim = (xDim == 0) ? 1 : xDim;
yDim = (yDim == 0) ? 1 : yDim;
allowedSpawnPoints = new ArrayList<>(xDim * yDim);
//offset left upper corner to center point.
eX = (xDim == 1) ? bound.getCenterX() : spawnElementBound.x + spawnElementBound.width / 2;
eY = (yDim == 1) ? bound.getCenterY() : spawnElementBound.y + spawnElementBound.height / 2;
logger.info(String.format(
"Dimension of Source is to small for at least one dimension to contain designated spawnElement with Bound (%.2f x %.2f) Set to (%d x %d)",
spawnElementBound.width, spawnElementBound.height, xDim, yDim));
} else {
allowedSpawnPoints = new ArrayList<>(xDim * yDim);
//offset left upper corner to center point.
eX = spawnElementBound.x + spawnElementBound.width / 2;
eY = spawnElementBound.y + spawnElementBound.height / 2;
}
firstSpawnPoint = new VPoint(bound.x + eX, bound.y + eY);
validSpawnPointMapInBoundShape = new HashMap<>();
int validIndex = 0;
for (int i = 0; i < (xDim * yDim); i++) {
VPoint candidatePoint = firstSpawnPoint.add(new VPoint(2 * eX * (i % xDim), 2 * eY * (i / xDim)));
VShape candidateShape = shapeProducer.apply(candidatePoint);
if (boundShape.containsShape(candidateShape)) {
validSpawnPointMapInBoundShape.put(i, validIndex);
allowedSpawnPoints.add(candidatePoint);
validIndex++;
}
}
allowedSpawnPoints.trimToSize();
}
public List<VPoint> getAllowedSpawnPoints(){
return allowedSpawnPoints;
}
}
package org.vadere.state.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Simplifies placement of a group within a {@link SpawnArray}. There are two placement strategies
* Simplifies placement of a group within a There are two placement strategies
* supported.
* <li>NoneOverlapping:</li>
* With this strategy a group spawns do not overlap. E.g The first 2x2 Group will start at (0,0) and
......@@ -13,8 +17,8 @@ package org.vadere.state.util;
*/
public class GroupPlacementHelper {
private final int xBound;
private final int yBound;
private final int boundedShapeGridCellsX;
private final int boundedShapeGridCellsY;
private final int groupSize;
private final int groupDimX;
......@@ -23,97 +27,96 @@ public class GroupPlacementHelper {
private final int noneOverlapXGroupCount;
private final int noneOverlapYGroupCount;
private final int overlapXGroupCount;
private final int overlapYGroupCount;
private final int groupPlacementCountX;
private final int groupPlacementCountY;
private ArrayList<Integer> validSpawnPointsForGroupInBound;
public GroupPlacementHelper(int xBound, int yBound, int groupSize) {
if (groupSize > xBound * yBound)
public GroupPlacementHelper(int boundedShapeGridCellsX, int boundedShapeGridCellsY,
int groupSize, HashMap<Integer, Integer> validSpawnPointMapInBoundShape) {
if (groupSize > boundedShapeGridCellsX * boundedShapeGridCellsY)
throw new IndexOutOfBoundsException("GroupSize: " + groupSize
+ "to big for given Bound " + xBound + " x " + yBound);
+ "to big for given Bound " + boundedShapeGridCellsX + " x " + boundedShapeGridCellsY);
this.xBound = xBound;
this.yBound = yBound;
this.boundedShapeGridCellsX = boundedShapeGridCellsX;
this.boundedShapeGridCellsY = boundedShapeGridCellsY;
this.groupSize = groupSize;
int dimGx, dimGy;
// dimension of smallest square contain a group of size groupSize
dimGx = (int) Math.ceil(Math.sqrt(groupSize));
dimGx = (dimGx > xBound) ? xBound : dimGx;
dimGx = (dimGx > boundedShapeGridCellsX) ? boundedShapeGridCellsX : dimGx;
// dimGy set to minimize lost space in resulting rectangle (or square if groupSize is a square number)
dimGy = dimGx * (dimGx - 1) < groupSize ? dimGx : dimGx - 1;
this.groupDimX = dimGx;
this.groupDimY = dimGy;
this.noneOverlapXGroupCount = xBound / dimGx;
this.noneOverlapYGroupCount = yBound / dimGy;
this.noneOverlapXGroupCount = boundedShapeGridCellsX / dimGx;
this.noneOverlapYGroupCount = boundedShapeGridCellsY / dimGy;
this.overlapXGroupCount = xBound - (dimGx - 1);
this.overlapYGroupCount = yBound - (dimGy - 1);
}
//
this.groupPlacementCountX = boundedShapeGridCellsX - (dimGx - 1);
this.groupPlacementCountY = boundedShapeGridCellsY - (dimGy - 1);
/**
* @param groupNumber zero-Based number of group of groupSize with the noneOverlapping strategy
* @return zero-Based index within {@link SpawnArray} corresponding to start index of
* groupNumber.
*/
public int getNoneOverlappingStart(int groupNumber) {
return (groupNumber % noneOverlapXGroupCount) * groupDimX + // offset in x
(groupNumber / noneOverlapXGroupCount) * xBound * groupDimY; // offset in y
validSpawnPointsForGroupInBound = new ArrayList<>();
for(int i = 0; i < getOverlappingGroupCount(); i++){ // i group spawn location
if (isGridCellWithinSource(validSpawnPointMapInBoundShape, i)){
validSpawnPointsForGroupInBound.add(i);
}
}
}
/**
* NoneOverlapping strategy
*
* @param groupNumber zero-Based number of group
* @param i zero-Based index within group. Must be smaller than groupSize
* @return zero-Based index within {@link SpawnArray} corresponding to groupNumber and index
* i
* @param validSpawnPointMapInBoundShape mapping of rectangular bound grid to valid coordinates
* within the source shape
* @param groupIndex groupIndex specifying the first ped within one group.
* @return true if all positions within the group are contained
* within the source shape.
*/
public int getNoneOverlappingIndex(int groupNumber, int i) {
assert i < groupSize;
int start = getNoneOverlappingStart(groupNumber);
return start + (i % groupDimX) + (i / groupDimX) * xBound;
boolean isGridCellWithinSource(HashMap<Integer, Integer> validSpawnPointMapInBoundShape, int groupIndex){
for (int pedIndexInGroup = 0; pedIndexInGroup < groupSize; pedIndexInGroup++){
boolean isValid = validSpawnPointMapInBoundShape.containsKey(getOverlappingIndex(groupIndex, pedIndexInGroup));
if (!isValid){
return false;
}
}
return true;
}
public int nextNoneOverlappingGroupNumber(int oldGroupNumber) {
return (oldGroupNumber + 1) % (noneOverlapXGroupCount * noneOverlapYGroupCount);
}
/**
* @return Number of groups based on noneOverlapping strategy
*/
public int getNoneOverlappingGroupCount() {
return noneOverlapXGroupCount * noneOverlapYGroupCount;
}
/**
* @param groupNumber zero-Based number of group of groupSize with the overlapping strategy
* @return zero-Based index within {@link SpawnArray} corresponding to groupNumber and index i
* @return zero-Based index within {@link GroupSpawnArray} corresponding to groupNumber and index i
*/
public int getOverlappingStart(int groupNumber) {
return (groupNumber % overlapXGroupCount) + // offset in x
(groupNumber / overlapXGroupCount) * xBound; // offset in y (groupDimY not needed)
return (groupNumber % groupPlacementCountX) + // offset in x
(groupNumber / groupPlacementCountX) * boundedShapeGridCellsX; // offset in y (groupDimY not needed)
}
/**
* Overlapping strategy
*
* @param groupNumber zero-Based number of group
* @param groupNumberInBound zero-Based number of group
* @param i zero-Based index within group. Must be smaller than groupSize
* @return zero-Based index within {@link SpawnArray} corresponding to groupNumber and index
* @return zero-Based index within {@link GroupSpawnArray} corresponding to groupNumber and index
* i
*/
public int getOverlappingIndex(int groupNumber, int i) {
public int getOverlappingIndex(int groupNumberInBound, int i) {
assert i < groupSize;
int start = getOverlappingStart(groupNumber);
return start + (i % groupDimX) + (i / groupDimX) * xBound;
int start = getOverlappingStart(groupNumberInBound);
return start + (i % groupDimX) + (i / groupDimX) * boundedShapeGridCellsX;
}
public int getOverlappingGroupCount() {
return overlapXGroupCount * overlapYGroupCount;
return groupPlacementCountX * groupPlacementCountY;
}
public ArrayList<Integer> getValidSpawnPointsForGroupInBound(){
return validSpawnPointsForGroupInBound;
}
public int getGroupSize() {
......
......@@ -2,88 +2,35 @@ package org.vadere.state.util;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.state.scenario.DynamicElement;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* <h1>Single Pedestrians</h1>
*
* The single spawn algorithm divides the source in a grid based on the width of the pedestrians.
* This grid is used to place newly spawn pedestrians. These points are called spawnPoints and
* This grid is used to place newly spawn pedestrians. These points are called allowedSpawnPoints and
* are saved as an 1D-array. Based on the Source Attribute values one of the four functions will
* be used to select the next spawnPoints.
* be used to select the next allowedSpawnPoints.
*
* use the next free spawn point in order (0..n) to place the next pedestrian. This function will
* try to place up to maxPoints pedestrian an will wrap around to spawnPoint 0 if needed. Also this
* function will allow overlapping pedestrians a complete overlap is not allowed due to numerical
* problems in OE-solvers.
*/
public class SingleSourceSpawnArray {
public class SingleSourceSpawnArray extends AbstractSpawnArray{
private static Logger logger = LogManager.getLogger(SingleSourceSpawnArray.class);
public static final double D = 0.01;
private final ArrayList<VPoint> spawnPoints;
public SingleSourceSpawnArray(final VShape boundShape, final VRectangle spawnElementBound, Function<VPoint, VShape> shapeProducer) {
VRectangle bound = new VRectangle(boundShape.getBounds2D());
// number of spawn elements in x and y Dimension.
int xDim = (int) (bound.width / (spawnElementBound.width + D));
int yDim = (int) (bound.height / (spawnElementBound.height + D));
// System.out.printf("SpawnElement: %f | %f %n", spawnElementBound.width, spawnElementBound.height);
double eX, eY;
if (xDim * yDim <= 0) {
xDim = (xDim == 0) ? 1 : xDim;
yDim = (yDim == 0) ? 1 : yDim;
spawnPoints = new ArrayList<>(xDim * yDim);
//offset left upper corner to center point.
eX = (xDim == 1) ? bound.getCenterX() : spawnElementBound.x + spawnElementBound.width / 2 + D / 2;
eY = (yDim == 1) ? bound.getCenterY() : spawnElementBound.y + spawnElementBound.height / 2 + D / 2;
logger.info(String.format(
"Dimension of Source is to small for at least one dimension to contain designated spawnElement with Bound (%.2f x %.2f) Set to (%d x %d)",
spawnElementBound.width, spawnElementBound.height, xDim, yDim));
} else {
spawnPoints = new ArrayList<>(xDim * yDim);
//offset left upper corner to center point.
eX = spawnElementBound.x + spawnElementBound.width / 2 + D/2;
eY = spawnElementBound.y + spawnElementBound.height / 2 + D/2;
}
VPoint firstSpawnPoint = new VPoint(bound.x + eX, bound.y + eY);
for (int i = 0; i < (xDim * yDim); i++) {
VPoint candidatePoint = firstSpawnPoint.add(new VPoint(2 * eX * (i % xDim), 2 * eY * (i / xDim)));
VShape candidateShape = shapeProducer.apply(candidatePoint);
if (boundShape.containsShape(candidateShape)) {
spawnPoints.add(candidatePoint);
}
}
spawnPoints.trimToSize();
super(boundShape, spawnElementBound, shapeProducer);
}
/**
* @return copy of spawnPoint used for underling source shape.
*/
public ArrayList<VPoint> getSpawnPoints() {
return spawnPoints;
}
}
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