Browse Source

Implement SurfaceView renderer

Add frameskip SeekBar
tags/Release_1
Loch Christian (uib05376) 3 years ago
parent
commit
771371ed06
6 changed files with 232 additions and 119 deletions
  1. +27
    -23
      app/src/main/java/de/hems/trafficsim/MainActivity.java
  2. +115
    -0
      app/src/main/java/de/hems/trafficsim/Renderer.java
  3. +7
    -77
      app/src/main/java/de/hems/trafficsim/TimeRecordView.java
  4. +3
    -2
      app/src/main/java/de/hems/trafficsim/Track.java
  5. +31
    -2
      app/src/main/java/de/hems/trafficsim/Worker.java
  6. +49
    -15
      app/src/main/res/layout/activity_main.xml

+ 27
- 23
app/src/main/java/de/hems/trafficsim/MainActivity.java View File

@@ -12,16 +12,18 @@ import android.widget.TextView;
import java.util.Observable;
import java.util.Observer;

public class MainActivity extends AppCompatActivity implements Observer, SeekBar.OnSeekBarChangeListener {
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
public static final int defaultNoOfVehicles = 25;
public static final int defaultTrackLength = 100;
public static final float defaultBrakeProb = 0.3f;
public static final float defaultMaxVelocity = 5.0f;
public static final int defaultDelay = 0;
public static final int defaultHistoryLength = 50;
public static final int defaultFrameskip = 0;
protected Track track;
protected TimeRecordView trackView;
protected Worker worker;
protected Renderer renderer;
protected LinearLayout viewStack;

@Override
@@ -30,10 +32,11 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
setContentView(R.layout.activity_main);
this.track = new Track(defaultNoOfVehicles, defaultTrackLength, defaultBrakeProb,
defaultMaxVelocity, defaultDelay, defaultHistoryLength);
this.track.addObserver(this);
this.viewStack = (LinearLayout) findViewById(R.id.trackViewStack);
this.trackView = new TimeRecordView(this, track, defaultHistoryLength);
this.trackView = new TimeRecordView(this, track);

viewStack.addView(this.trackView);
this.renderer = new Renderer(track, this.trackView.getHolder());

SeekBar trackLengthSeekBar = (SeekBar) findViewById(R.id.trackLengthSeekBar);
trackLengthSeekBar.setOnSeekBarChangeListener(this);
@@ -58,8 +61,14 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
SeekBar delaySeekBar = (SeekBar)(findViewById(R.id.simDelaySeekBar));
delaySeekBar.setOnSeekBarChangeListener(this);
delaySeekBar.setProgress(defaultDelay);
((TextView)(findViewById(R.id.simDelayTextView))).setText(String.valueOf(defaultDelay));

SeekBar frameskipSeekBar = (SeekBar)(findViewById(R.id.frameskipSeekBar));
frameskipSeekBar.setOnSeekBarChangeListener(this);
frameskipSeekBar.setProgress(defaultFrameskip);
((TextView)(findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip));

this.update(this.track, null);
this.updateStats();
}

public static float round(float number, int digits) {
@@ -67,37 +76,27 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
return Math.round(number*div)/div;
}

@Override
public void update(Observable observable, Object o) {
public void updateStats() {
final Track trackRef = this.track;
final LinearLayout viewStackRef = this.viewStack;
final MainActivity mainActivity = this;
final TextView lastAvgView = (TextView) findViewById(R.id.avgVeloLastView);
final TextView overallAvgView = (TextView) findViewById(R.id.avgVeloOverallView);
final TextView delayedAvgView = (TextView) findViewById(R.id.delayedAvgTextView);
final TextView stepsView = (TextView) findViewById(R.id.stepsTextView);
final TimeRecordView view = this.trackView;
runOnUiThread(new Runnable() {
@Override
public void run() {

//TimeRecordView newTrView = new TimeRecordView(mainActivity, trackRef);
trackView.invalidate();
//if (viewStackRef.getChildCount() > 0)
// viewStackRef.removeViewAt(0);
//viewStackRef.addView(newTrView);
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)));


}
});
}

public void onStepButtonClick(View view) {
this.track.timeElapse();
this.updateStats();
this.renderer.draw();
}

public void onPlayButtonClick(View view) {
@@ -110,7 +109,8 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
Button clearButton = (Button) findViewById(R.id.clearButton);
clearButton.setEnabled(false);

this.worker = new Worker(track);
int frameskip = ((SeekBar)(findViewById(R.id.frameskipSeekBar))).getProgress();
this.worker = new Worker(track, this, renderer, frameskip);
this.worker.start();
}

@@ -143,7 +143,6 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
}

public void onClearButtonClick(View view) {
this.track.deleteObserver(this);
if (this.worker != null) { // There was a simulation running
this.stopWorker();
}
@@ -154,7 +153,8 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
Button stopButton = (Button) findViewById(R.id.stopButton);
stopButton.setEnabled(false);
this.updateTrack();
this.update(this.track, null);
this.updateStats();
this.renderer.draw();

}

@@ -178,13 +178,13 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
float newBrakeProb = (float)brakeProbabilitySeekBar.getProgress() / (float)brakeProbabilitySeekBar.getMax();

int newDelay = ((SeekBar)(findViewById(R.id.simDelaySeekBar))).getProgress();
int newFrameskip = ((SeekBar)(findViewById(R.id.frameskipSeekBar))).getProgress();

this.track = new Track(newNoOfVehicles, newTrackLength, newBrakeProb, newMaxVelocity, newDelay, defaultHistoryLength);
this.trackView.setTrack(this.track);
this.track.addObserver(this);
this.renderer.setTrack(this.track);
if (this.worker != null) { // There was a simulation running already
this.stopWorker();
this.worker = new Worker(track);
this.worker = new Worker(track, this, renderer, newFrameskip);
this.worker.start();
}
}
@@ -208,6 +208,10 @@ public class MainActivity extends AppCompatActivity implements Observer, SeekBar
TextView tv = (TextView)(findViewById(R.id.simDelayTextView));
tv.setText(String.valueOf(seekBar.getProgress()));
this.track.setWaitTime(seekBar.getProgress());
} else if (seekBar == (SeekBar)(findViewById(R.id.frameskipSeekBar))) {
TextView tv = (TextView)(findViewById(R.id.frameSkipTextView));
tv.setText(String.valueOf(seekBar.getProgress()));
this.worker.setFrameskip(seekBar.getProgress());
}
}



+ 115
- 0
app/src/main/java/de/hems/trafficsim/Renderer.java View File

@@ -0,0 +1,115 @@
package de.hems.trafficsim;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;

import java.util.ConcurrentModificationException;
import java.util.List;

public class Renderer {
private List<VehicleTimeRecord> stepRecords;
protected Track track;
protected int pixelPerVehicle;
protected int pixelPerLine;
protected float tooShortPerTrackLength;
protected float tooShortPerHeight;
protected Paint paint;
protected SurfaceHolder holder;
protected Canvas canvas;
protected int width;
protected int height;

public Renderer(Track track, SurfaceHolder holder) {
this.track = track;
this.holder = holder;
this.paint = new Paint();
}

public void setSize(int width, int height) {
if (width > 0 && height > 0) {
this.pixelPerVehicle = width / (int) this.track.getTrackLength();
this.tooShortPerTrackLength =
(width - this.pixelPerVehicle * this.track.getTrackLength())
/ this.track.getTrackLength();
this.pixelPerLine = height / this.track.getHistoryLength();
this.tooShortPerHeight = (height - this.pixelPerLine * this.track.getHistoryLength())
/ (float)height;
System.out.println("Viewport: "+width+"x"+height);
this.width = width;
this.height = height;
}
}

public void setTrack(Track track) {
this.track = track;
this.setSize(width, height);
}

protected int getColor(float curVelocity, float maxVelocity) {
float perc = curVelocity / maxVelocity;
perc = 1 - perc;
if (perc <= 0.5) {
int red = ((int) (2*perc*0xFF)) << 16;
int green = 0xFF << 8;
int blue = 0;
return 0xff000000 | red | green | blue;
} else {
int red = 0xFF << 16;
int green = ((int)(0xFF-0xFF*(perc-0.5)*2)) << 8;
int blue = 0;
return 0xff000000 | red | green | blue;
}
}

protected void draw() {
try {
if (this.pixelPerVehicle == 0) {
Rect rect = this.holder.getSurfaceFrame();
this.setSize(rect.right, rect.bottom);
}
canvas = this.holder.lockCanvas();
synchronized (holder) {
int y = 0;
int left = 0;
int compensate = 0;
float vCompensate = 0;
canvas.drawColor(Color.BLACK);
for (int curStepIdx = this.track.getVtrList().size()-1;curStepIdx >= 0;
curStepIdx--) {
try {
try {
track.getListSemaphore().acquire();
} catch (InterruptedException ex) {
return;
}
vCompensate = Math.round(y * this.tooShortPerHeight);
stepRecords = this.track.getVtrList().get(curStepIdx);
int i = 0;
for (VehicleTimeRecord r : stepRecords) {
left = (int) (this.pixelPerVehicle * r.getPosition());
compensate = Math.round(r.getPosition() * this.tooShortPerTrackLength);

this.paint.setColor(getColor(r.getVelocity(), r.getMaxVelocity()));
canvas.drawRect(left + compensate, y+vCompensate,
left + this.pixelPerVehicle + compensate,
y + pixelPerLine + vCompensate, this.paint);
i++;
}
track.getListSemaphore().release();
y += pixelPerLine;
} catch (ConcurrentModificationException ex) {
System.out.println("Concurrent Exception occured, skipping record");
y += pixelPerLine;
}
}
}
} finally {
if (canvas != null) {
this.holder.unlockCanvasAndPost(canvas);
}
}
}
}

+ 7
- 77
app/src/main/java/de/hems/trafficsim/TimeRecordView.java View File

@@ -1,96 +1,26 @@
package de.hems.trafficsim;

import android.app.ActionBar;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.Layout;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;

import java.util.ConcurrentModificationException;
import java.util.List;

public class TimeRecordView extends View {
protected Paint paint;
protected Track track;
protected int pixelPerVehicle;
protected float tooShortPerTrackLength;
protected int historyLength;

public TimeRecordView(Context context, Track track, int historyLength) {
public class TimeRecordView extends SurfaceView {
public TimeRecordView(Context context, Track track) {
super(context);
this.track = track;
this.paint = new Paint();
this.historyLength = historyLength;
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
this.setBackgroundColor(Color.BLACK);
this.pixelPerVehicle = (int) (this.getWidth() / this.track.getTrackLength());
this.tooShortPerTrackLength = (this.getWidth() - this.pixelPerVehicle*this.track.getTrackLength())/this.track.getTrackLength();
}

public void setTrack(Track track) {
this.track = track;
}

protected int getColor(float curVelocity, float maxVelocity) {
float perc = curVelocity / maxVelocity;
perc = 1 - perc;
if (perc <= 0.5) {
int red = ((int) (2*perc*0xFF)) << 16;
int green = ((int)0xFF) << 8;
int blue = 0;
return 0xff000000 | red | green | blue;
} else {
int red = ((int)(0xFF)) << 16;
int green = ((int)(0xFF-0xFF*(perc-0.5)*2)) << 8;
int blue = 0;
return 0xff000000 | red | green | blue;
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, 10*50);
setMeasuredDimension(widthMeasureSpec, 10*50);
this.pixelPerVehicle = (int) (this.getWidth() / this.track.getTrackLength());
this.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.pixelPerVehicle = (int) (this.getWidth() / this.track.getTrackLength());
this.tooShortPerTrackLength = (this.getWidth() - this.pixelPerVehicle*this.track.getTrackLength())/this.track.getTrackLength();
}

@Override
protected void onDraw(Canvas canvas) {
int y = 0;
List<List<VehicleTimeRecord>> stepList = this.track.getVtrList();
for (int curStepIdx = this.historyLength-1;
curStepIdx >= 0 && stepList.size() >= curStepIdx;
curStepIdx--) {
try {
try {
track.getListSemaphore().acquire();
} catch (InterruptedException ex) { return; }
List<VehicleTimeRecord> step = stepList.get(curStepIdx);
int i = 0;
for (VehicleTimeRecord r : step) {
int left = (int) (this.pixelPerVehicle * r.getPosition());
int compensate = Math.round(r.getPosition()*this.tooShortPerTrackLength);
this.paint.setColor(getColor(r.getVelocity(), r.getMaxVelocity()));
canvas.drawRect(left+compensate, y,
left + this.pixelPerVehicle - 1+compensate,
y + 10, this.paint);
i++;
}
track.getListSemaphore().release();
y += 10;
} catch (ConcurrentModificationException ex) {
System.out.println("Concurrent Exception occured, skipping record");
y += 10;
continue;
}
}
}
}

+ 3
- 2
app/src/main/java/de/hems/trafficsim/Track.java View File

@@ -39,6 +39,7 @@ public class Track extends Observable {
}
public long getSteps() { return steps; }
public Semaphore getListSemaphore() { return listSemaphore; }
public int getHistoryLength() { return historyLength; }

public Track(int numberVehicles, float trackLength, float brakeProb, float maxVelocity, int waitTime, int historyLength) {
this.trackLength = trackLength;
@@ -117,9 +118,9 @@ public class Track extends Observable {

update_avg();
this.setChanged();
this.notifyObservers();
//this.notifyObservers();
this.clearChanged();
LockSupport.parkNanos(1);
//LockSupport.parkNanos(1);
try {
Thread.sleep(waitTime);
} catch (InterruptedException ex) { }


+ 31
- 2
app/src/main/java/de/hems/trafficsim/Worker.java View File

@@ -1,22 +1,51 @@
package de.hems.trafficsim;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

import java.util.ConcurrentModificationException;

public class Worker extends Thread {
protected Track track;
protected boolean stop;
public Worker(Track track) {
protected MainActivity gui;
protected Renderer renderer;
protected int frameskip;

public Worker(Track track, MainActivity gui, Renderer renderer, int frameskip) {
super();
this.track = track;
this.stop = false;
this.gui = gui;
this.renderer = renderer;
this.frameskip = frameskip;
}

void setStop(boolean stop) {
public void setStop(boolean stop) {
this.stop = stop;
}

public void setFrameskip(int frames) {
this.frameskip = frames;
}

public int getFrameskip() { return this.frameskip; }

@Override
public void run() {
Canvas canvas = null;
int i = 0;
while (!stop) {
this.track.timeElapse();
this.gui.updateStats();
if (i >= this.frameskip) {
this.renderer.draw();
i=0;
}
i++;
}
}

}



+ 49
- 15
app/src/main/res/layout/activity_main.xml View File

@@ -19,12 +19,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<View
android:id="@+id/divider3"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -106,7 +100,8 @@
android:id="@+id/maxVeloTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/textView5" />
app:layout_constraintEnd_toEndOf="@+id/textView5"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

<SeekBar
@@ -133,7 +128,8 @@
android:id="@+id/brakeProbTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/brakeProbLabel" />
app:layout_constraintEnd_toEndOf="@+id/brakeProbLabel"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

<SeekBar
@@ -269,13 +265,50 @@
<LinearLayout
android:id="@+id/trackViewStack"
android:layout_width="match_parent"
android:layout_height="500dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="8dp">
app:layout_constraintBottom_toTopOf="@+id/constraintLayout2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

</LinearLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/constraintLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">

<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Frameskip"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<SeekBar
android:id="@+id/frameskipSeekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="1000"
app:layout_constraintEnd_toStartOf="@id/frameSkipTextView"
app:layout_constraintStart_toEndOf="@id/textView8"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/frameSkipTextView"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:gravity="right"
android:text="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="0dp"
@@ -288,13 +321,15 @@
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delay:" />
android:text="Delay: "
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<SeekBar
android:id="@+id/simDelaySeekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="1000"
android:max="500"
android:min="0"
app:layout_constraintEnd_toStartOf="@+id/simDelayTextView"
app:layout_constraintStart_toEndOf="@+id/textView6"
@@ -317,8 +352,7 @@
android:orientation="horizontal"
android:paddingTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/playButton">
app:layout_constraintEnd_toEndOf="parent">

<Button
android:id="@+id/playButton"


Loading…
Cancel
Save