BufferedImage旋转后,如何同步旋转用于选择的边界框?
问题描述
我正在开发一个Java程序,需要先在BufferedImage上绘制对象,再将其绘制到JPanel上,这种方式方便管理同一JPanel上的多个可绘制、可旋转对象。
核心绘制与旋转代码如下:
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); }
我用下面的代码判断点击是否在预设边界框内:
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; }
现在遇到的问题是:BufferedImage旋转后,边界框并没有跟着旋转。我知道原因是边界框用的是全局X、Y坐标,而BufferedImage用的是局部坐标,请问怎么让边界框和BufferedImage一起旋转?
解决方案
要解决这个问题,核心是让点击检测逻辑和图像的旋转变换保持一致,有两种直接可行的方案:
方案一:坐标逆变换
把点击的全局坐标转换到未旋转的局部坐标系中,再用原始边界框判断:
public boolean isSelected(int x, int y) { // 创建和绘制时一致的旋转变换(注意:如果rotate是角度,要转成弧度) AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(rotate), X, Y); AffineTransform inverseTransform = null; try { // 获取逆变换,用于将全局坐标转成局部坐标 inverseTransform = transform.createInverse(); } catch (NoninvertibleTransformException e) { e.printStackTrace(); return false; } // 转换点击坐标 Point2D.Double globalPoint = new Point2D.Double(x, y); Point2D.Double localPoint = new Point2D.Double(); inverseTransform.transform(globalPoint, localPoint); // 用局部坐标判断是否在原始边界框内 boolean isIn = localPoint.getX() > X && localPoint.getX() < X + 40 + Width && localPoint.getY() > Y && localPoint.getY() < Y + 40 + Height; isselected = isIn; return isIn; }
方案二:旋转形状碰撞检测
直接创建旋转后的边界框形状,用Shape.contains()方法做检测:
public boolean isSelected(int x, int y) { // 创建原始的边界框矩形 Rectangle2D originalRect = new Rectangle2D.Double(X, Y, Width + 40, Height + 40); // 创建旋转变换,和绘制时的参数完全一致 AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(rotate), X, Y); // 获取旋转后的形状 Shape rotatedRect = transform.createTransformedShape(originalRect); // 判断点击点是否在旋转后的形状内 boolean isIn = rotatedRect.contains(x, y); isselected = isIn; return isIn; }
额外优化提示
- 你的
draw方法存在一个小问题:g2d.setTransform(old);中的old是g2的原始变换,应该替换为g2d自身的初始变换,避免影响后续绘图:
// 替换原来的AffineTransform old = g2.getTransform(); AffineTransform oldG2d = g2d.getTransform(); // ... 绘制代码 ... g2d.setTransform(oldG2d);
- 如果
rotate变量存储的是角度值(如0-360),必须用Math.toRadians()转换为弧度,因为AffineTransform的旋转方法默认使用弧度作为单位。
内容的提问来源于stack exchange,提问作者Gizmo Dailys
相关产品推荐
相关产品推荐

