Commit e9c6aea5 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen

issue #146: fixed density and floor field rendering, not finished jet.

parent 3cc29a10
......@@ -4,6 +4,8 @@ import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.gui.components.model.IDefaultModel;
import java.awt.*;
......@@ -15,6 +17,7 @@ import java.awt.geom.Rectangle2D;
*/
public class JViewportChangeListener implements ChangeListener {
private Logger logger = LogManager.getLogger(JViewportChangeListener.class);
private final JScrollBar verticalScrollBar;
private final IDefaultModel defaultModel;
......
......@@ -7,6 +7,7 @@ import org.vadere.gui.components.view.ISelectScenarioElementListener;
import org.vadere.state.scenario.ScenarioElement;
import org.vadere.state.types.ScenarioElementType;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import org.vadere.util.voronoi.VoronoiDiagram;
......@@ -123,6 +124,8 @@ public abstract class DefaultModel<T extends DefaultConfig> extends Observable i
@Override
public boolean setScale(final double scale) {
boolean hasChanged = true;
double oldScale = scaleFactor;
if (scale < MIN_SCALE_FACTOR) {
this.scaleFactor = MIN_SCALE_FACTOR;
} else if (scale > MAX_SCALE_FACTOR) {
......@@ -133,7 +136,15 @@ public abstract class DefaultModel<T extends DefaultConfig> extends Observable i
hasChanged = false;
}
// update the viewport, since it depends on the scaleFactor
if (hasChanged) {
Rectangle2D.Double oldViewPort = getViewportBound();
Rectangle2D.Double newViewPort = new Rectangle2D.Double(
oldViewPort.getMinX(),
oldViewPort.getMinY(),
oldViewPort.getWidth() * oldScale / scaleFactor,
oldViewPort.getHeight() * oldScale / scaleFactor);
setViewportBound(newViewPort);
setChanged();
}
return hasChanged;
......
......@@ -20,11 +20,7 @@ public class CLGaussianCalculator {
private IGaussianFilter filterPedestrians;
public CLGaussianCalculator(final SimulationModel model,
final double scale,
final double measurementRadius,
final Color color,
final boolean visualisation,
final boolean scenarioBound) {
final double scale) {
this.scenarioWidth = (int) model.getTopographyBound().getWidth();
this.scenarioHeight = (int) model.getTopographyBound().getHeight();
......@@ -60,7 +56,7 @@ public class CLGaussianCalculator {
// double bound = filter.getMaxFilteredValue();
double max = 1.00;
double factor = maxColorValue / max;
System.out.println(filterPedestrians.getMaxFilteredValue()); // 0.1259
//System.out.println(filterPedestrians.getMaxFilteredValue()); // 0.1259
for (int x = 0; x < filterPedestrians.getMatrixWidth(); x++) {
for (int y = 0; y < filterPedestrians.getMatrixHeight(); y++) {
......
......@@ -24,6 +24,7 @@ import org.vadere.gui.renderer.agent.AgentRender;
import org.vadere.state.scenario.Agent;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VTriangle;
public abstract class SimulationRenderer extends DefaultRenderer {
......@@ -55,8 +56,9 @@ public abstract class SimulationRenderer extends DefaultRenderer {
protected void renderPreTransformation(Graphics2D graphics2D, int width, int height) {
if (model.isFloorFieldAvailable() && (model.config.isShowTargetPotentialField() || model.config.isShowPotentialField())) {
synchronized (model) {
renderPotentialField(graphics2D,
(int)(Math.min(model.getTopographyBound().width, model.getViewportBound().width) * model.getScaleFactor()),
renderPotentialFieldOnViewport(graphics2D,
0, 0,
(int)(Math.min(model.getTopographyBound().width, model.getViewportBound().width) * model.getScaleFactor()),
(int)(Math.min(model.getTopographyBound().height, model.getViewportBound().height) * model.getScaleFactor()));
}
}
......@@ -150,9 +152,7 @@ public abstract class SimulationRenderer extends DefaultRenderer {
}
private void renderDensity(final Graphics2D g) {
CLGaussianCalculator densityCalculator = new CLGaussianCalculator(model, model.config.getDensityScale(),
model.config.getDensityMeasurementRadius(),
model.config.getDensityColor(), true, true);
CLGaussianCalculator densityCalculator = new CLGaussianCalculator(model, model.config.getDensityScale());
/*
* if (obstacleDensity == null || !model.config.getDensityColor().equals(lastDensityColor)
* || model.getTopographyId() != topographyId) {
......@@ -169,45 +169,94 @@ public abstract class SimulationRenderer extends DefaultRenderer {
*/
BufferedImage densityImage = densityCalculator.getDensityImage();
Rectangle2D.Double bound = model.getTopographyBound();
g.translate(bound.getX(), bound.getY());
g.scale(1.0 / model.config.getDensityScale(), 1.0 / model.config.getDensityScale());
g.drawImage(densityImage, 0, 0, null);
// g.drawImage(pedestrianDensity, 0, 0, null);
g.scale(model.config.getDensityScale(), model.config.getDensityScale());
g.scale(model.config.getDensityScale(), model.config.getDensityScale());
g.translate(-bound.getX(), -bound.getY());
densityCalculator.destroy();
}
private void renderPotentialField(final Graphics2D g, final int width, final int height) {
private void renderPotentialFieldOnViewport(final Graphics2D g, final int xPos, final int yPos, final int width, final int height) {
logger.info("resolution = " + width + ", " + height);
/*
* This calculation we need since the viewport.y = 0 if the user scrolls to the bottom
*/
VRectangle bound = new VRectangle(model.getTopographyBound());
Rectangle2D.Double viewportBound = model.getViewportBound();
double dy = model.getTopographyBound().getHeight() - viewportBound.getHeight();
int startX = (int) (viewportBound.getX() * model.getScaleFactor());
int startY = (int) (Math.max((dy - viewportBound.getY()), 0) * model.getScaleFactor());
potentialFieldImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
potentialFieldImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < potentialFieldImage.getWidth(); x++) {
for (int y = 0; y < potentialFieldImage.getHeight(); y++) {
Color c;
double potential = model.getPotential(x + startX, y + startY);
if (potential >= MAX_POTENTIAL) {
c = model.config.getObstacleColor();
} else if (potential % CONTOUR_STEP <= CONTOUR_THINKNESS) {
c = Color.BLACK;
} else {
c = colorHelper.numberToColor(potential % 100);
VPoint pos = new VPoint(
viewportBound.getMinX() + x / model.getScaleFactor(),
viewportBound.getMinY() + (potentialFieldImage.getHeight() - 1 - y) / model.getScaleFactor());
if(bound.contains(pos)) {
double potential = (double)model.getPotentialField().apply(pos);
if (potential >= MAX_POTENTIAL) {
c = model.config.getObstacleColor();
} else if (potential % CONTOUR_STEP <= CONTOUR_THINKNESS) {
c = Color.BLACK;
} else {
c = colorHelper.numberToColor(potential % 100);
}
potentialFieldImage.setRGB(x, y, c.getRGB());
}
potentialFieldImage.setRGB(x, y, c.getRGB());
}
}
g.drawImage(potentialFieldImage, 0, 0, null);
g.drawImage(potentialFieldImage, xPos, yPos, null);
}
private void renderPotentialField(final Graphics2D g, final int xPos, final int yPos, final int width, final int height) {
/*
* This calculation we need since the viewport.y = 0 if the user scrolls to the bottom
*/
VRectangle bound = new VRectangle(model.getTopographyBound());
Rectangle2D.Double viewportBound = model.getViewportBound();
double dy = model.getTopographyBound().getHeight() - viewportBound.getHeight();
int startX = (int) (viewportBound.getX() * model.getScaleFactor());
int startY = (int) (Math.max((dy - viewportBound.getY()), 0) * model.getScaleFactor());
potentialFieldImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < potentialFieldImage.getWidth(); x++) {
for (int y = 0; y < potentialFieldImage.getHeight(); y++) {
Color c;
VPoint pos = new VPoint(
bound.getMinX() + x / model.getScaleFactor(),
bound.getMinY() + bound.getHeight() - y / model.getScaleFactor());
if(bound.contains(pos)) {
double potential = (double)model.getPotentialField().apply(pos);
if (potential >= MAX_POTENTIAL) {
c = model.config.getObstacleColor();
} else if (potential % CONTOUR_STEP <= CONTOUR_THINKNESS) {
c = Color.BLACK;
} else {
c = colorHelper.numberToColor(potential % 100);
}
potentialFieldImage.setRGB(x, y, c.getRGB());
}
}
}
g.drawImage(potentialFieldImage, xPos, yPos, null);
}
protected abstract void renderSimulationContent(final Graphics2D g);
private float getGridLineWidth() {
......
......@@ -66,6 +66,14 @@ public interface IGaussianFilter {
double getMinFilteredValue();
int toXIndex(final double x);
int toYIndex(final double y);
double toXCoord(int xIndex);
double toYCoord(int yIndex);
/**
* This method has to be called if the Filter will no longer called!
*/
......
......@@ -68,7 +68,27 @@ public class ObstacleGaussianFilter implements IGaussianFilter {
return filter.getMinFilteredValue();
}
@Override
@Override
public int toXIndex(double x) {
return filter.toXIndex(x);
}
@Override
public int toYIndex(double y) {
return filter.toYIndex(y);
}
@Override
public double toXCoord(int xIndex) {
return filter.toXCoord(xIndex);
}
@Override
public double toYCoord(int yIndex) {
return filter.toYCoord(yIndex);
}
@Override
public void destroy() {
this.filter.destroy();
}
......@@ -76,15 +96,16 @@ public class ObstacleGaussianFilter implements IGaussianFilter {
private void setValues() {
for (int x = 0; x < getMatrixWidth(); x++) {
for (int y = 0; y < getMatrixHeight(); y++) {
double dx = x / getScale();
double dy = y / getScale();
double dx = topography.getBounds().getMinX() + x / getScale();
double dy = topography.getBounds().getMinY() + y / getScale();
if (topography.getObstacles().stream().map(obs -> obs.getShape()).anyMatch(s -> s.contains(dx, dy))) {
setInputValue(x, y, 1.0f);
} else if (topography.isBounded() &&
(dx <= topography.getBoundingBoxWidth() || dy <= topography.getBoundingBoxWidth()
|| dx >= topography.getBounds().getWidth() - topography.getBoundingBoxWidth()
|| dy >= topography.getBounds().getHeight() - topography.getBoundingBoxWidth())) {
(dx <= topography.getBounds().getMinX() + topography.getBoundingBoxWidth()
|| dy <= topography.getBounds().getMinY() + topography.getBoundingBoxWidth()
|| dx >= topography.getBounds().getMaxX() - topography.getBoundingBoxWidth()
|| dy >= topography.getBounds().getMaxY() - topography.getBoundingBoxWidth())) {
setInputValue(x, y, 1.0f);
} else {
setInputValue(x, y, 0.0f);
......
......@@ -2,6 +2,7 @@ package org.vadere.simulator.models.density;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.vadere.simulator.models.potential.timeCostFunction.loading.IPedestrianLoadingStrategy;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.util.geometry.shapes.VPoint;
......@@ -97,37 +98,66 @@ public class PedestrianGaussianFilter<E extends Pedestrian> implements IGaussian
return filter.getMinFilteredValue();
}
@Override
@Override
public int toXIndex(double x) {
return filter.toXIndex(x);
}
@Override
public int toYIndex(double y) {
return filter.toYIndex(y);
}
@Override
public double toXCoord(int xIndex) {
return filter.toXCoord(xIndex);
}
@Override
public double toYCoord(int yIndex) {
return filter.toYCoord(yIndex);
}
@Override
public void destroy() {
this.filter.destroy();
}
private void setValue(E pedestrian) {
VPoint position = pedestrian.getPosition();
VPoint filteredPosition = new VPoint(Math.max(0, position.x), Math.max(0, position.y));
private void setValue(@NotNull final E pedestrian) {
VPoint filteredPosition = pedestrian.getPosition();
//VPoint filteredPosition = new VPoint(Math.max(0, position.x), Math.max(0, position.y));
// better approximation
double indexX = filteredPosition.x * getScale();
double indexY = filteredPosition.y * getScale();
if (indexX == ((int) indexX) && indexY == ((int) indexY)) {
setInputValue(filteredPosition.x, filteredPosition.y,
getInputValue((int) indexX, (int) indexY) + pedestrianLoadingStrategy.calculateLoading(pedestrian));
} else if (indexX == ((int) indexX) && indexY != ((int) indexY)) {
splitY(filteredPosition, (int) indexX, (int) Math.floor(indexY), pedestrian);
splitY(filteredPosition, (int) indexX, (int) Math.ceil(indexY), pedestrian);
} else if (indexX != ((int) indexX) && indexY == ((int) indexY)) {
splitX(filteredPosition, (int) Math.floor(indexX), (int) indexY, pedestrian);
splitX(filteredPosition, (int) Math.ceil(indexX), (int) indexY, pedestrian);
int indexX = toXIndex(filteredPosition.x);
int indexY = toYIndex(filteredPosition.y);
double coordX = toXCoord(indexX);
double coordY = toYCoord(indexY);
int dx = toXCoord(indexX) > filteredPosition.x ? 1 : -1;
int dy = toYCoord(indexY) > filteredPosition.y ? 1 : -1;
//TODO: reactivate the split but refactor the code.
/* if (Double.compare(coordX, filteredPosition.x) == 0
&& Double.compare(coordY, filteredPosition.y) == 0) {
setInputValue(filteredPosition.x, filteredPosition.y,
getInputValue(indexX, indexY) + pedestrianLoadingStrategy.calculateLoading(pedestrian));
} else if (Double.compare(coordX, filteredPosition.x) == 0 && Double.compare(coordY, filteredPosition.y) != 0) {
splitY(filteredPosition, indexX, indexY, pedestrian);
splitY(filteredPosition, indexX, indexY + dy, pedestrian);
} else if (Double.compare(coordX, filteredPosition.x) != 0 && Double.compare(coordY, filteredPosition.y) == 0) {
splitX(filteredPosition, indexX, indexY, pedestrian);
splitX(filteredPosition, indexX + dx, indexY, pedestrian);
} else {
splitXY(filteredPosition, (int) Math.floor(indexX), (int) Math.floor(indexY), pedestrian);
splitXY(filteredPosition, (int) Math.floor(indexX), (int) Math.ceil(indexY), pedestrian);
splitXY(filteredPosition, (int) Math.ceil(indexX), (int) Math.floor(indexY), pedestrian);
splitXY(filteredPosition, (int) Math.ceil(indexX), (int) Math.ceil(indexY), pedestrian);
}
// setInputValue(filteredPosition.x, filteredPosition.y,
// pedestrianLoadingStrategy.calculateLoading(pedestrian));
splitXY(filteredPosition, indexX, indexY, pedestrian);
splitXY(filteredPosition, indexX, indexY + dy, pedestrian);
splitXY(filteredPosition, indexX + dx, indexY, pedestrian);
splitXY(filteredPosition, indexX + dx, indexY + dy, pedestrian);
}*/
setInputValue(filteredPosition.x, filteredPosition.y, pedestrianLoadingStrategy.calculateLoading(pedestrian));
}
private void splitXY(final VPoint filteredPosition, final int indexX, final int indexY, Pedestrian pedestrian) {
......
......@@ -21,7 +21,11 @@ public interface IPotentialField {
*/
double getPotential(final VPoint pos, final Agent agent);
static IPotentialField copyAgentField(final @NotNull IPotentialField potentialField, final @NotNull Agent agent, final @NotNull VRectangle bound, final double steps) {
static IPotentialField copyAgentField(
final @NotNull IPotentialField potentialField,
final @NotNull Agent agent,
final @NotNull VRectangle bound,
final double steps) {
final int gridWidth = (int)Math.ceil(bound.getWidth() / steps)+1;
final int gridHeight = (int)Math.ceil(bound.getHeight() / steps)+1;
......@@ -40,8 +44,8 @@ public interface IPotentialField {
int incX = 1;
int incY = 1;
int col = (int)(pos.getX() / steps);
int row = (int)(pos.getY() / steps);
int col = (int)((pos.getX() - bound.getMinX()) / steps);
int row = (int)((pos.getY() - bound.getMinY()) / steps);
if (col + 1 >= gridWidth) {
incX = 0;
......
......@@ -49,7 +49,7 @@ class PotentialFieldAndInitializer {
Rectangle2D bounds = topography.getBounds();
CellGrid cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(),
attributesPotential.getPotentialFieldResolution(), new CellState());
attributesPotential.getPotentialFieldResolution(), new CellState(), bounds.getMinX(), bounds.getMinY());
if (createMethod != EikonalSolverType.NONE) {
for (VShape shape : targetShapes) {
......
......@@ -38,7 +38,8 @@ public class PotentialFieldDistanceEikonalEq implements IPotentialField {
public PotentialFieldDistanceEikonalEq(@NotNull final Collection<VShape> obstacles,
@NotNull final VRectangle bounds,
@NotNull final AttributesFloorField attributesFloorField) {
CellGrid cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(), attributesFloorField.getPotentialFieldResolution(), new CellState());
CellGrid cellGrid = new CellGrid(bounds.getWidth(),
bounds.getHeight(), attributesFloorField.getPotentialFieldResolution(), new CellState(), bounds.getMinX(), bounds.getMinY());
for (VShape shape : obstacles) {
FloorDiscretizer.setGridValuesForShape(cellGrid, shape,
......
......@@ -36,7 +36,7 @@ public class PotentialFieldDistancesBruteForce implements IPotentialField {
@NotNull final AttributesFloorField attributesFloorField) {
double ms = System.currentTimeMillis();
this.obstacles = obstacles;
this.cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(), attributesFloorField.getPotentialFieldResolution(), new CellState());
this.cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(), attributesFloorField.getPotentialFieldResolution(), new CellState(), bounds.getMinX(), bounds.getMinY());
this.cellGrid.pointStream().forEach(p -> computeDistanceToGridPoint(p));
logger.info("floor field initialization time:" + (System.currentTimeMillis() - ms + "[ms]"));
}
......
......@@ -77,7 +77,7 @@ public class PotentialFieldTargetQueuingGrid implements IPotentialFieldTargetGri
topography.getElements(Pedestrian.class).forEach(this::addPedestrian);
Rectangle2D bounds = topography.getBounds();
CellGrid cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(), 0.1, new CellState());
CellGrid cellGrid = new CellGrid(bounds.getWidth(), bounds.getHeight(), 0.1, new CellState(), bounds.getMinX(), bounds.getMinY());
List<VShape> targetShapes =
topography.getTargets().stream().map(t -> t.getShape()).collect(Collectors.toList());
......
......@@ -156,18 +156,6 @@ public class Topography {
this.obstacleDistanceFunction = obstacleDistanceFunction;
}
public CellGrid getDistanceFunctionApproximation(final double cellSize) {
CellGrid cellGrid = new CellGrid(getBounds().width, getBounds().height, cellSize, new CellState());
for(int row = 0; row < cellGrid.getNumPointsY(); row++){
for(int col = 0; col < cellGrid.getNumPointsX(); col++){
cellGrid.setValue(col, row, new CellState(
distanceToObstacle(cellGrid.pointToCoord(col, row).add(new VPoint(getBounds().getMinX(), getBounds().getMinY()))),
PathFindingTag.Reachable));
}
}
return cellGrid;
}
public boolean containsTarget(final Predicate<Target> targetPredicate) {
return getTargets().stream().anyMatch(targetPredicate);
}
......
......@@ -35,6 +35,10 @@ public class CellGrid {
/** Number of points along y axis. */
protected final int numPointsY;
protected final double xMin;
protected final double yMin;
protected CellState[][] values;
/**
......@@ -42,10 +46,12 @@ public class CellGrid {
* point values are initialized with 'value'.
*/
public CellGrid(double width, double height, double resolution,
CellState value) {
CellState value, double xMin, double yMin) {
this.width = width;
this.height = height;
this.resolution = resolution;
this.xMin = xMin;
this.yMin = yMin;
/* 0.001 avoids that numPointsX/Y are too small due to numerical errors. */
numPointsX = (int) Math.floor(width / resolution + 0.001) + 1;
......@@ -56,6 +62,14 @@ public class CellGrid {
reset(value);
}
/**
* Creates an grid with the given width, height and resolution. All grid
* point values are initialized with 'value'.
*/
public CellGrid(double width, double height, double resolution, CellState value) {
this(width, height, resolution, value, 0, 0);
}
/**
* Creates a deep copy of the given grid.
*/
......@@ -66,6 +80,8 @@ public class CellGrid {
numPointsX = grid.numPointsX;
numPointsY = grid.numPointsY;
values = new CellState[numPointsX][numPointsY];
xMin = grid.xMin;
yMin = grid.yMin;
for (int row = 0; row < numPointsY; row++) {
for (int col = 0; col < numPointsX; col++) {
......@@ -140,7 +156,7 @@ public class CellGrid {
* Converts the matrix indices to coordinates.
*/
public VPoint pointToCoord(int pointX, int pointY) {
return new VPoint(pointX * resolution, pointY * resolution);
return new VPoint(xMin + pointX * resolution, yMin + pointY * resolution);
}
/**
......@@ -185,66 +201,34 @@ public class CellGrid {
* Returns the closest grid point (matrix index) to the given coordinates.
*/
public Point getNearestPoint(double x, double y) {
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (y > getHeight()) {
y = getHeight();
}
if (x > getWidth()) {
x = getWidth();
}
return new Point((int) (x / resolution + 0.5),
(int) (y / resolution + 0.5));
}
/**
* Returns the closest grid point (matrix index) to the given coordinates
* towards origin.
*/
public Point getNearestPointTowardsOrigin(double x, double y) {
if (x < 0) {
x = 0;
if (x < xMin) {
x = xMin;
}
if (y < 0) {
y = 0;
if (y < yMin) {
y = yMin;
}
if (y > getHeight()) {
y = getHeight();
if (y > getHeight() + yMin) {
y = getHeight() + yMin;
}
if (x > getWidth()) {
x = getWidth();
if (x > getWidth() + xMin) {
x = getWidth() + xMin;
}
return new Point((int) (x / resolution), (int) (y / resolution));
return new Point((int) ((x - xMin) / resolution + 0.5),
(int) ((y - yMin) / resolution + 0.5));
}
/**
* Returns the closest grid point (matrix index) to the given coordinates
* towards origin.
*/
public Point getNearestPointTowardsOrigin(VPoint p) {
return getNearestPointTowardsOrigin(p.x, p.y);
}
public Point getNearestPointTowardsOrigin(Point p) {
return getNearestPointTowardsOrigin(p.x, p.y);
}
/**
* Resturns the distance of grid points specified by its matrix indices.
* Returns the distance of grid points specified by its matrix indices.
*/
public double pointDistance(int pointX1, int pointY1, int pointX2,
int pointY2) {
int pointY2) {
return Math.sqrt(Math.pow(pointY2 - pointY1, 2)
+ Math.pow(pointX2 - pointX1