【问题标题】:java move a rotated shapejava移动一个旋转的形状
【发布时间】:2012-04-18 22:36:31
【问题描述】:

我正在实现一个 java swing 应用程序,它有一个用作绘图表面的 JPanel。表面呈现(活动)不同的元素。每个元素都有自己的形状和用于渲染和碰撞检测的仿射变换(每个元素都是用局部坐标绘制的,然后用仿射变换进行变换——比如 opengl)。

元素可以在表面上移动和旋转(这是通过变换完成的)。每次应用变换时,都会使用形状和变换创建一个 Area 对象(用于准确的碰撞检测)。

问题是当我旋转元素(45 度)然后将其移动 10 像素时。当我移动它时,元素会沿我不想要的旋转方向移动。

有什么简单的方法可以克服这个问题吗?
(如果我的描述不够,我会发布一些示例代码)。

编辑:

 class Element
 {
     private AffineTransform transform = new AffineTransform();
     private Shape shape = new Rectangle(0,0,100,100);       
     private Area boundingBox;       

     public void onMouseDrag(MouseEvent e)
     {
        translate(dx,dy); // dx,dy are calculated from event
     }

     public void onMouseMove(MouseEvent e)
     {
        rotate(Math.atan2(dx/dy); // dx,dy are calculated from event
     }

     public void translate(int dx,int dy)
     {
        transform.translate(dx,dy);
        boundingBox = new Area(shape);
        boundingBox.transform(transform);
     }

     public void rotate(double angle)
     {
         transform.rotate(Math.toRadians(angle));
         boundingBox = new Area(shape);
         boundingBox.transform(transform);
     }

     public void draw(Graphics2D g2d)
     {
         g2d.setTransform(transform); 

         g2d.draw(shape); 
        ...
      }

 } 

【问题讨论】:

  • 使用此代码,您可以使用rotate(theta, x, y) 而不是rotate(theta) 围绕当前位置旋转。无论如何,不​​鼓励这样的增量转换,你应该考虑给Element 一个positionrotation 属性,用于在它们发生变化时重新计算AffineTransform,或者每帧/更新一次。
  • 来自web.eecs.utk.edu/~bvz/gui/notes/transforms/transform.html:“在包括Java 在内的大多数图形工具包中,转换是按后进先出顺序应用的。也就是说,代码中列出的最后一个转换是实际应用的第一个转换,并且代码中列出的第一个转换是实际应用的最后一个转换。”
  • @Torious 如果您要发布带有示例的答案,请接受它

标签: java swing transform


【解决方案1】:

我通过提供Element positionorientation 字段修改了您的代码,您可能希望通过getter/setter 公开它们(可能在返回/设置之前防御性地复制Point 实例)。 updateTransform() 只是根据Element 的当前positionorientation 重新构建AffineTransform

public class Element {

    private Point position = new Point();
    private float orientation;

    private AffineTransform transform = new AffineTransform();
    private Shape shape = new Rectangle(0, 0, 100, 100);
    private Area boundingBox;

    public void onMouseDrag(MouseEvent e) {
        translate(dx, dy);
    }

    public void onMouseMove(MouseEvent e) {
        rotate(Math.atan2(dx / dy));
    }

    public void translate(int dx, int dy) {
        position.translate(dx, dy);
        updateTransform();
    }

    public void rotate(double angle) {
        orientation += Math.toRadians(angle);
        updateTransform();
    }

    private void updateTransform() {
        transform.setToIdentity();
        transform.translate(position.x, position.y);
        transform.rotate(orientation);
        // ... update bounding box here ...
    }

    public void draw(Graphics2D g2d) {
        g2d.setTransform(transform);
        g2d.draw(shape);
    }

}

另外,请考虑以下方法:

  • draw(Graphics2D) 更改为draw(Graphics2D, AffineTransform transform)
  • 创建AffineTransform Element.getCompositeTransform(),它构建当前的AffineTransform 并返回它(如updateTransform())。
  • Element 中删除transform 字段。
  • 在您的渲染方法中,执行以下操作:

.

for (Element element : elements) {
    AffineTransform transform = element.getCompositeTransform();
    element.draw(graphics, transform);
}

为什么?这使您可以灵活地在外部控制Element 的绘图变换。如果您要构建一个节点图(例如当Element 可以有Element 子节点时)并且您将递归地渲染图,连接变换,这将非常有用。顺便说一句,这种方法在 3D 编程中非常标准。

【讨论】:

  • 谢谢。我有子女抚养费,但集合是 z 排序的,而不是基于树的。但是转换合并是类似的。
【解决方案2】:

颠倒您应用转换的顺序。或者取消旋转对象,移动它,然后重新旋转对象。我们真的需要查看您的代码(甚至是伪代码)才能为您提供比这更好的答案。

【讨论】:

  • 用相关代码更新了我的问题。问题是我不知道转换的顺序或顺序。
  • +1 这个相关的Q&A 检查连接转换的(非交换)顺序。
猜你喜欢
  • 2018-12-06
  • 1970-01-01
  • 1970-01-01
  • 2018-05-17
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
相关资源
最近更新 更多