Rectangleの縦横比

タグの編集
投稿者 yamaV1.02β  (社会人) 投稿日時 2009/5/10 04:33:48
お世話になります。

フォームに正方形が描画されることを期待して、フォームのペイントイベントに以下のコードを書いてみました。

        Dim g As Graphics
        Dim p As New Pen(Color.Red)
        p.Width = 10
        g = e.Graphics
        g.DrawRectangle(p, 10, 10, 100, 100)

これで、左上隅10,10の場所に、幅*高さ100*100の正方形が描画されるかと思ったのですが、どう見ても縦横比1:1.2程度の縦長の長方形が描画されます。とても正方形には見えません。
線の幅も縦幅の方が太く見えます。

どういう理屈なのでしょうか。






投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/5/10 05:27:54
正しくは、
Using p As New Pen(Color.Red)
        p.Width = 10
        Dim g As Graphics = e.Graphics
        g.DrawRectangle(p, 10, 10, 100, 100)
End Using
ですよね。今回の件とは関係ないですけれども。


> どう見ても縦横比1:1.2程度の縦長の長方形が描画されます。
お使いのモニタは液晶画面でしょうか。それともCRT(ブラウン管)ディスプレイでしょうか。

CRT だとしたら、モニタ側のスイッチやハードウェアメニュー等で、画面の縦横比を
調整できる機能が無いかどうか確認してください。

液晶画面だとしたら、画面の推奨解像度と、OS 指定の解像度が一致しているかどうかを
確認してください。

これらの設定に問題がある場合、見た目上の縦横比が崩れて見えてしまう可能性があります。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/5/10 05:39:21
ひとつ確認。

その描画したフォームを、[Ctrl]+[Print Screen]キーで撮影し、
それをアクセサリの「ペイント」に貼り付けてみてください。

その後、それをペイントの[変形]-[反転と回転]で 90度回転させて
縦と横を入れ替えてみてください。

もし、1.0 : 1.2 の割合で描画されていたのだとしたら、
1.2 : 1.0 の割合になるはずですが、そうなりますか?
それとも、回転後も 1.0 : 1.2 のままですか?

もし、回転させて縦横を入れ替えても 1.0 : 1.2 で見えてしまうのであれば、
先に書いたモニタ側の設定の問題だと思います。

逆に、回転させると比率も入れ替わって 1.2 : 1.0 になるのなら、
本当に縦横比が違ってしまっていることになりますね。この場合、
モニタ側は正しくて、コード側に問題があるのかも知れません。
たとえば、Graphics クラスの ScaleTransform メソッドを呼び出すと
縦横比が変化しますが、そのようなコードがどこかに含まれていないでしょうか。
投稿者 yamaV1.02β  (社会人) 投稿日時 2009/5/10 07:03:45
>液晶画面だとしたら、画面の推奨解像度と、OS 指定の解像度が一致しているかどうかを
>確認してください。

魔界の仮面弁士さん、いつもありがとうございます。

・画面の推奨解像度
・OS 指定の解像度

それぞれどう確認するのかわからないでいますが、解像度を変えてみると確かに正方形に見える解像度がありました。

おかげ様で正方形に見えるようになりました。

Using p As New Pen(Color.Red)
        p.Width = 10
        Dim g As Graphics = e.Graphics
        g.DrawRectangle(p, 10, 10, 100, 100)
End Using

が正しいのですね。
エラーがでないように書くのが精一杯の状態です。

エクセルVBAで試してみたのですが、
Sub testRectangle()
    Dim myRec As Shape
        Set myRec = _
            Sheet1.Shapes.AddShape( _
            msoShapeRectangle, 0, 0, 100, 100)
        
        Debug.Print myRec.Width, myRec.Height
End Sub

解像度を変えてみる前は縦にながい長方形でした。で、図形を90度回転させると、やっぱり縦長・同じ縦横比の四角形になったので、解像度の問題だと結論しました。

ところで、エクセルVBAでは縦横100ポイントの四角形をしていしているのに、
myRec.Width, myRec.Height は 99.75 を返します。
これはこれで不思議なのですが、ここで質問することではないような気がするようなしないようなです。

エクセルVBAだと、myRec.Width という書き方ができるのですが、
.NETで、

Debug.Print(g.Width, g.Height)

と書きたくても書けません。

どう書けばよいのでしょうか。






投稿者 neptune  (社会人) 投稿日時 2009/5/10 08:07:50
こんにちは

Debug.Print(g.Width, g.Height)
は、べた書きでごめんなさいですが、System名前空間を使って
console.writeline(g.width.tostring("d"),g.Height.tostring("d"))
でいけませんか?出力ウィンドウに出力されます。

>myRec.Width, myRec.Height は 99.75 を返します。
の方は良く判りませんが、これも解像度が絡む可能性はあります。
解像度によってピクセルとポイントの関係も変わってきますしね。
それか、どんな仕様かは判りませんけどExcelの都合の良い所で切りを付ける仕様とか?
投稿者 yamaV1.02β  (社会人) 投稿日時 2009/5/10 08:59:45
neptuneさん返信ありがとうございます。
   
>console.writeline(g.width.tostring("d"),g.Height.tostring("d"))
       Using p As New Pen(Color.Red)
            p.Width = 10
            Dim g As Graphics = e.Graphics
            g.DrawRectangle(p, 10, 20, 100, 100)
            Console.WriteLine(g.width.tostring("d"), g.Height.tostring("d"))
                              ~~~~~~~~               ~~~~~~~~~
        End Using


~~~~部分で widthは'System.Drawing.Graphics'のメンバーではありません、とエラーになります。

エクセルVBAだと、

g.DrawRectangle(p, 10, 20, 100, 100)

で描画した図形を任意に宣言したオブジェクト変数に SET できるのですが、
Sub testRectangle()
    Dim myRec As Shape
        Set myRec = _
            Sheet1.Shapes.AddShape( _
            msoShapeRectangle, 0, 0, 100, 100)
        
        Debug.Print myRec.Width, myRec.Height
End Sub

ここでやってる事はワークシート上のシェイプオブジェクトに対する操作なので、比較して論じる事自体がまちがっているという認識をしつつあります。

みなさんの環境では、「myRec.Width, myRec.Height は 99.75 を返します。」という状況は再現しませんでしょうか?

僕のPCだと、Xl2000とXl2003の両方で再現できます。

どんなものでしょうか。

http://www.moug.net/faq/viewtopic.php?p=261089#261089
↑にマルチスレッドたてました
投稿者 yamaV1.02β  (社会人) 投稿日時 2009/5/10 09:49:34
エクセルVBAの1辺100ポイント指定で作成した1辺の長さを再取得すると、99.75になる件について一応納得できない事もない回答をマルチ先で受けましたので報告いたします。

imari さんの引用:
たしかオートシェイプの幅と高さの最小単位は0.75(ポイント)なので、 
そのように丸められると思います。


int(100/0.75)*0.75=99.75 という事のようです。

エクセルVBAとの違いについては、今はこだわらない事にします。<(_ _)>

さて、
http://homepage1.nifty.com/rucio/main/dotnet/shokyu/standard48.htm
Visual Basic 初級講座 第48回 高度なメソッド・プロパティ
■リスト18:DrawEdgeメソッドの多重定義

の以下のコードが間違っているのでは、と混乱しながら読んだのですが、私の勘違いなのかコードの間違いなのか確認させて頂きたく。

'■DrawEdge 
''' <summary>でっぱった四角形を描画します。</summary> 
''' <param name="g">描画に使用するGraphicsクラスを指定します。</param> 
''' <param name="Point1">描画する範囲の左上を表す点</param> 
''' <param name="Point2">描画する範囲の右下を表す点</param> 
''' <remarks></remarks> 
Private Overloads Sub DrawEdge(ByVal g As Graphics, ByVal Point1 As Point, ByVal Point2 As Point)

    Dim Rect As Rectangle

    Rect.X = Point1.X
    Rect.Y = Point1.Y
    Rect.Width = Point2.X
                ~~~~~~~~~~~
    Rect.Height = Point2.Y
                ~~~~~~~~~~~
    Call DrawEdge(g, Rect)

波線部分は、
        With Rect
            .X = Point1.X
            .Y = Point1.Y
            .Width = point2.X - Point1.X
            .Height = point2.Y - Point1.Y
        End With

が正しいと思うのですが、(With...End Withステートメントを使う使わないかはどうでも良いのですが、私はついついこう書きたくなります。)

私何か勘違いしてますでしょうか?

どんなものでしょう。
投稿者 neptune  (社会人) 投稿日時 2009/5/10 18:59:16
0.75の正体。

画面の解像度が 96dpi(96 ピクセル/1 インチ 確かこれがWindowsの標準)
1 インチ = 72 ポイント なので 
1 ピクセル = 72 / 96 = 0.75 ポイント 
↑の関係があるから解像度も関係している可能性がある旨を書きました。
なので0.75が絶対と言う訳ではないのです。圧倒的に多いとは思いますけど。
詳しくは「インチ ポイント ピクセル 解像度 Windows 換算」をキーワードにググって見て下さい。
沢山ヒットします。

画面の解像度はGraphicsメンバに取得するプロパティがあるみたいです。
APIならGetDeviceCaps 関数で取得できます。
後は、ご自分で調べて下さい。


>Console.WriteLine(g.width.tostring("d"), g.Height.tostring("d"))
>~~~~部分で widthは'System.Drawing.Graphics'のメンバーではありません、とエラーになります。
先の投稿で、
>べた書きでごめんなさいですが
と書いていたのですが、・・・・・。
Helpを見れば判るのですが、gにはwidthやheightプロパティはないです。

Dim rc As New Drawing.Rectangle(10, 10, 100, 100)
g.DrawRectangle(p, rc)
Console.WriteLine("Width : " & rc.Width.ToString("d") & "   Height : " & rc.Height.ToString("d"))
投稿者 yamaV1.02β  (社会人) 投稿日時 2009/5/10 21:00:38
>0.75の正体。

なるほど、とピンときた部分もあります。関連ページを斜め読みしてたら消化できず頭バンク状態です。

ですが概要はなんとなくわかりました。

neptune さん回答ありがとうございました。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2009/5/11 22:31:55
> ・画面の推奨解像度
> ・OS 指定の解像度

推奨解像度は、モニタに付属していた説明書で確認してください。


【CRT】タイプの画面の場合、複数の解像度に対応しています。CRT モニタの場合には、
電源スイッチの近くに、投影時の歪みを補正するためのスイッチ群が付いている事が多いです。
これを調整することで、「正方形」に見せる事ができるでしょう。
(スイッチ群が無く、自動設定タイプの物もあるようです)


一方【液晶】タイプの画面の場合も、同様のスイッチを持っている物はありますが、
CRT のような歪みは発生しないため、設定項目はより簡素化されている物が多く、
殆どの場合、そうした調整を行う必要がありません。
しかし液晶では、縦横のドット数が固定であるため、モニタが持っている解像度と
異なる画面設定をしていた場合、正しい比率では描画されません。

たとえばデスクトップのサイズが 1024×768 で、それを 1280×1024 対応のモニタに出力した場合、
 (1) 画面の端に、描画されない黒い領域が現れる。
 (2) 少しぼやけた形で、モニタ全体に拡大表示される。
 (3) 対応していない解像度なので表示されない。
のいずれかで出力されるかと思います。

もし、上記(2)のパターンで出力されるのだとすれば、単純計算では
 横方向 = 4 ピクセルの画像データを、モニタ上では 5 ドットで表現。
 縦方向 = 3 ピクセルの画像データを、モニタ上では 4 ドットで表現。
という事になりますね。(表示方法は、ディスプレイドライバによって異なります)

この場合、たとえば 60×60 平方ピクセルの正方形画像を表示しようとすると、通常であれば
60×60 ドットで表現されるべきところを、解像度が適合していないが故に、縦横比の異なる
75×80 ドットで表現されることになり、見た目上は長方形に見えてしまう可能性があります。