341 lines
12 KiB
Java
341 lines
12 KiB
Java
package de.hems.trafficsim;
|
|
|
|
import android.os.Bundle;
|
|
import android.view.View;
|
|
import android.widget.Button;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.SeekBar;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
/**
|
|
* Main user interface class, containing all necessary gui elements and their control flow.
|
|
*/
|
|
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
|
|
/**
|
|
* default value of the number of vehicles on the track
|
|
*/
|
|
public static final int defaultNoOfVehicles = 25;
|
|
|
|
/**
|
|
* default value of the length of the track
|
|
*/
|
|
public static final int defaultTrackLength = 100;
|
|
|
|
/**
|
|
* default value of brake probability
|
|
*/
|
|
public static final float defaultBrakeProb = 0.3f;
|
|
|
|
/**
|
|
* default value of the maximum velocity of the vehicles
|
|
*/
|
|
public static final float defaultMaxVelocity = 5.0f;
|
|
|
|
/**
|
|
* default value of the delay between two simulation steps
|
|
*/
|
|
public static final int defaultDelay = 0;
|
|
|
|
/**
|
|
* default value of the number of vehicles on the track
|
|
*/
|
|
public static final int defaultHistoryLength = 50;
|
|
|
|
/**
|
|
* default value of the number of vehicles on the track
|
|
*/
|
|
public static final int defaultFrameskip = 0;
|
|
|
|
/**
|
|
* the track to show in the activity
|
|
*/
|
|
protected Track track;
|
|
|
|
/**
|
|
* the surface view on which the renderer draws the track history
|
|
*/
|
|
protected TimeRecordView trackView;
|
|
|
|
/**
|
|
* the thread which runs the simulation and visualization
|
|
*/
|
|
protected Worker worker;
|
|
|
|
/**
|
|
* the renderer instance drawing the track history
|
|
*/
|
|
protected Renderer renderer;
|
|
|
|
/**
|
|
* the layout which keeps the surface view
|
|
*/
|
|
protected LinearLayout viewStack;
|
|
|
|
/**
|
|
* Utility function to round a float to a given amount of digits.
|
|
*
|
|
* @param number number to round
|
|
* @param digits amount of digits
|
|
* @return rounded number
|
|
*/
|
|
public static float round(float number, int digits) {
|
|
float div = (float) Math.pow(10.0f, digits);
|
|
return Math.round(number * div) / div;
|
|
}
|
|
|
|
/**
|
|
* Constructor for MainActivity
|
|
*
|
|
* @param savedInstanceState Bundle with previously saved activity state, otherwise null
|
|
*/
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.activity_main);
|
|
this.track = new Track(defaultNoOfVehicles, defaultTrackLength, defaultBrakeProb,
|
|
defaultMaxVelocity, defaultDelay, defaultHistoryLength);
|
|
this.viewStack = findViewById(R.id.trackViewStack);
|
|
this.trackView = new TimeRecordView(this, track);
|
|
|
|
viewStack.addView(this.trackView);
|
|
this.renderer = new Renderer(track, this.trackView.getHolder());
|
|
|
|
SeekBar trackLengthSeekBar = findViewById(R.id.trackLengthSeekBar);
|
|
trackLengthSeekBar.setOnSeekBarChangeListener(this);
|
|
trackLengthSeekBar.setProgress(defaultTrackLength);
|
|
((TextView)(findViewById(R.id.trackLengthTextView))).setText(String.valueOf(defaultTrackLength));
|
|
|
|
SeekBar maxVelocitySeekBar = findViewById(R.id.maxVelocitySeekBar);
|
|
maxVelocitySeekBar.setOnSeekBarChangeListener(this);
|
|
maxVelocitySeekBar.setProgress((int)defaultMaxVelocity);
|
|
((TextView)(findViewById(R.id.maxVeloTextView))).setText(String.valueOf((int)defaultMaxVelocity));
|
|
|
|
SeekBar noOfVehiclesSeekBar = findViewById(R.id.noOfVehiclesSeekBar);
|
|
noOfVehiclesSeekBar.setOnSeekBarChangeListener(this);
|
|
noOfVehiclesSeekBar.setProgress(defaultNoOfVehicles);
|
|
((TextView)(findViewById(R.id.noOfVehiclesTextView))).setText(String.valueOf(defaultNoOfVehicles));
|
|
|
|
SeekBar brakeProbabilitySeekBar = findViewById(R.id.brakeProbabilitySeekBar);
|
|
brakeProbabilitySeekBar.setOnSeekBarChangeListener(this);
|
|
brakeProbabilitySeekBar.setProgress((int)(defaultBrakeProb*20));
|
|
((TextView)(findViewById(R.id.brakeProbTextView))).setText(String.valueOf(defaultBrakeProb));
|
|
|
|
SeekBar delaySeekBar = findViewById(R.id.simDelaySeekBar);
|
|
delaySeekBar.setOnSeekBarChangeListener(this);
|
|
delaySeekBar.setProgress(defaultDelay);
|
|
((TextView) (findViewById(R.id.simDelayTextView))).setText(String.valueOf(defaultDelay));
|
|
|
|
SeekBar frameskipSeekBar = findViewById(R.id.frameskipSeekBar);
|
|
frameskipSeekBar.setOnSeekBarChangeListener(this);
|
|
frameskipSeekBar.setProgress(defaultFrameskip);
|
|
((TextView) (findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip));
|
|
|
|
this.updateStats();
|
|
}
|
|
|
|
/**
|
|
* Updates the statistics view.
|
|
*/
|
|
public void updateStats() {
|
|
final Track trackRef = this.track;
|
|
final TextView lastAvgView = findViewById(R.id.avgVeloLastView);
|
|
final TextView overallAvgView = findViewById(R.id.avgVeloOverallView);
|
|
final TextView delayedAvgView = findViewById(R.id.delayedAvgTextView);
|
|
final TextView stepsView = findViewById(R.id.stepsTextView);
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
lastAvgView.setText(String.valueOf(round(trackRef.getLastAvg(), 2)));
|
|
overallAvgView.setText(String.valueOf(round(trackRef.getOverallAvg(), 2)));
|
|
stepsView.setText(String.valueOf(trackRef.getSteps()));
|
|
delayedAvgView.setText(String.valueOf(round(trackRef.getDelayedAvg(), 2)));
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handler function for clicks on the "Step" button.
|
|
*
|
|
* @param view the view the event is generated from
|
|
*/
|
|
public void onStepButtonClick(View view) {
|
|
this.track.timeElapse();
|
|
this.updateStats();
|
|
this.renderer.draw();
|
|
}
|
|
|
|
/**
|
|
* Handler function for clicks on the "Play" button.
|
|
*
|
|
* @param view the view the event is generated from
|
|
*/
|
|
public void onPlayButtonClick(View view) {
|
|
Button playButton = findViewById(R.id.playButton);
|
|
playButton.setEnabled(false);
|
|
Button stepButton = findViewById(R.id.stepButton);
|
|
stepButton.setEnabled(false);
|
|
Button stopButton = findViewById(R.id.stopButton);
|
|
stopButton.setEnabled(true);
|
|
Button clearButton = findViewById(R.id.clearButton);
|
|
clearButton.setEnabled(false);
|
|
|
|
int frameskip = ((SeekBar) (findViewById(R.id.frameskipSeekBar))).getProgress();
|
|
this.worker = new Worker(track, this, renderer, frameskip);
|
|
this.worker.start();
|
|
}
|
|
|
|
/**
|
|
* Handler function for clicks on the "Stop" button.
|
|
*
|
|
* @param view the view the event is generated from
|
|
*/
|
|
public void onStopButtonClick(View view) {
|
|
Button playButton = findViewById(R.id.playButton);
|
|
playButton.setEnabled(true);
|
|
Button stepButton = findViewById(R.id.stepButton);
|
|
stepButton.setEnabled(true);
|
|
Button stopButton = findViewById(R.id.stopButton);
|
|
stopButton.setEnabled(false);
|
|
Button clearButton = findViewById(R.id.clearButton);
|
|
clearButton.setEnabled(true);
|
|
|
|
this.stopWorker();
|
|
this.worker = null;
|
|
}
|
|
|
|
/**
|
|
* Stops the current worker thread.
|
|
*/
|
|
protected void stopWorker() {
|
|
this.worker.setStop(true);
|
|
try {
|
|
this.worker.join();
|
|
} catch (InterruptedException ex) {
|
|
}
|
|
this.worker = null;
|
|
}
|
|
|
|
/**
|
|
* Handler function for clicks on the "Stop" button.
|
|
*
|
|
* @param view the view the event is generated from
|
|
*/
|
|
public void onClearButtonClick(View view) {
|
|
if (this.worker != null) { // There was a simulation running
|
|
this.stopWorker();
|
|
}
|
|
Button playButton = findViewById(R.id.playButton);
|
|
playButton.setEnabled(true);
|
|
Button stepButton = findViewById(R.id.stepButton);
|
|
stepButton.setEnabled(true);
|
|
Button stopButton = findViewById(R.id.stopButton);
|
|
stopButton.setEnabled(false);
|
|
this.updateTrack();
|
|
this.updateStats();
|
|
this.renderer.draw();
|
|
|
|
}
|
|
|
|
/**
|
|
* Creates a new track with the current settings. This methods restarts the simulation it
|
|
* it was running before.
|
|
*/
|
|
protected void updateTrack() {
|
|
int newTrackLength = ((SeekBar) (findViewById(R.id.trackLengthSeekBar))).getProgress();
|
|
SeekBar noOfVehiclesSeekBar = findViewById(R.id.noOfVehiclesSeekBar);
|
|
int newNoOfVehicles = noOfVehiclesSeekBar.getProgress();
|
|
if (newTrackLength < newNoOfVehicles) { // Dont allow values greater than track length!
|
|
newNoOfVehicles = newTrackLength;
|
|
noOfVehiclesSeekBar.setProgress(newNoOfVehicles);
|
|
|
|
}
|
|
TextView noOfVehiclesTextView = findViewById(R.id.noOfVehiclesTextView);
|
|
noOfVehiclesTextView.setText(String.valueOf(newNoOfVehicles));
|
|
|
|
TextView trackLengthTextView = findViewById(R.id.trackLengthTextView);
|
|
trackLengthTextView.setText(String.valueOf(newTrackLength));
|
|
|
|
float newMaxVelocity = ((SeekBar) findViewById(R.id.maxVelocitySeekBar)).getProgress();
|
|
SeekBar brakeProbabilitySeekBar = findViewById(R.id.brakeProbabilitySeekBar);
|
|
float newBrakeProb = (float) brakeProbabilitySeekBar.getProgress()
|
|
/ (float)brakeProbabilitySeekBar.getMax();
|
|
|
|
int newDelay = ((SeekBar)(findViewById(R.id.simDelaySeekBar))).getProgress();
|
|
int newFrameskip = ((SeekBar)(findViewById(R.id.frameskipSeekBar))).getProgress();
|
|
|
|
boolean wasRunning = this.worker != null;
|
|
if (wasRunning) {
|
|
this.worker.setStop(true);
|
|
try {
|
|
this.worker.join();
|
|
} catch (InterruptedException ex) {
|
|
}
|
|
}
|
|
|
|
this.track = new Track(newNoOfVehicles, newTrackLength, newBrakeProb, newMaxVelocity,
|
|
newDelay, defaultHistoryLength);
|
|
this.renderer.setTrack(this.track);
|
|
|
|
if (wasRunning) {
|
|
this.worker = new Worker(track, this, renderer, newFrameskip);
|
|
this.worker.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handler function for changes on the seek bars.
|
|
*
|
|
* @param seekBar the seek bar changed
|
|
* @param progress the new progress value of the seek bar
|
|
* @param fromUser flag if the event is the result of an user action
|
|
*/
|
|
@Override
|
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
if (seekBar == findViewById(R.id.noOfVehiclesSeekBar)) {
|
|
this.updateTrack();
|
|
} else if (seekBar == findViewById(R.id.brakeProbabilitySeekBar)) {
|
|
float newBrakeProb = (float) seekBar.getProgress() / (float) seekBar.getMax();
|
|
this.track.setBrakeProb(newBrakeProb);
|
|
TextView newBrakeProbTextView = findViewById(R.id.brakeProbTextView);
|
|
newBrakeProbTextView.setText(String.valueOf((newBrakeProb)));
|
|
} else if (seekBar == findViewById(R.id.maxVelocitySeekBar)) {
|
|
this.track.setMaxVelocity(seekBar.getProgress());
|
|
TextView tv = findViewById(R.id.maxVeloTextView);
|
|
tv.setText(String.valueOf(progress));
|
|
} else if (seekBar == findViewById(R.id.trackLengthSeekBar)) {
|
|
this.updateTrack();
|
|
} else if (seekBar == findViewById(R.id.simDelaySeekBar)) {
|
|
TextView tv = findViewById(R.id.simDelayTextView);
|
|
tv.setText(String.valueOf(seekBar.getProgress()));
|
|
this.track.setWaitTime(seekBar.getProgress());
|
|
} else if (seekBar == findViewById(R.id.frameskipSeekBar)) {
|
|
TextView tv = findViewById(R.id.frameSkipTextView);
|
|
tv.setText(String.valueOf(seekBar.getProgress()));
|
|
if (this.worker != null) {
|
|
this.worker.setFrameskip(seekBar.getProgress());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handler function for beginning touch events on a seek bar.
|
|
*
|
|
* @param seekBar the seek bar touched
|
|
*/
|
|
@Override
|
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
|
|
}
|
|
|
|
/**
|
|
* Handler function for ending touch events on a seek bar.
|
|
*
|
|
* @param seekBar the seek bar touched
|
|
*/
|
|
@Override
|
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
|
|
}
|
|
} |