0

I have a problem.

How do I check if path in Android intersect with itself?

I have this class:

public class GameView extends SurfaceView implements Runnable {

    Thread gameThread = null;

    SurfaceHolder surfaceHolder;

    volatile boolean playing;

    Canvas canvas;

    private final static int MAX_FPS = 60;
    private final static int FRAME_PERIOD =  1000 / MAX_FPS;

    ArrayList<Point> points = new ArrayList<>();

    private Context ctx;

    Path path;

    public static final int DEFAULT_COLOR = Color.RED;
    public static final int DEFAULT_BG_COLOR = Color.WHITE;

    private static final float TOUCH_TOLERANCE = 4;

    private float mX, mY;
    private Path mPath;
    private Paint mPaint;
    private int backgroundColor = DEFAULT_BG_COLOR;


    public GameView(Context context) {
        super(context);

        ctx = context;

        surfaceHolder = getHolder();
        surfaceHolder.setFormat(PixelFormat.RGBA_8888);

        path = new Path();


        playing = true;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(DEFAULT_COLOR);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setXfermode(null);
        mPaint.setAlpha(0xff);
        mPaint.setStrokeWidth(12);

    }


    static Boolean isPathComplex(List<Point> path) {

        if (path == null || path.size() <= 2) {
            return false;
        }

        int len = path.size();

        for (int i = 1; i < len; i++) {
            Point lineAStart = path.get(i - 1);
            Point lineAEnd = path.get(i);

            for (int j = i + 1; j < len; j++) {
                Point lineBStart = path.get(j - 1);
                Point lineBEnd = path.get(j);
                if (lineSegmentsIntersect(lineAStart.x,lineAStart.y,
                        lineAEnd.x,lineAEnd.y,
                        lineBStart.x,lineBStart.y,
                        lineBEnd.x,lineBEnd.y)) {
                    return true;
                }

            } // inner loop

        } // outer loop

        return false;
    }


    static Boolean lineSegmentsIntersect(float p0_x, float p0_y, float p1_x, float p1_y,
                                         float p2_x, float p2_y, float p3_x, float p3_y) {
        float s1_x, s1_y, s2_x, s2_y;
        s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
        s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

        float s, t;
        s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
        t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

        if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
        {
            // Collision detected

            return true;
        }

        return false; // No collision
    }



    public void update(){
        isPathComplex(points);
    }


    public void draw(){
        if(surfaceHolder.getSurface().isValid()){
            canvas = surfaceHolder.lockCanvas();

            if(canvas != null) {
                canvas.drawColor(backgroundColor);

                if (mPath != null) {

                    canvas.drawPath(mPath, mPaint);
                }

                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }


    @Override
    public void run() {

        while(playing){

            long started = System.currentTimeMillis();

            float deltaTime = (System.currentTimeMillis() - started);

            int sleepTime = (int) (FRAME_PERIOD - deltaTime);

            update();
            draw();

            if (sleepTime > 0) {
                try {
                    Thread.sleep(sleepTime);
                }
                catch (InterruptedException e) {
                }
            }
            while (sleepTime < 0) {
                update();
                draw();
                sleepTime += FRAME_PERIOD;
            }
        }

    }  


   private void touchStart(float x, float y) {
        mPath = new Path();
        points.add(new Point((int)x,(int)y));
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }


    private void touchMove(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        points.add(new Point((int)x,(int)y));

        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);

            mX = x;
            mY = y;
        }
    }


    private void touchUp() {
        mPath.lineTo(mX, mY);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch(event.getAction()) {
            case MotionEvent.ACTION_DOWN :
                touchStart(x, y);
                break;
            case MotionEvent.ACTION_MOVE :
                touchMove(x, y);
                break;
            case MotionEvent.ACTION_UP :
                touchUp();
                break;
        }

        return true;
    }


    public void pause() {
        playing = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {
            Log.e("Error:", "joining thread");
        }

    }


    public void resume(Context context) {
        playing = true;
        gameThread = new Thread(this);
        gameThread.start();
    }

}

Please do you know some solution for this problem?

I tried a lot but nothing helped me with my problem.

What I want to achieve is to detect intersect like this

I tried solution with points from Check android.graphics.path intersection with itself but it seems not to be working for me.

I could also be doing something wrong because i am newbie to android development and programming in general :) .

Thank you in advance!

Edit:

To detect intersect i followed above solution and modified this function https://stackoverflow.com/a/1968345/9339525 but It returns almost all the time true even if path is not crossing itself.

All code for detect intersection:

static Boolean isPathComplex(List<Point> path) {

    if (path == null || path.size() <= 2) {
        return false;
    }

    int len = path.size();

    for (int i = 1; i < len; i++) {
        Point lineAStart = path.get(i - 1);
        Point lineAEnd = path.get(i);

        for (int j = i + 1; j < len; j++) {
            Point lineBStart = path.get(j - 1);
            Point lineBEnd = path.get(j);
            if (lineSegmentsIntersect(lineAStart.x,lineAStart.y,
                    lineAEnd.x,lineAEnd.y,
                    lineBStart.x,lineBStart.y,
                    lineBEnd.x,lineBEnd.y)) {
                return true;
            }

        } // inner loop

    } // outer loop

    return false;
}


static Boolean lineSegmentsIntersect(float p0_x, float p0_y, float p1_x, float p1_y,
                                     float p2_x, float p2_y, float p3_x, float p3_y) {
    float s1_x, s1_y, s2_x, s2_y;
    s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
    s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

    float s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
        // Collision detected

        return true;
    }

    return false; // No collision
}

Edit:

I modified my class GameView and added method to detect intersection.

JonnyJo
  • 13
  • 4
  • What do you mean with an intersecting path in Android? Never heard of that. – greenapps Feb 09 '18 at 17:34
  • I mean I want to detect if path cross with itself if you for example do something like this https://i.stack.imgur.com/zSVxl.png . – JonnyJo Feb 09 '18 at 17:39
  • The answer in the link you provided should do. How did you implement the function left as exercise? – greenapps Feb 09 '18 at 17:41
  • You should have started your post telling that you were drawing a line on a canvas. – greenapps Feb 09 '18 at 17:42
  • Sorry for inconvenience but it is my first post. I hope it helps. – JonnyJo Feb 09 '18 at 18:13
  • Can you tell which method/formula you use to determine intersection. And you also did not tell if it determines intersection ok. What does it return if you call it for if 5,2 to 5,4 intersects with 3,3 to 6,3 ? – greenapps Feb 09 '18 at 18:22
  • It retruns true for this points. – JonnyJo Feb 09 '18 at 18:33
  • That is correct. Now try with line sections of which you know they dont intersect. I looked a bit at your links. It looks as if you discovered a lot of discussions and problems. So take the right code. Your question is wrong. You suggest that you do not know how to do it. Well you know i think. Only your determine if there is intersection code is not doing its job. – greenapps Feb 09 '18 at 19:01
  • Thanks. I will try to do something with it but rly i don't know why it returns true if path is not intersecting with itself. – JonnyJo Feb 09 '18 at 19:08

1 Answers1

0

I could not stand that is was that difficult so i switched on the computer and coded something.

I tried only a few values and dont know what happens if begin-end points of segments coincide.

Please try out.

static boolean lineSegmentsDoIntersect(
          float Ax, float Ay
        , float Bx, float By
        , float Cx, float Cy
        , float Dx, float Dy) {

    // two line segments: AB and CD
    // segment AB intersects segment CD
    // if  A and B on different sides of line through C and D
    // AND C and D on different sides of line through A and B

    // note the difference between line and  segment!

    if ( ! pointsOnDifferentSidesOfLineThrough(Ax, Ay, Bx, By, Cx, Cy, Dx, Dy) )
        return false;

    if ( ! pointsOnDifferentSidesOfLineThrough(Cx, Cy, Dx, Dy, Ax, Ay, Bx, By) )
        return false;

    return true;
}

static boolean pointsOnDifferentSidesOfLineThrough(
          float Ax, float Ay
        , float Bx, float By
        , float x1, float y1
        , float x2, float y2) {

    // determine equation of line through C and D

    // y = ax + b
    // a = (y2-y1)/(x2-x1)   but.. ( x2-x1) not equal to zero
    // y-y1 = a (x-x1)
    // y = a (x-x1) + y1
    // y = ax -ax1 + y1
    // b = -ax1 + y1

    //but.. (x2-x1) not 0

    if ( x2==x1)
    {
        if ( Ax > x1 && Bx > x1 )
            return false;
        if ( Ax < x1 && Bx < x1 )
            return false;

        return true;
    }

    float a = (y2-y1)/(x2-x1);
    float b = -a * x1 + y1;

    float yA = a * Ax + b;
    float yB = a * Bx + b;

    if ( yA > Ay && yB > By )
        return false;
    if ( yA < Ay && yB < By )
        return false;

    return true;
}
greenapps
  • 11,154
  • 2
  • 16
  • 19
  • Thanks for replay. Your code works and this which I posted ( function lineSegmentsIntersect() ) works too. I noticed that the problem was in isPathComplex() function. I just changed inner's loop starting point for larger ( instead i + 1 I wrote i + 5 ) and it worked properly. I think the problem was that the points where too close to each other. Thank you again. – JonnyJo Feb 10 '18 at 06:19
  • It looks as if you compared a segment with itself first. As that has to be avoided. – greenapps Feb 10 '18 at 09:06