13

scale()2つの異なるパラメーター(x方向とy方向の異なる比率によるスケーリング)でGraphics2D関数を使用すると、このGraphics2Dオブジェクトに後で描画されるすべてのものもスケーリングされます。これには、ある方向に描かれた線が別の方向に描かれた線よりも太いという奇妙な効果があります。次のプログラムはこの効果を生み出し、このウィンドウを表示します。

スクリーンショットの例

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オブジェクトは、ユーザー座標でペイントされる長方形のストローク形状を作成し、それが画面座標に変換されます)、これを解決する方法がわかりません。

  • X方向とY方向でシェイプを異なるストロークにする(これにより、ここで歪みを元に戻す)新しいストローク実装を作成する必要がありますか?(または、誰かがそのような実装をすでに知っていますか?)
  • 図形を画面座標に変換して、そこでストロークする必要がありますか?
  • 他の(より良い)アイデアはありますか?
4

3 に答える 3

10

私の質問はそれほど難しいものではなく、質問で与えられた私の 2 つのアイデアは実際には同じアイデアであることがわかりました。これは、を変換しTransformedStrokeて歪んだ を実装するクラスです。StrokeShape

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));
}

次に、私のウィンドウは次のようになります。

歪みのないストロークのスクリーンショット

私はこれで十分満足していますが、誰かがもっとアイデアを持っている場合でも、遠慮なく答えてください。


注意:これは、 の後に適用される変換だけでなく、デバイス空間に対するgの完全な変換をg.getTransform()返しています。したがって、Graphics をコンポーネントに渡す前にスケーリングを行った場合でも、メソッドに指定された 2 ピクセルのグラフィックスではなく、2 デバイス ピクセル幅のストロークで描画されます。これが問題になる場合は、次のように使用します。.create()

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 では通常、 に指定された GraphicspaintComponentは変換されるだけで ((0,0) はコンポーネントの左上隅になります)、スケーリングされないため、違いはありません。

于 2011-02-18T21:23:40.963 に答える
0

アプリケーションの int x と int y を int x = 500 int y = 900 のように大きくしようとしたことがありますか? また、私の提案は、コード全体を書き直さずに、上部と下部の四角形を 2 倍にするように、アプリが互いに接近しているときに recs がより厚い場所を実装することですが、アプリが拡張されると上部と下部の recs が移動することです。通常に戻ります...

于 2011-03-05T00:57:00.800 に答える