Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

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

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