0

This is actually our Thesis, we are required to use the Ramer-Douglas-Peucker Algorithm in simplifying lines, can anyboy help me how to implement this in an Android App.

I just want to know how to get the string of points from the line I've drawn and simplify the line by reducing the total no. points based on the given code below?

This is the main class.

public class SketchTimeNewActivity extends GraphicsView implements ColorOption.OnColorChangedListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new MyView(this));

    myPaint = new Paint();
    myPaint.setAntiAlias(true);
    myPaint.setDither(true);
    myPaint.setColor(Color.CYAN);
    myPaint.setStyle(Paint.Style.STROKE);
    myPaint.setStrokeJoin(Paint.Join.ROUND);
    myPaint.setStrokeCap(Paint.Cap.ROUND);
    myPaint.setStrokeWidth(12); 
}

private Paint       myPaint;    

    public void colorChanged(int color) {
    myPaint.setColor(color);
}


public class MyView extends View {

    private static final float MINP = 0.25f;
    private static final float MAXP = 0.75f;

    private Bitmap  mBitmap;
    private Canvas  mCanvas;
    private Path    mPath;
    private Paint   mBitmapPaint;

    public MyView(Context c) {
        super(c);

        mPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldwidth, int oldheight) {
        super.onSizeChanged(width, height, oldwidth, oldheight);
        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(color.black);

        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

        canvas.drawPath(mPath, myPaint);
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private void touch_start(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }
    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
            mX = x;
            mY = y;
        }
    }

    private void touch_up() {
        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, myPaint);
        // kill this so we don't double draw
        mPath.reset();
    }

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

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }
}

private static final int COLOR_MENU_ID = Menu.FIRST;
private static final int EXISTING_MENU_ID = Menu.FIRST + 2;
private static final int ENHANCED_MENU_ID = Menu.FIRST + 3;
private static final int ERASE_MENU_ID = Menu.FIRST + 1;


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    menu.add(0, COLOR_MENU_ID, 0, "Color").setShortcut('1', 'c');
    menu.add(0, EXISTING_MENU_ID, 0, "Enhanced").setShortcut('2', 's');
    menu.add(0, ENHANCED_MENU_ID, 0, "Existing").setShortcut('3', 'z');
    menu.add(0, ERASE_MENU_ID, 0, "Erase").setShortcut('4', 'z');

    return true;
}



@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    return true;
}



@Override
public boolean onOptionsItemSelected(MenuItem item) {
    myPaint.setXfermode(null);
    myPaint.setAlpha(0xFFAAAAAA);

When the EXISTING MENU is clicked, it will simplify the line being drawn and display a line that has lesser points or a line that is already simplified. I'm planning to create a new class for it but I don't know how to get the string of points from the line being drawn in the canvas.

    switch (item.getItemId()) {

        case COLOR_MENU_ID:
            new ColorOption(this, this, myPaint.getColor()).show();
            return true;

  /**      case EXISTING_MENU_ID:

            return true;

        case ENHANCED_MENU_ID:

            return true;*/

        case ERASE_MENU_ID:{

                  myPaint.setColor(color.black);
                  myPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                  return true;
              }

    }

    return super.onOptionsItemSelected(item);
}

}
user1081908
  • 31
  • 1
  • 2
  • 7
  • You are generating the points on the line from your `onTouchEvent`, so in stead of trying to query the Canvas afterwards, why not simply keep a list of these created points? You can add a point as you draw each new line segment. – MH. Dec 23 '11 at 07:52
  • Thank you so much for your help. Can you please explain further how will I do it? Thank you so much, – user1081908 Dec 23 '11 at 14:29
  • When you say this is your thesis, do you mean that it is homework? – Zoot Dec 23 '11 at 16:29
  • yes. maybe you ca help me, thanks a lot – user1081908 Dec 25 '11 at 08:42

1 Answers1

0

Copy paste from my original comment for some context:

You are generating the points on the line from your onTouchEvent, so in stead of trying to query the Canvas afterwards, why not simply keep a list of these created points? You can add a point as you draw each new line segment.

In terms of code, your most basic example will look something like this:

List<Point> mPoints = new ArrayList<Point>();

private void touch_up() {
    // save this point on the line
    mPoints.add(new Point(mX, mY);
    // add point to path
    mPath.lineTo(mX, mY);
    // commit the path to our offscreen
    mCanvas.drawPath(mPath, myPaint);
    // kill this so we don't double draw
    mPath.reset();
}

I'm assuming here that touch_up() is where you add each line segment's start or endpoint to the path.

//Edit: Upon reading your problem once more, I feel like you might be asking for all points on the line you've drawn - since your code snippet includes curves? My guess is that the only way to realise this is by evaluating every (x,y) using the different underlying mathematical equations and store the result for each point. In other words: by writing your own lineTo and quadTo functions.

For straight lines this is relatively trivial, but curves will increase the level of difficulty. You might want to take a look at the Path's source code, which internally delegates most of its work to a java.awt.geom.GeneralPath object.

MH.
  • 45,303
  • 10
  • 103
  • 116
  • yes this involve curved lines, so this means i have to create or apply some mathematical equations in getting all the points in the line? – user1081908 Dec 25 '11 at 08:43
  • Well, I would say that's your best and fastest option, although perhaps not the easiest to implement. Alternatively you could potentially do some sort of 'approximation' by iterating over all the screen's pixels after drawing and check for the colour you're drawing with. That will however not give you any information about drawing order. If lines are not crossing, you could easily enhance this a bit, since you know at least know the start and end points of every line segment, but it'll be not as neat as using the math. – MH. Dec 25 '11 at 09:04
  • Thank you. but I really find it difficult to start, can you please give me a sample code for that.. or check my code above, how will I implement it there? Thank you again for your help I appreciate it so much. – user1081908 Dec 25 '11 at 15:17
  • Key to getting the (x,y) coordinates for every point on a linear line or curve is having the mathematical representation (formula). Those for lines are highschool math, whereas details on quadratic Bezier curves are widespread - e.g. [Wikipedia](http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves) for the general formula. There are also lots of example implementations you can use as a starting point - you might even be able to mostly copy-paste/modify Java's built-in QuadCurve2D class (it's missing in Android though) for your purposes. – MH. Dec 26 '11 at 01:20
  • I already found some code snippets for QuadCurve2D class and I'm still trying to analyze it. I am also searching for Douglas-Peucker implementation in java. Do you have any idea where can I find a good one? thank you again – user1081908 Dec 26 '11 at 06:15
  • [Here's one](http://code.google.com/p/savitzky-golay-filter/source/browse/trunk/src/mr/go/sgfilter/RamerDouglasPeuckerFilter.java), and [here](http://screamyguy.net/SGImplify/index.htm), and [here](http://www.java2s.com/Open-Source/Java-Document/GIS/jts/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java.htm) (Google is your friend). The actual algorithm doesn't look too complicated though, so you could probably also just translate the [pseudocode on WP](http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) to Java. – MH. Dec 26 '11 at 06:35
  • I still can't work on it. I tried running the sample codes in Java Eclipse by creating New Android Project but I got errors. – user1081908 Dec 26 '11 at 14:20