【问题标题】:Rotating shape and drawing it at the original position旋转形状并在原始位置绘制
【发布时间】:2021-04-28 23:05:38
【问题描述】:

我正在尝试在给定点绘制一个旋转的形状。举个例子,在下图中,红色矩形是在一个点绘制的非旋转矩形,然后蓝色矩形旋转并绘制在同一位置。蓝色矩形是我想要的结果。

我一直在尝试不同的方法。目前,这是我用于图像的内容:

Point point = new Point(300, 300);
Dimension dim = new Dimension(200, 100);
double radians = Math.toRadians(30);
g.setColor(new java.awt.Color(1f, 0f, 0f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);
translate(g, dim, radians);
g.rotate(radians, point.getX(), point.getY());
g.setColor(new java.awt.Color(0f, 0f, 1f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);

private static void translate(Graphics2D g, Dimension dim, double radians) {
    if (radians > Math.toRadians(360)) {
        radians %= Math.toRadians(360);
    }
    
    int xOffsetX = 0;
    int xOffsetY = 0;
    int yOffsetX = 0;
    int yOffsetY = 0;
    if (radians > 0 && radians <= Math.toRadians(90)) {
        xOffsetY -= dim.getHeight();
    } else if (radians > Math.toRadians(90) && radians <= Math.toRadians(180)) {
        xOffsetX -= dim.getWidth();
        xOffsetY -= dim.getHeight();
        yOffsetY -= dim.getHeight();
    } else if (radians > Math.toRadians(180) && radians <= Math.toRadians(270)) {
        xOffsetX -= dim.getWidth();
        yOffsetX -= dim.getWidth();
        yOffsetY -= dim.getHeight();
    } else {
        yOffsetX -= dim.getWidth();
    }
    int x = rotateX(xOffsetX, xOffsetY, radians);
    int y = rotateY(yOffsetX, yOffsetY, radians);
    g.translate(x, y);
}

private static int rotateX(int x, int y, double radians) {
    if (x == 0 && y == 0) {
        return 0;
    }
    return (int) Math.round(x * Math.cos(radians) - y * Math.sin(radians));
}

private static int rotateY(int x, int y, double radians) {
    if (x == 0 && y == 0) {
        return 0;
    }
    return (int) Math.round(x * Math.sin(radians) + y * Math.cos(radians));
}

这适用于矩形,但不适用于其他类型的形状。我试图弄清楚是否有一种方法可以为每种类型的形状完成此操作。另请注意,该代码仅用于测试目的,其中有很多不好的做法,例如调用 Math.toRadians。

【问题讨论】:

  • @AndrewThompson 在我发布的示例中,它使用了您通过Graphics2D.html#rotate(double, double, double) 链接的getRotateInstance 方法,具有相同的效果。你也是对的,我试图让它旋转和未旋转形状之间的最小 X 和 Y 值是相同的。这可能需要以某种方式通过旋转然后平移来完成,反之亦然。
  • 前几天我用类似的图片回答了问题。它在哪里?

标签: java swing geometry 2d


【解决方案1】:

这样的?

可以先使用旋转变换来实现,然后以旋转形状的边界为基础,可以使用平移变换将其移回以满足最顶部的y 和最左侧的x 值原来的矩形。

请参阅getImage() 方法以了解该方法的一种实现方式。

int a = angleModel.getNumber().intValue();
AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
// rotate the original shape with no regard to the final bounds
Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
// get the bounds of the rotated shape
Rectangle2D rotatedRect = rotatedShape.getBounds2D();
// calculate the x,y offset needed to shift it to top/left bounds of original rectangle
double xOff = rectangle.getX()-rotatedRect.getX();
double yOff = rectangle.getY()-rotatedRect.getY();
AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
// shift the new shape to the top left of original rectangle
Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);

这里是完整的源代码:

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.EmptyBorder;

public class TransformedShape {

    private JComponent ui = null;
    JLabel output = new JLabel();
    JToolBar tools = new JToolBar("Tools");
    ChangeListener changeListener = (ChangeEvent e) -> {
        refresh();
    };
    int pad = 5;
    Rectangle2D.Double rectangle = new Rectangle2D.Double(pad,pad,200,100);
    SpinnerNumberModel angleModel = new SpinnerNumberModel(30, 0, 90, 1);

    public TransformedShape() {
        initUI();
    }

    private BufferedImage getImage() {
        int a = angleModel.getNumber().intValue();
        AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
        Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
        Rectangle2D rotatedRect = rotatedShape.getBounds2D();
        double xOff = rectangle.getX()-rotatedRect.getX();
        double yOff = rectangle.getY()-rotatedRect.getY();
        AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
        Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);
        Area combinedShape = new Area(rotateAndTranslateShape);
        combinedShape.add(new Area(rectangle));
        Rectangle2D r = combinedShape.getBounds2D();
        BufferedImage bi = new BufferedImage((int)(r.getWidth()+(2*pad)), (int)(r.getHeight()+(2*pad)), BufferedImage.TYPE_INT_ARGB);

        Graphics2D g = bi.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        g.setColor(new Color(255,0,0,127));
        g.fill(rectangle);
        
        g.setColor(new Color(0,0,255,127));
        g.fill(rotateAndTranslateShape);
        
        g.dispose();
        
        return bi;
    }

    private void addModelToToolbar(String label, SpinnerNumberModel model) {
        tools.add(new JLabel(label));
        JSpinner spinner = new JSpinner(model);
        spinner.addChangeListener(changeListener);
        tools.add(spinner);
    }

    public final void initUI() {
        if (ui!=null) return;

        ui = new JPanel(new BorderLayout(4,4));
        ui.setBorder(new EmptyBorder(4,4,4,4));
        
        ui.add(output);
        
        ui.add(tools,BorderLayout.PAGE_START);

        addModelToToolbar("Angle", angleModel);
              
        refresh();
    }
    
    private void refresh() {
        output.setIcon(new ImageIcon(getImage()));
    }
    
    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            TransformedShape o = new TransformedShape();
            
            JFrame f = new JFrame(o.getClass().getSimpleName());
            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            f.setLocationByPlatform(true);
            
            f.setContentPane(o.getUI());
            f.pack();
            f.setMinimumSize(f.getSize());
            
            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);
    }
}

【讨论】:

    【解决方案2】:

    你有一个形状,任何形状。
    您有一个点 (px,py),并且您想围绕该点旋转形状并逆时针测量角度 ag

    对于形状的每个点,过程分为三个步骤:

    1. 转换为(px,py)
    2. 旋转
    3. 转回(0,0)

    翻译很简单

    xNew = xOld - px
    yNew = yOld - py
    

    旋转没那么简单

    xRot = xNew * cos(ag) - yNew * sin(ag)
    yRot = xNew * sin(ag) + yNew * cos(ag)
    

    终于翻译回来了:

    xDef = xRot + px
    yDef = yRot + py
    

    一点解释:任何转换都可以通过两种方式看到:1)我移动形状 2)我移动轴系。如果你想一想,你会发现这种转变是相对的:从轴的角度看,或者从形状的角度看。
    所以,你可以说“我想要翻译后的系统中的坐标”,或者你也可以说“我想要翻译后的形状的坐标”。
    不管你选择什么观点,方程式都是一样的。

    我解释了这么多,只是为了让你知道哪个是角度的正方向:顺时针或逆时针。

    【讨论】:

    • 这只是围绕给定点旋转形状,对吗?
    • @stripies 正确。旋转方程围绕(0,0)。所以首先平移(使给定点成为新的原点),然后旋转然后平移回来。
    • 不幸的是,简单地围绕一个特定点旋转并没有帮助,除非有一种方法可以计算该点来旋转它以获得所需的结果。
    猜你喜欢
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 2013-08-26
    • 1970-01-01
    • 2014-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多