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; /** * User interface class rendering the track history on a SurfaceView. */ public class Renderer { /** * the track to render */ protected Track track; /** * width of a rectangle representing one vehicle */ protected int pixelPerVehicle; /** * height of a rectangle representing one vehicle */ protected int pixelPerLine; /** * amount of pixels per track position which are lost by rounding */ protected float tooShortPerTrackLength; /** * amount of pixels per height which are lost by rounding */ protected float tooShortPerHeight; /** * Paint instance of the renderer */ protected Paint paint; /** * holder of the surface the renderer draws to */ protected SurfaceHolder holder; /** * width of the surface to draw to */ protected int width; /** * height of the surface to draw to */ protected int height; /** * temporary reference to the time records of one simulation step */ private List stepRecords; /** * temporary reference to the canvas to draw to */ private Canvas canvas; /** * Constuctor for a Renderer. * * @param track the track to render * @param holder the holder of the surface to draw to */ public Renderer(Track track, SurfaceHolder holder) { this.track = track; this.holder = holder; this.paint = new Paint(); } /** * Updates the dimension information of the renderer. * * @param width the width of the SurfaceView * @param height the height of the SurfaceView */ 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; } } /** * Updates the track of the Surface. * * @param track the new track to render */ public void setTrack(Track track) { this.track = track; this.setSize(width, height); } /** * Utility function which calculates a color from the relation between the current speed of a * vehicle and it's maximum speed (from red over yellow to green). * * @param curVelocity current velocity of the vehicle * @param maxVelocity maximum velocity of the vehilce * @return color encoded as Android color integer */ 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; } } /** * Draws the current state of the track history to the Surface. */ 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 hCompensateStart = 0; int hCompensateEnd = 0; int vCompensateStart = 0; int vCompensateEnd = 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; } vCompensateStart = Math.round(y * this.tooShortPerHeight); vCompensateEnd = Math.round((y+this.pixelPerLine) * this.tooShortPerHeight); stepRecords = this.track.getVtrList().get(curStepIdx); int i = 0; for (VehicleTimeRecord r : stepRecords) { left = (int) (this.pixelPerVehicle * r.getPosition()); hCompensateStart = Math.round(r.getPosition() * this.tooShortPerTrackLength); hCompensateEnd = Math.round((r.getPosition() + this.pixelPerVehicle) * this.tooShortPerTrackLength); this.paint.setColor(getColor(r.getVelocity(), r.getMaxVelocity())); canvas.drawRect(left + hCompensateStart, y+vCompensateStart, left + this.pixelPerVehicle + hCompensateEnd, y + pixelPerLine + vCompensateEnd, 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); } } } }