开发者

Getting all points in Path in android - FingerPaint.java

开发者 https://www.devze.com 2023-03-10 07:00 出处:网络
I tried to get all the x,y coordinates that are drawn in the path on the canvas. But i couldn\'t get the points. I am getting only a few points. Why is that?

I tried to get all the x,y coordinates that are drawn in the path on the canvas. But i couldn't get the points. I am getting only a few points. Why is that?

Is there any way i can get all the x and y points?

I use FingerPaint.java as a starting point.

Link to Original FingerPaint.java

Here is the code i modified and using:

package com.schogini.whiteboard;

import java.util.ArrayList;
import java.util.StringTokenizer;

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
//import android.widget.Toast;

public class FingerPaint extends Activity  implements ColorPickerDialog.OnColorChangedListener {
    //color - ARGB
    int a = 255;
    int r = 255;
    int g = 0;
    int b = 0;

    //erase Flag
    boolean eraseFlag=false;
    //Internet Posting
    static String toSend;
    static String parseStr;

    //array for storing x,y,r,g,b,a
    static ArrayList<String> rArray,gArray,bArray,alphaArray,xPointArray,yPointArray;
    //ArrayList<String> rArray_in,gArray_in,bArray_in,alphaArray_in,xPointArray_in,yPointArray_in;

    //Drawing types
    private static Paint mPaint;
    private MaskFilter  mEmboss;
    private MaskFilter  mBlur;

    //touch - Path Coordinates
    private static float mX;
    private static float mY;

    //MyView object
    MyView m;

    //point populate
    float dx,dy,distance;
    int dd,ii;

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

        //move to appropriate module
        rArray = new ArrayList<String>(); 
        gArray = new ArrayList<String>();
        bArray = new ArrayList<String>();
        alphaArray = new ArrayList<String>();
        xPointArray = new ArrayList<String>();
        yPointArray = new ArrayList<String>();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(0xFFFF0000);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        //width of the drawing brush
        mPaint.setStrokeWidth(12);

        mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
        mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
    }

    public void parsing(String str22){
         //example string received from server
//      parseStr = "255,0,0,1.0,88.0,100.0||255,0,0,1.0,88.0,101.0||255,0,0,1.0,88.0,102.0||255,0,0,1.0,88.0,103.0||255,0,0,1.0,88.0,104.0||255,0,0,1.0,88.0,105.0||255,0,0,1.0,88.0,106.0||255,0,0,1.0,88.0,107.0||255,0,0,1.0,88.0,108.0," +
//              "||255,0,0,1.0,88.0,109.0||255,0,0,1.0,88.0,110.0||255,0,0,1.0,88.0,111.0||255,0,0,1.0,88.0,112.0||255,0,0,1.0,88.0,113.0||255,0,0,1.0,88.0,114.0||255,0,0,1.0,88.0,115.0||255,0,0,1.0,88.0,116.0||255,0,0,1.0,88.0,117.0," +
//              "||255,0,0,1.0,88.0,118.0||255,0,0,1.0,88.0,119.0||255,0,0,1.0,88.0,120.0||255,0,0,1.0,80.0,100.0||255,0,0,1.0,81.0,100.0||255,0,0,1.0,82.0,100.0||255,0,0,1.0,83.0,100.0||255,0,0,1.0,84.0,100.0||255,0,0,1.0,85.0,100.0," +
//              "||255,0,0,1.0,86.0,100.0||255,0,0,1.0,87.0,100.0||255,0,0,1.0,88.0,100.0||255,0,0,1.0,89.0,100.0||255,0,0,1.0,91.0,100.0||255,0,0,1.0,92.0,100.0||255,0,0,1.0,93.0,100.0||255,0,0,1.0,94.0,100.0||255,0,0,1.0,95.0,100.0," +
//              "||255,0,0,1.0,96.0,100.0||255,0,0,1.0,97.0,100.0||255,0,0,1.0,98.0,100.0||255,0,0,1.0,99.0,100.0||255,0,0,1.0,100.0,100.0||255,0,0,1.0,101.0,100.0||255,0,0,1.0,102.0,100.0||255,0,0,1.0,103.0,100.0,";
        parseStr="255,0,0,255,85.351585,90.17996||255,0,0,255,85.351585,90.17996||255,0,0,255,123.97784,80.883064||255,0,0,255,164.47311,71.58616||255,0,0,255,182.22874,65.543175||255,0,0,255,233.9381,48.3439||255,0,0,255,247.95567,42.30091||255,0,0,255,252.62822,40.441532||255,0,0,255,252.62822,40.441532||255,0,0,255,86.9091,167.34427||255,0,0,255,86.9091,167.34427||255,0,0,255,86.9091,167.34427||255,0,0,255,151.70154,133.41057||255,0,0,255,190.3278,118.07068||255,0,0,255,235.18411,98.54717||255,0,0,255,248.57867,92.03935||255,0,0,255,256.36627,89.71512||255,0,0,255,257.30078,89.250275||255,0,0,255,257.30078,89.250275||255,0,0,255,82.548065,277.97742||255,0,0,255,82.548065,277.97742||255,0,0,255,82.548065,277.97742||255,0,0,255,116.50179,258.91876||255,0,0,255,152.63605,240.7898||255,0,0,255,209.95244,215.68816||255,0,0,255,239.23363,199.41858|开发者_如何转开发|255,0,0,255,249.8247,192.4459||255,0,0,255,261.35028,186.86777||255,0,0,255,261.9733,186.86777||255,0,0,255,261.9733,186.86777||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,48.905834,62.7541||255,0,0,255,39.56077,133.87541||255,0,0,255,44.856304,185.93808||255,0,0,255,53.578365,266.82114||255,0,0,255,53.578365,287.27435||255,0,0,255,51.70935,301.6845||255,0,0,255,51.39785,303.07904||255,0,0,255,51.086346,303.5439||255,0,0,255,51.086346,303.5439||255,0,0,255,90.02411,362.57922||255,0,0,255,88.155106,355.14172||255,0,0,255,109.02575,317.0244||255,0,0,255,133.01141,323.53226||255,0,0,255,155.43958,340.26666||255,0,0,255,166.65363,344.91513||255,0,0,255,185.03226,338.4073||255,0,0,255,193.13133,326.78616||255,0,0,255,210.26395,292.38763||255,0,0,255,228.01956,296.10638||255,0,0,255,259.7928,283.09073||255,0,0,255,266.33432,263.5672||255,0,0,255,267.58032,258.91876||255,0,0,255,269.76086,256.1297||255,0,0,255,271.94138,255.20001||255,0,0,255,284.71295,248.22733||255,0,0,255,294.3695,232.42259||255,0,0,255,297.48453,212.8991||255,0,0,255,297.48453,212.8991||255,0,0,255,101.86119,202.67252||255,0,0,255,101.86119,202.67252||255,0,0,255,101.86119,202.67252||255,0,0,255,113.07527,194.77013||255,0,0,255,141.73346,178.9654||255,0,0,255,144.8485,179.43024||255,0,0,255,166.96513,192.4459||255,0,0,255,170.70317,195.23497||255,0,0,255,193.75432,195.69983||255,0,0,255,201.85338,185.93808||255,0,0,255,212.75597,146.42624||255,0,0,255,217.4285,136.19965||255,0,0,255,231.44609,135.26996||255,0,0,255,241.41415,136.66449||255,0,0,255,250.13622,136.19965||255,0,0,255,279.41742,128.76212||255,0,0,255,312.12512,100.406555||255,0,0,255,312.12512,100.406555||255,0,0,255,92.20462,52.527508||255,0,0,255,92.20462,52.527508||255,0,0,255,159.48909,44.160294||255,0,0,255,186.90128,34.398544||255,0,0,255,242.97165,20.918034||255,0,0,255,269.13785,13.48051||255,0,0,255,288.76245,11.62113||255,0,0,255,288.76245,11.62113,";

        StringTokenizer tokens = new StringTokenizer(parseStr, "||");
        while(tokens.hasMoreTokens())
        {
            String second = tokens.nextToken();
            StringTokenizer tempTokens = new StringTokenizer(second, ",");
            int index = 0;
            while(tempTokens.hasMoreTokens())
            {
                String splitStr = tempTokens.nextToken();
                switch (index%6) {
                case 0:
                    rArray.add(splitStr);
                    break;
                case 1:
                    gArray.add(splitStr);
                    break;
                case 2:
                    bArray.add(splitStr);
                    break;
                case 3:
                    alphaArray.add(splitStr);
                    break;
                case 4:
                    xPointArray.add(splitStr);
                    break;
                case 5:
                    yPointArray.add(splitStr);
                    break;
                default:
                    break;
                }
                index++;
                Log.v("String44"," value = "+splitStr);
            }
        }
        m.redraw();
    }

    public void colorChanged(int color) {
        mPaint.setColor(color);

        //method to convert integer color to r g b a
        b = (color)&0xFF;
        g = (color>>8)&0xFF;
        r = (color>>16)&0xFF;
        a = Math.round(((color>>>24)/255)*10);
    }

    public class MyView extends View {

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

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

            // reading screen size (for device Independence)
            DisplayMetrics displaymetrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);

            mBitmap = Bitmap.createBitmap(displaymetrics.widthPixels,displaymetrics.heightPixels, Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBitmap);
            mPath = new Path();
            mBitmapPaint = new Paint(Paint.DITHER_FLAG);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            //background -- White
            canvas.drawColor(0xFFFFFFFF);

            //draw the bitmap from 0,0 the firstpoint in device's screen
            canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
            canvas.drawPath(mPath, mPaint);
        }

        public void touch_start(float x, float y) {
            //reset touch so that a new path is to be drawn
            mPath.reset();
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
        }

        public void touch_move(float x, float y) {
            mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);


            //point tracing
            tracePoints((int)x, (int)y,(int)mX,(int)mY);
            mX = x;
            mY = y;
        }

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

        public void arrayPopulate(float atx,float aty){

            if(eraseFlag==false){
                //entering points into arrayLists
                xPointArray.add(""+atx); 
                yPointArray.add(""+aty);

                //add color as well
                alphaArray.add(""+a); 
                rArray.add(""+r);
                gArray.add(""+g); 
                bArray.add(""+b);

//              if(toSend==null){
//                  toSend=""+r+","+g+","+b+","+a+","+atx+","+aty+"||";
//              }
//              else{
//                  toSend+=""+r+","+g+","+b+","+a+","+atx+","+aty+"||";
//              }

                //add data to a queue for sending to server

               // Log.v("touch", toSend);
                //send data here to server
                //eraseFlag is true ends
            }
        }

        @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);
                    arrayPopulate(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_MOVE:
                    touch_move(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    touch_up();
                    arrayPopulate(x, y);
                    invalidate();
                    break;
            }
            return true;
        }

        public void redraw() {
            int arraysize = FingerPaint.rArray.size();

            int index=0;
            float xx,yy;

            //setting properties of tPaint
            Paint tPaint= new Paint();
            tPaint.setAntiAlias(true);
            tPaint.setDither(true);
            tPaint.setStyle(Paint.Style.STROKE);
            tPaint.setStrokeJoin(Paint.Join.ROUND);
            tPaint.setStrokeCap(Paint.Cap.ROUND);
            tPaint.setColor(-16777216);
            tPaint.setStrokeWidth(12);

            while(index<arraysize){
                    //tPaint.setARGB(Integer.parseInt(alphaArray.get(0)), Integer.parseInt(rArray.get(0)), Integer.parseInt(gArray.get(0)), Integer.parseInt(bArray.get(0)));
                    //tPaint.setColor(Color.argb(Integer.parseInt(alphaArray.get(0)), Integer.parseInt(rArray.get(0)), Integer.parseInt(gArray.get(0)), Integer.parseInt(bArray.get(0))));

                    xx=Float.parseFloat(xPointArray.get(index));
                    yy=Float.parseFloat(yPointArray.get(index));

                    m.mCanvas.drawPoint(xx,yy,tPaint);
                    m.invalidate();
                    index++;
                    Log.d("__REDRAW__", "X="+xx+"   Y="+yy);
              }
        }
    }

    //creating Options Menu - Inflater
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
    }

    //Option Menu Selection - Actions
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        mPaint.setXfermode(null);
        mPaint.setAlpha(255);

        switch (item.getItemId()) {
            case R.id.COLOR:
                this.eraseFlag=false;
                new ColorPickerDialog(this, this, mPaint.getColor()).show();
                return true;
            case R.id.EMBOSS:
                this.eraseFlag=false;
                if (mPaint.getMaskFilter() != mEmboss) {
                    mPaint.setMaskFilter(mEmboss);
                } else {
                    mPaint.setMaskFilter(null);
                }
                return true;
            case R.id.BLUR:
                this.eraseFlag=false;
                if (mPaint.getMaskFilter() != mBlur) {
                    mPaint.setMaskFilter(mBlur);
                } else {
                    mPaint.setMaskFilter(null);
                }
                return true;
            case R.id.ERASE:
                //for toggle detection
                this.eraseFlag=true;
                //reset blurr & Emboss
                mPaint.setMaskFilter(null);
                mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
                return true;
            case R.id.REDRAW:
                this.eraseFlag=false;
                //redraw the drawn points
                m.redraw();
                return true;
        }
       return super.onOptionsItemSelected(item);
    }

    //Bressenham's mid-point algorithm for finding all points between any two points --- Straight line
    public void tracePoints(int xxx,int yyy,int x2, int y2) {
        int w = x2 - xxx ;
        int h = y2 - yyy ;
        int dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0 ;
        if (w<0) dx1 = -1 ; else if (w>0) dx1 = 1 ;
        if (h<0) dy1 = -1 ; else if (h>0) dy1 = 1 ;
        if (w<0) dx2 = -1 ; else if (w>0) dx2 = 1 ;
        int longest = Math.abs(w) ;
        int shortest = Math.abs(h) ;
        if (!(longest>shortest)) {
            longest = Math.abs(h) ;
            shortest = Math.abs(w) ;
            if (h<0) dy2 = -1 ; else if (h>0) dy2 = 1 ;
            dx2 = 0 ;            
        }
        int numerator = longest >> 1 ;
        for (int i=0;i<=longest;i++) {
            m.arrayPopulate((float)xxx,(float)yyy) ;
            numerator += shortest ;
            if (!(numerator<longest)) {
                numerator -= longest ;
                xxx += dx1 ;
                yyy += dy1 ;
            } else {
                xxx += dx2 ;
                yyy += dy2 ;
            }
        }
    }
}


Try the following code. I hope it solves your problem. When I draw, I perfectly get all the points, whether I draw slowly or at a rapid rate.

public class HRCanvas extends Activity implements OnTouchListener{

        DrawPanel dp;
        private ArrayList<Path> pointsToDraw = new ArrayList<Path>();
        private Paint mPaint;
        Path path;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            dp = new DrawPanel(this);
            dp.setOnTouchListener(this);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            mPaint = new Paint();
            mPaint.setDither(true);
            mPaint.setColor(Color.WHITE);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStrokeWidth(30);

            FrameLayout fl = new FrameLayout(this);  
            fl.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));  
            fl.addView(dp);  
            setContentView(fl);  

        }

        @Override
        protected void onPause() {
            // TODO Auto-generated method stub
            super.onPause();
            dp.pause();
        }



        @Override
        protected void onResume() {
            // TODO Auto-generated method stub
            super.onResume();
            dp.resume();
        }



        public class DrawPanel extends SurfaceView implements Runnable{

            Thread t = null;
            SurfaceHolder holder;
            boolean isItOk = false ;

            public DrawPanel(Context context) {
                super(context);
                // TODO Auto-generated constructor stub
                holder = getHolder();
            }

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while( isItOk == true){

                    if(!holder.getSurface().isValid()){
                        continue;
                    }

                    Canvas c = holder.lockCanvas();
                    c.drawARGB(255, 0, 0, 0);
                    onDraw(c);
                    holder.unlockCanvasAndPost(c);
                }
            }

            @Override
            protected void onDraw(Canvas canvas) {
                // TODO Auto-generated method stub
                super.onDraw(canvas);
                            synchronized(pointsToDraw)
                            {
                for (Path path : pointsToDraw) {
                    canvas.drawPath(path, mPaint);
                }
                            }
            }

            public void pause(){
                isItOk = false;
                while(true){
                    try{
                        t.join();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    break;
                }
                t = null;
            }

            public void resume(){
                isItOk = true;  
                t = new Thread(this);
                t.start();

            }



        }


        @Override
        public boolean onTouch(View v, MotionEvent me) {
            // TODO Auto-generated method stub
                    synchronized(pointsToDraw)
                    {
            if(me.getAction() == MotionEvent.ACTION_DOWN){
                path = new Path();
                path.moveTo(me.getX(), me.getY());
                //path.lineTo(me.getX(), me.getY());
                pointsToDraw.add(path);
            }else if(me.getAction() == MotionEvent.ACTION_MOVE){
                path.lineTo(me.getX(), me.getY());
            }else if(me.getAction() == MotionEvent.ACTION_UP){
                //path.lineTo(me.getX(), me.getY());
            }
            }       
            return true;

        }

    }


You need to interpolate to find the points between updates. I recommend cosine interpolation. Its easy to implement, produces decent results, and is computationally inexpensive.

public double cosineInterpolation(double x1, double x2, double normal)
{
    double ft = normal * 3.1415927;
    double f = (1 - Math.cos(ft)) * .5;
    return  x1 * (1 - f) + x2 * f;
}

You'll need to call this method twice; once for the x, and once for the y coordinates of 2 subsequent points.


You are able to solve this using MATH equation.

Use the line equation, y = mx + c Put (x1,y1) and and (x2,y2) to get two equations and solve to get values of m and c. (you will be able to find the direct equations to get m and c somewhere). Then create a loop to start from x = x1 till x = x2 Using y=mx+c, get the value of y (now that you know m and c)

I hope this is help.


Replace this code for your touch event....

    float downx = 0;
    float downy = 0;
    float upx = 0;
    float upy = 0;
    public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    switch (action)
    {
    case MotionEvent.ACTION_DOWN:
    downx = event.getX();
    downy = event.getY();
    break;
   case MotionEvent.ACTION_MOVE:
     upx = event.getX();
     upy = event.getY();
     canvas.drawLine(downx, downy, upx, upy, paint);
     imageView.invalidate();
     downx = upx;
     downy = upy;
     break;
    break;
    case MotionEvent.ACTION_UP:
    upx = event.getX();
    upy = event.getY();
    canvas.drawLine(downx, downy, upx, upy, paint);
    imageView.invalidate();
    break;
    case MotionEvent.ACTION_CANCEL:
    break;
    default:
    break;
    }
    return true;
    }


Check out this code. The project is unfinished, but the lines draw quickly and smoothly on my Atrix. Although it does use quadTo which it sounds like you want to avoid. What device are you testing with?


I noticed that you aren't calling arrayPopulate(x, y); in the onTouchEvent(MotionEvent event) function under the MotionEvent.ACTION_MOVE case. It's quite possible you're losing data there?

Is there any reason why you're trying to recode the Path class? If you really need to you'll probably have to include both bezier curves and straight lines and more if you ever decide to use any other functions in it. If they are using native to do bezier curves, then you'll probably want to as well.


I am facing the same problem as AndroidKid,

Due to the number of background actions, my app doesnt able to record all points of the touch event. My test app captures twenty location for an action, wheras my actual app captures only 5 to 6 points. So am getting polygon like object as the result.

As Android Kid said, when im drawing slowly, i get the proper curve as the points are recorded properly.

In my test app, i had a path object to append the points and used canvas.drawPath, which gave me better result than canvas.drawLine.

Android Kid,

Also try this if you can compromise the sharp edges with smooth curves in your path.

paint.setPathEffect(new CornerPathEffect(25));

Anyways, will keep you guys Posted!!


I know this question is old, but you can get all the points in an event, when the hardware tracks the events faster than they are delivered using the event's history. Something like:

int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
    float historicalX = event.getHistoricalX(i);
    float historicalY = event.getHistoricalY(i);
    expandDirtyRect(historicalX, historicalY);
    path.lineTo(historicalX, historicalY);
}

Source


Why don't you just draw it onto a bitmap using black color, then loop through all the pixels to get the points?

        Paint cPaint = new Paint();

        cPaint.setAntiAlias(false);
        cPaint.setColor(0xFF000000);
        Bitmap topBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas topCanvas = new Canvas(topBitmap);
        topCanvas.drawColor(0xFFFFFFFF, PorterDuff.Mode.CLEAR);
        topCanvas.drawPath(mPath, cPaint);

        for (int x=0; x<w; x++) {
            for (int y=0; y<h; y++) {
                if (topBitmap.getPixel(x, y) == 0xFF000000) {
                    // record the point
                }
            }
        }

In any case, a path is not going to contain every point drawn. Also, if the other device is also running android or has something similar to a path object you can just recreate the path there using the points from your motion event.

0

精彩评论

暂无评论...
验证码 换一张
取 消