java - 不变换 Stroke 的 AffineTransform?

标签 java awt java-2d affinetransform stroke

当使用具有两个不同参数的 Graphics2D scale() 函数时(在 x 和 y 方向上按不同的比例缩放),稍后在此 Graphics2D 对象上绘制的所有内容也会缩放。这会产生奇怪的效果,即在一个方向绘制的线条比在另一个方向绘制的线条粗。下面的程序产生了这个效果,它显示了这个窗口:

example screenshot

public class StrokeExample extends JPanel {


    public void paintComponent(Graphics context) {
        super.paintComponent(context);
        Graphics2D g = (Graphics2D)context.create();
        g.setStroke(new BasicStroke(0.2f));

        int height = getHeight();
        int width = getWidth();

        g.scale(width/7.0, height/4.0);

        g.setColor(Color.BLACK);
        g.draw(new Rectangle( 2, 1, 4, 2));
    }

    public static void main(String[] params) {
        EventQueue.invokeLater(new Runnable(){public void run() {

            StrokeExample example = new StrokeExample();

            JFrame f = new JFrame("StrokeExample");
            f.setSize(100, 300);
            f.getContentPane().setLayout(new BorderLayout());
            f.getContentPane().add(example);
            f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            f.setVisible(true);
        }});

    }

}

我正在使用此坐标转换来避免手动将我的应用程序模型坐标(本例中的 (2,1, 2,4))转换为屏幕(或组件)像素坐标,但我不想此笔画变形。换句话说,我想让所有线条都具有相同的宽度,与当前的 x 和 y 比例因子无关。

我知道是什么产生了这种效果(Stroke 对象创建了一个要在用户坐标中绘制的矩形的描边形状,然后将其转换为屏幕坐标),但我不确定如何解决这个问题。

  • 我是否应该创建一个新的 Stroke 实现,在 X 和 Y 方向上对形状进行不同的描边(从而消除此处的扭曲)? (或者是否有人已经知道这样的实现?)
  • 我应该将我的形状转换为屏幕坐标并在那里描边吗?
  • 还有其他(更好的)想法吗?

最佳答案

原来我的问题并没有那么难,而且我在问题中给出的两个想法实际上是同一个想法。这是一个 TransformedStroke 类,它通过转换 Shape 来实现变形的 Stroke

import java.awt.*;
import java.awt.geom.*;


/**
 * A implementation of {@link Stroke} which transforms another Stroke
 * with an {@link AffineTransform} before stroking with it.
 *
 * This class is immutable as long as the underlying stroke is
 * immutable.
 */
public class TransformedStroke
    implements Stroke
{
    /**
     * To make this serializable without problems.
     */
    private static final long serialVersionUID = 1;

    /**
     * the AffineTransform used to transform the shape before stroking.
     */
    private AffineTransform transform;
    /**
     * The inverse of {@link #transform}, used to transform
     * back after stroking.
     */
    private AffineTransform inverse;

    /**
     * Our base stroke.
     */
    private Stroke stroke;


    /**
     * Creates a TransformedStroke based on another Stroke
     * and an AffineTransform.
     */
    public TransformedStroke(Stroke base, AffineTransform at)
        throws NoninvertibleTransformException
    {
        this.transform = new AffineTransform(at);
        this.inverse = transform.createInverse();
        this.stroke = base;
    }


    /**
     * Strokes the given Shape with this stroke, creating an outline.
     *
     * This outline is distorted by our AffineTransform relative to the
     * outline which would be given by the base stroke, but only in terms
     * of scaling (i.e. thickness of the lines), as translation and rotation
     * are undone after the stroking.
     */
    public Shape createStrokedShape(Shape s) {
        Shape sTrans = transform.createTransformedShape(s);
        Shape sTransStroked = stroke.createStrokedShape(sTrans);
        Shape sStroked = inverse.createTransformedShape(sTransStroked);
        return sStroked;
    }

}

我使用它的绘画方法看起来像这样:

public void paintComponent(Graphics context) {
    super.paintComponent(context);
    Graphics2D g = (Graphics2D)context.create();

    int height = getHeight();
    int width = getWidth();

    g.scale(width/4.0, height/7.0);

    try {
        g.setStroke(new TransformedStroke(new BasicStroke(2f),
                                          g.getTransform()));
    }
    catch(NoninvertibleTransformException ex) {
        // should not occur if width and height > 0
        ex.printStackTrace();
    }

    g.setColor(Color.BLACK);
    g.draw(new Rectangle( 1, 2, 2, 4));
}

然后我的窗口看起来像这样:

screenshot of undistorted stroke

我对此很满意,但如果有人有更多想法,请随时回答。


注意:此g.getTransform() 返回g 相对于设备的完整 转换空间,不仅是在 .create() 之后应用的转换。因此,如果有人在将图形提供给我的组件之前进行了一些缩放,这仍然会使用 2 设备像素宽度的笔划进行绘制,而不是为我的方法提供的图形的 2 像素。如果这是一个问题,请像这样使用它:

public void paintComponent(Graphics context) {
    super.paintComponent(context);
    Graphics2D g = (Graphics2D)context.create();

    AffineTransform trans = new AffineTransform();

    int height = getHeight();
    int width = getWidth();

    trans.scale(width/4.0, height/7.0);
    g.transform(trans);

    try {
        g.setStroke(new TransformedStroke(new BasicStroke(2f),
                                          trans));
    }
    catch(NoninvertibleTransformException ex) {
        // should not occur if width and height > 0
        ex.printStackTrace();
    }

    g.setColor(Color.BLACK);
    g.draw(new Rectangle( 1, 2, 2, 4));
}

在 Swing 中,通常您提供给 paintComponent 的 Graphics 只会被翻译(所以 (0,0) 是您组件的左上角),不会被缩放,所以没有区别。

关于java - 不变换 Stroke 的 AffineTransform?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5046088/

相关文章:

java - 复制并镜像通用路径

java - 在另一个类方法中调用小程序

java 在要求输入两次时执行

垃圾回收 : static variables not lasting through the applications lifetime

java - Google 距离矩阵 API - 火车距离

java - 如果程序仅通过 doClick() 进行交互,则 JFrame 永远不会呈现;

java - Javadoc 报告生成中发生错误 - 未知标签

Java如何绘制和填充有孔的多边形

java - 如何在Java中正确使用keyListener

java - 用Java画一个漂亮的圆圈