How to keep a circle inside another circle android view control - android

I am trying to create a pad-like view in android. I got a circle that follows user's gestures and I am using distance to keep the circle of going outside the main circle of the pad control.
My problem is I want the circle to keep following the gesture, but to stay inside of the main circle. I am using the formula for finding a point using angle and radius, but it does some funky stuff.
I am translating the canvas, so that the center of the main circle is at 0, 0.
Here is the code:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(this.mainRadius, this.mainRadius);
canvas.drawCircle(0, 0, this.mainRadius, this.debugPaint);
canvas.drawCircle(this.handleX, this.handleY, this.handleRadius, this.handlePaint);
}
private void translateHandle(MotionEvent event) {
int x = (int) (event.getX() - this.mainRadius);
int y = (int) (event.getY() - this.mainRadius);
double distance = distanceFromCenter(x, y);
if (distance <= this.maxDistance) {
this.handleX = x;
this.handleY = y;
} else {
float angle = (float) Math.toDegrees(Math.atan2(y, x));
if (angle < 0)
angle += 360;
this.handleX = (int) ((this.mainRadius - this.handleRadius) * Math.cos(angle));
this.handleY = (int) ((this.mainRadius - this.handleRadius) * Math.sin(angle));
}
//onTranslateHandle(distance);
}
And here is the funky stuff in a gif image:

I cannot verify this change into your code snippet but do hope it gives some idea how to proceed further anyway;
private void translateHandle(MotionEvent event) {
float x = event.getX() - this.mainRadius;
float y = event.getY() - this.mainRadius;
double distance = distanceFromCenter(x, y);
if (distance > this.maxDistance) {
// If distance is i.e 2.0 and maxDistance is 1.0 ==> adjust is 0.5
// which repositions x and y making distance 1.0 maintaining direction
double adjust = this.maxDistance / distance;
x = (float)(x * adjust);
y = (float)(y * adjust);
}
this.handleX = (int)x;
this.handleY = (int)y;
}
I can update the answer where needed if this does not give any useful results.

Related

How to create dripping Spray can tool for Graffiti app in Android

I am trying to create a spray can tool in Android for a Graffiti app. I want to achieve something similar done in the below image
Dripping Spray Can Sample image
Requirements: If somebody is spraying the can then it should drip some color downwards while drawing a spray line
I am currently using the following code, but not getting the correct results:
if (bufferPaint == null) {
bufferPaint = new Paint();
bufferPaint.setAntiAlias(true);
bufferPaint.setDither(true);
bufferPaint.setXfermode(null);
if (mPaint == null)
setupColor(0f, 100f);
}
int alpha = Math.round((float) 100 / 100 * 255);
bufferPaint.setAlpha(alpha);
private void paintOneDab(float x, float y) {
float radius = 5 / 2f;
float scatter = 500 / 100f;
x += radius 2 scatter (1 - 2 (float) Math.random());
y += radius 2 scatter (1 - 2 (float) Math.random());
float hardness = 20 / 100f;
positions[0] = 0;
positions[1] = (float) (Math.sqrt(hardness));
positions[2] = 1;
bufferPaint.setShader(new RadialGradient(x, y, radius, colors, positions, Shader.TileMode.CLAMP));
mCanvas.save();
int angle = 180;
mCanvas.rotate(angle, x, y);
int roundness = 100;
mCanvas.scale(1f, 100f / roundness, x, y);
mCanvas.drawCircle(x, y, radius, bufferPaint);
mCanvas.restore();
}

How to spin bitmap along with Circle ProgressBar?

I am trying to modify a little this library: https://github.com/Shinelw/ColorArcProgressBar
And this is what i've done so far, without the purple circle indicator:
arc progress bar
My question is: how can i animate the purple circle along with the progress(as shown in the image)?
Inside ColorArcProgressBar class that extends View, at onDraw method, this is how the progress is draw: canvas.drawArc(bgRect, startAngle, currentAngle, false, progressPaint);
and the animation is
private void setAnimation(float last, float current, int length) {
progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngle = (float) animation.getAnimatedValue();
curValues = currentAngle / k;
}
});
progressAnimator.start();
}
I managed to position the purple bitmat at the start of the progress like this:
canvas.drawBitmap(mIcon,bgRect.left+mIcon.getWidth()/2 +30, bgRect.bottom - 40 +mIcon.getHeight()/2 , null);
Now, how can I animate it along the arc like the progress?
Didn't try this out, if it's not right it's close...
// since currentAngle is a "sweep" angle, the
// final angle should be current + start
float thetaD = startAngle + currentAngle;
if (thetaD > 360F) {
thetaD -= 360F;
}
// convert degrees to radians
float theta = (float) Math.toRadians(thetaD);
// polar to Cartesian coordinates
float x = (float) (Math.cos(theta) * bgRect.width() / 2) + bgRect.centerX();
float y = (float) (Math.sin(theta) * bgRect.height() / 2) + bgRect.centerY();
canvas.drawBitmap(mIcon, x - mIcon.getWidth()/2, y - mIcon.getHeight()/2 , null);
It helped that your bitmap is circular, so we didn't have to rotate that...

Animating a list of points to move away from a given touch location

The animation I am working on achieving is shown below in Figure 1.
1)I have a list containing points on a 2D plane(left) that I am working on animating.
2)A touch is made at location x, and an invisible circle is drawn (middle). I want all of the points contained in that circle to move away from the center (x).
3) A final result example (right)
I am planning on applying this in a way that I can supply any list of points, touch location, numFrames, and force applied on each point per frame. An array of length numFrames is returned, each array item being an already animated list.
Since I plan on implementing this on screen touch, it is possible for another touch to happen while the first touch animation is already in progress. How can I handle these collisions?
Figure 1:
here's my 2 cents, you need a list of points, these points should have a position, a velocity and a method to update the position. something like this
public class Point {
public float x, y;
public boolean isMoving;
private float dx,dy;
public Point(float x, float y){
this.x = x;
this.y = y;
}
public boolean move(float maxWidth, float maxHeight){
if(!isMoving) return false;
// update the position
x += dx;
y += dy;
dx /= FRICTION;
dy /= FRICTION;
// bounce...?
if(x < 0 || x > maxWidth){
dx *= -1;
}
if(y < 0 || y > maxHeight){
dy *= -1;
}
if(Math.abs(dx) < MOVEMENT_THRESHOLD && Math.abs(dy) < MOVEMENT_THRESHOLD){
isMoving = false;
}
return isMoving;
}
}
on every touch event you apply a force to every point within the radius and set their velocity
for(Point point : mPoints){
float distance = point.distance(x,y);
if(distance > mTouchRange) continue;
float force = (float) Math.pow(1 - (distance / mTouchRange), 2) * mForceFactor;
float angle = point.angle(x,y);
point.dx -= Math.cos(angle) * force;
point.dy -= Math.sin(angle) * force;
point.isMoving = true;
}
then you need an animation that call move on every frame and eventully stops when there are no moving points
you can find the full example here

How to redraw correctly angle on circle, which starts from current time, to the point affected on the circle?

I have a circle drawn on canvas. On top on this circle I draw an angle, which starts from part of the circle equal to current time.
Then onTouchEvent, the angle should be redrawn from start point (current time), to end point, based on a point on the circle.
The problem is with calculation dynamically sweep angle in method onTouchEvent.
I've tried different calculations, taken from different Stackoverflow posts / suggestions, and none did not work according to my expectation. The reaction of the angle, onTouchEvent, was always a bit unpredicted.
My code snippet:
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
int radius = getRadius();
int startAngle = getStartAngle();
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, this.bkgPaint);
this.arcRect = new RectF((getWidth() / 2) - radius, (getHeight() / 2) - radius, (getWidth() / 2) + radius, (getHeight() / 2) + radius);
canvas.drawArc(this.arcRect, startAngle, sweepAngle, true, arcPaint);
}
#Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_MOVE) {
sweepAngle = (int) ((360.0D + Math.toDegrees(Math.atan2(e.getX() - 360.0D, 360.0D - e.getY()))) % 360.0D);
invalidate();
}
return true;
}
private int getStartAngle() {
Calendar cal = Calendar.getInstance();
int minutes = cal.get(Calendar.HOUR_OF_DAY) * 60 + cal.get(Calendar.MINUTE);
if (minutes > 720) {
minutes -= 720;
}
int angle = minutes / 2;
return (angle += 270) % 360;
}
private int getRadius() {
return ((80 * getWidth()) / 100) / 2;
}
I've used similar calculations in my color picker. It's open source, you can find sources here.
In your case I would start with calculating end angle, like this:
#Override
public boolean onTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) e.getX();
int y = (int) e.getY();
int cx = x - getWidth() / 2;
int cy = y - getHeight() / 2;
endAngle = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 360f) % 360f;
invalidate();
return true;
}
return super.onTouchEvent(e);
}
Adding 360 degrees with modulo is required. After this operation you'll have 0 degrees on the right side of the circle, 90 at the bottom, 180 on the left and 270 at the top.
Now your sweep angle will be endAngle - startAngle. And that's it!

Following a straight line (via Path?)

I'm working on a game which will use projectiles. So I've made a Projectile class and a new instance is created when the user touches the screen:
#Override
public boolean onTouch(View v, MotionEvent e){
float touch_x = e.getX();
float touch_y = e.getY();
new Projectile(touch_x, touch_y);
}
And the Projectile class:
public class Projectile{
float target_x;
float target_y;
Path line;
public Projectile(float x, float y){
target_x = x;
target_y = y;
line = new Path();
line.moveTo(MyGame.mPlayerXPos, MyGame.mPlayerYPos);
line.lineTo(target_x, target_y);
}
}
So this makes a Path with 2 points, the player's position and and touch coords. My question is - How can you access points on this line? For example, if I wanted to get the x,y coords of the Projectile at the half point of the line, or the point the Projectile would be at after 100 ticks (moving at a speed of X pixels/tick)?
I also need the Projectile to continue moving after it reaches the final point.. do I need to use line.addPath(line) to keep extending the Path?
EDIT
I managed to get the Projectiles moving in a straight line, but they're going in strange directions. I had to fudge some code up:
private void moveProjectiles(){
ListIterator<Projectile> it = Registry.proj.listIterator();
while ( it.hasNext() ){
Projectile p = it.next();
p.TimeAlive++;
double dist = p.TimeAlive * p.Speed;
float dx = (float) (Math.cos(p.Angle) * dist);
float dy = (float) (Math.sin(p.Angle) * dist);
p.xPos += dx;
p.yPos += -dy;
}
}
The Angle must be the problem.. I'm using this method, which works perfectly:
private double getDegreesFromTouchEvent(float x, float y){
double delta_x = x - mCanvasWidth/2;
double delta_y = mCanvasHeight/2 - y;
double radians = Math.atan2(delta_y, delta_x);
return Math.toDegrees(radians);
}
However, it returns 0-180 for touches above the center of the screen, and 0 to -180 for touches below. Is this a problem?
The best way to model this is with parametric equations. No need to use trig functions.
class Path {
private final float x1,y1,x2,y2,distance;
public Path( float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.distance = Math.sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
public Point position( float t) {
return new Point( (1-t)*x1 + t*x2,
(1-t)*y1 + t*y2);
}
public Point position( float ticks, float speed) {
float t = ticks * speed / distance;
return position( t);
}
}
Path p = new Path(...);
// get halfway point
p.position( 0.5);
// get position after 100 ticks at 1.5 pixels per tick
p.position( 100, 1.5);
From geometry, if it's a straight line you can calculate any point on it by using polar coordinates.
If you find the angle of the line:
ang = arctan((target_y - player_y) / (target_x - player_x))
Then any point on the line can be found using trig:
x = cos(ang) * dist_along_line
y = sin(ang) * dist_along_line
If you wanted the midpoint, then you just take dist_along_line to be half the length of the line:
dist_along_line = line_length / 2 = (sqrt((target_y - player_y)^2 + (target_x - player_x)^2)) / 2
If you wanted to consider the point after 100 ticks, moving at a speed of X pixels / tick:
dist_along_line = 100 * X
Hopefully someone can comment on a way to do this more directly using the android libs.
First of all, the Path class is to be used for drawing, not for calculation of the projectile location.
So your Projectile class could have the following attributes:
float positionX;
float positionY;
float velocityX;
float velocityY;
The velocity is calculated from the targetX, targetY, playerX and playerY like so:
float distance = sqrt(pow(targetX - playerX, 2)+pow(targetY - playerY, 2))
velocityX = (targetX - playerX) * speed / distance;
velocityY = (targetY - playerY) * speed / distance;
Your position after 20 ticks is
x = positionX + 20 * velocityX;
y = positionY + 20 * velocityY;
The time it takes to reach terget is
ticksToTarget = distance / velocity;
Location of halp way point is
halfWayX = positionX + velocityX * (tickToTarget / 2);
halfWayY = positionY + velocityY * (tickToTarget / 2);

Categories

Resources