emotionhero/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java

340 lines
12 KiB
Java
Raw Normal View History

2016-08-17 13:17:04 +02:00
package com.rubenvandeven.emotionhero;
import android.content.Context;
import android.graphics.Canvas;
2016-08-17 19:21:16 +02:00
import android.graphics.Color;
2016-09-10 01:03:10 +02:00
import android.graphics.Matrix;
2016-08-17 13:17:04 +02:00
import android.graphics.Paint;
2016-08-17 19:21:16 +02:00
import android.graphics.Path;
2016-08-17 13:17:04 +02:00
import android.graphics.PixelFormat;
2016-09-10 01:03:10 +02:00
import android.graphics.Point;
import android.graphics.Rect;
2016-08-19 13:38:59 +02:00
import android.graphics.Typeface;
import android.util.Log;
2016-08-17 13:17:04 +02:00
import android.view.SurfaceHolder;
import android.view.SurfaceView;
2016-08-17 19:21:16 +02:00
import com.affectiva.android.affdex.sdk.detector.Face;
import java.util.HashMap;
import java.util.Map;
2016-08-17 13:17:04 +02:00
/**
* Created by ruben on 16/08/16.
*/
public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback {
private PanelThread _thread;
private Scenario _scenario;
2016-09-21 15:58:49 +02:00
// bottom at 60%;
public static float BAR_POSITION = 0.7f;
2016-08-17 19:21:16 +02:00
/**
* The scorres in this moment, as to draw them on the screen.
* Indexes are Emotion ordinals
*/
private Map<Emotion, Float> currentAttributeScores = new HashMap<>();
2016-08-19 13:38:59 +02:00
//avoid object instantiation on each onDraw
private Map<Emotion, Paint> emoPaints = new HashMap<>();
private Map<Emotion, Paint> emoOutlinePaints = new HashMap<>();
private Map<Emotion, Paint> emoScoredPaints = new HashMap<>();
private Map<Emotion, Path> emoNamePaths = new HashMap<>();
2016-08-19 13:38:59 +02:00
private Paint mainPaint = new Paint();
private Paint attrScorePaint = new Paint();
private Paint attrScoreLinePaint = new Paint();
2016-08-19 13:38:59 +02:00
private Paint linePaint = new Paint();
private Paint missedPaint = new Paint();
private Paint scorePaint = new Paint();
2016-09-15 17:17:20 +02:00
private Paint bonusScorePaint = new Paint();
2016-09-10 01:03:10 +02:00
private Paint bonusBarPaint = new Paint();
private Point mLeftTop;
private Point mRightTop;
private Point mLeftBot;
private Point mRightBot;
private Matrix matrix;
2016-08-17 13:17:04 +02:00
public boolean drawOverlay = false;
public boolean noFace = false;
2016-08-17 13:17:04 +02:00
public ScenarioView(Context context, Scenario s) {
super(context);
// setLayerType(LAYER_TYPE_HARDWARE, null); // doesn't seem to make a difference?
2016-08-17 13:17:04 +02:00
getHolder().addCallback(this);
_scenario = s;
2016-08-19 13:38:59 +02:00
Typeface font = Typeface.createFromAsset(context.getAssets(), "unifont-9.0.02.ttf");
scorePaint.setColor(Color.YELLOW);
2016-09-21 15:58:49 +02:00
scorePaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.scenario_scorevalue));
scorePaint.setTypeface(Typeface.DEFAULT_BOLD);
2016-09-15 17:17:20 +02:00
bonusScorePaint.setColor(Color.rgb(132, 0, 255));
bonusScorePaint.setTextSize(20);
bonusScorePaint.setTypeface(Typeface.DEFAULT_BOLD);
2016-09-10 01:03:10 +02:00
bonusBarPaint.setColor(Color.rgb(132, 0, 255));
2016-08-19 20:56:58 +02:00
2016-08-19 13:38:59 +02:00
//setup paints for drawing
mainPaint.setColor(Color.GRAY);
linePaint.setColor(Color.GRAY);
linePaint.setStrokeWidth(5);
attrScorePaint.setColor(Color.rgb(190,190,190));
missedPaint.setColor(Color.DKGRAY);
2016-08-19 13:38:59 +02:00
attrScoreLinePaint.setColor(Color.DKGRAY);
attrScoreLinePaint.setStrokeWidth(2);
2016-08-19 13:38:59 +02:00
for(Emotion emotion: Emotion.values()) {
Paint emoPaint = new Paint();
2016-09-21 15:58:49 +02:00
emoPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.scenario_emolabel));
// emoPaint.setTypeface(font);//gets ugly when transformed
2016-08-19 13:38:59 +02:00
emoPaint.setColor(emotion.getColor());
2016-09-10 01:03:10 +02:00
// emoPaint.setShadowLayer(10,0,0,Color.BLACK);
2016-08-19 13:38:59 +02:00
emoPaints.put(emotion, emoPaint);
Paint emoPaintOutline = new Paint();
emoPaintOutline.setColor(emotion.getColor());
emoPaintOutline.setStyle(Paint.Style.STROKE);
emoPaintOutline.setStrokeWidth(5);
2016-09-10 01:03:10 +02:00
// emoPaintOutline.setShadowLayer(10,0,0,Color.BLACK);
2016-08-19 13:38:59 +02:00
emoOutlinePaints.put(emotion, emoPaintOutline);
Paint emoScoredPaint = new Paint();
float darkenFactor = 0.4f;
int red = (int) ((Color.red(emotion.getColor()) * darkenFactor));
int green = (int) ((Color.green(emotion.getColor()) * darkenFactor));
int blue = (int) ((Color.blue(emotion.getColor()) * darkenFactor));
int emoLightenedColor = Color.rgb(red, green, blue);
emoScoredPaint.setColor(emoLightenedColor);
2016-09-21 15:58:49 +02:00
emoScoredPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.scenario_emolabel));
2016-08-19 13:38:59 +02:00
emoScoredPaint.setStrokeWidth(2);
emoScoredPaint.setStyle(Paint.Style.FILL_AND_STROKE);
2016-09-10 01:03:10 +02:00
// emoScoredPaint.setShadowLayer(10,0,0,Color.BLACK);
2016-08-19 13:38:59 +02:00
emoScoredPaints.put(emotion, emoScoredPaint);
emoNamePaths.put(emotion, new Path());
// was probably here for anti-aliasing? can't quite remember (I should make more notes!)
// setLayerType(LAYER_TYPE_SOFTWARE, emoPaint);
// setLayerType(LAYER_TYPE_SOFTWARE, emoPaintOutline);
// setLayerType(LAYER_TYPE_SOFTWARE, emoScoredPaint);
2016-09-10 01:03:10 +02:00
2016-08-19 13:38:59 +02:00
}
2016-08-17 13:17:04 +02:00
}
@Override
public void onDraw(Canvas canvas) {
if(drawOverlay) {
canvas.drawColor(0x770000FF);
}
2016-09-10 01:03:10 +02:00
2016-08-17 13:17:04 +02:00
//do drawing stuff here.
2016-08-17 19:21:16 +02:00
if (noFace)
return;
2016-08-17 19:21:16 +02:00
float height = canvas.getHeight();
float width = canvas.getWidth();
Log.i("ScenarioView", "onDraw "+ width+" x " +height);
if(_scenario != null)
{
float hitWidth = width*(_scenario.getHitPercentage()/100);
float misWidth = width*(_scenario.getMissedPercentage()/100);
float bonusWidth = width*(_scenario.getBonusPercentage()/100);
float bonus =_scenario.getBonusTotalValue();
String scoreText = String.format("%1$.0f", _scenario.getHitTotalValue() + bonus);
Rect scoreTextBounds = new Rect();
scorePaint.getTextBounds(scoreText, 0, scoreText.length(), scoreTextBounds);
canvas.drawText(scoreText, hitWidth, height*0.95f-scoreTextBounds.height(), scorePaint);
/*if(bonus > 0) {
String bonusText = String.format("+ %1$.0f", bonus);
Rect bonusTextBounds = new Rect();
bonusScorePaint.getTextBounds(bonusText, 0, bonusText.length(), bonusTextBounds);
canvas.drawText(bonusText, hitWidth+scoreTextBounds.width()+3, height*0.95f-bonusTextBounds.height()-10, bonusScorePaint);
}*/
canvas.drawRect(0, height*0.95f, width, height, mainPaint);
float stepSize = width / _scenario.targets.size();
for(int i = _scenario.targets.size(); i > 0; i--) {
canvas.drawLine(stepSize*i, height*0.95f, stepSize*i, height, attrScoreLinePaint);
}
canvas.drawRect(0, height*0.95f, hitWidth, height, emoPaints.get(Emotion.JOY)); // paint: yellow
canvas.drawRect(hitWidth, height*0.95f, hitWidth+misWidth, height, missedPaint);
// canvas.drawRect(0, height*0.95f, bonusWidth, height*0.975f, bonusBarPaint);
}
if(matrix != null) {
canvas.concat(matrix);
}
2016-09-21 15:58:49 +02:00
// bottom at 60%;
float bottomline_height = height * BAR_POSITION;
2016-08-17 19:21:16 +02:00
// draw current hits:
2016-09-21 15:58:49 +02:00
float used_width = (float) 0.9; // 0.7: only use center
2016-08-17 19:21:16 +02:00
float padding_left = canvas.getWidth() * (1-used_width) / 2;
float step_y = (canvas.getWidth() * used_width) / Emotion.values().length;
float max_ball_radius = step_y * (float) 0.8 / 2;
// canvas.drawLine(0, bottomline_height, width, bottomline_height, linePaint);
2016-08-17 19:21:16 +02:00
for(Emotion emotion: Emotion.values())
{
float value = 0;
if(currentAttributeScores.containsKey(emotion)) {
value = currentAttributeScores.get(emotion);
}
if(value < 5) {
value = 5;
}
float cx = padding_left + (step_y * emotion.ordinal() + step_y / 2);
float cy = bottomline_height;
// canvas.drawCircle(cx, cy, max_ball_radius, mainPaint);
2016-08-30 19:06:18 +02:00
canvas.drawCircle(cx, cy, max_ball_radius + 5, emoOutlinePaints.get(emotion));
2016-08-17 19:21:16 +02:00
2016-08-19 13:38:59 +02:00
// canvas.drawCircle(cx, cy, max_ball_radius * value/100, emoPaints.get(emotion.ordinal()));
canvas.drawCircle(cx, cy, max_ball_radius * value/100, attrScorePaint);
2016-08-17 19:21:16 +02:00
Path emoNamePath = emoNamePaths.get(emotion);
emoNamePath.moveTo(cx - max_ball_radius * 1.25f, cy + max_ball_radius * 1.60f);
// more curly line to draw on:
// emoNamePath.rCubicTo(width*0.1f,0, width*0.1f, height*0.2f,width*0.2f,height*0.2f);
emoNamePath.rLineTo(1000,1000);
2016-08-17 19:21:16 +02:00
// canvas.drawText(emotion.toString(), cx, cy + max_ball_radius * (float) 1.3, emoPaint);
2016-08-19 13:38:59 +02:00
canvas.drawTextOnPath(emotion.toString(), emoNamePath, 0, 0, emoPaints.get(emotion));
2016-08-17 19:21:16 +02:00
}
// Draw targets:
float sec_height = height * (float) 0.2; // each second is 20% of canvas height
// current moved position
if(_scenario != null) {
float diff_y = sec_height * _scenario.getTime();
for(Scenario.Target target: _scenario.getTargets()) {
// don't draw hit targets
if(target.isHit){
continue;
}
float cy = bottomline_height - (target.timestamp * sec_height) + diff_y;
2016-08-17 19:21:16 +02:00
2016-09-10 01:03:10 +02:00
if(cy < (-1 * canvas.getHeight()) ) {
continue;
}
float cx = padding_left + (step_y * target.emotion.ordinal() + step_y / 2);
2016-08-17 19:21:16 +02:00
canvas.drawCircle(cx, cy, max_ball_radius * target.value/100, emoPaints.get(target.emotion));
2016-08-17 19:21:16 +02:00
if(target.isHit) {
Hit hit = _scenario.getHitForTarget(target);
canvas.drawText(Integer.toString(Math.round(hit.score)), cx, cy, emoPaints.get(target.emotion));
canvas.drawCircle(cx, cy, max_ball_radius * hit.score/100, emoScoredPaints.get(target.emotion));
}
2016-08-19 13:38:59 +02:00
}
// canvas.restore(); // triggers error that there are less saves then restores?
2016-08-19 20:56:58 +02:00
2016-09-10 01:03:10 +02:00
}
2016-08-17 19:21:16 +02:00
}
2016-08-19 13:38:59 +02:00
// TODO: create AttributeScoreCollection class, with this method.
2016-08-17 19:21:16 +02:00
public void setCurrentAttributeScoresForFace(Face face)
{
for(Emotion emotion: Emotion.values()) {
currentAttributeScores.put(emotion, emotion.getValueFromFace(face));
}
2016-08-17 13:17:04 +02:00
}
/**
* Called when surface is created or size is changed (eg. when switching landscape/portrait)
* @param holder
* @param format
* @param width
* @param height
*/
2016-08-17 13:17:04 +02:00
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// define matrix that skews, providing a sense of depth for the targets to come down
2016-09-10 01:03:10 +02:00
mLeftTop = new Point((int) (width*0.2), height/2);
mRightTop = new Point((int) (width*0.8), height/2);
mLeftBot = new Point(0, height);
mRightBot = new Point(width, height);
matrix = new Matrix();
matrix.setPolyToPoly(new float[]{0, 0,
width - 1, 0,
0, height - 1,
width - 1, height - 1},
0,
new float[]{mLeftTop.x, mLeftTop.y,
mRightTop.x, mRightTop.y,
mLeftBot.x, mLeftBot.y,
mRightBot.x, mRightBot.y
}
, 0, 4);
2016-08-17 13:17:04 +02:00
}
@Override
public SurfaceHolder getHolder() {
return super.getHolder();
}
2016-08-17 13:17:04 +02:00
@Override
public void surfaceCreated(SurfaceHolder holder) {
setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()
2016-08-30 19:06:18 +02:00
// this.setZOrderOnTop(true); // performance impact
2016-08-30 16:56:21 +02:00
// holder.setFormat(PixelFormat.TRANSPARENT);
2016-08-30 19:06:18 +02:00
// holder.setFormat(PixelFormat.TRANSLUCENT);
2016-08-17 13:17:04 +02:00
_thread = new PanelThread(getHolder(), this); //Start the thread that
_thread.setRunning(true); //will make calls to
_thread.start(); //onDraw()
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
if(_thread != null) {
_thread.setRunning(false); //Tells thread to stop
_thread.join(); //Removes thread from mem.
}
} catch (InterruptedException e) {}
}
}