/*
* This file provided by Facebook is for non-commercial testing and evaluation purposes only.
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.facebook.rebound.playground.examples;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.facebook.rebound.BaseSpringSystem;
import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringConfig;
import com.facebook.rebound.SpringListener;
import com.facebook.rebound.SpringSystem;
import com.facebook.rebound.SpringSystemListener;
import com.facebook.rebound.playground.app.Util;
import java.util.ArrayList;
import java.util.List;
public class BallExample extends FrameLayout implements SpringListener, SpringSystemListener {
private final Spring xSpring;
private final Spring ySpring;
private final SpringSystem springSystem;
private final SpringConfig COASTING;
private float x;
private float y;
private Paint paint;
private boolean dragging;
private float radius = 100;
private float downX;
private float downY;
private float lastX;
private float lastY;
private VelocityTracker velocityTracker;
private float centerX;
private float centerY;
private float attractionThreshold = 200;
private SpringConfig CONVERGING = SpringConfig.fromOrigamiTensionAndFriction(20, 3);
private List<PointF> points = new ArrayList<PointF>();
private ArgbEvaluator colorEvaluator = new ArgbEvaluator();
private Integer startColor = Color.argb(255, 0, 255, 48);
private Integer endColor = Color.argb(255, 0, 228, 255);
public BallExample(Context context) {
this(context, null);
}
public BallExample(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BallExample(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
COASTING = SpringConfig.fromOrigamiTensionAndFriction(0, 0.5);
COASTING.tension = 0;
setBackgroundColor(Color.WHITE);
springSystem = SpringSystem.create();
springSystem.addListener(this);
xSpring = springSystem.createSpring();
ySpring = springSystem.createSpring();
xSpring.addListener(this);
ySpring.addListener(this);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
centerX = getWidth() / 2f;
centerY = getHeight() / 2f;
xSpring.setCurrentValue(centerX).setAtRest();
ySpring.setCurrentValue(centerY).setAtRest();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
int offsetH = (int) ((getHeight() - (2 * radius)) % 800) / 2;
int offsetW = (int) ((getWidth() - (2 * radius)) % 800) / 2;
for (float i = offsetH + radius; i < getHeight() - offsetH; i += 400) {
for (float j = offsetW + radius; j < getWidth() - offsetW; j += 400) {
points.add(new PointF(j, i));
}
}
}
});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int bg = Color.argb(255, 240, 240, 240);
canvas.drawColor(bg);
int i = 0;
for (PointF point : points) {
paint.setColor(Color.argb(255, 255, 255, 255));
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(point.x, point.y, attractionThreshold - 80, paint);
Integer color = (Integer) colorEvaluator.evaluate(
(i + 1) / (float) points.size(), startColor, endColor);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
canvas.drawCircle(point.x, point.y, attractionThreshold - 80, paint);
i++;
}
paint.setColor(Color.argb(200, 255, 0, 0));
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x, y, radius, paint);
paint.setColor(Color.WHITE);
paint.setTextSize(36);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("TOUCH", x, y + 10, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getRawX();
float touchY = event.getRawY();
boolean ret = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = touchX;
downY = touchY;
lastX = downX;
lastY = downY;
velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
if (downX > x - radius && downX < x + radius && downY > y - radius && downY < y + radius) {
dragging = true;
ret = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (!dragging) {
break;
}
velocityTracker.addMovement(event);
float offsetX = lastX - touchX;
float offsetY = lastY - touchY;
xSpring.setCurrentValue(xSpring.getCurrentValue() - offsetX).setAtRest();
ySpring.setCurrentValue(ySpring.getCurrentValue() - offsetY).setAtRest();
checkConstraints();
ret = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (!dragging) {
break;
}
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
dragging = false;
ySpring.setSpringConfig(COASTING);
xSpring.setSpringConfig(COASTING);
downX = 0;
downY = 0;
xSpring.setVelocity(velocityTracker.getXVelocity());
ySpring.setVelocity(velocityTracker.getYVelocity());
ret = true;
}
lastX = touchX;
lastY = touchY;
return ret;
}
@Override
public void onSpringUpdate(Spring spring) {
x = (float) xSpring.getCurrentValue();
y = (float) ySpring.getCurrentValue();
invalidate();
}
@Override
public void onSpringAtRest(Spring spring) {
}
@Override
public void onSpringActivate(Spring spring) {
}
@Override
public void onSpringEndStateChange(Spring spring) {
}
@Override
public void onBeforeIntegrate(BaseSpringSystem springSystem) {
}
@Override
public void onAfterIntegrate(BaseSpringSystem springSystem) {
checkConstraints();
}
private void checkConstraints() {
if (x + radius >= getWidth()) {
xSpring.setVelocity(-xSpring.getVelocity());
xSpring.setCurrentValue(xSpring.getCurrentValue() - (x + radius - getWidth()), false);
}
if (x - radius <= 0) {
xSpring.setVelocity(-xSpring.getVelocity());
xSpring.setCurrentValue(xSpring.getCurrentValue() - (x - radius), false);
}
if (y + radius >= getHeight()) {
ySpring.setVelocity(-ySpring.getVelocity());
ySpring.setCurrentValue(ySpring.getCurrentValue() - (y + radius - getHeight()), false);
}
if (y - radius <= 0) {
ySpring.setVelocity(-ySpring.getVelocity());
ySpring.setCurrentValue(ySpring.getCurrentValue() - (y - radius), false);
}
for (PointF point : points) {
if (dist(x, y, point.x, point.y) < attractionThreshold &&
Math.abs(xSpring.getVelocity()) < 900 &&
Math.abs(ySpring.getVelocity()) < 900 &&
!dragging) {
xSpring.setSpringConfig(CONVERGING);
xSpring.setEndValue(point.x);
ySpring.setSpringConfig(CONVERGING);
ySpring.setEndValue(point.y);
}
}
}
private float dist(double posX, double posY, double pos2X, double pos2Y) {
return (float) Math.sqrt(Math.pow(pos2X - posX, 2) + Math.pow(pos2Y - posY, 2));
}
}