diff --git a/VadereGui/resources/messages.properties b/VadereGui/resources/messages.properties index 72719b89a080d5f6dc05e1268f5b0692e19dee99..a85c37a60c2272c4613abdadbb6d3f30669cf338 100644 --- a/VadereGui/resources/messages.properties +++ b/VadereGui/resources/messages.properties @@ -204,6 +204,7 @@ OutputprocessorsView.dataProcessor.text={Processor: ""} AdjustPanel.lblVelocity.text=FPS AdjustPanel.lblTime=Time AdjustPanel.lblStep.text=Timestep +AdjustPanel.lblVisTime.text=Res. ProjectView.warning.lwjgl.title=Warning ProjectView.warning.opencl.title=Warning diff --git a/VadereGui/resources/messages_de_DE.properties b/VadereGui/resources/messages_de_DE.properties index c497443027ffa1e7356bcaaaa0360aa6f3e76c00..7c8b41a2361cd6ecfd23c22cb5ae27f7fc4e43dd 100644 --- a/VadereGui/resources/messages_de_DE.properties +++ b/VadereGui/resources/messages_de_DE.properties @@ -209,6 +209,7 @@ OutputprocessorsView.dataProcessor.text={"Processor": ""} AdjustPanel.lblVelocity.text=FPS AdjustPanel.lblTime=Zeit AdjustPanel.lblStep.text=Zeitschritt +AdjustPanel.lblVisTime.text=Aufl. ProjectView.mntmOutputToSceneario.text=Generiere Szenario ProjectView.mntmRunOutput.text=Offline erneut starten diff --git a/VadereGui/src/org/vadere/gui/components/model/DefaultModel.java b/VadereGui/src/org/vadere/gui/components/model/DefaultModel.java index bd986dbc62c6fe188c05332dd5c4ce5f03fa46d4..785889a981603193f80f050fbdd2952d264ae7b6 100644 --- a/VadereGui/src/org/vadere/gui/components/model/DefaultModel.java +++ b/VadereGui/src/org/vadere/gui/components/model/DefaultModel.java @@ -135,8 +135,26 @@ public abstract class DefaultModel extends Observable i @Override public boolean setScale(final double scale) { - boolean hasChanged = true; double oldScale = scaleFactor; + boolean hasChanged = setScaleWithoutChangingViewport(scale); + + // 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; + } + + @Override + public boolean setScaleWithoutChangingViewport(double scale) { + boolean hasChanged = true; if (scale < MIN_SCALE_FACTOR) { this.scaleFactor = MIN_SCALE_FACTOR; @@ -148,17 +166,10 @@ public abstract class DefaultModel 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); + if(hasChanged) { setChanged(); } + return hasChanged; } @@ -254,7 +265,7 @@ public abstract class DefaultModel extends Observable i } @Override - public void setViewportBound(final Rectangle2D.Double viewportBound) { + public synchronized void setViewportBound(final Rectangle2D.Double viewportBound) { Rectangle2D.Double oldViewportBound = this.viewportBound; if (!oldViewportBound.equals(viewportBound)) { diff --git a/VadereGui/src/org/vadere/gui/components/model/IDefaultModel.java b/VadereGui/src/org/vadere/gui/components/model/IDefaultModel.java index 1b22d8cabf92a9291337a79af8831f523c2eea4f..8f3b647509fa4719c1042c88cb397a0e1259b0af 100644 --- a/VadereGui/src/org/vadere/gui/components/model/IDefaultModel.java +++ b/VadereGui/src/org/vadere/gui/components/model/IDefaultModel.java @@ -28,6 +28,8 @@ public interface IDefaultModel extends Iterable extends }*/ @Override - public void notifyObservers() { + public synchronized void notifyObservers() { // synchronized (config) { if (config.hasChanged()) { setChanged(); diff --git a/VadereGui/src/org/vadere/gui/components/view/DefaultRenderer.java b/VadereGui/src/org/vadere/gui/components/view/DefaultRenderer.java index b18d91dba13af6bd83f6fa45332b9633da4edb26..8a413dc914eb1a1febfec24891ecf6d624451c6e 100644 --- a/VadereGui/src/org/vadere/gui/components/view/DefaultRenderer.java +++ b/VadereGui/src/org/vadere/gui/components/view/DefaultRenderer.java @@ -59,37 +59,45 @@ public abstract class DefaultRenderer { * @param height */ public void render(final Graphics2D targetGraphics2D, final int width, final int height) { - render(targetGraphics2D, 0, 0, width, height); + synchronized (defaultModel) { + render(targetGraphics2D, 0, 0, width, height); + } } public void render(final Graphics2D targetGraphics2D, final int x, final int y, final int width, final int height) { - targetGraphics2D.drawImage(renderImage(width, height), x, y, null); - targetGraphics2D.dispose(); + synchronized (defaultModel) { + targetGraphics2D.drawImage(renderImage(width, height), x, y, null); + targetGraphics2D.dispose(); + } } public void renderGraphics(final Graphics2D targetGraphics2D, final int width, final int height) { - targetGraphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + synchronized (defaultModel) { + targetGraphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // (1) clear background - targetGraphics2D.setColor(Color.GRAY); - //targetGraphics2D.fill(); - targetGraphics2D.fillRect(0, 0, width, height); + // (1) clear background + targetGraphics2D.setColor(Color.GRAY); + //targetGraphics2D.fill(); + targetGraphics2D.fillRect(0, 0, width, height); - // (2) render everything which can be rendered before the transformation - renderPreTransformation(targetGraphics2D, width, height); + // (2) render everything which can be rendered before the transformation + renderPreTransformation(targetGraphics2D, width, height); - // (3) - transformGraphics(targetGraphics2D); + // (3) + transformGraphics(targetGraphics2D); - // (4) render everything which can be rendered after the transformation - renderPostTransformation(targetGraphics2D, width, height); + // (4) render everything which can be rendered after the transformation + renderPostTransformation(targetGraphics2D, width, height); + } } public BufferedImage renderImage(final int width, final int height) { - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - Graphics2D bufferGraphics2D = (Graphics2D) image.getGraphics(); - renderGraphics(bufferGraphics2D, width, height); - return image; + synchronized (defaultModel) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D bufferGraphics2D = (Graphics2D) image.getGraphics(); + renderGraphics(bufferGraphics2D, width, height); + return image; + } } public void setLogo(final BufferedImage logo) { @@ -99,33 +107,36 @@ public abstract class DefaultRenderer { protected void renderPreTransformation(final Graphics2D graphics2D, final int width, final int height) {} protected void renderPostTransformation(final Graphics2D graphics2D, final int width, final int height) { - graphics2D.setColor(Color.WHITE); - Rectangle2D.Double topographyBound = defaultModel.getTopographyBound(); - fill(new VRectangle( - topographyBound.getMinX() + defaultModel.getBoundingBoxWidth(), - topographyBound.getMinY() + defaultModel.getBoundingBoxWidth(), - (defaultModel.getTopographyBound().getWidth() - defaultModel.getBoundingBoxWidth() * 2), - (defaultModel.getTopographyBound().getHeight() - defaultModel.getBoundingBoxWidth() * 2)), graphics2D); + synchronized (defaultModel) { + graphics2D.setColor(Color.WHITE); + Rectangle2D.Double topographyBound = defaultModel.getTopographyBound(); + fill(new VRectangle( + topographyBound.getMinX() + defaultModel.getBoundingBoxWidth(), + topographyBound.getMinY() + defaultModel.getBoundingBoxWidth(), + (defaultModel.getTopographyBound().getWidth() - defaultModel.getBoundingBoxWidth() * 2), + (defaultModel.getTopographyBound().getHeight() - defaultModel.getBoundingBoxWidth() * 2)), graphics2D); + } } protected void transformGraphics(final Graphics2D graphics2D) { - Rectangle2D.Double topographyBound = defaultModel.getTopographyBound(); - mirrowHorizonzal(graphics2D, (int) (topographyBound.getHeight() * defaultModel.getScaleFactor())); - graphics2D.scale(defaultModel.getScaleFactor(), defaultModel.getScaleFactor()); + synchronized (defaultModel) { + Rectangle2D.Double topographyBound = defaultModel.getTopographyBound(); + mirrowHorizonzal(graphics2D, (int) (topographyBound.getHeight() * defaultModel.getScaleFactor())); + graphics2D.scale(defaultModel.getScaleFactor(), defaultModel.getScaleFactor()); - //graphics2D.translate(-topographyBound.getMinX(), -topographyBound.getMinY()); + //graphics2D.translate(-topographyBound.getMinX(), -topographyBound.getMinY()); - /* - * This calculation we need since the viewport.y = 0 if the user scrolls to the bottom - */ - Rectangle2D.Double viewportBound = defaultModel.getViewportBound(); - double dy = topographyBound.getHeight() - viewportBound.getHeight(); - - graphics2D.translate(-viewportBound.getX(), Math.max((dy - viewportBound.getY()), - viewportBound.getY())); - // graphics2D.translate(+viewportBound.getX(), -Math.max((dy - viewportBound.getY()), 0)); + /* + * This calculation we need since the viewport.y = 0 if the user scrolls to the bottom + */ + Rectangle2D.Double viewportBound = defaultModel.getViewportBound(); + double dy = topographyBound.getHeight() - viewportBound.getHeight(); + graphics2D.translate(-viewportBound.getX(), Math.max((dy - viewportBound.getY()), - viewportBound.getY())); + // graphics2D.translate(+viewportBound.getX(), -Math.max((dy - viewportBound.getY()), 0)); + } } protected void renderScenarioElement(final Iterable elements, final Graphics2D g, diff --git a/VadereGui/src/org/vadere/gui/postvisualization/PostVisualizationConsole.java b/VadereGui/src/org/vadere/gui/postvisualization/PostVisualizationConsole.java index bc56bb4bdfefaf2db69cefc5d28d1749d3293a5a..14a25ef2b00c943a08f9ad68c7bbb9338b3a76c7 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/PostVisualizationConsole.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/PostVisualizationConsole.java @@ -11,6 +11,7 @@ import org.vadere.gui.postvisualization.utils.ImageGenerator; import org.vadere.gui.postvisualization.utils.SVGGenerator; import org.vadere.gui.postvisualization.utils.TikzGenerator; import org.vadere.gui.postvisualization.view.PostvisualizationRenderer; +import org.vadere.simulator.projects.Scenario; import org.vadere.simulator.projects.io.IOOutput; import org.vadere.simulator.projects.io.TrajectoryReader; import org.vadere.util.io.IOUtils; @@ -22,7 +23,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.Locale; import java.util.Optional; @@ -118,8 +118,9 @@ public class PostVisualizationConsole { Optional scenarioFile = IOUtils.getFirstFile(path.toFile(), IOUtils.SCENARIO_FILE_EXTENSION); if (trajectoryFile.isPresent() && scenarioFile.isPresent()) { - TrajectoryReader reader = new TrajectoryReader(trajectoryFile.get().toPath()); - model.init(reader.readFile(), IOOutput.readScenario(scenarioFile.get().toPath()), trajectoryFile.get().getParent()); + Scenario scenario = IOOutput.readScenario(scenarioFile.get().toPath()); + TrajectoryReader reader = new TrajectoryReader(trajectoryFile.get().toPath(), scenario); + model.init(reader.readFile(), scenario, trajectoryFile.get().getParent()); } else { System.err.println("could not find trajectory or scenario file in: " + outputDirectoryPath); } @@ -142,7 +143,7 @@ public class PostVisualizationConsole { if (step != -1) { model.setStep(step); } else { - model.setTime(time); + model.setVisTime(time); } if (showGroups){ diff --git a/VadereGui/src/org/vadere/gui/postvisualization/control/Player.java b/VadereGui/src/org/vadere/gui/postvisualization/control/Player.java index c6ef943218cfd95fe2401442a476fbc07252fb83..c36445b696916ce4a6f90efe3a0261ce676c4f48 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/control/Player.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/control/Player.java @@ -2,13 +2,15 @@ package org.vadere.gui.postvisualization.control; import org.vadere.gui.postvisualization.model.PostvisualizationModel; +import org.vadere.state.simulation.Step; import org.vadere.util.logging.Logger; +import java.util.Optional; + public class Player implements Runnable { private static Logger logger = Logger.getLogger(Player.class); private static volatile Player instance; private Thread currentThread; - private int currentStep; enum State { STOPPED, PAUSED, RUNNING @@ -29,7 +31,6 @@ public class Player implements Runnable { private Player(final PostvisualizationModel model) { this.model = model; - this.currentStep = 1; this.currentThread = null; state = State.STOPPED; } @@ -41,7 +42,6 @@ public class Player implements Runnable { public void stop() { state = State.STOPPED; running = false; - currentStep = 1; if (currentThread != null) { currentThread.interrupt(); @@ -63,8 +63,6 @@ public class Player implements Runnable { currentThread.start(); } state = State.RUNNING; - - model.getStep().ifPresent(s -> currentStep = s.getStepNumber()); synchronized (model) { model.notifyAll(); } @@ -79,8 +77,8 @@ public class Player implements Runnable { } } - private boolean isRunable() { - return model.getStep().isPresent() && model.getFirstStep().isPresent() && model.getLastStep().isPresent(); + private boolean isRunnable() { + return !model.isEmpty(); } @Override @@ -91,17 +89,16 @@ public class Player implements Runnable { // synchronized (model) { switch (state) { case RUNNING: { - if (isRunable()) { - if (model.getLastStep().get().getStepNumber() > model.getStep().get().getStepNumber()) { - currentStep = model.getStep().get().getStepNumber() + 1; - model.setStep(currentStep); - } else { - pause(); + if (isRunnable()) { + double newSimeTimeInSec = model.getSimTimeInSec() + model.getVisTimeStepLength(); + if(model.getSimTimeInSec() >= model.getMaxSimTimeInSec()) { + newSimeTimeInSec = 0; } + model.setVisTime(newSimeTimeInSec); } } - model.notifyObservers(); - break; + model.notifyObservers(); + break; case PAUSED: { synchronized (model) { @@ -112,17 +109,18 @@ public class Player implements Runnable { } } } - break; - default: - break; + break; + default: break; } // } diffMs = System.currentTimeMillis() - ms; - sleepTimeMS = (int) Math.round((1000.0 - diffMs) / model.config.getFps()); - try { - Thread.sleep(Math.max(0, sleepTimeMS)); - } catch (InterruptedException e) { - logger.info("Player interrupted while sleeping"); + sleepTimeMS = (int) Math.round((1000.0 / model.config.getFps() - diffMs)); + if(sleepTimeMS > 0) { + try { + Thread.sleep(Math.max(0, sleepTimeMS)); + } catch (InterruptedException e) { + logger.info("Player interrupted while sleeping"); + } } } } diff --git a/VadereGui/src/org/vadere/gui/postvisualization/model/PostvisualizationModel.java b/VadereGui/src/org/vadere/gui/postvisualization/model/PostvisualizationModel.java index b8e274976c5545d27564094009fb2c22e4259033..2b31c2d3318c5991bce0187f57fe56122e036eb5 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/model/PostvisualizationModel.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/model/PostvisualizationModel.java @@ -7,6 +7,7 @@ import org.vadere.gui.components.model.SimulationModel; import org.vadere.gui.postvisualization.control.TableListenerLogicExpression; import org.vadere.gui.postvisualization.utils.PotentialFieldContainer; import org.vadere.simulator.projects.Scenario; +import org.vadere.state.attributes.AttributesSimulation; import org.vadere.state.scenario.Agent; import org.vadere.state.scenario.Pedestrian; import org.vadere.state.scenario.ScenarioElement; @@ -39,7 +40,15 @@ public class PostvisualizationModel extends SimulationModel> agentsByStep; - private Comparator stepComparator = (s1, s2) -> s1.getStepNumber() - s2.getStepNumber(); + private Comparator stepComparator = Comparator.comparingInt(Step::getStepNumber); private List steps; @@ -73,7 +82,9 @@ public class PostvisualizationModel extends SimulationModel(); - + this.simTimeStepLength = new AttributesSimulation().getSimTimeStepLength(); + this.visTimeStepLength = this.simTimeStepLength; + this.visTime = 0; /*for (int i = 0; i < 5; i++) { try { colorEvalFunctions.put(i, new JsonLogicParser("false").parse()); @@ -93,7 +104,6 @@ public class PostvisualizationModel extends SimulationModel> agentsByStep, final Scenario scenario, final String projectPath) { + simTimeStepLength = scenario.getAttributesSimulation().getSimTimeStepLength(); logger.info("start the initialization of the PostvisualizationModel."); init(scenario, projectPath); this.agentsByStep = agentsByStep; @@ -124,7 +135,7 @@ public class PostvisualizationModel extends SimulationModel(); @@ -151,6 +166,18 @@ public class PostvisualizationModel extends SimulationModel getLastStep() { if (!steps.isEmpty()) { return Optional.of(steps.get(steps.size() - 1)); @@ -167,6 +194,14 @@ public class PostvisualizationModel extends SimulationModel getPotentialField() { Function f = p -> 0.0; try { - if (potentialContainer != null && step != null) { - final CellGrid potentialField = potentialContainer.getPotentialField(step.getStepNumber()); + if (potentialContainer != null) { + final CellGrid potentialField = potentialContainer.getPotentialField(Step.toFloorStep(getSimTimeInSec(), getSimTimeStepLength()).getStepNumber()); f = potentialField.getInterpolationFunction(); } } catch (IOException e) { @@ -188,12 +223,12 @@ public class PostvisualizationModel extends SimulationModel getAgents() { - return getAlivePedestrians().map(t -> t.getAgent(step)).filter(ped -> ped.isPresent()).map(ped -> ped.get()) + return getAlivePedestrians().map(t -> t.getAgent(getSimTimeInSec())).filter(ped -> ped.isPresent()).map(ped -> ped.get()) .collect(Collectors.toList()); } @@ -212,22 +247,49 @@ public class PostvisualizationModel extends SimulationModel getStep() { + /*public synchronized Optional getStep() { return Optional.ofNullable(step); } + public synchronized Optional getRatio() { + return Optional.ofNullable(ratio); + }*/ + public int getStepCount() { return steps.size(); } - public synchronized void setTime(final double time) { - if (steps.size() >= 2 && steps.get(1).getSimTimeInSec().isPresent() - && steps.get(0).getSimTimeInSec().isPresent()) { - double dt = steps.get(1).getSimTimeInSec().get() - steps.get(0).getSimTimeInSec().get(); - int step = (int) Math.round(time / dt) + 1; - setStep(step); - } else if (1.0e-10 < Math.abs(time)) { - setStep(1); + public synchronized void setVisTime(final double visTimeInSec) { + if(!isEmpty()) { + double validVisTime = Math.min(Step.toSimTimeInSec(last(), simTimeStepLength), Math.max(Step.toSimTimeInSec(fist(), simTimeStepLength), visTimeInSec)); + if(this.visTime != validVisTime) { + this.visTime = validVisTime; + + if (isVoronoiDiagramAvailable() && isVoronoiDiagramVisible()) { + synchronized(getVoronoiDiagram()) { + getVoronoiDiagram().computeVoronoiDiagram( + trajectories.values().stream() + .filter(t -> t.isAlive(visTime)) + .map(t -> t.getAgent(visTime).get().getPosition()) + .collect(Collectors.toList())); + } + } + + if (isElementSelected() && getSelectedElement() instanceof Pedestrian) { + Trajectory trajectory = trajectories.get(getSelectedElement().getId()); + if (trajectory != null) { + Optional ped = trajectory.getAgent(visTime); + setSelectedElement(ped.orElse(null)); + } + } + + // so the new pedestrian position is displayed! + if (isElementSelected()) { + notifySelectSecenarioElementListener(getSelectedElement()); + } + + setChanged(); + } } } @@ -258,9 +320,34 @@ public class PostvisualizationModel extends SimulationModel optionalStep = steps.size() >= step && steps.get(step - 1).getStepNumber() == step + /*public synchronized void setStep(final double visTimeInSec) { + this.visTime = visTimeInSec; + if (isVoronoiDiagramAvailable() && isVoronoiDiagramVisible()) { + synchronized(getVoronoiDiagram()) { + getVoronoiDiagram().computeVoronoiDiagram( + trajectories.values().stream() + .filter(t -> t.isAlive(visTimeInSec)) + .map(t -> t.getAgent(visTimeInSec).get().getPosition()) + .collect(Collectors.toList())); + } + } + + if (isElementSelected() && getSelectedElement() instanceof Pedestrian) { + Trajectory trajectory = trajectories.get(getSelectedElement().getId()); + if (trajectory != null) { + Optional ped = trajectory.getAgent(visTimeInSec); + setSelectedElement(ped.orElse(null)); + } + } + + // so the new pedestrian position is displayed! + if (isElementSelected()) { + notifySelectSecenarioElementListener(getSelectedElement()); + } + + setChanged(); + + /*Optional optionalStep = steps.size() >= step && steps.get(step - 1).getStepNumber() == step ? Optional.of(steps.get(step - 1)) : Optional.empty(); if (!optionalStep.isPresent()) { @@ -269,53 +356,66 @@ public class PostvisualizationModel extends SimulationModel t.isPedestrianAlive(this.step)) - .map(t -> t.getAgent(this.step).get().getPosition()) - .collect(Collectors.toList())); + this.ratio = ratio; + this.visTime = this.step.getSimTimeInSec() + ratio * getSimTimeStepLength(); + logger.info("calculated time = " + visTime + "," + System.currentTimeMillis()); + int istep = ratio == 0 ? this.step.getStepNumber() : this.step.getStepNumber() + 1; + + Step nextStep = new Step(istep, stepToTime(istep)); + this.ratio = ratio; + if (isVoronoiDiagramAvailable() && isVoronoiDiagramVisible()) { + synchronized(getVoronoiDiagram()) { + getVoronoiDiagram().computeVoronoiDiagram( + trajectories.values().stream() + .filter(t -> t.isAlive(nextStep)) + .map(t -> t.getAgent(this.step, ratio).get().getPosition()) + .collect(Collectors.toList())); + } } if (isElementSelected() && getSelectedElement() instanceof Pedestrian) { Trajectory trajectory = trajectories.get(getSelectedElement().getId()); if (trajectory != null) { - Optional ped = trajectory.getAgent(this.step); + Optional ped = trajectory.getAgent(this.step, ratio); setSelectedElement(ped.orElseGet(() -> null)); } } - // so the new pedestrian position is displayed! + if (isElementSelected()) { notifySelectSecenarioElementListener(getSelectedElement()); } - /* - * if(isFloorFieldAvailable()) { - * getPotentialField(); - * } - */ setChanged(); } + }*/ + + public synchronized void setStep(final int step) { + setVisTime(Step.toSimTimeInSec(new Step(step), simTimeStepLength)); } - @Override - public double getSimTimeInSec() { - return getStep().map(this::getSimTimeInSec).orElse(0.0); + public synchronized Step getStep() { + return Step.toFloorStep(getSimTimeInSec(), simTimeStepLength); } - private double getSimTimeInSec(final Step step) { - return step.getSimTimeInSec() - .orElse(step.getStepNumber() * vadere.getScenarioStore().getAttributesSimulation().getSimTimeStepLength()); + @Override + public double getSimTimeInSec() { + return visTime; } public synchronized void setPotentialFieldContainer(final PotentialFieldContainer container) { this.potentialContainer = container; } + public double getMaxSimTimeInSec() { + return Step.toSimTimeInSec(last(), simTimeStepLength); + } + /** * Returns all trajectories. E.g. also trajectories from pedestrian that are not already * appeared and pedestrians that are already reach their target @@ -323,7 +423,7 @@ public class PostvisualizationModel extends SimulationModel getAppearedPedestrians() { - return trajectories.values().stream().filter(t -> t.isPedestrianAppeared(this.step)); + return trajectories.values().stream().filter(t -> t.hasAppeared(getSimTimeInSec())); } /** @@ -333,11 +433,11 @@ public class PostvisualizationModel extends SimulationModel getAlivePedestrians() { - return trajectories.values().stream().filter(t -> t.isPedestrianAlive(this.step)); + return trajectories.values().stream().filter(t -> t.isAlive(getSimTimeInSec())); } public boolean isEmpty() { - return agentsByStep.size() == 0; + return agentsByStep.isEmpty(); } @Override diff --git a/VadereGui/src/org/vadere/gui/postvisualization/utils/ImageGenerator.java b/VadereGui/src/org/vadere/gui/postvisualization/utils/ImageGenerator.java index d2b51fa0c690e521b46bfcccb8bae6e6ff6a8bb9..6d359bf6ed249a83451f7c74752a6ab92db5e1e5 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/utils/ImageGenerator.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/utils/ImageGenerator.java @@ -23,28 +23,31 @@ public class ImageGenerator { } public BufferedImage generateImage(final double scaleFactor) { - BufferedImage bi = null; - synchronized (renderer) { + synchronized (model) { + BufferedImage bi = null; double oldScale = model.getScaleFactor(); try { - model.setScale(scaleFactor); - bi = renderer.renderImage(ImageGenerator.calculateOptimalWidth(model), - ImageGenerator.calculateOptimalHeight(model)); + model.setScaleWithoutChangingViewport(scaleFactor); + bi = renderer.renderImage(ImageGenerator.calculateOptimalWidth(model), ImageGenerator.calculateOptimalHeight(model)); } catch (Exception e) { logger.error("could not render image " + e.getMessage()); } finally { - model.setScale(oldScale); + model.setScaleWithoutChangingViewport(oldScale); } + return bi; } - return bi; } public BufferedImage generateImage(final Rectangle2D.Double imageSize) { - return generateImage(imageSize.getWidth() / model.getTopographyBound().getWidth()); + synchronized (model) { + return generateImage(imageSize.getWidth() / model.getTopographyBound().getWidth()); + } } public BufferedImage generateImage() { - return generateImage(model.getScaleFactor()); + synchronized (model) { + return generateImage(model.getScaleFactor()); + } } public static int calculateOptimalWidth(final DefaultModel model) { diff --git a/VadereGui/src/org/vadere/gui/postvisualization/utils/MovRecorder.java b/VadereGui/src/org/vadere/gui/postvisualization/utils/MovRecorder.java index 012aa2ba8bbc420eee145d7b5f448ca5733c21e0..52cea9ece980509044b8d60bf2b4059728872a6b 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/utils/MovRecorder.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/utils/MovRecorder.java @@ -27,86 +27,102 @@ public class MovRecorder implements IRecorder { private final PostvisualizationModel model; private ImageGenerator generator; private SequenceEncoder enc; - private int step; + private double simTimeInSec; private Rectangle2D.Double imageSize; private Rectangle2D.Double viewport; + private boolean finished; public MovRecorder(final PostvisualizationRenderer renderer) { this.model = renderer.getModel(); this.imageSize = model.getWindowBound(); this.viewport = model.getViewportBound(); this.generator = new ImageGenerator(renderer, renderer.getModel()); - this.step = 0; + this.simTimeInSec = 0.0; this.enc = null; + this.finished = false; } @Override - public synchronized void update(Observable o, Object arg) { - try { - if (model.config.isRecording() && model.getStep().isPresent() - && model.getStep().get().getStepNumber() != step) { - step = model.getStep().get().getStepNumber(); - addPicture(); + public void update(Observable o, Object arg) { + synchronized(model) { + try { + if (model.config.isRecording() && model.getSimTimeInSec() > simTimeInSec) { + addPicture(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + logger.error(ioe.getMessage()); } - } catch (IOException ioe) { - ioe.printStackTrace(); - logger.error(ioe.getMessage()); } } - private synchronized void addPicture() throws IOException { - Rectangle2D.Double oldViewport = model.getViewportBound(); - model.setViewportBound(viewport); - BufferedImage bi = generator.generateImage(imageSize); - enc.encodeImage(bi); - logger.info(this + " add picture"); - model.setViewportBound(oldViewport); + private void addPicture() throws IOException { + synchronized(model) { + if(!this.finished) { + simTimeInSec = model.getSimTimeInSec(); + Rectangle2D.Double oldViewport = model.getViewportBound(); + model.setViewportBound(viewport); + BufferedImage bi = generator.generateImage(imageSize); + logger.info(this + " add picture " + bi.getWidth() + " (width), " + bi.getHeight() + " (height)."); + enc.encodeImage(bi); + model.setViewportBound(oldViewport); + } + } } @Override - public synchronized void stopRecording() throws IOException { - try { - enc.finish(); - } catch (IndexOutOfBoundsException error) { - logger.debug("Nothing recorded! " + error.getMessage()); - throw error; + public void stopRecording() throws IOException { + synchronized(model) { + try { + this.finished = true; + enc.finish(); + } catch (IndexOutOfBoundsException error) { + logger.debug("Nothing recorded! " + error.getMessage()); + throw error; + } + logger.info(this + " stop recording"); } - logger.info(this + " stop recording"); } @Override - public synchronized void startRecording(final Rectangle2D.Double imageSize) throws IOException { - try { - this.imageSize = imageSize; - this.viewport = model.getViewportBound(); - startRecording(); - logger.info(this + " start recording"); - } catch (IOException e) { - e.printStackTrace(); - logger.error(e.getMessage()); + public void startRecording(final Rectangle2D.Double imageSize) throws IOException { + synchronized(model) { + try { + this.finished = false; + this.imageSize = imageSize; + this.viewport = model.getViewportBound(); + this.simTimeInSec = 0; + startRecording(); + logger.info(this + " start recording"); + } catch (IOException e) { + e.printStackTrace(); + logger.error(e.getMessage()); + } } } @Override - public synchronized void startRecording() throws IOException { - Date todaysDate = new java.util.Date(); - SimpleDateFormat formatter = new SimpleDateFormat(resources.getProperty("SettingsDialog.dataFormat")); - String formattedDate = formatter.format(todaysDate); - JFileChooser fileChooser = new JFileChooser(Preferences.userNodeForPackage(PostVisualisation.class).get("SettingsDialog.snapshotDirectory.path", ".")); - File outputFile = new File(Messages.getString("FileDialog.filenamePrefix") + formattedDate + ".mov"); - fileChooser.setSelectedFile(outputFile); - - int returnVal = fileChooser.showDialog(null, "Save"); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - - outputFile = fileChooser.getSelectedFile().toString().endsWith(".mov") ? fileChooser.getSelectedFile() - : new File(fileChooser.getSelectedFile().toString() + ".mov"); - try { - enc = new SequenceEncoder(outputFile); - } catch (IOException e) { - enc = null; - throw e; + public void startRecording() throws IOException { + synchronized(model) { + Date todaysDate = new java.util.Date(); + SimpleDateFormat formatter = new SimpleDateFormat(resources.getProperty("SettingsDialog.dataFormat")); + String formattedDate = formatter.format(todaysDate); + JFileChooser fileChooser = new JFileChooser(Preferences.userNodeForPackage(PostVisualisation.class).get("SettingsDialog.snapshotDirectory.path", ".")); + File outputFile = new File(Messages.getString("FileDialog.filenamePrefix") + formattedDate + ".mov"); + fileChooser.setSelectedFile(outputFile); + + int returnVal = fileChooser.showDialog(null, "Save"); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + + outputFile = fileChooser.getSelectedFile().toString().endsWith(".mov") ? fileChooser.getSelectedFile() + : new File(fileChooser.getSelectedFile().toString() + ".mov"); + try { + enc = new SequenceEncoder(outputFile); + } catch (IOException e) { + enc = null; + throw e; + } } } } diff --git a/VadereGui/src/org/vadere/gui/postvisualization/utils/TikzGenerator.java b/VadereGui/src/org/vadere/gui/postvisualization/utils/TikzGenerator.java index c892867a84381be8669a0368d001f11b802dac59..1f0f1664ce487190544782edf0e80ac8c6b9067c 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/utils/TikzGenerator.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/utils/TikzGenerator.java @@ -286,28 +286,27 @@ public class TikzGenerator { final StringBuffer generatedCode = new StringBuffer(""); Stream trajectoryStream = model.getAlivePedestrians(); - Step currentTimeStep = model.getStep().orElseGet(null); + //Step currentTimeStep = model.getStep().orElseGet(null); - if (currentTimeStep != null) { - trajectoryStream.forEach(trajectory -> { - Stream trajectoryPoints = trajectory.getPositionsReverse(currentTimeStep); - // Use a newline ("to\n") for joining because TeX could possibly choke up on long lines. - String trajectoryAsTikzString = trajectoryPoints - .map(point -> String.format(Locale.US, "(%f,%f)", point.x, point.y)) - .collect(Collectors.joining(" to\n")); + trajectoryStream.forEach(trajectory -> { + Stream trajectoryPoints = trajectory.getPositionsReverse(model.getSimTimeInSec()); - String coloredTrajectory = applyAgentColorToTrajectory(trajectoryAsTikzString, trajectory.getAgent(currentTimeStep)); + // Use a newline ("to\n") for joining because TeX could possibly choke up on long lines. + String trajectoryAsTikzString = trajectoryPoints + .map(point -> String.format(Locale.US, "(%f,%f)", point.x, point.y)) + .collect(Collectors.joining(" to\n")); - int pedestrianId = trajectory.getPedestrianId(); - Optional trajectoryEndStep = trajectory.getEndStep(); - String trajectoryEndStepAsString = (trajectoryEndStep.isPresent()) ? "" + trajectoryEndStep.get().toString() : "unknown end step" ; - String currentTimeStepAsString = currentTimeStep.toString(); + String coloredTrajectory = applyAgentColorToTrajectory(trajectoryAsTikzString, trajectory.getAgent(model.getSimTimeInSec())); + + int pedestrianId = trajectory.getPedestrianId(); + Optional trajectoryEndStep = trajectory.getEndStep(); + String trajectoryEndStepAsString = (trajectoryEndStep.isPresent()) ? "" + trajectoryEndStep.get().toString() : "unknown end step" ; + + generatedCode.append(String.format(Locale.US, "%% Trajectory Agent %d @ simTimeInSec %f of %s\n", pedestrianId, model.getSimTimeInSec(), trajectoryEndStepAsString)); + generatedCode.append(coloredTrajectory); + }); - generatedCode.append(String.format(Locale.US, "%% Trajectory Agent %d @ step %s of %s\n", pedestrianId, currentTimeStepAsString, trajectoryEndStepAsString)); - generatedCode.append(coloredTrajectory); - }); - } return generatedCode.toString(); diff --git a/VadereGui/src/org/vadere/gui/postvisualization/view/AdjustPanel.java b/VadereGui/src/org/vadere/gui/postvisualization/view/AdjustPanel.java index df70b31ad95db7809d3a2bda30393558c9417e4c..8370bc431e1b2f351f20fe3de1b08ed22ecf9280 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/view/AdjustPanel.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/view/AdjustPanel.java @@ -4,18 +4,18 @@ import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import org.vadere.gui.components.utils.Messages; import org.vadere.gui.components.utils.Resources; import org.vadere.gui.postvisualization.control.ActionSetTimeStep; import org.vadere.gui.postvisualization.control.EJSliderAction; import org.vadere.gui.postvisualization.model.PostvisualizationModel; +import org.vadere.state.simulation.Step; import java.awt.*; import java.util.Observable; import java.util.Observer; +import java.util.Optional; public class AdjustPanel extends JPanel implements Observer { private static Resources resources = Resources.getInstance("postvisualization"); @@ -23,10 +23,14 @@ public class AdjustPanel extends JPanel implements Observer { private final JSlider slider; private final JSpinner sStep; private final JSpinner sVelocity; + private final JSpinner sVisTimeStepLength; + private final JSpinner sTime; private final SpinnerModel sModelTime; private final SpinnerModel sModelTimeStep; private final SpinnerModel sModelVelocity; + + private final SpinnerModel sModelVisTimeStepLength; private final PostvisualizationModel model; public AdjustPanel(final PostvisualizationModel model) { @@ -42,11 +46,15 @@ public class AdjustPanel extends JPanel implements Observer { // sStep.setEditable(false); sModelVelocity = new SpinnerNumberModel(model.config.getFps(), 1, 200, 1); sModelTimeStep = new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1); - sModelTime = new SpinnerNumberModel(0.0, 0.0, 0.0, 0); + sModelTime = new SpinnerNumberModel(0.0, 0.0, Double.MAX_VALUE, 0.01); + sModelVisTimeStepLength = new SpinnerNumberModel(model.getVisTimeStepLength(), 0.01, Double.MAX_VALUE, 0.01); + sVelocity = new JSpinner(sModelVelocity); sTime = new JSpinner(sModelTime); sStep = new JSpinner(sModelTimeStep); - sTime.setEnabled(false); + sVisTimeStepLength = new JSpinner(sModelVisTimeStepLength); + + //sTime.setEnabled(false); // lTime.set @@ -54,9 +62,10 @@ public class AdjustPanel extends JPanel implements Observer { sStep.setPreferredSize(new Dimension(50, 30)); sVelocity.setPreferredSize(new Dimension(50, 30)); sTime.setPreferredSize(new Dimension(70, 30)); + sVisTimeStepLength.setPreferredSize(new Dimension(70, 30)); // Layout definition! FormLayout layout = new FormLayout( - "2dlu, default:grow, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu", // col + "2dlu, default:grow, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu", // col "2dlu, default, 2dlu"); // rows setLayout(layout); CellConstraints cc = new CellConstraints(); @@ -67,23 +76,31 @@ public class AdjustPanel extends JPanel implements Observer { add(sTime, cc.xy(10, 2)); add(new JLabel(Messages.getString("AdjustPanel.lblStep.text")), cc.xy(12, 2)); add(sStep, cc.xy(14, 2)); + add(new JLabel(Messages.getString("AdjustPanel.lblVisTime.text")), cc.xy(16, 2)); + add(sVisTimeStepLength, cc.xy(18, 2)); - sVelocity.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - model.config.setFps((int) sVelocity.getValue()); - } + sVelocity.addChangeListener(e -> { + model.config.setFps((int) sVelocity.getValue()); + model.notifyObservers(); }); - sStep.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - if ((int) sStep.getValue() <= model.getStepCount()) { - model.setStep((int) sStep.getValue()); - model.notifyObservers(); - } + + sTime.addChangeListener(e -> { + model.setVisTime((double)sModelTime.getValue()); + model.notifyObservers(); + }); + + sStep.addChangeListener(e -> { + if ((int) sStep.getValue() <= model.getStepCount()) { + model.setStep((int) sStep.getValue()); + model.notifyObservers(); } }); + sVisTimeStepLength.addChangeListener(e -> { + model.setVisTimeStepLength((double) sVisTimeStepLength.getValue()); + model.notifyObservers(); + }); + ActionSetTimeStep setTimeStepAction = new ActionSetTimeStep("setTimeStep", model); slider.addChangeListener(setTimeStepAction); } @@ -94,10 +111,11 @@ public class AdjustPanel extends JPanel implements Observer { slider.setMaximum(model.getLastStep().map(step -> step.getStepNumber()).orElse(1)); slider.setMinimum(model.getFirstStep().map(step -> step.getStepNumber()).orElse(1)); - int currentStepNumber = model.getStep().map(step -> step.getStepNumber()).orElse(1); + int currentStepNumber = model.getStep().getStepNumber(); slider.setValue(currentStepNumber); sStep.setValue(currentStepNumber); sTime.setValue(model.getSimTimeInSec()); + sModelVisTimeStepLength.setValue(model.getVisTimeStepLength()); } } diff --git a/VadereGui/src/org/vadere/gui/postvisualization/view/PostvisualizationRenderer.java b/VadereGui/src/org/vadere/gui/postvisualization/view/PostvisualizationRenderer.java index ecedac806faac45eb65ac7036aee16493fd3afe1..053847a34cdbd1897b10d58a6f164ea9efd64208 100644 --- a/VadereGui/src/org/vadere/gui/postvisualization/view/PostvisualizationRenderer.java +++ b/VadereGui/src/org/vadere/gui/postvisualization/view/PostvisualizationRenderer.java @@ -5,7 +5,6 @@ import org.vadere.gui.components.view.SimulationRenderer; import org.vadere.gui.postvisualization.model.PostvisualizationModel; import org.vadere.gui.renderer.agent.AgentRender; import org.vadere.state.scenario.Agent; -import org.vadere.state.simulation.Step; import org.vadere.state.simulation.Trajectory; import org.vadere.util.geometry.shapes.VPoint; import org.vadere.util.logging.Logger; @@ -62,13 +61,13 @@ public class PostvisualizationRenderer extends SimulationRenderer { } else { trajectoriesStream = model.getAlivePedestrians(); } - model.getStep().ifPresent(step -> trajectoriesStream.forEach(t -> renderTrajectory(g, color, t, step))); + trajectoriesStream.forEach(t -> renderTrajectory(g, color, t, model.getSimTimeInSec())); } } - private void renderTrajectory(final Graphics2D g, final Color color, final Trajectory trajectory, final Step step) { + private void renderTrajectory(final Graphics2D g, final Color color, final Trajectory trajectory, final double simTimeInSec) { - Optional optionalPedestrian = trajectory.getAgent(step); + Optional optionalPedestrian = trajectory.getAgent(simTimeInSec); AgentRender agentRender = getAgentRender(); if (optionalPedestrian.isPresent()) { @@ -87,7 +86,7 @@ public class PostvisualizationRenderer extends SimulationRenderer { // renderImage the pedestrian if (model.config.isShowPedestrians()) { - if (model.config.isShowFaydedPedestrians() || !trajectory.isPedestrianDisappeared(step)) { + if (model.config.isShowFaydedPedestrians() || !trajectory.hasDisappeared(simTimeInSec)) { agentRender.render(pedestrian, nonGroupColor, g); if (model.config.isShowPedestrianIds()) { DefaultRenderer.paintAgentId(g, pedestrian); @@ -96,13 +95,13 @@ public class PostvisualizationRenderer extends SimulationRenderer { } // renderImage the trajectory - if (model.config.isShowTrajectories() && step.getStepNumber() > 0) { - renderTrajectory(g, trajectory.getPositionsReverse(step), pedestrian); + if (model.config.isShowTrajectories()) { + renderTrajectory(g, trajectory.getPositionsReverse(simTimeInSec), pedestrian); } // renderImage the arrows indicating the walking direction if (model.config.isShowWalkdirection() && - (model.config.isShowFaydedPedestrians() || !trajectory.isPedestrianDisappeared(step))) { + (model.config.isShowFaydedPedestrians() || !trajectory.hasDisappeared(simTimeInSec))) { int pedestrianId = pedestrian.getId(); VPoint lastPosition = lastPedestrianPositions.get(pedestrianId); @@ -133,10 +132,7 @@ public class PostvisualizationRenderer extends SimulationRenderer { } } } else { - logger.error("Optional should not be empty at this point! Step: " + step + ", Ped: " - + trajectory.getPedestrianId()); + logger.error("Optional should not be empty at this point! Step: " + simTimeInSec + ", Ped: " + trajectory.getPedestrianId()); } } - - } diff --git a/VadereGui/tests/org/vadere/gui/postvisualization/TestTrajectory.java b/VadereGui/tests/org/vadere/gui/postvisualization/TestTrajectory.java index 20954ff435210e7a9f2e08b5bbeac4e896b28e8e..01ea947e586ac75a907813e54c4db09cf5717ae2 100644 --- a/VadereGui/tests/org/vadere/gui/postvisualization/TestTrajectory.java +++ b/VadereGui/tests/org/vadere/gui/postvisualization/TestTrajectory.java @@ -29,7 +29,7 @@ public class TestTrajectory extends TestCase { @Override protected void setUp() throws Exception { pedestriansByStep = new HashMap<>(); - List steps = Arrays.asList(new Step(2), new Step(4), new Step(5), new Step(7)); + List steps = Arrays.asList(new Step(1), new Step(4), new Step(5), new Step(7)); List pedestrians = Arrays.asList( new Pedestrian(new AttributesAgent(1), new Random()), new Pedestrian(new AttributesAgent(2), new Random()), @@ -47,7 +47,7 @@ public class TestTrajectory extends TestCase { @Test public void testGetPedestrian() { - Trajectory trajectory = new Trajectory(pedestriansByStep, 1); + Trajectory trajectory = new Trajectory(pedestriansByStep, 1, 0.4); assertEquals(trajectory.getAgent(new Step(3)), (trajectory.getAgent(new Step(2)))); assertTrue(trajectory.getAgent(new Step(3)).isPresent()); assertEquals(trajectory.getAgent(new Step(6)), (trajectory.getAgent(new Step(5)))); @@ -66,8 +66,9 @@ public class TestTrajectory extends TestCase { @Test public void testGetPositionReverse() { - Trajectory trajectory = new Trajectory(pedestriansByStep, 2); - IntStream.rangeClosed(1, 17).forEach(stepNumber -> assertTrue( + Trajectory trajectory = new Trajectory(pedestriansByStep, 2, 0.4); + trajectory.fill(); + IntStream.rangeClosed(1, 7).forEach(stepNumber -> assertTrue( trajectory.getPositionsReverse(new Step(stepNumber)).count() + "!=" + stepNumber, trajectory.getPositionsReverse(new Step(stepNumber)).count() == stepNumber)); List reversePositions = trajectory.getPositionsReverse(new Step(12)).collect(Collectors.toList()); diff --git a/VadereSimulator/src/org/vadere/simulator/control/OfflineSimulation.java b/VadereSimulator/src/org/vadere/simulator/control/OfflineSimulation.java index 29f5d935da99a820a35dcc33dfa8cc8e90be3ce8..ca24ad1d34153d161370168380dc6f293228df30 100644 --- a/VadereSimulator/src/org/vadere/simulator/control/OfflineSimulation.java +++ b/VadereSimulator/src/org/vadere/simulator/control/OfflineSimulation.java @@ -38,7 +38,7 @@ public class OfflineSimulation { .flatMap(entry -> entry.getValue().stream()) .map(ped -> ped.getId()) .distinct() - .map(id -> new Trajectory(pedestriansByStep, id)) + .map(id -> new Trajectory(pedestriansByStep, id, vadere.getAttributesSimulation().getSimTimeStepLength())) .collect(Collectors.toMap(t -> t.getPedestrianId(), t -> t)); topographyController.prepareTopography(); @@ -51,11 +51,11 @@ public class OfflineSimulation { topography.reset(); // add pedestrians to the topography trajectories.values().stream() - .filter(t -> t.isPedestrianAlive(step)) + .filter(t -> t.isAlive(step)) .map(t -> t.getAgent(step)) .filter(opt -> opt.isPresent()).forEach(opt -> topography.addElement(opt.get())); return new SimulationState(vadere.getName(), topography, vadere.getScenarioStore(), - step.getSimTimeInSec().orElse(Double.NaN), step.getStepNumber(), null); + (step.getStepNumber()-1) * vadere.getAttributesSimulation().getSimTimeStepLength(), step.getStepNumber(), null); } private void prepareOutput() { diff --git a/VadereSimulator/src/org/vadere/simulator/projects/io/TrajectoryReader.java b/VadereSimulator/src/org/vadere/simulator/projects/io/TrajectoryReader.java index 1f001eb9b2952c6a0785c44c35bffc6ae9bab366..f74fa8cd856bc505bbb61e5ec0f6cd9081005775 100644 --- a/VadereSimulator/src/org/vadere/simulator/projects/io/TrajectoryReader.java +++ b/VadereSimulator/src/org/vadere/simulator/projects/io/TrajectoryReader.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.vadere.simulator.projects.Scenario; import org.vadere.simulator.projects.dataprocessing.outputfile.OutputFile; import org.vadere.simulator.projects.dataprocessing.processor.PedestrianPositionProcessor; +import org.vadere.state.attributes.AttributesSimulation; import org.vadere.state.attributes.scenario.AttributesAgent; import org.vadere.state.behavior.SalientBehavior; import org.vadere.state.events.types.Event; @@ -75,18 +76,19 @@ public class TrajectoryReader { private int stridesIndex; private int mostImportantEventIndex; private int salientBehaviorIndex; + private double simTimeStepLength; private static final int NOT_SET_COLUMN_INDEX_IDENTIFIER = -1; public TrajectoryReader(final Path trajectoryFilePath, final Scenario scenario) { - this(trajectoryFilePath, scenario.getAttributesPedestrian()); + this(trajectoryFilePath, scenario.getAttributesPedestrian(), scenario.getAttributesSimulation().getSimTimeStepLength()); } public TrajectoryReader(final Path trajectoryFilePath) { - this(trajectoryFilePath, new AttributesAgent()); + this(trajectoryFilePath, new AttributesAgent(), new AttributesSimulation().getSimTimeStepLength()); } - private TrajectoryReader(final Path trajectoryFilePath, final AttributesAgent attributesAgent) { + private TrajectoryReader(final Path trajectoryFilePath, final AttributesAgent attributesAgent, final double simTimeStepLength) { this.trajectoryFilePath = trajectoryFilePath; this.attributesPedestrian = attributesAgent; pedestrianIdKeys = new HashSet<>(); @@ -129,6 +131,7 @@ public class TrajectoryReader { stridesIndex = NOT_SET_COLUMN_INDEX_IDENTIFIER; mostImportantEventIndex = NOT_SET_COLUMN_INDEX_IDENTIFIER; salientBehaviorIndex = NOT_SET_COLUMN_INDEX_IDENTIFIER; + this.simTimeStepLength = simTimeStepLength; } public Map> readFile() throws IOException { @@ -299,6 +302,6 @@ public class TrajectoryReader { ped.setSalientBehavior(salientBehavior); } - return simTimeIndex == NOT_SET_COLUMN_INDEX_IDENTIFIER ? Pair.create(new Step(step), ped) : Pair.create(new Step(step, simTime), ped); + return Pair.create(new Step(step), ped); } } diff --git a/VadereState/src/org/vadere/state/scenario/Agent.java b/VadereState/src/org/vadere/state/scenario/Agent.java index acc17b15fc09e3bb80ce6ce09be27c891e88d87b..5b505ad493140df1ac4ab4781159d3826d7eb9aa 100644 --- a/VadereState/src/org/vadere/state/scenario/Agent.java +++ b/VadereState/src/org/vadere/state/scenario/Agent.java @@ -247,4 +247,5 @@ public abstract class Agent extends DynamicElement { this.freeFlowSpeed = freeFlowSpeed; } + public abstract Agent clone(); } diff --git a/VadereState/src/org/vadere/state/simulation/Step.java b/VadereState/src/org/vadere/state/simulation/Step.java index 33c5837a1f69dc15b85736dc8bd0bd72e5523f6e..27ee354346a1a28e080267e92a4de92896401871 100644 --- a/VadereState/src/org/vadere/state/simulation/Step.java +++ b/VadereState/src/org/vadere/state/simulation/Step.java @@ -1,44 +1,106 @@ package org.vadere.state.simulation; -import java.util.Optional; /** - * Java Bean that store the stepNumber and the simulation time in seconds of a specific time step. + * Immutable class. Java Bean that store the stepNumber and the simulation time in seconds of a specific time step. + * + * @author Benedikt Zoennchen * */ public class Step implements Comparable { - private final Integer stepNumber; - private final Double simTimeInSec; + private final int stepNumber; + private final static int MINIMAL_STEP = 1; + private final static Step FIST_STEP = new Step(1); + private final static double MIN_TOLERANCE = 0.001; + private final static double MAX_TOLERANCE = 0.999; public Step(final int stepNumber) { this.stepNumber = stepNumber; - this.simTimeInSec = null; } - public Step(final int stepNumber, final double simTimeInSec) { - this.stepNumber = stepNumber; - this.simTimeInSec = simTimeInSec; + /** + * The number of this step. The smallest step number is 1. + * + * @return number of this step + */ + public int getStepNumber() { + return stepNumber; } /** - * Returns an Optional since the simulation time in seconds may not stored. - * - * @return an Optional + * Securely increments the step. + * + * @return the incremented step */ - public Optional getSimTimeInSec() { - if (simTimeInSec == null) { - return Optional.empty(); + public Step increment() { + return new Step(stepNumber + 1); + } + + /** + * Securely substracts the step from this. + * + * @param step a step + * @return the subtraction result or the minimal step if the result would generate a step smaller than the minimal step. + */ + public Step subtract(final Step step) { + int diff = stepNumber - step.getStepNumber(); + + if (diff >= MINIMAL_STEP) { + return new Step(diff); + } else { + return FIST_STEP; } - return Optional.of(simTimeInSec); } /** - * The number of this step. The smallest step number is 1. - * - * @return number of this step + * Securely decrements the step by 1. + * + * @return the decremented step or this step (if this step is the minimal step). */ - public int getStepNumber() { - return stepNumber; + public Step decrement() { + return new Step(stepNumber - 1); + } + + public boolean isGreaterThan(final Step step) { + return compareTo(step) > 0; + } + + public boolean isGreaterEqThan(final Step step) { + return compareTo(step) >= 0; + } + + public boolean isSmallerThan(final Step step) { + return compareTo(step) < 0; + } + + public boolean isSmallerEqThan(final Step step) { + return compareTo(step) <= 0; + } + + public static Step toFloorStep(final double simTimeInSec, final double simStepLengthInSec) { + Step base = new Step((int) (simTimeInSec / simStepLengthInSec)); + double r = simTimeInSec - toSimTimeInSec(base, simStepLengthInSec); + + if(r / simStepLengthInSec > MAX_TOLERANCE) { + return base.increment(); + } else{ + return base; + } + } + + public static Step toCeilStep(final double simTimeInSec, final double simStepLengthInSec) { + Step base = new Step((int) (simTimeInSec / simStepLengthInSec)); + double r = simTimeInSec - toSimTimeInSec(base, simStepLengthInSec); + + if(r / simStepLengthInSec < MIN_TOLERANCE) { + return base; + } else { + return base.increment(); + } + } + + public static double toSimTimeInSec(final Step step, final double simStepLengthInSec) { + return step.getStepNumber() * simStepLengthInSec; } @Override @@ -50,12 +112,12 @@ public class Step implements Comparable { return false; } Step other = (Step) obj; - return stepNumber.equals(other.stepNumber); + return stepNumber == other.stepNumber; } @Override public String toString() { - return "(" + stepNumber + ", " + simTimeInSec + ")"; + return "(" + stepNumber + ")"; } @Override diff --git a/VadereState/src/org/vadere/state/simulation/Trajectory.java b/VadereState/src/org/vadere/state/simulation/Trajectory.java index 4057e2a4158aeebfc5b2e1eabd912e8c0038c5ae..7a90b033ac6a5f25f9bb6172bcb4871abce80a27 100644 --- a/VadereState/src/org/vadere/state/simulation/Trajectory.java +++ b/VadereState/src/org/vadere/state/simulation/Trajectory.java @@ -1,6 +1,5 @@ package org.vadere.state.simulation; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,89 +19,115 @@ import org.vadere.util.geometry.shapes.VPoint; * {@link org.vadere.state.scenario.Pedestrian} objects has * the same id but the state of a pedestrian changes over time. * + * @author Benedikt Zoennchen + * */ public class Trajectory { private final Map trajectoryPoints; - private Comparator stepReverseComparator = (s1, s2) -> -s1.compareTo(s2); - private Optional firstStep; private Optional lastStep; private int pedestrianId; - public Trajectory(final int pedestrianId) { + private double simStepLengthInSec; + + /** + * Constructs an empty {@link Trajectory} for a pedestrian defined by pedestrianId. + * + * @param pedestrianId + */ + public Trajectory(final int pedestrianId, final double simStepLengthInSec) { this.pedestrianId = pedestrianId; this.trajectoryPoints = new HashMap<>(); this.firstStep = Optional.empty(); this.lastStep = Optional.empty(); + this.simStepLengthInSec = simStepLengthInSec; } - public Trajectory(final Map> pedestrianByStep, final int pedestrianId) { + /** + * Constructs a {@link Trajectory} for a pedestrian defined by pedestrianId + * by extracting the required trajectory points from pedestrianByStep. + * + * Note that calling this constructor for each pedestrian (id) is rather expensive, + * since the complete {@link Map} pedestrianByStep has to be iterated for each + * pedestrian. A better way is to construct all {@link Trajectory} objects at once. + * + * @param pedestrianByStep container for a set of trajectories + * @param pedestrianId a specific and unique pedestrian id + * @param simStepLengthInSec step length of one time step in seconds + */ + public Trajectory(final Map> pedestrianByStep, final int pedestrianId, final double simStepLengthInSec) { this.pedestrianId = pedestrianId; + this.simStepLengthInSec = simStepLengthInSec; // create for each step that contains an pedestrian with the specific pedestrianId - trajectoryPoints = pedestrianByStep.entrySet().stream() - .filter(entry -> entry.getValue().stream().map(ped -> ped.getId()) - .anyMatch(pedId -> pedId == pedestrianId)) - .collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().stream().filter(ped -> ped.getId() == pedestrianId).findAny().get())); - - firstStep = pedestrianByStep.keySet().stream().filter(step -> trajectoryPoints.containsKey(step)) - .min(Step::compareTo); - lastStep = pedestrianByStep.keySet().stream().filter(step -> trajectoryPoints.containsKey(step)) - .max(Step::compareTo); - - if (firstStep.isPresent() && lastStep.isPresent()) { - // fill in missing steps by taking the pedestrian of the nearest step smaller than the - // missing one. - Stream.iterate(firstStep.get(), s -> new Step(s.getStepNumber() + 1)) - .limit(lastStep.get().getStepNumber() - firstStep.get().getStepNumber()) - .filter(s -> !trajectoryPoints.containsKey(s)).forEachOrdered( - s -> trajectoryPoints.put(s, trajectoryPoints.get(new Step(s.getStepNumber() - 1)))); - } + this.trajectoryPoints = pedestrianByStep.entrySet().stream() + .filter(entry -> containsAgent(entry.getValue())) + .collect(Collectors.toMap(e -> e.getKey(), e -> findAnyAgent(e.getValue()))); + this.firstStep = pedestrianByStep.keySet().stream().filter(step -> trajectoryPoints.containsKey(step)).min(Step::compareTo); + this.lastStep = pedestrianByStep.keySet().stream().filter(step -> trajectoryPoints.containsKey(step)).max(Step::compareTo); - if (trajectoryPoints == null || trajectoryPoints.isEmpty()) { - throw new IllegalArgumentException("empty trajectory map is not allowed"); + if (!isEmpty()) { + fill(); } } - public void fill() { - if(!trajectoryPoints.isEmpty()) { - for(Step step : trajectoryPoints.keySet()) { - if(!firstStep.isPresent() || firstStep.get().getStepNumber() > step.getStepNumber()) { - firstStep = Optional.of(step); - } - - if(!lastStep.isPresent() || lastStep.get().getStepNumber() < step.getStepNumber()) { - lastStep = Optional.of(step); - } - } + private boolean containsAgent(final List agents) { + return agents.stream().anyMatch(agent -> agent.getId() == pedestrianId); + } - int start = firstStep.get().getStepNumber(); - int end = lastStep.get().getStepNumber(); + private Agent findAnyAgent(final List agents) { + return agents.stream().filter(agent -> agent.getId() == pedestrianId).findAny().get(); + } - for(int i = start+1; i < end; i++) { - Step currentStep = new Step(i); - if(!trajectoryPoints.containsKey(currentStep)) { - trajectoryPoints.put(currentStep, trajectoryPoints.get(new Step(currentStep.getStepNumber()-1))); - } - } - } + private Step last() { + return lastStep.get(); + } + + private Step first() { + return firstStep.get(); + } + + private boolean hasFirstStep() { + return firstStep.isPresent(); + } + + private boolean hasLastStep() { + return lastStep.isPresent(); + } + + private boolean isMissing(final Step step) { + return !contains(step); + } + + private boolean contains(final Step step) { + return trajectoryPoints.containsKey(step); + } + /** + * Fills in missing positions e.g. if there is are positions for steps 1, 2, 4, 7, 11 after fill + * there will be positions for steps 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 where position 3 = 2, 5 = 6 = 4, + * 8 = 9 = 10 = 7. + */ + public void fill() { + Stream.iterate(first(), s -> s.isSmallerEqThan(last()), s -> s.increment()) + .filter(s -> isMissing(s)) + .forEachOrdered(s -> addStep(s, getAgent(s.decrement()).get())); } public void addStep(final Step step, @NotNull final Agent agent) { - if(!firstStep.isPresent() || firstStep.get().getStepNumber() > step.getStepNumber()) { + if(!hasFirstStep() || first().getStepNumber() > step.getStepNumber()) { firstStep = Optional.of(step); } - if(!lastStep.isPresent() || lastStep.get().getStepNumber() < step.getStepNumber()) { - firstStep = Optional.of(step); + if(!hasLastStep() || last().getStepNumber() < step.getStepNumber()) { + lastStep = Optional.of(step); } + trajectoryPoints.put(step, agent); } @@ -129,8 +154,12 @@ public class Trajectory { * @param step the time step * @return true if the pedestrian is alive at the specific time step */ - public boolean isPedestrianAlive(final Step step) { - return trajectoryPoints.containsKey(step); + public boolean isAlive(final Step step) { + return contains(step); + } + + public boolean isAlive(final double simTimeInSec) { + return isAlive(Step.toCeilStep(simTimeInSec, simStepLengthInSec)); } /** @@ -139,9 +168,13 @@ public class Trajectory { * @param step the time step * @return true if the pedestrian appeared, otherwise false */ - public boolean isPedestrianAppeared(final Step step) { - return trajectoryPoints.containsKey(step) || firstStep.isPresent() && firstStep.get().compareTo(step) <= 0; + public boolean hasAppeared(final Step step) { + return contains(step) || hasFirstStep() && first().isGreaterEqThan(step); + } + public boolean hasAppeared(final double simTimeInSec) { + Step base = Step.toFloorStep(simTimeInSec,simStepLengthInSec); + return contains(base) || hasFirstStep() && first().isGreaterEqThan(base); } /** @@ -150,10 +183,17 @@ public class Trajectory { * @param step the time step * @return true if the pedestrian disappeared, otherwise false */ - public boolean isPedestrianDisappeared(final Step step) { - return !trajectoryPoints.containsKey(step) && (!lastStep.isPresent() || lastStep.get().compareTo(step) <= 0); + public boolean hasDisappeared(final Step step) { + return isMissing(step) && (!hasLastStep() || last().isGreaterThan(step)); } + public boolean hasDisappeared(final double simTimeInSec) { + return hasDisappeared(Step.toCeilStep(simTimeInSec, simStepLengthInSec)); + } + + private boolean isEmpty() { + return !getStartStep().isPresent() || !getEndStep().isPresent(); + } /** * Returns an Optional object. If the pedestrain has not appeared at step, the @@ -165,13 +205,13 @@ public class Trajectory { * @return an Optional object which is empty if the trajectory is completely empty. */ public Optional getAgent(final Step step) { - if (getStartStep().isPresent() && getEndStep().isPresent()) { - if (isPedestrianAlive(step)) { + if (!isEmpty()) { + if (isAlive(step)) { return Optional.of(trajectoryPoints.get(step)); - } else if (step.compareTo(getStartStep().get()) <= 0) { - return Optional.of(trajectoryPoints.get(getStartStep().get())); - } else if (step.compareTo(getEndStep().get()) >= 0) { - return Optional.of(trajectoryPoints.get(getEndStep().get())); + } else if (step.isSmallerEqThan(first())) { + return Optional.of(trajectoryPoints.get(first())); + } else if (step.isGreaterEqThan(last())) { + return Optional.of(trajectoryPoints.get(last())); } else { return Optional.empty(); } @@ -179,6 +219,31 @@ public class Trajectory { return Optional.empty(); } + public Optional getAgent(final double simTimeInSec) { + if(!isEmpty()) { + Step base = Step.toFloorStep(simTimeInSec, simStepLengthInSec); + Step next = Step.toCeilStep(simTimeInSec, simStepLengthInSec); + + if(base.equals(next) || base.equals(last())) { + return getAgent(base); + } else { + double r = simTimeInSec - Step.toSimTimeInSec(base, simStepLengthInSec); + Optional optionalAgent1 = getAgent(base); + Optional optionalAgent2 = getAgent(base.increment()); + Agent agent1 = optionalAgent1.get(); + Agent agent2 = optionalAgent2.get(); + VPoint position1 = agent1.getPosition(); + VPoint position2 = agent2.getPosition(); + VPoint position = position1.add(position2.subtract(position1).scalarMultiply(r / simStepLengthInSec)); + Agent agent = agent1.clone(); + agent.setPosition(position); + return Optional.of(agent); + } + } else { + return Optional.empty(); + } + } + /** * Return a {@link java.util.stream.Stream<>} stream of * {@link org.vadere.util.geometry.shapes.VPoint} pedestrian positions @@ -188,12 +253,28 @@ public class Trajectory { * @return a stream of pedestrian positions to from 1 to step.getStepNumber() in reverse order */ public Stream getPositionsReverse(final Step step) { - return Stream.iterate(step, s -> new Step(s.getStepNumber() - 1)) - .limit(step.getStepNumber()) + return getPositionsReverse(Step.toSimTimeInSec(step, simStepLengthInSec)); + } + + public Stream getPositionsReverse(final double simTimeInSec) { + Step tail = Step.toFloorStep(simTimeInSec, simStepLengthInSec); + Step head = Step.toCeilStep(simTimeInSec, simStepLengthInSec); + + Stream headStream = Stream.empty(); + if(!tail.equals(head)) { + Optional optionalAgent = getAgent(simTimeInSec); + if(optionalAgent.isPresent()) { + headStream = Stream.of(optionalAgent.get().getPosition()); + } + } + + Stream tailStream = Stream.iterate(tail, s -> s.isGreaterEqThan(first()), s -> s.decrement()) .map(s -> getAgent(s)) .filter(optAgent -> optAgent.isPresent()) .map(optAgent -> optAgent.get()) .flatMap(agent -> toPointStream(agent)); + + return Stream.concat(headStream, tailStream); } private Stream toPointStream(@NotNull final Agent agent) {