0

I am working on a drawing app where the user can create a rectangle, a row of holes, a grid of holes, and polygons. Right now I have the functionality to move these objects by determining if the touch point is within the bounds of an object, updating the objects coordinates during a TouchMove action, and then redrawing the object (along with everything else drawn) with the updated coordinates. My next step is to implement rotation. So far I am able to get a rotation angle, use that to rotate the canvas when drawing the rotated object, and then restore the canvas so the other objects are drawn without an angle. This works perfectly. The problem comes when I try to move/rotate the rotated object. I store the objects rotation, but I don't update it's stored coordinates; So when I try to determine if the user has touched the rotated object it doesn't match up with what the user sees on their screen. What I need is to be able to either modify the objects coordinates or the touch coordinates to reflect the rotation. Is there a way to do this? Am I going about this all wrong?

Here's some snippets of my code:

This determines if a grid of circles has been touched:

     //retrieve touch coordinates. Adjust for matrix transformations
 float currX=(event.getX()-matrixValues[2])/matrixValues[0];
 float currY=(event.getY()-matrixValues[5])/matrixValues[4];
 //... skip other code for snippet
 else if(drawObject.getDrawAction()=="array")
 {
     ArrayList <DrawAction> redrawArray=drawObject.getDrawArray();
     if(drawMode=="remove")
     {
         //determine which hole in grid was touched. "Remove" it
         for(int y=0; y<redrawArray.size();y++)
        {
             DrawAction arrayAction=redrawArray.get(y);
            if(currX>(arrayAction.getDrawX()-arrayAction.getDrawRadius()) && currX<(arrayAction.getDrawX()+arrayAction.getDrawRadius()) && currY<(arrayAction.getDrawY()+arrayAction.getDrawRadius()) && currY>(arrayAction.getDrawY()-arrayAction.getDrawRadius()))
            {
                arrayAction.setRemoved(true);
                //add undo object for this action
                DrawAction undoRemove= new DrawAction("remove", arrayAction);
                undoList.add(undoRemove);
                //redraw objects with updated status
                redrawObjects(false);

                break outerloop;

            }
         }
     }
     else
     {
        //determine all coordinates within the grid. If we are within the grid, then we want to move the whole grid
         float holeRadius=redrawArray.get(0).getDrawRadius();
         float lowestX=redrawArray.get(0).getDrawX()-holeRadius;
         float lowestY=redrawArray.get(0).getDrawY()-holeRadius;
         float highestX=redrawArray.get(redrawArray.size()-1).getDrawX()+holeRadius;
         float highestY=redrawArray.get(redrawArray.size()-1).getDrawY()+holeRadius;


         if(currX>lowestX && currX<highestX && currY>lowestY && currY<highestY)
         {
            touchObject=drawObject;
            //store coordinates for new coordinate calculations
            touchedX=currX;
            touchedY=currY;                     

            break outerloop;
         }


     }

}

this snippet is how I determine the angle:

touchObject.setRotationAngle((float) getDegreesFromTouchEvent(currX, currY));
touchObject.setRotated(true);   

this is the function that returns the angle:

 //determine rotation from touch event and screen dimensions
  private double getDegreesFromTouchEvent(float x, float y){
      double delta_x = x - (screenWidth) /2;
      double delta_y = (screenHeight) /2 - y;
      double radians = Math.atan2(delta_y, delta_x);

      return (-1)*Math.toDegrees(radians);
  }

and here's where I redraw everything after movement/rotation:

//redraw everything
    for(int x=0; x<undoList.size();x++)
    { 
        //retrieve next object to draw
        DrawAction drawObject=undoList.get(x);
        //retrieve object's brush width/paint color
        this.setBrushSize(drawObject.getBrushWidth());
        drawPaint.setColor(drawObject.getPaintColor());

        //save state of canvas. This is so we can rotate it, draw, and then rotate back to its original state
        drawCanvas.save();

        //if this object has been rotated, then we need to rotate the canvas before drawing it
        if(drawObject.isRotated())
        {
            drawCanvas.rotate(drawObject.getRotationAngle(),drawObject.getCenterX(),drawObject.getCenterY());
        }

        if(drawObject.getDrawAction()=="circle"  && !drawObject.isRemoved())
        {
             drawCanvas.drawCircle(drawObject.getDrawX(), drawObject.getDrawY(), drawObject.getDrawRadius(),drawPaint);  
        }
        if(drawObject.getDrawAction()=="rectangle")
         {
            if(!drawObject.isRemoved())
            {
                drawCanvas.drawRect(drawObject.getDrawLeft(), drawObject.getDrawTop(), drawObject.getDrawRight(), drawObject.getDrawBottom(),drawObject.getDrawPaint());
                drawCanvas.drawCircle(drawObject.getCenterX(), drawObject.getCenterY(), 2, drawObject.getDrawPaint());
            }                
         }
        if(drawObject.getDrawAction()=="text" || drawObject.getDrawAction()=="delay")
         {
             drawCanvas.drawText(drawObject.getDrawtext(), drawObject.getDrawX(), drawObject.getDrawY(), drawObject.getDrawPaint()); 
         }      
         if(drawObject.getDrawAction()=="array")
         {
            ArrayList <DrawAction> redrawArray=drawObject.getDrawArray();
            for(int y=0; y<redrawArray.size();y++)
            {
                 DrawAction arrayAction=redrawArray.get(y);
                 if(!arrayAction.isRemoved())
                 {
                     drawCanvas.drawCircle(arrayAction.getDrawX(), arrayAction.getDrawY(), arrayAction.getDrawRadius(),drawPaint);
                 }                   
            }

         }
         if(drawObject.getDrawAction()=="path")
         {
             drawCanvas.drawPath(drawObject.getDrawPath(), drawPaint);             
         }
         //restore canvas to original state
         drawCanvas.restore();
    }
Loktar
  • 34,764
  • 7
  • 90
  • 104
IT_Guy
  • 223
  • 4
  • 16
  • 1
    see my answer here http://stackoverflow.com/questions/21633545/android-imageview-scaling-and-translating-issue – pskink Jun 27 '14 at 19:48
  • @pskink Thanks for the reply, but that would require me to essentially start over. Everything is drawn on a single bitmap which is drawn on a SurfaceView canvas. It's done this way so the user can pan/zoom around the single bitmap which has a predetermined size. My users' drawing space HAS to be larger than the screen so this functionality is essential. I'm also unsure as to how I would draw dynamically sized rectangles and paths. Your example uses drawables. – IT_Guy Jul 01 '14 at 18:17
  • 1
    just replace canvas.drawBitmap(Bitmap, Matrix, Paint) with canvas. concat(Matrix) and do your drawing... – pskink Jul 01 '14 at 19:01
  • @pskink Thats what I currently have in my onDraw method: "canvas.concat(matrix); canvas.drawPath(drawPath, drawPaint); canvas.drawBitmap(canvasBitmap,0,0,canvasPaint);" My issue would be drawing circles,grids,rectangles, arrows, and painting on those layer bitmaps and then appending them to my main bitmap and then being able to handle undrawing and moving them about the main bitmap.Would this be easy to do with this? – IT_Guy Jul 02 '14 at 14:57
  • (cont.)I would get the touch point on my main canvas, draw the object on a new bitmap at (0,0) making the bitmap no larger than the drawn object, and then place that new bitmap at the touched coordinates on my main bitmap. Is that easy enough to do? – IT_Guy Jul 02 '14 at 14:58
  • 1
    yes, it shouldnt be very hard – pskink Jul 02 '14 at 15:51
  • @pskink Thanks for posting this solution. It works wonderfully and it only took me about a day to tweak the code to work together. – IT_Guy Jul 03 '14 at 15:29

1 Answers1

0

@pskink's solution is great. It took some tweaking to get it to work with my code, but that only took about a day. I had to change everything from being drawn directly on the main canvas to being drawn on its own bitmap. This bitmap has the dimensions of the object that is drawn on it. These bitmaps are stored in a LinkedList of bitmap Layers and added to the main bitmap via canvas.drawBitmap(bitmap, x, y, null);

here's a link to his example: Android ImageView Scaling and translating issue

and his source:https://github.com/pskink/android-gesture-detectors

Community
  • 1
  • 1
IT_Guy
  • 223
  • 4
  • 16