Reputation: 5061
I have a list of points that represents a curve which I draw on a canvas using a Path object.
path.moveTo(x, y);
for (int i = 0; i < points.size(); i++) {
path.lineTo(points.get(i).x, points.get(i).y);
}
canvas.drawPath(path, paint);
What I want to achieve is ability to set control points that user can touch and move and based on that movement my points will get transformed. Same thing that photoshop does with Pen Tool
see image:
Note: android Path is only used for drawing, I don't need to modify Path I need to modify coordinates. so code above can be replaced with
canvas.drawLine();
It nothing has to do with Path object.
Upvotes: 4
Views: 2642
Reputation: 24740
this is the simple view that uses one "anchor" point and two control points, if you need more anchors, add another cubicTo
to your path:
class V extends View {
static final float RADIUS = 32;
Path path = new Path();
Paint pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint controlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
PointF ctrl1 = new PointF();
PointF ctrl2 = new PointF();
PointF ctrl3 = new PointF();
PointF ctrl4 = new PointF();
PointF anchor = new PointF();
GestureDetector detector;
Layout layout;
public V(Context context) {
super(context);
pathPaint.setColor(Color.RED);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeWidth(16);
controlPaint.setColor(Color.GREEN);
controlPaint.setAlpha(128);
detector = new GestureDetector(context, listener);
}
GestureDetector.OnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
PointF target;
@Override
public boolean onDown(MotionEvent e) {
PointF[] targets = { ctrl2, ctrl3, anchor };
for (PointF t : targets) {
if (Math.hypot(t.x - e.getX(), t.y - e.getY()) < RADIUS) {
target = t;
return true;
}
}
target = null;
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (target == null) return false;
target.offset(-distanceX, -distanceY);
if (target == ctrl2 || target == ctrl3) {
PointF otherControl = target == ctrl2 ? ctrl3 : ctrl2;
// anchor just between points
double a = Math.atan2(anchor.y - target.y, anchor.x - target.x);
double r = Math.hypot(otherControl.x - anchor.x, otherControl.y - anchor.y);
otherControl.set((float) (anchor.x + r * Math.cos(a)), (float) (anchor.y + r * Math.sin(a)));
// anchor always in the center
// otherControl.set(2 * anchor.x - target.x, 2 * anchor.y - target.y);
} else {
ctrl2.offset(-distanceX, -distanceY);
ctrl3.offset(-distanceX, -distanceY);
}
rebuildPath();
invalidate();
return true;
}
};
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
ctrl1.set(w * 0.0f, h * 1.0f);
ctrl2.set(w * 0.1f, h * 0.5f);
ctrl3.set(w * 0.9f, h * 0.5f);
ctrl4.set(w * 1.0f, h * 1.0f);
anchor.set(w * 0.5f, h * 0.5f);
rebuildPath();
CharSequence src = "you can drag any green circle: the both control points or the anchor point\n\n" +
"notice that the control points can be adjusted individually - the only constraint for a smooth line is that the anchor point is between them (but not necessarily in the exact center)";
TextPaint tp = new TextPaint();
tp.setColor(Color.WHITE);
tp.setTextSize(32);
layout = new StaticLayout(src, tp, w - 64, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
}
private void rebuildPath() {
path.reset();
path.moveTo(ctrl1.x, ctrl1.y);
path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, anchor.x, anchor.y);
path.cubicTo(ctrl3.x, ctrl3.y, ctrl4.x, ctrl4.y, ctrl4.x, ctrl4.y);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
detector.onTouchEvent(event);
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(32, 32);
layout.draw(canvas);
canvas.restore();
canvas.drawPath(path, pathPaint);
controlPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(anchor.x, anchor.y, RADIUS, controlPaint);
canvas.drawCircle(ctrl2.x, ctrl2.y, RADIUS, controlPaint);
canvas.drawCircle(ctrl3.x, ctrl3.y, RADIUS, controlPaint);
controlPaint.setStyle(Paint.Style.STROKE);
canvas.drawLine(ctrl2.x, ctrl2.y, ctrl3.x, ctrl3.y, controlPaint);
}
}
Upvotes: 5