下面列出了android.graphics.Path#cubicTo ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
public void animateCirclesFall() throws Exception
{
List<OBAnim> anims = new ArrayList<>();
for(OBControl circle : eventTargets)
{
PointF startPoint = OBMisc.copyPoint(circle.position());
PointF endPoint = (PointF) circle.propertyValue("startloc") ;
circle.setZPosition ( 2);
Path path = new Path();
PointF cp1 = new PointF(startPoint.x+ (startPoint.x > endPoint.x ? -2.5f * circle.width() : 2.5f * circle.width() ), startPoint.y);
PointF cp2 = new PointF(endPoint.x, endPoint.y - circle.width() * 2);
path.moveTo(startPoint.x, startPoint.y);
path.cubicTo(cp1.x,cp1.y,cp2.x,cp2.y, endPoint.x, endPoint.y);
anims.add(OBAnim.pathMoveAnim(circle,path,false,0));
anims.add(OBAnim.colourAnim("fillColor",currentcolour,((OBGroup)circle).objectDict.get("background")));
}
playSfxAudio("number_fly",false);
OBAnimationGroup.runAnims(anims,1,true,OBAnim.ANIM_EASE_IN,this);
waitSFX();
}
private static Path createCubic(float controlX1, float controlY1,
float controlX2, float controlY2) {
final Path path = new Path();
path.moveTo(0.0f, 0.0f);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
return path;
}
@Override
protected void drawUp(Canvas canvas, boolean isAnim) {
// 设置渐变曲线配置
if (isAnim) {
updatePath(canvas, getPoints(),mMode);
} else {
upLgMoreConfig(canvas);
Path path = new Path();
path.moveTo(mRadius * 2 + mPointPaint.getStrokeWidth(), mView_H - mRadius - mPointPaint.getStrokeWidth());
path.cubicTo(control1.x, control1.y, control2.x, control2.y, mView_W - (mRadius * 2) - mPointPaint.getStrokeWidth(), control2.y + DensityUtil.dp2px(2));
canvas.drawPath(path, mPaint);
}
}
private Path calculateUnderHalfPath(Indicator indicator) {
Path mBezierPath = new Path();
mBezierPath.moveTo(indicator.point0.x, indicator.point0.y);
mBezierPath.cubicTo(indicator.point1.x, indicator.point1.y, indicator.point2.x, indicator.point2.y, indicator.point3.x, indicator.point3.y);
mBezierPath.cubicTo(indicator.point4.x, indicator.point4.y, indicator.point5.x, indicator.point5.y, indicator.point6.x, indicator.point6.y);
mBezierPath.cubicTo(indicator.point7.x, indicator.point7.y, indicator.point8.x, indicator.point8.y, indicator.point9.x, indicator.point9.y);
mBezierPath.cubicTo(indicator.point10.x, indicator.point10.y, indicator.point11.x, indicator.point11.y, indicator.point0.x, indicator.point0.y);
mBezierPath.close();
return mBezierPath;
}
public LollipopView(Wrapped wrapped, boolean top) {
this.wrapped = new WeakReference<>(wrapped);
this.top = top;
Path startPath = new Path();
startPath.lineTo(0.5f, 0f);
startPath.cubicTo(0.7f, 0f, 0.6f, 1f, 1f, 1f);
startInterpolator = new PathInterpolator(startPath);
Path endPath = new Path();
endPath.cubicTo(0.2f, 0f, 0.1f, 1f, 0.5f, 1f);
endPath.lineTo(1f, 1f);
endInterpolator = new PathInterpolator(endPath);
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setStrokeCap(Paint.Cap.SQUARE);
ringPaint.setStrokeJoin(Paint.Join.MITER);
}
private void drawPath1(Path path, float factor){
float x1 = getWidth();
float y1 = 0f;
float x3 = getWidth() / 2;
float y3 = getHeight() / 2 + factor;
float x2 = getWidth() - factor * 2;
float y2 = getHeight() / 2;
// move and draw
path.moveTo(getWidth(), 0f);
path.cubicTo(x1, y1, x2, y2, x3, y3);
}
private static Path createCubic(
float controlX1, float controlY1, float controlX2, float controlY2) {
final Path path = new Path();
path.moveTo(0.0f, 0.0f);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0f, 1.0f);
return path;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.translate(mCenterX, mCenterY); // 将坐标系移动到画布中央
Path path = new Path();
path.moveTo(5.82f * mScaleW, 51.78f * mScaleH);
path.quadTo(24 * mScaleW, 28 * mScaleH, 51.2f * mScaleW, 34.4f * mScaleH);
path.cubicTo(51f * mScaleW, 14 * mScaleH, 113 * mScaleW, -3 * mScaleH, mWidth, 51.78f * mScaleH);
path.lineTo(5.82f * mScaleW, 51.78f * mScaleH);
canvas.drawPath(path, mPaint);
}
private Path makePathAndBoundingBox(SVG.Ellipse obj)
{
float cx = (obj.cx != null) ? obj.cx.floatValueX(this) : 0f;
float cy = (obj.cy != null) ? obj.cy.floatValueY(this) : 0f;
float rx = obj.rx.floatValueX(this);
float ry = obj.ry.floatValueY(this);
float left = cx - rx;
float top = cy - ry;
float right = cx + rx;
float bottom = cy + ry;
if (obj.boundingBox == null) {
obj.boundingBox = new Box(left, top, rx*2, ry*2);
}
float cpx = rx * BEZIER_ARC_FACTOR;
float cpy = ry * BEZIER_ARC_FACTOR;
Path p = new Path();
p.moveTo(cx, top);
p.cubicTo(cx+cpx, top, right, cy-cpy, right, cy);
p.cubicTo(right, cy+cpy, cx+cpx, bottom, cx, bottom);
p.cubicTo(cx-cpx, bottom, left, cy+cpy, left, cy);
p.cubicTo(left, cy-cpy, cx-cpx, top, cx, top);
p.close();
return p;
}
private Path makePathAndBoundingBox(SVG.Ellipse obj)
{
float cx = (obj.cx != null) ? obj.cx.floatValueX(this) : 0f;
float cy = (obj.cy != null) ? obj.cy.floatValueY(this) : 0f;
float rx = obj.rx.floatValueX(this);
float ry = obj.ry.floatValueY(this);
float left = cx - rx;
float top = cy - ry;
float right = cx + rx;
float bottom = cy + ry;
if (obj.boundingBox == null) {
obj.boundingBox = new Box(left, top, rx*2, ry*2);
}
float cpx = rx * BEZIER_ARC_FACTOR;
float cpy = ry * BEZIER_ARC_FACTOR;
Path p = new Path();
p.moveTo(cx, top);
p.cubicTo(cx+cpx, top, right, cy-cpy, right, cy);
p.cubicTo(right, cy+cpy, cx+cpx, bottom, cx, bottom);
p.cubicTo(cx-cpx, bottom, left, cy+cpy, left, cy);
p.cubicTo(left, cy-cpy, cx-cpx, top, cx, top);
p.close();
return p;
}
public static Path getTwoCircle_CutLineMergePath(Circle c1, Circle c2, double expandAngel) {
float startAngel = GeometryUtils.getP2AngleByX(c2.center, c1.center);
Matrix m = new Matrix();
float[] src = new float[]{c2.center.x, c2.center.y};
float[] dst = new float[2];//代替了c2.center的点
m.postRotate(-startAngel, c1.center.x, c1.center.y); //现在都在右边了
m.mapPoints(dst, src);
//现在为止 就是 c1,c2 x点都是相同了 并且 c2在c1右边
ZPointF pA, pB, pC, pD;//AD 在c1, BC在c2上
ZPointF pApDControl, pBpCControl;//AD 在c1, BC在c2上
pA = new ZPointF((float) Math.cos(Math.toRadians(expandAngel / 2)) * c1.r, -(float) Math.sin(Math.toRadians(expandAngel / 2)) * c1.r);
pA.offset(c1.center);
pD = new ZPointF((float) Math.cos(Math.toRadians(expandAngel / 2)) * c1.r, (float) Math.sin(Math.toRadians(expandAngel / 2)) * c1.r);
pD.offset(c1.center);
pApDControl = new ZPointF((float) (c1.r / Math.cos(Math.toRadians(expandAngel / 2))), 0);
pApDControl.offset(c1.center);
pB = new ZPointF(-(float) Math.cos(Math.toRadians(expandAngel / 2)) * c2.r, -(float) Math.sin(Math.toRadians(expandAngel / 2)) * c2.r);
pB.offset(dst[0], dst[1]);
pC = new ZPointF(-(float) Math.cos(Math.toRadians(expandAngel / 2)) * c2.r, (float) Math.sin(Math.toRadians(expandAngel / 2)) * c2.r);
pC.offset(dst[0], dst[1]);
pBpCControl = new ZPointF(-(float) (c2.r / Math.cos(Math.toRadians(expandAngel / 2))), 0);
pBpCControl.offset(dst[0], dst[1]);
Path resultPath = new Path();
resultPath.moveTo(pA.x, pA.y);
resultPath.cubicTo(pApDControl.x, pApDControl.y, pBpCControl.x, pBpCControl.y, pB.x, pB.y);
resultPath.lineTo(pC.x, pC.y);
resultPath.cubicTo(pBpCControl.x, pBpCControl.y, pApDControl.x, pApDControl.y, pD.x, pD.y);
resultPath.close();
Matrix m2 = new Matrix();
m2.postRotate(startAngel, c1.center.x, c1.center.y);
resultPath.transform(m2);
return resultPath;
}
protected Path transformSmile(float trans, float fraction, Path path, Smile s1, Smile s2, FloatEvaluator evaluator) {
path.reset();
path.moveTo(
evaluator.evaluate(fraction, s1.START_POINT.x, s2.START_POINT.x) + trans,
evaluator.evaluate(fraction, s1.START_POINT.y, s2.START_POINT.y)
);
path.cubicTo(
evaluator.evaluate(fraction, s1.TOP_CURVE[0].x, s2.TOP_CURVE[0].x) + trans,
evaluator.evaluate(fraction, s1.TOP_CURVE[0].y, s2.TOP_CURVE[0].y),
evaluator.evaluate(fraction, s1.TOP_CURVE[1].x, s2.TOP_CURVE[1].x) + trans,
evaluator.evaluate(fraction, s1.TOP_CURVE[1].y, s2.TOP_CURVE[1].y),
evaluator.evaluate(fraction, s1.TOP_CURVE[2].x, s2.TOP_CURVE[2].x) + trans,
evaluator.evaluate(fraction, s1.TOP_CURVE[2].y, s2.TOP_CURVE[2].y)
);
path.cubicTo(
evaluator.evaluate(fraction, s1.RIGHT_CURVE[0].x, s2.RIGHT_CURVE[0].x) + trans,
evaluator.evaluate(fraction, s1.RIGHT_CURVE[0].y, s2.RIGHT_CURVE[0].y),
evaluator.evaluate(fraction, s1.RIGHT_CURVE[1].x, s2.RIGHT_CURVE[1].x) + trans,
evaluator.evaluate(fraction, s1.RIGHT_CURVE[1].y, s2.RIGHT_CURVE[1].y),
evaluator.evaluate(fraction, s1.RIGHT_CURVE[2].x, s2.RIGHT_CURVE[2].x) + trans,
evaluator.evaluate(fraction, s1.RIGHT_CURVE[2].y, s2.RIGHT_CURVE[2].y)
);
path.cubicTo(
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[0].x, s2.BOTTOM_CURVE[0].x) + trans,
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[0].y, s2.BOTTOM_CURVE[0].y),
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[1].x, s2.BOTTOM_CURVE[1].x) + trans,
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[1].y, s2.BOTTOM_CURVE[1].y),
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[2].x, s2.BOTTOM_CURVE[2].x) + trans,
evaluator.evaluate(fraction, s1.BOTTOM_CURVE[2].y, s2.BOTTOM_CURVE[2].y)
);
path.cubicTo(
evaluator.evaluate(fraction, s1.LEFT_CURVE[0].x, s2.LEFT_CURVE[0].x) + trans,
evaluator.evaluate(fraction, s1.LEFT_CURVE[0].y, s2.LEFT_CURVE[0].y),
evaluator.evaluate(fraction, s1.LEFT_CURVE[1].x, s2.LEFT_CURVE[1].x) + trans,
evaluator.evaluate(fraction, s1.LEFT_CURVE[1].y, s2.LEFT_CURVE[1].y),
evaluator.evaluate(fraction, s1.LEFT_CURVE[2].x, s2.LEFT_CURVE[2].x) + trans,
evaluator.evaluate(fraction, s1.LEFT_CURVE[2].y, s2.LEFT_CURVE[2].y)
);
path.close();
return path;
}
private Path makePathAndBoundingBox(Rect obj)
{
float x, y, w, h, rx, ry;
if (obj.rx == null && obj.ry == null) {
rx = 0;
ry = 0;
} else if (obj.rx == null) {
rx = ry = obj.ry.floatValueY(this);
} else if (obj.ry == null) {
rx = ry = obj.rx.floatValueX(this);
} else {
rx = obj.rx.floatValueX(this);
ry = obj.ry.floatValueY(this);
}
rx = Math.min(rx, obj.width.floatValueX(this) / 2f);
ry = Math.min(ry, obj.height.floatValueY(this) / 2f);
x = (obj.x != null) ? obj.x.floatValueX(this) : 0f;
y = (obj.y != null) ? obj.y.floatValueY(this) : 0f;
w = obj.width.floatValueX(this);
h = obj.height.floatValueY(this);
if (obj.boundingBox == null) {
obj.boundingBox = new Box(x, y, w, h);
}
float right = x + w;
float bottom = y + h;
Path p = new Path();
if (rx == 0 || ry == 0)
{
// Simple rect
p.moveTo(x, y);
p.lineTo(right, y);
p.lineTo(right, bottom);
p.lineTo(x, bottom);
p.lineTo(x, y);
}
else
{
// Rounded rect
// Bexier control point lengths for a 90 degress arc
float cpx = rx * BEZIER_ARC_FACTOR;
float cpy = ry * BEZIER_ARC_FACTOR;
p.moveTo(x, y+ry);
p.cubicTo(x, y+ry-cpy, x+rx-cpx, y, x+rx, y);
p.lineTo(right-rx, y);
p.cubicTo(right-rx+cpx, y, right, y+ry-cpy, right, y+ry);
p.lineTo(right, bottom-ry);
p.cubicTo(right, bottom-ry+cpy, right-rx+cpx, bottom, right-rx, bottom);
p.lineTo(x+rx, bottom);
p.cubicTo(x+rx-cpx, bottom, x, bottom-ry+cpy, x, bottom-ry);
p.lineTo(x, y+ry);
}
p.close();
return p;
}
protected void DrawCubicPath(Canvas canvas, List<ChartData> dataList, Paint paint, float height, boolean area_spline) {
final int lineSize = dataList.size();
float prePriviousX = Float.NaN;
float prePreviousY = Float.NaN;
float previousX = Float.NaN;
float previousY = Float.NaN;
float curr_x = Float.NaN;
float curr_y = Float.NaN;
float next_x= Float.NaN;
float next_y = Float.NaN;
Path path = new Path();
if(area_spline){
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setAlpha(100);
path.moveTo(dataList.get(0).getX_values(), height);
path.lineTo(dataList.get(0).getX_values(),
dataList.get(0).getY_values());
}
for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
if (Float.isNaN(curr_x)) {
curr_x = dataList.get(valueIndex).getX_values();
curr_y = dataList.get(valueIndex).getY_values();
}
if (Float.isNaN(previousX)) {
if (valueIndex > 0) {
previousX = dataList.get(valueIndex - 1).getX_values();
previousY = dataList.get(valueIndex - 1).getY_values();
} else {
previousX = curr_x;
previousY = curr_y;
}
}
if (Float.isNaN(prePriviousX)) {
if (valueIndex > 1) {
prePriviousX = dataList.get(valueIndex - 2).getX_values();
prePreviousY = dataList.get(valueIndex - 2).getY_values();
}
else {
prePriviousX = previousX;
prePreviousY = previousY;
}
}
// nextPoint is always new one or it is equal currentPoint.
if (valueIndex < lineSize - 1) {
next_x = dataList.get(valueIndex + 1).getX_values();
next_y = dataList.get(valueIndex + 1).getY_values();
}
else {
next_x = curr_x;
next_y = curr_y;
}
if (valueIndex == 0) {
if(!area_spline)
path.moveTo(curr_x, curr_y);
}
else {
// Calculate control points.
final float first_diff_x = (curr_x - prePriviousX);
final float fisrt_diff_y = (curr_y - prePreviousY);
final float sec_diff_x = (next_x - previousX);
final float sec_diff_y = (next_y - previousY);
final float first_control_x = previousX + (curve_intensity * first_diff_x);
final float first_control_y = previousY + (curve_intensity * fisrt_diff_y);
final float secondControlPointX = curr_x - (curve_intensity * sec_diff_x);
final float secondControlPointY = curr_y - (curve_intensity * sec_diff_y);
path.cubicTo(first_control_x, first_control_y, secondControlPointX, secondControlPointY,
curr_x, curr_y);
}
prePriviousX = previousX;
prePreviousY = previousY;
previousX = curr_x;
previousY = curr_y;
curr_x = next_x;
curr_y = next_y;
}
if(area_spline){
path.lineTo(dataList.get(dataList.size()-1).getX_values(),
height);
}
canvas.drawPath(path, paint);
path.reset();
}
/**
* Converts an arc to cubic Bezier segments and records them in p.
*
* @param p The target for the cubic Bezier segments
* @param cx The x coordinate center of the ellipse
* @param cy The y coordinate center of the ellipse
* @param a The radius of the ellipse in the horizontal direction
* @param b The radius of the ellipse in the vertical direction
* @param e1x E(eta1) x coordinate of the starting point of the arc
* @param e1y E(eta2) y coordinate of the starting point of the arc
* @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
* @param start The startAnimation angle of the arc on the ellipse
* @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
*/
private static void arcToBezier(Path p,
double cx,
double cy,
double a,
double b,
double e1x,
double e1y,
double theta,
double start,
double sweep) {
// Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
// and http://www.spaceroots.org/documents/ellipse/node22.html
// Maximum of 45 degrees per cubic Bezier segment
int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
double eta1 = start;
double cosTheta = Math.cos(theta);
double sinTheta = Math.sin(theta);
double cosEta1 = Math.cos(eta1);
double sinEta1 = Math.sin(eta1);
double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
double anglePerSegment = sweep / numSegments;
for (int i = 0; i < numSegments; i++) {
double eta2 = eta1 + anglePerSegment;
double sinEta2 = Math.sin(eta2);
double cosEta2 = Math.cos(eta2);
double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
double tanDiff2 = Math.tan((eta2 - eta1) / 2);
double alpha =
Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
double q1x = e1x + alpha * ep1x;
double q1y = e1y + alpha * ep1y;
double q2x = e2x - alpha * ep2x;
double q2y = e2y - alpha * ep2y;
// Adding this no-op call to workaround a proguard related issue.
p.rLineTo(0, 0);
p.cubicTo((float) q1x,
(float) q1y,
(float) q2x,
(float) q2y,
(float) e2x,
(float) e2y);
eta1 = eta2;
e1x = e2x;
e1y = e2y;
ep1x = ep2x;
ep1y = ep2y;
}
}
private void drawRouge(String blushColor, Bitmap getBitmap) {
selectedBrushColor = blushColor;
Log.d(TAG + " selectedBlushColor ", selectedBrushColor);
Canvas drawCanvas = new Canvas(getBitmap);
Paint mPaint = new Paint();
int rougeColor = stringColorRGBToARGB(blushColor, 40 + alphaValueRouge, 0, 0, 0);
mPaint.setColor(rougeColor);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeJoin(Paint.Join.ROUND); // set the join to round you want
mPaint.setStrokeCap(Paint.Cap.ROUND); // set the paint cap to round too
mPaint.setPathEffect(new CornerPathEffect(70));
mPaint.setXfermode(mXfermode);
mPaint.setStrokeWidth(1f);
int sc = drawCanvas.saveLayer(0, 0, getBitmap.getWidth(), getBitmap.getHeight(), null, Canvas.ALL_SAVE_FLAG);
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));
//nose_contour_left2 - contour_left3
float rouge_left_x = landmark_pt_x.get(30) - (landmark_pt_x.get(30) - landmark_pt_x.get(2)) / 2f;
float rouge_left_y = landmark_pt_y.get(31);
float slope = ((landmark_pt_y.get(31) - landmark_pt_y.get(35)) / (landmark_pt_x.get(31) - landmark_pt_x.get(35)));
rouge_left_y = ((rouge_left_x - landmark_pt_x.get(31)) * slope) + landmark_pt_y.get(31);
// drawCanvas.drawCircle(rouge_left_x, rouge_left_y, 30f, mPaint);
// nose_contour_right2 - contour_right3
float rouge_right_x = landmark_pt_x.get(30) + (landmark_pt_x.get(14) - landmark_pt_x.get(30)) / 2f;
float rouge_right_y = landmark_pt_y.get(35);
rouge_right_y = ((rouge_right_x - landmark_pt_x.get(35)) * slope) + landmark_pt_y.get(35);
Path path = new Path();
path.reset();
//draw left rouge
// contour_left3
path.moveTo(landmark_pt_x.get(2) + 3f, landmark_pt_y.get(2));
path.cubicTo(landmark_pt_x.get(3) + 3f, landmark_pt_y.get(3),
landmark_pt_x.get(4) + 3f, landmark_pt_y.get(4),
landmark_pt_x.get(5) + 3f, landmark_pt_y.get(5) - 4f
);
path.lineTo(rouge_left_x, rouge_left_y);
path.lineTo(landmark_pt_x.get(2) + 3f, landmark_pt_y.get(2));
path.close();
drawCanvas.drawPath(path, mPaint);
path.reset();
//draw right rouge
// contour_right3
path.moveTo(landmark_pt_x.get(14) - 3f, landmark_pt_y.get(14));
path.cubicTo(landmark_pt_x.get(13) - 3f, landmark_pt_y.get(13),
landmark_pt_x.get(12) - 3f, landmark_pt_y.get(12),
landmark_pt_x.get(11) - 3f, landmark_pt_y.get(11) - 4f
);
path.lineTo(rouge_right_x, rouge_right_y);
path.lineTo(landmark_pt_x.get(14) - 3f, landmark_pt_y.get(14));
drawCanvas.drawPath(path, mPaint);
//设置混合模式
mPaint.setXfermode(mXfermode);
//清除混合模式
mPaint.setXfermode(null);
//还原画布
drawCanvas.restoreToCount(sc);
drawCanvas.setBitmap(getBitmap);
}
@Override
public Path getPath(float startX, float startY, float endX, float endY) {
Path path = new Path();
path.moveTo(startX, startY);
float ex;
float ey;
if (startY == endY) {
ex = (startX + endX) / 2;
ey = startY + mMinimumHorizontalTangent * Math.abs(endX - startX) / 2;
} else if (startX == endX) {
ex = startX + mMinimumVerticalTangent * Math.abs(endY - startY) / 2;
ey = (startY + endY) / 2;
} else {
float deltaX = endX - startX;
float deltaY;
if (endY < startY) {
deltaY = startY - endY; // Y is inverted compared to diagram above.
} else {
deltaY = endY - startY;
}
// hypotenuse squared.
float h2 = deltaX * deltaX + deltaY * deltaY;
// Midpoint between start and end
float dx = (startX + endX) / 2;
float dy = (startY + endY) / 2;
// Distance squared between end point and mid point is (1/2 hypotenuse)^2
float midDist2 = h2 * 0.25f;
float minimumArcDist2;
if (Math.abs(deltaX) < Math.abs(deltaY)) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
// bd = hypotenuse/2
// fb = deltaY
float eDistY = h2 / (2 * deltaY);
ey = endY + eDistY;
ex = endX;
minimumArcDist2 = midDist2 * mMinimumVerticalTangent
* mMinimumVerticalTangent;
} else {
// Same as above, but flip X & Y
float eDistX = h2 / (2 * deltaX);
ex = endX + eDistX;
ey = endY;
minimumArcDist2 = midDist2
* mMinimumHorizontalTangent
* mMinimumHorizontalTangent;
}
float arcDistX = dx - ex;
float arcDistY = dy - ey;
float arcDist2 = arcDistX * arcDistX + arcDistY * arcDistY;
float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
float newArcDistance2 = 0;
if (arcDist2 < minimumArcDist2) {
newArcDistance2 = minimumArcDist2;
} else if (arcDist2 > maximumArcDist2) {
newArcDistance2 = maximumArcDist2;
}
if (newArcDistance2 != 0) {
float ratio2 = newArcDistance2 / arcDist2;
float ratio = (float) Math.sqrt(ratio2);
ex = dx + (ratio * (ex - dx));
ey = dy + (ratio * (ey - dy));
}
}
float controlX1 = (startX + ex) / 2;
float controlY1 = (startY + ey) / 2;
float controlX2 = (ex + endX) / 2;
float controlY2 = (ey + endY) / 2;
path.cubicTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
return path;
}
private void backAnimation() {
float endX = 0;
float endY = 0;
float startX = actionButton.getTranslationX();
float startY = actionButton.getTranslationY();
final float curveRadius = -background.getHeight() / 2;
Path arcPath = new Path();
float midX = startX + ((endX - startX) / 2);
float midY = startY + ((endY - startY) / 2);
float xDiff = midX - startX;
float yDiff = midY - startY;
double angle = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90;
double angleRadians = Math.toRadians(angle);
float pointX = (float) (midX + curveRadius * Math.cos(angleRadians));
float pointY = (float) (midY + curveRadius * Math.sin(angleRadians));
arcPath.moveTo(startX, startY);
arcPath.cubicTo(startX, startY, pointX, pointY, endX, endY);
ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1);
pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private float point[] = new float[2];
private PathMeasure pathMeasure = new PathMeasure(arcPath, false);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float value = animation.getAnimatedFraction();
// Gets the point at the fractional path length
pathMeasure.getPosTan(pathMeasure.getLength() * value, point, null);
// Sets view location to the above point
actionButton.setTranslationX(point[0]);
actionButton.setTranslationY(point[1]);
}
});
pathAnimator.setInterpolator(new DecelerateInterpolator());
pathAnimator.setDuration(duration(R.integer.path_duration));
pathAnimator.start();
}
/**
* Converts an arc to cubic Bezier segments and records them in p.
*
* @param p The target for the cubic Bezier segments
* @param cx The x coordinate center of the ellipse
* @param cy The y coordinate center of the ellipse
* @param a The radius of the ellipse in the horizontal direction
* @param b The radius of the ellipse in the vertical direction
* @param e1x E(eta1) x coordinate of the starting point of the arc
* @param e1y E(eta2) y coordinate of the starting point of the arc
* @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
* @param start The start angle of the arc on the ellipse
* @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
*/
private static void arcToBezier(Path p,
double cx,
double cy,
double a,
double b,
double e1x,
double e1y,
double theta,
double start,
double sweep) {
// Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
// and http://www.spaceroots.org/documents/ellipse/node22.html
// Maximum of 45 degrees per cubic Bezier segment
int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
double eta1 = start;
double cosTheta = Math.cos(theta);
double sinTheta = Math.sin(theta);
double cosEta1 = Math.cos(eta1);
double sinEta1 = Math.sin(eta1);
double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
double anglePerSegment = sweep / numSegments;
for (int i = 0; i < numSegments; i++) {
double eta2 = eta1 + anglePerSegment;
double sinEta2 = Math.sin(eta2);
double cosEta2 = Math.cos(eta2);
double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
double tanDiff2 = Math.tan((eta2 - eta1) / 2);
double alpha =
Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
double q1x = e1x + alpha * ep1x;
double q1y = e1y + alpha * ep1y;
double q2x = e2x - alpha * ep2x;
double q2y = e2y - alpha * ep2y;
p.cubicTo((float) q1x,
(float) q1y,
(float) q2x,
(float) q2y,
(float) e2x,
(float) e2y);
eta1 = eta2;
e1x = e2x;
e1y = e2y;
ep1x = ep2x;
ep1y = ep2y;
}
}
Landscape(LandscapeView landscapeView, int color, int color2, float height, float fluctuation, float scale) {
this.landscapeView = landscapeView;
this.color = color;
this.color2 = color2;
this.height = height;
this.fluctuation = fluctuation;
this.scale = scale;
path = new Path();
path.moveTo(0, landscapeView.getMaximumHeight());
path.lineTo(landscapeView.getWidth(), landscapeView.getMaximumHeight());
float prevX = landscapeView.getWidth();
float prevY = (float) (landscapeView.getMaximumHeight() - height - Math.random() * fluctuation);
path.lineTo(prevX, prevY);
int widthDiv100 = (int) (landscapeView.getWidth() / (landscapeView.getResources().getDimension(R.dimen.landscapeView_1dip) * 100));
int segments = LandscapeView.random.nextInt(widthDiv100) + widthDiv100;
for (int i = 0; i <= segments; i++) {
float x = landscapeView.getWidth() * (segments - i) / segments;
float y = (float) (landscapeView.getMaximumHeight() - height - Math.random() * fluctuation);
float x33 = MathUtils.lerp(prevX, x, 0.33f);
float x67 = MathUtils.lerp(prevX, x, 0.67f);
path.cubicTo(x33, prevY, x67, y, x, y);
if (LandscapeView.random.nextFloat() > LandscapeView.TREE_RANDOM)
trees.add(new Tree(landscapeView,prevX, prevY, scale));
if (LandscapeView.random.nextFloat() > LandscapeView.TREE_RANDOM)
trees.add(new Tree(landscapeView,x33, MathUtils.lerp(prevY, y, 0.33f), scale));
if (LandscapeView.random.nextFloat() > LandscapeView.TREE_RANDOM)
trees.add(new Tree(landscapeView,x67, MathUtils.lerp(prevY, y, 0.67f), scale));
if (LandscapeView.random.nextFloat() > LandscapeView.TREE_RANDOM)
trees.add(new Tree(landscapeView,x, y, scale));
prevX = x;
prevY = y;
}
path.close();
if (landscapeView.drawClouds) {
clouds.clear();
int cloudCount = LandscapeView.random.nextInt(MAX_CLOUDS - MIN_CLOUDS) + MIN_CLOUDS;
for (int i = 0; i < cloudCount; i++)
clouds.add(new Cloud(LandscapeView.random.nextInt(landscapeView.getWidth()), LandscapeView.random.nextInt((int) landscapeView.skyHeight) + landscapeView.padding, landscapeView.cloudColor,scale));
}
}