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

Commit 75c7c2b3 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck

allow polygon sources

parent 7c87c7cd
......@@ -256,6 +256,8 @@ public class TopographyWindow extends JPanel {
obstacleAndTargetDrawModes.add(pen2);
sourceDrawModes.add(rectangle);
sourceDrawModes.add(pen);
sourceDrawModes.add(pen2);
sourceDrawModes.add(dot);
/* open obstacle paint method dialog action */
......
......@@ -14,6 +14,8 @@ ScenarioChecker.source.targetIdNotFound=The following target ids where not found
ScenarioChecker.source.noTargetIdSet=No Target Ids set for Source.
ScenarioChecker.source.noTargetIdAndNoSpawn=No Target Ids set for Source with SpawnNumber 0. This might be an error.
ScenarioChecker.source.idNotUnique=Multiple Sources have the same ID.
ScenarioChecker.source.spawnAtRandomButNotAtFreeSpace=Combination isSpawnAtRandomPositions=true and isUseFreeSpaceOnly=false not allowed.
ScenarioChecker.source.spawnUseNotAtFreeSpace=Deprecated. Will be removed in future relases. Not all models can handle overlapping pedestrians.
ScenarioChecker.stairs.wrongTreadDim=Stair treadDepth outside of allowed dimension. Change the thread numbers to compensate.
ScenarioChecker.target.unused=The target is not used in any source. Remove target to increase performance.
ScenarioChecker.pedestrian.speedsetup=speedDistributionMean must be within min/max range.
......
......@@ -13,6 +13,8 @@ ScenarioChecker.type.processor.warning=Procesor Warnung
ScenarioChecker.source.targetIdNotFound=Die folgenden Ziel-IDs wurden nicht im Szenario gefunden.
ScenarioChecker.source.noTargetIdSet=In der Quelle wurden keine Ziel Ids vergeben.
ScenarioChecker.source.noTargetIdAndNoSpawn=In der Quelle wurden keine Ziel Ids vergeben, aber die Spawn Anzahl ist bei 0.
ScenarioChecker.source.spawnAtRandomButNotAtFreeSpace=Kombination aus isSpawnAtRandomPositions=true und isUseFreeSpaceOnly=false ist nicht erlaubt.
ScenarioChecker.source.spawnUseNotAtFreeSpace=Deprecated. Dieses Attribut wird in zuk\u00fcnfigen version entfernt. Nicht alle Modell k\u00f6nnen mit \u00fcberlappungen umgehen.
ScenarioChecker.source.idNotUnique=Quellen haben keine eindeutige ID.
ScenarioChecker.stairs.wrongTreadDim=Stufentiefe ist au\u00dferhalb des Definitionsbereichs. Passen Sie die Anzahl der Stufen entsprechend an.
ScenarioChecker.target.unused=Das Ziel wird von keiner Quelle verwendet. Entferne das Ziel um die Performance zu erh\u00f6hen
......
......@@ -5,7 +5,9 @@ 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.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import java.util.Iterator;
import java.util.LinkedList;
......@@ -16,6 +18,7 @@ public class GroupSourceController extends SourceController {
private final GroupModel groupModel;
private LinkedList<Integer> groupsToSpawn;
protected final SpawnArray spawnArray;
public GroupSourceController(Topography scenario, Source source,
DynamicElementFactory dynamicElementFactory,
......@@ -24,6 +27,12 @@ public class GroupSourceController extends SourceController {
super(scenario, source, dynamicElementFactory, attributesDynamicElement, random);
this.groupModel = groupModel;
this.groupsToSpawn = new LinkedList<>();
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() ));
}
@Override
......
......@@ -4,11 +4,17 @@ 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;
......@@ -23,13 +29,19 @@ public class SingleSourceController extends SourceController {
private int numberToSpawn;
private DynamicElementFactory dynamicElementFactory;
private static final int NUMBER_OF_REPOSITION_TRIES = 10;
private static final int NUMBER_OF_POINT_SEARCH = 1_000; // todo based on shape and position of source
private SingleSourceSpawnArray spawnArray;
public SingleSourceController(Topography scenario, Source source,
DynamicElementFactory dynamicElementFactory,
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()),
dynamicElementFactory::getDynamicElementRequiredPlace);
}
@Override
......@@ -42,12 +54,11 @@ public class SingleSourceController extends SourceController {
if (sourceAttributes.isSpawnAtRandomPositions()) {
if (sourceAttributes.isUseFreeSpaceOnly()) {
//spawnPoints = spawnArray.getNextFreeRandomSpawnPoints(numberToSpawn, random, getDynElementsAtSource());
spawnPoints = getRealRandomPositions(
numberToSpawn,
random,
getDynElementsAtSource().stream()
.map(element -> element.getPosition())
.map(PointPositioned::getPosition)
.map(position -> dynamicElementFactory.getDynamicElementRequiredPlace(position))
.collect(Collectors.toList())
);
......@@ -56,19 +67,25 @@ public class SingleSourceController extends SourceController {
assert (numberToSpawn >= 0);
} else {
throw new IllegalArgumentException("use random position without free space only makes no sense.");
/*spawnPoints = spawnArray.getNextRandomSpawnPoints(numberToSpawn, random, getDynElementsAtSource());
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);*/
}
} else {
if (sourceAttributes.isUseFreeSpaceOnly()) {
spawnPoints = spawnArray.getNextFreeSpawnPoints(numberToSpawn, getDynElementsAtSource());
spawnPoints = getRealPositions(
numberToSpawn,
getDynElementsAtSource().stream()
.map(PointPositioned::getPosition)
.map(position -> dynamicElementFactory.getDynamicElementRequiredPlace(position))
.collect(Collectors.toList())
);
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
} else {
spawnPoints = spawnArray.getNextSpawnPoints(numberToSpawn, getDynElementsAtSource());
spawnPoints = getRealPositions(
numberToSpawn,
new ArrayList<>()
);
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
}
......@@ -85,6 +102,34 @@ public class SingleSourceController extends SourceController {
}
}
private List<VPoint> getRealPositions(final int numberToSpawn, @NotNull final List<VShape> blockPedestrianShapes){
List<VPoint> positions = new ArrayList<>(numberToSpawn);
for(int i = 0; i < numberToSpawn; i++) {
Optional<VPoint> optPosition = getNextPosition(blockPedestrianShapes, spawnArray.getSpawnPoints());
if (optPosition.isPresent()) {
VPoint position = optPosition.get();
blockPedestrianShapes.add(dynamicElementFactory.getDynamicElementRequiredPlace(position));
positions.add(position);
}
}
return positions;
}
private Optional<VPoint> getNextPosition(List<VShape> blockPedestrianShapes, List<VPoint> spawnPoints) {
for (VPoint spawnPoint : spawnPoints){
VShape freeSpaceRequired = dynamicElementFactory.getDynamicElementRequiredPlace(spawnPoint);
if (testFreeSpace(freeSpaceRequired, blockPedestrianShapes)){
return Optional.of(spawnPoint);
}
}
return Optional.empty();
}
/**
* Computes numberToSpawn or less random positions based on the blockPedestrianShapes which contains the shapes representing the required space of each pedestrian.
* For each required position the algorithms tries {@link SingleSourceController#NUMBER_OF_REPOSITION_TRIES} times to get a feasible free position.
......@@ -98,7 +143,7 @@ public class SingleSourceController extends SourceController {
List<VPoint> randomPositions = new ArrayList<>(numberToSpawn);
for(int i = 0; i < numberToSpawn; i++) {
Optional<VPoint> optRandomPosition = getNextRandomPosition(random, blockPedestrianShapes, NUMBER_OF_REPOSITION_TRIES);
Optional<VPoint> optRandomPosition = getNextRandomPosition(random, blockPedestrianShapes, NUMBER_OF_POINT_SEARCH, NUMBER_OF_REPOSITION_TRIES);
if (optRandomPosition.isPresent()) {
VPoint randomPosition = optRandomPosition.get();
......@@ -110,16 +155,26 @@ public class SingleSourceController extends SourceController {
return randomPositions;
}
private Optional<VPoint> getNextRandomPosition(@NotNull final Random random, @NotNull final List<VShape> blockPedestrianShapes, final int tries) {
private Optional<VPoint> getNextRandomPosition(@NotNull final Random random, @NotNull final List<VShape> blockPedestrianShapes,
final int tries_find_valid_point, final int tries_reposition) {
Rectangle2D rec = source.getShape().getBounds2D();
for(int i = 0; i < tries; i++) {
VPoint randomPoint = new VPoint(rec.getMinX() + random.nextDouble() * rec.getWidth(), rec.getMinY() + random.nextDouble() * rec.getHeight());
VShape freeSpaceRequired = dynamicElementFactory.getDynamicElementRequiredPlace(randomPoint);
for(int i = 0; i < tries_reposition; i++) {
VShape freeSpaceRequired = null;
VPoint randomPoint = null;
boolean pointFound = false;
// find point in source boundary
int j = 0;
while(j < tries_find_valid_point && !pointFound){
randomPoint = new VPoint(rec.getMinX() + random.nextDouble() * rec.getWidth(), rec.getMinY() + random.nextDouble() * rec.getHeight());
freeSpaceRequired = dynamicElementFactory.getDynamicElementRequiredPlace(randomPoint);
pointFound = source.getShape().containsShape(freeSpaceRequired);
j++;
}
// no intersection with other free spaces (obstacles & other pedestrians)
if(blockPedestrianShapes.stream().noneMatch(shape -> shape.intersects(freeSpaceRequired))
&& this.getTopography().getObstacles().stream().noneMatch(obs -> obs.getShape().intersects(freeSpaceRequired))) {
if(testFreeSpace(freeSpaceRequired, blockPedestrianShapes)) {
return Optional.of(randomPoint);
}
}
......@@ -127,6 +182,11 @@ public class SingleSourceController extends SourceController {
return Optional.empty();
}
private boolean testFreeSpace(final VShape freeSpace, final List<VShape> blockPedestrianShapes){
return blockPedestrianShapes.stream().noneMatch(shape -> shape.intersects(freeSpace)) &&
this.getTopography().getObstacles().stream()
.map(Obstacle::getShape).noneMatch(shape -> shape.intersects(freeSpace));
}
@Override
protected boolean isQueueEmpty() {
......
......@@ -26,7 +26,7 @@ import java.util.Random;
public abstract class SourceController {
protected final double NO_EVENT = Double.MAX_VALUE;
public static final double SPAWN_BUFFER_SIZE = 0.03;
// public static final double SPAWN_BUFFER_SIZE = 0.03;
protected final Source source;
private final DynamicElementFactory dynamicElementFactory;
......@@ -40,7 +40,6 @@ public abstract class SourceController {
protected final AttributesSource sourceAttributes;
protected final AttributesDynamicElement attributesDynamicElement;
protected int dynamicElementsCreatedTotal;
protected final SpawnArray spawnArray;
public SourceController(Topography scenario, Source source,
......@@ -54,11 +53,6 @@ public abstract class SourceController {
this.topography = scenario;
this.random = random;
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() + SPAWN_BUFFER_SIZE, elementBound.getHeight() + SPAWN_BUFFER_SIZE));
timeOfNextEvent = sourceAttributes.getStartTime();
try {
DistributionFactory factory = DistributionFactory
......
......@@ -5,6 +5,7 @@ import org.vadere.simulator.projects.Scenario;
import org.vadere.simulator.utils.scenariochecker.checks.ScenarioCheckerTest;
import org.vadere.simulator.utils.scenariochecker.checks.simulation.SimulationTimeStepLengthCheck;
import org.vadere.simulator.utils.scenariochecker.checks.topography.PedestrianSpeedSetupCheck;
import org.vadere.simulator.utils.scenariochecker.checks.topography.SourceSpawnSettingCheck;
import org.vadere.simulator.utils.scenariochecker.checks.topography.StairTreadSanityCheck;
import org.vadere.simulator.utils.scenariochecker.checks.topography.TopographyOverlapCheck;
import org.vadere.simulator.utils.scenariochecker.checks.topography.UniqueSourceIdCheck;
......@@ -61,6 +62,7 @@ public class ScenarioChecker {
ret.addAll(checkPedestrianSpeedSetup());
ret.addAll(checkOverlap());
ret.addAll(checkSimulationAttribues());
ret.addAll(checkSourceSpawnSetting());
return ret;
}
......@@ -70,6 +72,10 @@ public class ScenarioChecker {
return ret;
}
public PriorityQueue<ScenarioCheckerMessage> checkSourceSpawnSetting(){
return runCheck(new SourceSpawnSettingCheck());
}
public PriorityQueue<ScenarioCheckerMessage> checkPedestrianSpeedSetup() {
return runCheck(new PedestrianSpeedSetupCheck());
}
......
......@@ -13,6 +13,8 @@ public enum ScenarioCheckerReason {
SOURCE_NO_TARGET_ID_SET("ScenarioChecker.source.noTargetIdSet"),
SOURCE_NO_TARGET_ID_NO_SPAWN("ScenarioChecker.source.noTargetIdAndNoSpawn"),
SOURCE_ID_NOT_UNIQUE("ScenarioChecker.source.idNotUnique"),
SOURCE_SPAWN_RND_POS_NOT_FREE_SPACE("ScenarioChecker.source.spawnAtRandomButNotAtFreeSpace"),
SOURCE_SPAWN_USE_NOT_FREE_SPACE("ScenarioChecker.source.spawnUseNotAtFreeSpace"),
STAIRS_TREAD_DIM_WRONG("ScenarioChecker.stairs.wrongTreadDim"),
OVERLAP_STAIR_STAIR("ScenarioChecker.overlap.stair.stair"),
......
package org.vadere.simulator.utils.scenariochecker.checks;
import org.vadere.simulator.utils.scenariochecker.ScenarioCheckerMessage;
import org.vadere.simulator.utils.scenariochecker.ScenarioCheckerMessageBuilder;
import java.util.PriorityQueue;
public abstract class AbstractScenarioCheck implements ScenarioCheckerTest{
protected ScenarioCheckerMessageBuilder msgBuilder;
protected PriorityQueue<ScenarioCheckerMessage> messages;
public AbstractScenarioCheck(){
msgBuilder = new ScenarioCheckerMessageBuilder();
messages = new PriorityQueue<>();
}
}
package org.vadere.simulator.utils.scenariochecker.checks.topography;
import org.vadere.simulator.utils.scenariochecker.ScenarioChecker;
import org.vadere.simulator.utils.scenariochecker.ScenarioCheckerMessage;
import org.vadere.simulator.utils.scenariochecker.ScenarioCheckerReason;
import org.vadere.simulator.utils.scenariochecker.checks.AbstractScenarioCheck;
import org.vadere.simulator.utils.scenariochecker.checks.TopographyCheckerTest;
import org.vadere.state.attributes.scenario.AttributesSource;
import org.vadere.state.scenario.Source;
import org.vadere.state.scenario.Topography;
import java.util.List;
import java.util.PriorityQueue;
public class SourceSpawnSettingCheck extends AbstractScenarioCheck implements TopographyCheckerTest {
@Override
public PriorityQueue<ScenarioCheckerMessage> runScenarioCheckerTest(Topography topography) {
List<Source> sourceList = topography.getSources();
for (Source source : sourceList) {
AttributesSource attr = source.getAttributes();
if (attr.isSpawnAtRandomPositions() && !attr.isUseFreeSpaceOnly()){
messages.add(msgBuilder.topographyError().target(source)
.reason(ScenarioCheckerReason.SOURCE_SPAWN_RND_POS_NOT_FREE_SPACE).build());
} else if (!attr.isUseFreeSpaceOnly()){
messages.add(msgBuilder.topographyWarning().target(source)
.reason(ScenarioCheckerReason.SOURCE_SPAWN_USE_NOT_FREE_SPACE).build());
}
}
return messages;
}
}
......@@ -130,7 +130,7 @@ public class TopographyOverlapCheck extends AbstractScenarioCheck implements Top
Source source = (Source) elementOfType(e1, e2, ScenarioElementType.SOURCE);
if (source.overlapWith(obstacle)) {
ret.add(msgBuilder.topographyError().target(source, obstacle)
ret.add(msgBuilder.topographyWarning().target(source, obstacle)
.reason(ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE).build());
}
}
......
......@@ -168,7 +168,7 @@ public class TestSourceControllerUsingConstantSpawnRate {
.setOneTimeSpawn(0)
.setSpawnNumber(100)
.setUseFreeSpaceOnly(true)
.setSourceDim(new VRectangle(0,0,0.1,0.1)); // small source
.setSourceDim(new VRectangle(0,0,0.4,0.4)); // small source
initialize(builder);
for (double simTimeInSec = 0; simTimeInSec < 1000; simTimeInSec += 1.0) {
......
......@@ -101,7 +101,7 @@ public class TestSourceControllerUsingDistributions extends TestSourceController
.setStartTime(startTime).setEndTime(endTime)
.setSpawnNumber(100)
.setUseFreeSpaceOnly(true)
.setSourceDim(new VRectangle(0,0,0.1,0.1));
.setSourceDim(new VRectangle(0,0,0.4,0.4));
initialize(builder);
doUpdates(0, 100, startTime, endTime + 1);
......@@ -125,7 +125,7 @@ public class TestSourceControllerUsingDistributions extends TestSourceController
.setOneTimeSpawn(startTime)
.setSpawnNumber(100)
.setUseFreeSpaceOnly(true)
.setSourceDim(new VRectangle(0,0,0.1,0.1));
.setSourceDim(new VRectangle(0,0,0.4,0.4));
initialize(builder);
doUpdates(0, 100, 0, startTime + 1);
......
......@@ -253,7 +253,7 @@ public class ScenarioCheckerTest implements TestResourceHandler {
PriorityQueue<ScenarioCheckerMessage> out = checker.checkOverlap();
ScenarioCheckerMessage msg = hasOneElement(out);
isErrorMsg(msg);
isWarnMsg(msg);
assertEquals(ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE, msg.getReason());
assertEquals(testSource, msg.getMsgTarget().getTargets().get(0));
assertEquals(testObstacle, msg.getMsgTarget().getTargets().get(1));
......@@ -494,16 +494,14 @@ public class ScenarioCheckerTest implements TestResourceHandler {
List<ScenarioCheckerMessage> errorMsg = out.stream()
.filter(m -> m.getMsgType().equals(ScenarioCheckerMessageType.TOPOGRAPHY_ERROR))
.collect(Collectors.toList());
assertEquals(6, errorMsg.size());
assertEquals(4, errorMsg.size());
List<ScenarioCheckerMessage> warnMsg = out.stream()
.filter(m -> m.getMsgType().equals(ScenarioCheckerMessageType.TOPOGRAPHY_WARN))
.collect(Collectors.toList());
assertEquals(16, warnMsg.size());
assertEquals(18, warnMsg.size());
// Errors
assertIdAndReason(1,6,ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE, errorMsg);
assertIdAndReason(2,6,ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE, errorMsg);
assertIdAndReason(9,8,ScenarioCheckerReason.OVERLAP_OBSTACLE_TARGET_ERR, errorMsg);
assertIdAndReason(11,13,ScenarioCheckerReason.OVERLAP_OBSTACLE_STAIRS_ERR, errorMsg);
assertIdAndReason(35,36,ScenarioCheckerReason.OVERLAP_STAIR_STAIR, errorMsg);
......@@ -511,6 +509,8 @@ public class ScenarioCheckerTest implements TestResourceHandler {
// Warnings
assertIdAndReason(1,6,ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE, warnMsg);
assertIdAndReason(2,6,ScenarioCheckerReason.OVERLAP_OBSTACLE_SOURCE, warnMsg);
assertIdAndReason(4,5,ScenarioCheckerReason.OVERLAP_OBSTACLE_OBSTACLE, warnMsg);
assertIdAndReason(9,7,ScenarioCheckerReason.OVERLAP_OBSTACLE_TARGET_WARN, warnMsg);
assertIdAndReason(11,12,ScenarioCheckerReason.OVERLAP_OBSTACLE_STAIRS_WARN, warnMsg);
......
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
* 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.
*
* 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 {
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();
}
/**
* @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