基于点的曲线变换。 Android中的Bezier曲线变换

时间:2017-06-18 08:28:03

标签: java android bezier curve smoothing

我有一个点列表,表示我使用Path对象在画布上绘制的曲线。

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);

我想要实现的是能够设置用户可以触摸和移动的控制点,并根据该移动我的点将被转换。与photoshop Pen Tool相同的事情看到图片: enter image description here

注意:android Path仅用于绘图,我不需要修改Path我需要修改坐标。所以上面的代码可以替换为

canvas.drawLine();

它与Path对象无关。

1 个答案:

答案 0 :(得分:3)

这是使用一个“锚点”和两个控制点的简单视图,如果您需要更多锚点,请在路径中添加另一个cubicTo

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);
    }
}