javabufferedimagegraphics2d

How to translate a bounding box for selection purposes with a bufferedimage rotation


I am trying to keep a bounding box local to a buffered image after it is being rotated.

Good day everyone. I am having an issue with a program I am currently working on. I draw an image to a buffered image then that buffered image on to the actual panel. I did it this way because I have multiple objects being drawn and rotated on one jpanel and it seemed easier to keep track of and rotate them to me.

public void draw(Graphics2D g2, JPanel p)
{
    BufferedImage bufferedImage = new BufferedImage(Width + 40, Height + 40, BufferedImage.TYPE_INT_ARGB); 
    Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();  // Graphics for the first object only
        
    AffineTransform old = g2.getTransform();
        
    g2d.setStroke(new BasicStroke(2));
    g2d.setColor(Color.blue);
        
    if(type.equals("cone"))
    {
            
    }
    else if(type.equals("cube"))
    {
        g2d.drawRect(20, 20, Width, Height);
    }
    else if(type.equals("circle"))
    {
        g2d.drawOval(20, 20, Width, Height);
    }
    else if(type.equals("line"))
    {
        g2d.drawRect(20, 20, Width, Height);
    }
        
    if(isselected)
        drawbounding(g2d, p);
        
    g2.rotate((double) rotate, X, Y);
    g2.drawImage(bufferedImage, X, Y, p);
        
    g2d.setTransform(old);
}

The above code is the way I draw and rotate everything. I am then using this next code segment to calculate whether a click is inside of a pre determined bounding box.

public boolean isSelected(int x, int y)
{
    if(x > X && x < X + 40 + Width)
    {
        if(y > Y && y < Y + 40 + Height)
        {
            isselected = true;
            return true;
        }
    }
        
    isselected = false;
    return false;
}

The issue I am running in to is that when the buffered image gets rotated the bounding box doesnt rotate with it. I know this has to do with the fact that the bounding box uses global x, y coordinates and the buffered image is local coordinates to the buffered image itself. How would i go about rotating the bounding box with the buffered image?


Solution

  • If you need hit detection you can keep a List of Shape objects to paint.

    1. in the paintComponent(...) method you then iterate through the List to paint each Shape
    2. In the MouseListener you iterate through the List and invoke the contains(...) method on the Shape.

    Before adding the Shape to the List you can use the ShapeUtils.rotate(...) method to rotate the Shape so the Shape is painted correctly and hit detection works.

    import java.awt.Point;
    import java.awt.Polygon;
    import java.awt.Rectangle;
    import java.awt.Shape;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Path2D;
    import java.awt.geom.GeneralPath;
    
    /**
     *  Collection of static methods to make working with Shapes easier.
     */
    public class ShapeUtils
    {
        /**
         *  The points of the Shape are calculated by specifying the radius of each
         *  point. You must specify the number of points to be calculated as well
         *  as an array (varargs) containing the radius for each point. The
         *  calculation of each Point will use the radius modulo array.length.
         *  For example you can create Shapes like the following:
         *
         *  Shape hexagon = ShapeUtils.radiusShape(6, 10);
         *  Shape star = ShapeUtils.radiusShape(16, 20, 8);
         *  Shape doubleStar = ShapeUtils.radiusShape(16, 20, 8, 14, 8);
         *
         *  @param point the number of points contained in the Shape
         *  @param radii one or more radius used to calculate a specific point
         *
         *  @returns a Shape containing all the calculated points
         */
         public static Shape radiusShape(int points, int... radii)
         {
            Polygon polygon = new Polygon();
    
            for (int i = 0; i < points; i++)
            {
                double radians = Math.toRadians(i * 360 / points);
                int radius = radii[i % radii.length];
    
                double x = Math.cos(radians) * radius;
                double y = Math.sin(radians) * radius;
    
                polygon.addPoint((int)x, (int)y);
            }
    
            Rectangle bounds = polygon.getBounds();
            polygon.translate(-bounds.x, -bounds.y);
    
            return polygon;
        }
    
        /**
         *  Rotate a Shape about its center in a clockwise direction by a specified angle
         *
         *  @param shape the Shape to rotate
         *  @param angle the angle in degrees
         *
         *  @returns a new rotated Shape object
         */
        public static Shape rotate(Shape shape, int angle)
        {
            double radians = Math.toRadians( angle );
            Rectangle bounds = shape.getBounds();
            double anchorX = bounds.x + bounds.width / 2;
            double anchorY = bounds.y + bounds.height / 2;
            AffineTransform at = AffineTransform.getRotateInstance(radians, anchorX, anchorY);
            Shape rotated = at.createTransformedShape(shape);
    
            return rotated;
        }
    
        /**
         *  Translate a Shape
         *
         *  @param shape the Shape to rotate
         *  @param point the Point containing the requested translation
         *
         *  @returns a new translated Shape object
         */
        public static Shape translate(Shape shape, Point point)
        {
            AffineTransform at = AffineTransform.getTranslateInstance(point.x, point.y);
            Shape translated = at.createTransformedShape(shape);
    
            return translated;
        }
    }
    

    Check out: Drag a Painted Shape for a complete working example.

    Note, I changed code in the above link:

    //shapePanel.addShape(barBell, Color.RED);
    shapePanel.addShape( ShapeUtils.rotate(barBell, 45), Color.RED);
    

    to demonstrate how to rotate any Shape.

    You can also check out Playing With Shapes for more information and the most current version of the ShapeUtils code (in case it changes).