package gui; import gl.GLCamera; import gl.HasColor; import gl.HasPosition; import gl.scenegraph.Shape; import util.EfficientList; import util.Vec; import worldData.Obj; import worldData.RenderableEntity; import worldData.UpdateTimer; import worldData.Updateable; import worldData.World; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; public class RadarView extends SimpleCustomView implements Updateable { private static final int DEFAULT_VIEW_SIZE = 100; private static final int MARGIN = 4; private static final float DEFAULT_UPDATE_SPEED = 0.1f; private static final int DEFAULT_RADAR_MAX_DISTANCE = 200; private static final int MIN_DISP_RADIUS = 20; private Paint paint; private Paint linePaint; private int minimumSize = DEFAULT_VIEW_SIZE; private int mySize; private int myHalfSize; private Vec myRotVec; private int myDisplRadius = DEFAULT_RADAR_MAX_DISTANCE; private boolean displayOutOfRadarArea = true; private boolean rotateNeedle = false; private EfficientList<RenderableEntity> items; private GLCamera myCamera; private Bitmap background; private double myRotation; private UpdateTimer myTimer; private float myUpdateSpeed = DEFAULT_UPDATE_SPEED; private double myTouchScaleFactor = 5; private String debug; public RadarView(Context context, GLCamera camera, int minimumRadarViewSize, int displRadiusInMeters, float updateSpeed, boolean rotateNeedle, boolean displayOutOfRadarArea, EfficientList<RenderableEntity> items) { super(context); init(minimumRadarViewSize); myCamera = camera; setRotateNeedle(rotateNeedle); setRadarDisplRadius(displRadiusInMeters); setDisplayOutOfRadarArea(displayOutOfRadarArea); setItems(items); setUpdateSpeed(updateSpeed); } public void setUpdateSpeed(float myUpdateSpeed) { this.myUpdateSpeed = myUpdateSpeed; } public void setItems(EfficientList<RenderableEntity> items) { this.items = items; } public void setRotateNeedle(boolean rotateNeedle) { this.rotateNeedle = rotateNeedle; setRotation(myRotation); } public void setDisplayOutOfRadarArea(boolean displayOutOfRadarArea) { this.displayOutOfRadarArea = displayOutOfRadarArea; } public void setRadarDisplRadius(int displRadiusInMeters) { this.myDisplRadius = displRadiusInMeters; } @Deprecated public RadarView(Context context, AttributeSet attrs) { super(context, attrs); init(DEFAULT_VIEW_SIZE); } /** * @param myTargetActivity * @param radarViewSize * size of the radar view in pixels (e.g. pass * {@link Setup#getScreenWidth()/3} * @param camera * @param items * e.g. the complete virtual {@link World} (then use * {@link World#getAllItems()}) */ public RadarView(Activity myTargetActivity, int radarViewSize, GLCamera camera, EfficientList<RenderableEntity> items) { this(myTargetActivity, camera, radarViewSize, DEFAULT_RADAR_MAX_DISTANCE, DEFAULT_UPDATE_SPEED, false, true, items); } private void init(int minimumViewSize) { myTimer = new UpdateTimer(myUpdateSpeed, null); paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.WHITE); linePaint = new Paint(); linePaint.setStyle(Paint.Style.STROKE); linePaint.setStrokeWidth(2); this.minimumSize = minimumViewSize; setSize(minimumViewSize); if (isInEditMode()) loadDemoValues(); } public void setSize(int viewSize) { if (viewSize < minimumSize) viewSize = minimumSize; mySize = viewSize; myHalfSize = viewSize / 2; setRotation(myRotation); background = null; getBackGround(); } /** * This method will only be called when the view is displayed in the eclipse * xml layout editor */ private void loadDemoValues() { setRotateNeedle(true); setRotation(45); setDisplayedAreaSize(200); setElementsOutOfRadarAreaVisible(true); myCamera = new GLCamera(); myCamera.setPosition(new Vec(40, 40, 0)); items = new EfficientList<RenderableEntity>(); items.add(newObj(40, 500)); items.add(newObj(10, 10)); items.add(newObj(200, 200)); items.add(newObj(200, -200)); } private RenderableEntity newObj(int x, int y) { Obj o = new Obj(); Shape s = new Shape(gl.Color.getRandomRGBColor()); s.setPosition(new Vec(x, y, 0)); o.setComp(s); return o; } public void setElementsOutOfRadarAreaVisible(boolean b) { displayOutOfRadarArea = b; } public void setDisplayedAreaSize(int areaRadiusInMeters) { myDisplRadius = areaRadiusInMeters; } public void setRotation(double rotation) { myRotation = rotation; myRotVec = new Vec(myHalfSize / 2.5f, 0, 0); myRotVec.rotateAroundZAxis(rotation - 90); this.postInvalidate(); } @Override protected void onDraw(Canvas canvas) { /* * TODO store in bitmap object and only redraw if something changes to * increase performance! */ drawBackGround(canvas); if (items != null) drawItems(canvas); paint.setColor(Color.BLACK); drawCircle(canvas, myHalfSize, myHalfSize, myHalfSize / 30, paint); linePaint.setColor(Color.BLACK); drawCircle(canvas, myHalfSize, myHalfSize, myHalfSize - MARGIN, linePaint); drawCompassNeedle(canvas); if (debug != null) { paint.setColor(Color.RED); canvas.drawText(debug, 0, myHalfSize, paint); } } @Override public boolean onTouchEvent(MotionEvent event) { return onTouch(event.getX() - myHalfSize, event.getY() - myHalfSize); } private boolean onTouch(float x, float y) { double distFromCenter = Math.sqrt(x * x + y * y); // TODO use the myHalfSize to calculate percent value. important to stay // size independent! distFromCenter *= myTouchScaleFactor; myDisplRadius = (int) distFromCenter; if (myDisplRadius < MIN_DISP_RADIUS) myDisplRadius = MIN_DISP_RADIUS; return true; } private void drawCompassNeedle(Canvas canvas) { linePaint.setColor(Color.RED); if (rotateNeedle) { canvas.drawLine(myHalfSize, myHalfSize, myHalfSize + myRotVec.x, myHalfSize + myRotVec.y, linePaint); } else { canvas.drawLine(myHalfSize, myHalfSize, myHalfSize, myHalfSize - myHalfSize / 2.5f, linePaint); } } private void drawItems(Canvas canvas) { for (int i = 0; i < items.myLength; i++) { if (items.get(i) instanceof HasPosition) { RenderableEntity element = items.get(i); Vec pos = ((HasPosition) element).getPosition().copy() .sub(myCamera.getPosition()); float length = pos.getLength(); if (length > myDisplRadius) { if (displayOutOfRadarArea) { pos.setLength(myDisplRadius); length = myDisplRadius; } else continue; } if (!rotateNeedle) { pos.rotateAroundZAxis(myRotation); } /* * now convert the distance in meters into a distance in pixels: */ pos.setLength(length / myDisplRadius * (myHalfSize - MARGIN)); /* * the canvas coords are not like the opengl coords! 10,10 means * down on the screen */ // debug = "" + pos; float northPos = myHalfSize - pos.y; float eastPos = myHalfSize + pos.x; // debug="n="+northPos+", e="+eastPos; drawElement(element, canvas, northPos, eastPos); } } } private void drawElement(RenderableEntity element, Canvas canvas, float northPos, float eastPos) { paint.setColor(Color.WHITE); if (element instanceof HasColor) { gl.Color c = ((HasColor) element).getColor(); if (c != null) paint.setColor(c.toIntARGB()); } drawCircle(canvas, eastPos, northPos, 6, paint); } private void drawBackGround(Canvas canvas) { canvas.drawBitmap(getBackGround(), 0, 0, paint); } private Bitmap getBackGround() { if (background == null) background = createBackground(mySize, myHalfSize); return background; } /** * This method is used to create a static background bitmap for better * performance when drawing the radar * * @param size * @param halfSize * @return */ private Bitmap createBackground(int size, int halfSize) { Bitmap b = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); Paint p = new Paint(); p.setAntiAlias(true); p.setColor(Color.WHITE); p.setAlpha(150); drawCircle(c, halfSize, halfSize, halfSize - MARGIN, p); // shadow p.setColor(Color.BLACK); p.setAlpha(100); p.setStyle(Paint.Style.STROKE); p.setStrokeWidth(4); int shadowOffset = 2; drawCircle(c, halfSize + shadowOffset, halfSize + shadowOffset, halfSize - MARGIN, p); p.setColor(Color.BLACK); p.setStrokeWidth(2); drawCircle(c, halfSize, halfSize, halfSize - MARGIN, p); return b; } @Override public boolean update(float timeDelta, Updateable parent) { if (myTimer.update(timeDelta, parent)) { setRotation(myCamera.getCameraAnglesInDegree()[0]); } /* * TODO if view was removed from parent it can return false here! */ return true; } @Override public void onResizeEvent(int recommendedHeight, int recommendedWidth) { int min = Math.min(recommendedHeight, recommendedWidth); setSize(min); this.setMeasuredDimension(mySize, mySize); } }