How to determine the normal when ball hits rectangle? - android

I have this constants defined, but I'm not sure how to find the normal index.
public static final int LEFT = 0;
public static final int RIGHT = 1;
public static final int UP = 2;
public static final int DOWN = 3;
public float[] normals = {-1, 0, 1, 0, 0, 1, 0, -1};
public float4 getNormal(int index)
{
return new float4(normals[index * 2], normals[index * 2 + 1], 0);
}
public static int normal(float x, float y, RectF rect)
{
// need to find the normal index here
return normal_index;
}

I think this should work for inside and outside points (radius centers):
public static int normal_index(float x, float y, RectF rect)
{
float cx = rect.left + rect.width() * 0.5f;
float cy = rect.top + rect.height() * 0.5f;
boolean a = x - rect.left < rect.bottom - y;
boolean b = x - rect.left < y - rect.top;
boolean c = x - rect.right < y - rect.bottom;
boolean d = x - rect.right < rect.top - y;
if (a && b && x < cx ) return LEFT;
else if (!c && !d && x >= cx ) return RIGHT;
else if (!a && c && y >= cy ) return DOWN;
else if (!b && d && y < cy ) return UP;
return -1;
}
The question seems related to this question Given a point inside a rectangle, determine the side that's closest to the point
For the lines a, b, c, d, I've got similar picture:

Related

MpAndroidChart Pie chart with rounded corners

I'm trying to draw a pie chart with rounded corners using MpAndroidChart library.
Expected output is something similar to this.
Both ends need to be outer round. There is a method pieChart.setDrawRoundedSlices(true), but the issue is start point of the pie chart getting inner round.
This is the actual output.
// initialise pie chart UI
fun initChart(mChart: PieChart) {
mChart.description.isEnabled = false
mChart.holeRadius = 75f
mChart.transparentCircleRadius = 60f
mChart.setHoleColor(Color.TRANSPARENT)
mChart.legend.isEnabled = false
mChart.isRotationEnabled = false
mChart.setTouchEnabled(false)
mChart.maxAngle = 270f
mChart.rotation = -135f
mChart.animateX(400)
mChart.setDrawRoundedSlices(true)
}
I was faced with the same challenge recently, this is the code of the renderer in case anyone may need it:
public class RoundedSlicesPieChartRenderer extends PieChartRenderer {
public RoundedSlicesPieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
chart.setDrawRoundedSlices(true);
}
#Override
protected void drawDataSet(Canvas c, IPieDataSet dataSet) {
float angle = 0;
float rotationAngle = mChart.getRotationAngle();
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
final RectF circleBox = mChart.getCircleBox();
final int entryCount = dataSet.getEntryCount();
final float[] drawAngles = mChart.getDrawAngles();
final MPPointF center = mChart.getCenterCircleBox();
final float radius = mChart.getRadius();
final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled();
final float userInnerRadius = drawInnerArc
? radius * (mChart.getHoleRadius() / 100.f)
: 0.f;
final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
final RectF roundedCircleBox = new RectF();
int visibleAngleCount = 0;
for (int j = 0; j < entryCount; j++) {
// draw only if the value is greater than zero
if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) {
visibleAngleCount++;
}
}
final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet);
final Path pathBuffer = new Path();
final RectF mInnerRectBuffer = new RectF();
for (int j = 0; j < entryCount; j++) {
float sliceAngle = drawAngles[j];
float innerRadius = userInnerRadius;
Entry e = dataSet.getEntryForIndex(j);
// draw only if the value is greater than zero
if (!(Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) {
angle += sliceAngle * phaseX;
continue;
}
// Don't draw if it's highlighted, unless the chart uses rounded slices
if (mChart.needsHighlight(j) && !drawInnerArc) {
angle += sliceAngle * phaseX;
continue;
}
final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
mRenderPaint.setColor(dataSet.getColor(j));
final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
0.f :
sliceSpace / (Utils.FDEG2RAD * radius);
final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY;
float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY;
if (sweepAngleOuter < 0.f) {
sweepAngleOuter = 0.f;
}
pathBuffer.reset();
float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
// Android is doing "mod 360"
pathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW);
} else {
if (drawInnerArc) {
float x = center.x + (radius - roundedRadius) * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD);
float y = center.y + (radius - roundedRadius) * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD);
roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
pathBuffer.arcTo(roundedCircleBox, startAngleOuter - 180, 180);
}
pathBuffer.arcTo(
circleBox,
startAngleOuter,
sweepAngleOuter
);
}
// API < 21 does not receive floats in addArc, but a RectF
mInnerRectBuffer.set(
center.x - innerRadius,
center.y - innerRadius,
center.x + innerRadius,
center.y + innerRadius);
if (drawInnerArc && (innerRadius > 0.f || accountForSliceSpacing)) {
if (accountForSliceSpacing) {
float minSpacedRadius =
calculateMinimumRadiusForSpacedSlice(
center, radius,
sliceAngle * phaseY,
arcStartPointX, arcStartPointY,
startAngleOuter,
sweepAngleOuter);
if (minSpacedRadius < 0.f)
minSpacedRadius = -minSpacedRadius;
innerRadius = Math.max(innerRadius, minSpacedRadius);
}
final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ?
0.f :
sliceSpace / (Utils.FDEG2RAD * innerRadius);
final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY;
float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY;
if (sweepAngleInner < 0.f) {
sweepAngleInner = 0.f;
}
final float endAngleInner = startAngleInner + sweepAngleInner;
if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) {
// Android is doing "mod 360"
pathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW);
} else {
float x = center.x + (radius - roundedRadius) * (float) Math.cos(endAngleInner * Utils.FDEG2RAD);
float y = center.y + (radius - roundedRadius) * (float) Math.sin(endAngleInner * Utils.FDEG2RAD);
roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius);
pathBuffer.arcTo(roundedCircleBox, endAngleInner, 180);
pathBuffer.arcTo(mInnerRectBuffer, endAngleInner, -sweepAngleInner);
}
} else {
if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) {
if (accountForSliceSpacing) {
float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f;
float sliceSpaceOffset =
calculateMinimumRadiusForSpacedSlice(
center,
radius,
sliceAngle * phaseY,
arcStartPointX,
arcStartPointY,
startAngleOuter,
sweepAngleOuter);
float arcEndPointX = center.x +
sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD);
float arcEndPointY = center.y +
sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD);
pathBuffer.lineTo(
arcEndPointX,
arcEndPointY);
} else {
pathBuffer.lineTo(
center.x,
center.y);
}
}
}
pathBuffer.close();
mBitmapCanvas.drawPath(pathBuffer, mRenderPaint);
angle += sliceAngle * phaseX;
}
MPPointF.recycleInstance(center);
}
}
And then you use it like this:
mChart.setRenderer(new RoundedSlicesPieChartRenderer(pieChart, pieChart.getAnimator(), pieChart.getViewPortHandler()));

Bitmap Bounding RectF Collision

I have been trying to create a simple breakout type game. I have a ball and a bat and want to invert the ball's direction when it collides with the bat. The bat is controlled by the user's touch and can freely move around the screen.
#Override
public boolean onTouch(View v, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
return true;
}
In my game loop:
int cx = -1;
int cy = -1;
private int xVelocity = -11;
private int yVelocity = -14;
Bitmap player, ball;
RectF boundsPlayer, boundsBall;
float boundCX, boundCY;
boundCX = (float) cx;
boundCY = (float) cy;
player = BitmapFactory.decodeResource(getResources(), R.drawable.nightplayer);
canvas.drawBitmap(player, x - (player.getWidth() / 2), y - (player.getHeight() + 100), null);
ball = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
boundsPlayer = new RectF((x - (player.getWidth()/2)), (y - (player.getHeight() + 150)), ((x - (player.getWidth()/2)) + player.getWidth()), ((y - (player.getHeight() + 150)) + player.getHeight()));
boundsBall = new RectF(boundCX, boundCY, boundCX + ball.getWidth(), boundCY + ball.getHeight());
if (RectF.intersects(boundsBall, boundsPlayer)) {
collision();
}
private void collision() {
yVelocity = yVelocity * -1;
score = score + 1;
//Log.d(TAG, String.valueOf(score));
yVelocity = yVelocity - 2;
if(xVelocity < 0) {
xVelocity = xVelocity - 2;
} else if (xVelocity > 0) {
xVelocity = xVelocity + 2;
}
collision = false;
}
The issue I am having is that when the ball collides with the bat it seems to collide multiple times and repeatedly inverts the direction instead of "bouncing off" of the bat.

How to give interactions to a bar in a bar graph i.e. increase and decrease of height of each bar in android without using any library

I am able to plot bar graph using canvas and drawing rectangle in the view. But the onTouch interaction works for the whole view and hence I am not able to interact with each bar separately.I am not looking for using any library for plotting graphs. Any suggestions would be helpful. Thanks!
protected void onDraw(Canvas canvas) {
float border = 20;
float horstart = border * 2;
float height = getHeight();
float width = getWidth() - 1;
float max = getMax();
float min = getMin();
float diff = max - min;
float graphheight = height - (2 * border);
float graphwidth = width - (2 * border);
paint.setTextAlign(Align.LEFT);
int vers = verlabels.length - 1;
for (int i = 0; i < verlabels.length; i++) {
paint.setColor(Color.BLUE);
float y = ((graphheight / vers) * i) + border;
//canvas.drawLine(horstart, y, width, y, paint);
paint.setColor(Color.BLUE);
canvas.drawText(verlabels[i], 0, y, paint);
}
int hors = horlabels.length - 1;
for (int i = 0; i < horlabels.length; i++) {
paint.setColor(Color.BLUE);
float x = ((graphwidth / hors) * i) + horstart;
//canvas.drawLine(x, height - border, x, border, paint);
paint.setTextAlign(Align.CENTER);
if (i == horlabels.length - 1)
paint.setTextAlign(Align.RIGHT);
if (i == 0)
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.BLUE);
canvas.drawText(horlabels[i], x, height - 4, paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border - 4, paint);
if (max != min) {
paint.setColor(Color.BLUE);
if (type == BAR) {
float datalength = values.length;
float colwidth = (graphwidth / hors);
for (int i = 0; i < values.length; i++) {
float val = values[i] - min;
float rat = val / diff;
float h = graphheight * rat;
canvas.drawRect((i * colwidth) + horstart, (border - h)
+ graphheight+curY, ((i * colwidth) + horstart)
+ (colwidth - 1), height - (border - 1), paint);
}
} else {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
float halfcol = colwidth / 2;
float lasth = 0;
float h = 0;
for (int i = 0;i<values.length;i++)
canvas.drawLine(((i - 1) * colwidth) + (horstart + 1)
+ halfcol, (border - lasth) + graphheight+curY,
(i * colwidth) + (horstart + 1) + halfcol,
(border - h) + graphheight, paint);
lasth = h;
}
}
}
onTouch method for the view :
public boolean onTouch(View v, MotionEvent event)
{
boolean result=false;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_MOVE:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
case MotionEvent.ACTION_UP:
curX= (int)event.getX();
curY= (int)event.getY();
result=true;
break;
}
if (result) invalidate();
return result;
}
Whenever an user touches that view, it causes the onTouch method gets called. Once it's getting called, you're given two float numbers: x and y indicating where user's finger touches your view relative to the view coordination system.
As you might grasped the idea, you should get those numbers and internally in your custom view (i.e. bar chart) calculate which bar is affected. Then, you can for example apply a hover effect or do something else.
Note: For updating appearance of your view, you should store changes in data models of your chart view and then issue invalidate(). Subsequently, as a result, your onDraw is invoked and in which you can re-draw your chart. (i.e. You should each time, re-draw your whole chart again)

How do I draw a graph on Android without using any external API? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I understand how to draw lines using a canvas, but how can I use the same lines using a canvas to draw a graph?
The problem is with the coordinates. (0,0) starts right at the top left corner of the device. How can I set (0,0) as the margin and draw the particular line with respect to the margin?
1)Create an activity.
public class GraphView1 extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
float[] values = new float[] { "your values"};
String[] verlabels = new String[] { "your values" };
String[] horlabels = new String[] { "your values"
GraphView graphView = new GraphView(this, values,"GraphView",horlabels, verlabels, GraphView.BAR);
setContentView(graphView);
}
}
2)Then create another class extends View:
public class GraphView2 extends View{
public static boolean LINE = true;
private Paint paint;
private float[] values;
private String[] str;
private String[] verlabels;
private String title;
private boolean type;
Context context;
public GraphView(Context context, float[] values, String title, String[] str,String[] verlabels, boolean type) {
super(context);
if (values == null)
values = new float[0];
else
this.values = values;
if (title == null)
title = "";
else
this.title = title;
if (str == null)
this.str = new String[0];
else
this.str = str;
if (verlabels == null)
this.verlabels = new String[0];
else
this.verlabels = verlabels;
this.type = type;
paint = new Paint();
}
#Override
protected void onDraw(final Canvas canvas) {
context=getContext();
float border = 15;
float horstart = border * 2;
float height = getHeight();
float width = getWidth();
float max = getMax();
Log.w("max", ""+max);
float min = getMin();
Log.w("min", ""+min);
float diff = max - min;
float graphheight = height - (2 * border);
float graphwidth = width - (2 * border);
paint.setTextAlign(Align.LEFT);
int vers = verlabels.length;
for (int i = 0; i < verlabels.length; i++) {
paint.setColor(Color.DKGRAY);
float y = ((graphheight / vers) * i) + border;
canvas.drawLine(horstart, y, width, y, paint);
paint.setColor(Color.WHITE);
paint.setTextSize(10);
canvas.drawText(verlabels[i], 0, y, paint);
}
int hors = values.length;
for (int i = 0; i < str.length; i++) {
paint.setColor(Color.DKGRAY);
float x = ((graphwidth / hors) * i) + horstart;
canvas.drawLine(x, height - border, x, border, paint);
paint.setTextAlign(Align.LEFT);
if (i==str.length)
paint.setTextAlign(Align.RIGHT);
if (i==0)
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.WHITE);
paint.setTextSize(9);
canvas.drawText( str[i], x, height - 4, paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border - 4, paint);
if (max != min) {
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
if (type == BAR) {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
for (int i = 0; i < values.length; i++) {
// float val = values[i] - min;
// float rat = val / diff;
// float h = graphheight * rat;
// canvas.drawRect((i * colwidth) + horstart, (border - h) + graphheight, ((i * colwidth) + horstart) + (colwidth - 1), height - (border - 1), paint);
float graph_h = getHeight()-(border*2);
// Log.e("", "graph_h > "+graph_h);
float ind_h = graph_h/7;
//Log.e("", "ind_h > "+ind_h);
float t = values[i]/5;
float top = (graph_h - ind_h*(t));
// Log.e("", " > "+i+1);
// Log.e("", "top > "+top);
//for values between 0 and 5 ,vice versa
//Log.e("", " values[i] > "+values[i]);
float acc = ind_h/5;
acc = acc * (values[i]%5);
// Log.e("", " acc > "+acc);
canvas.drawRect((i * colwidth) + horstart, top+border-acc , ((i * colwidth) + horstart) + (colwidth - 1), graph_h+border, paint);
}
} else {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
float halfcol = colwidth / 2;
float lasth = 0;
for (int i = 0; i < values.length; i++) {
float val = values[i] - min;
float rat = val / diff;
float h = graphheight * rat;
if (i > 0)
canvas.drawLine(((i - 1) * colwidth) + (horstart + 1) + halfcol, (border - lasth) + graphheight, (i * colwidth) + (horstart + 1) + halfcol, (border - h) + graphheight, paint);
lasth = h;
}
}
}
}
private float getMax() {
float largest = Integer.MIN_VALUE;
for (int i = 0; i < values.length; i++)
if (values[i] > largest)
largest = values[i];
return largest;
}
private float getMin() {
float smallest = Integer.MAX_VALUE;
for (int i = 0; i < values.length; i++)
if (values[i] < smallest)
smallest = values[i];
return smallest;
}
}

Image Edges Pixelated Depending on Position Android

I have a dial that I display wind direction in and the arrow displays well in some positions, but others its edges are pixelated. Here is the code to render the image:
public class DialView extends View {
private Context mContext;
private Bitmap mArrow;
private WeatherDataModel mWdm;
private float iters = 10.0f;
private static float previousAngle = 0.0f;
private int mHourIndex = 0;
private boolean isHourly = false;
private final int XLARGE = 0x4;
public DialView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
int screenLayout = mContext.getApplicationContext().getResources().getConfiguration().screenLayout;
mArrow = Utilities.applyFilter(context, BitmapFactory.decodeResource(context.getResources(), R.drawable.wind_arrow));
}
#Override
public void onDraw(Canvas canvas) {
float degrees = 0.0f;
degrees = (!isHourly) ? cardinalToDegrees(mWdm) : cardinalToDegrees(mWdm.hourly.get(mHourIndex));
Bitmap bit;
int originY = getHeight() / 2;
int originX = getWidth() / 2;
int r = originY > originX ? getWidth() * 8 / 27 : getHeight() * 8 / 27;
int x, y;
Matrix matrix = new Matrix();
degrees = (previousAngle * (iters / 10.0f) + degrees * (10.0f - iters) / 10.0f);
//Log.d(DEBUG_TAG, "Previous angle = " + previousAngle + " degrees" + degrees);
matrix.postRotate(degrees - 90.f);
bit = Bitmap.createBitmap(mArrow, 0, 0, mArrow.getWidth(), mArrow.getHeight(), matrix, false);
x = (int)(Math.cos(Math.PI * degrees / 180.0f) * r) + originX - (bit.getWidth() / 2);
y = (int)(Math.sin(Math.PI * degrees / 180.0f) * r) + originY - (bit.getHeight() / 2);
//Log.d(DEBUG_TAG, "x: " + x + " y: " + y);
canvas.drawBitmap(bit, x, y, null);
if (iters > 0) {
invalidate();
iters--;
}
previousAngle = degrees;
}
Here is the arrow good:
Here it is pixelated:
Any ideas how to handle this?
try to define a paint object and enable AntiAlias
like this:
mPaint.setAntiAlias(true);
canvas.drawBitmap(bit, x, y, mPaint);

Categories

Resources