From 771371ed06bbcafdfb13d4f90b5dad3c349ae3c2 Mon Sep 17 00:00:00 2001 From: "Loch Christian (uib05376)" Date: Sat, 14 Nov 2020 21:36:41 +0100 Subject: [PATCH] Implement SurfaceView renderer Add frameskip SeekBar --- .../java/de/hems/trafficsim/MainActivity.java | 50 ++++---- .../java/de/hems/trafficsim/Renderer.java | 115 ++++++++++++++++++ .../de/hems/trafficsim/TimeRecordView.java | 84 ++----------- .../main/java/de/hems/trafficsim/Track.java | 5 +- .../main/java/de/hems/trafficsim/Worker.java | 33 ++++- app/src/main/res/layout/activity_main.xml | 64 +++++++--- 6 files changed, 232 insertions(+), 119 deletions(-) create mode 100644 app/src/main/java/de/hems/trafficsim/Renderer.java diff --git a/app/src/main/java/de/hems/trafficsim/MainActivity.java b/app/src/main/java/de/hems/trafficsim/MainActivity.java index d3857e9..66800ae 100644 --- a/app/src/main/java/de/hems/trafficsim/MainActivity.java +++ b/app/src/main/java/de/hems/trafficsim/MainActivity.java @@ -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()); } } diff --git a/app/src/main/java/de/hems/trafficsim/Renderer.java b/app/src/main/java/de/hems/trafficsim/Renderer.java new file mode 100644 index 0000000..5b274a5 --- /dev/null +++ b/app/src/main/java/de/hems/trafficsim/Renderer.java @@ -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 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); + } + } + } +} diff --git a/app/src/main/java/de/hems/trafficsim/TimeRecordView.java b/app/src/main/java/de/hems/trafficsim/TimeRecordView.java index f8570be..da9aedd 100644 --- a/app/src/main/java/de/hems/trafficsim/TimeRecordView.java +++ b/app/src/main/java/de/hems/trafficsim/TimeRecordView.java @@ -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> 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 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; - } - } } } diff --git a/app/src/main/java/de/hems/trafficsim/Track.java b/app/src/main/java/de/hems/trafficsim/Track.java index f46604e..d372319 100644 --- a/app/src/main/java/de/hems/trafficsim/Track.java +++ b/app/src/main/java/de/hems/trafficsim/Track.java @@ -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) { } diff --git a/app/src/main/java/de/hems/trafficsim/Worker.java b/app/src/main/java/de/hems/trafficsim/Worker.java index d482e0d..89fdcf8 100644 --- a/app/src/main/java/de/hems/trafficsim/Worker.java +++ b/app/src/main/java/de/hems/trafficsim/Worker.java @@ -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++; } } + } + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 80e2bd8..8f9d408 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -19,12 +19,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - + app:layout_constraintEnd_toEndOf="@+id/textView5" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintEnd_toEndOf="@+id/brakeProbLabel" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintBottom_toTopOf="@+id/constraintLayout2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + + android:text="Delay: " + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintEnd_toEndOf="parent">