一行包含一个点

时间:2014-01-02 17:01:40

标签: android path android-canvas point

我希望用户能够在画布周围拖动方块的边缘。使用我当前的解决方案它可以工作,但有毛刺,有时无法选择边缘。是否有一种干净的方法来判断是否已经点击了一条线(例如,通过坐标)?这就是我目前正在测试的方式:

// check edge pressed, edge is the line between to
// coords e.g. (i) & (i = 1)
for (int i = 0; i < coords.size(); i++) {
    p1 = coords.get(i);
    if ((i + 1) > (coords.size() - 1)) p2 = coords.get(0);
    else p2 = coords.get(i + 1);

    // is this the line pressed
    if (p1.x <= event.getX() + 5 && event.getX() - 5 <= p2.x && p1.y <= event.getY() + 5 && event.getY() - 5 <= p2.y) {
        // points found, set to non temp
        // variable for use in ACTION_MOVE
        point1 = p1;
        point2 = p2;
        break;
    } else if (p1.x >= event.getX() + 5 && event.getX() - 5 >= p2.x && p1.y >= event.getY() + 5 && event.getY() - 5 >= p2.y) {
        // points found, set to non temp
        // variable for use in ACTION_MOVE
        point1 = p1;
        point2 = p2;
        break;
    }
}

下面的代码//这是按下的行是最重要的,也是最有可能的问题。 +5和-5用于使用更大的区域来点击。

以下是点击事件的全部内容:

public void EditEdge() {

    //TODO this works like shit             
    // Detect the two coordinates along the edge pressed and drag
    // them
    scene.setOnTouchListener(new View.OnTouchListener() {
        private int startX;
        private int startY;
        private Point point1 = new Point(0, 0);
        private Point point2 = new Point(0, 0);

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    startX = (int) event.getX();
                    startY = (int) event.getY();

                    Point p1;
                    Point p2;

                    // check edge pressed, edge is the line between to
                    // coords e.g. (i) & (i = 1)
                    for (int i = 0; i < coords.size(); i++) {
                        p1 = coords.get(i);
                        if ((i + 1) > (coords.size() - 1)) p2 = coords.get(0);
                        else p2 = coords.get(i + 1);

                        // is this the line pressed
                        if (p1.x <= event.getX() + 5 && event.getX() - 5 <= p2.x && p1.y <= event.getY() + 5 && event.getY() - 5 <= p2.y) {
                            // points found, set to non temp
                            // variable for use in ACTION_MOVE
                            point1 = p1;
                            point2 = p2;
                            break;
                        } else if (p1.x >= event.getX() + 5 && event.getX() - 5 >= p2.x && p1.y >= event.getY() + 5 && event.getY() - 5 >= p2.y) {
                            // points found, set to non temp
                            // variable for use in ACTION_MOVE
                            point1 = p1;
                            point2 = p2;
                            break;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    point1 = new Point(0, 0);
                    point2 = new Point(0, 0);
                    // scene.setOnTouchListener(scene.editModeOnTouchListener);
                    break;
                case MotionEvent.ACTION_MOVE:

                    for (Point p: new Point[] {
                        point1, point2
                    }) {
                        int modX = (int)(p.x + (event.getX() - startX));
                        int modY = (int)(p.y + (event.getY() - startY));
                        p.set(modX, modY);
                    }

                    SetCoords(coords);
                    startX = (int) event.getX();
                    startY = (int) event.getY();

                    break;
                default:
                    return false;
            }
            return true;
        }
    });
}

那么是否有一种更简单的方法可以判断一条线被点击/通过一个点还是不是问题?

由于

3 个答案:

答案 0 :(得分:7)

使用线方程y = mx + b来确定该点是否在一行

float EPSILON = 0.001f;

public boolean isPointOnLine(Point linePointA, Point linePointB, Point point) {
    float m = (linePointB.y - linePointA.y) / (linePointB.x - linePointA.x);
    float b = linePointA.y - m * linePointA.x;
    return Math.abs(point.y - (m*point.x+b)) < EPSILON);
}

答案 1 :(得分:1)

@tyczj的优秀代码!
我添加了一个用例来处理垂直行,它给出了以下代码片段:

public boolean isPointOnLine(PointF lineStaPt, PointF lineEndPt, PointF point) {
    final float EPSILON = 0.001f;
    if (Math.abs(staPt.x - endPt.x) < EPSILON) {
        // We've a vertical line, thus check only the x-value of the point.
        return (Math.abs(point.x - lineStaPt.x) < EPSILON);
    } else {
        float m = (lineEndPt.y - lineStaPt.y) / (lineEndPt.x - lineStaPt.x);
        float b = lineStaPt.y - m * lineStaPt.x;
        return (Math.abs(point.y - (m * point.x + b)) < EPSILON);
    }
}

也是一段代码,用于检查点是否位于线段上:

public boolean isPointOnLineSegment(PointF staPt, PointF endPt, PointF point) {
    final float EPSILON = 0.001f;
    if (isPointOnLine(staPt, endPt, point)) {
        // Create lineSegment bounding-box.
        RectF lb = new RectF(staPt.x, staPt.y, endPt.x, endPt.y);
        // Extend bounds with epsilon.
        RectF bounds = new RectF(lb.left - EPSILON, lb.top - EPSILON, lb.right + EPSILON, lb.bottom + EPSILON);
        // Check if point is contained within lineSegment-bounds.
        return bounds.contains(point.x, point.y);
    }
    return false;
}

答案 2 :(得分:0)

你可以定义8 Rect来检查 - 4个边和4个角(这样你就可以一次移动2个边)。边缘的线条应该具有可触摸区域的宽度。

以触摸事件为中心定义Point,然后有方法检查矩形是否包含点。