2

私のプロジェクト (VC++2010、MFC) では、CDC::Ellipse を使用して円を描画したいと考えています。2 つのポイントを設定します。1 つ目は円の中心、2 つ目は円周上のポイントです。

左上隅と右下隅の座標を CDC::Ellipse( int x1, int y1, int x2, int y2 ) に渡します。

簡単に言うと、ピタゴラの定理を使用して、2 点間の距離 ( radius ) を計算し、この値を中心の座標から引いて左上隅を取得し、加算して右下隅を取得します。

円と点を描いてズームインすると、2 番目の円が円周上になく、0°、45°、90° などに設定しない限り、わずかに内側にあることがわかります。座標の絶対系に関して。

次に、CDC::Polyline を使用して同じ円を描画しようとしました。半径に等しい距離で、中心を中心に別の点を回転させて取得した点をこのメソッドに与えました。この場合、ポイントは設定したすべての円周上にあります。

これらの 2 つの円の重なりは、それらが 0°、45°、90° などで完全に重なり合うことを示していますが、ギャップは 22.5°、67.5° などで最大になります。

誰かが似たような行動に気づいたことがありますか?

私を助けることができるすべての人に感謝します!

コード スニペット:

これは、2点を指定して半径を計算する方法です。

centerPX = vvFPoint( 1380, 845 );
secondPointPX = vvFPoint( 654,654 );
double radiusPX = (sqrt( (secondPointPX.x - centerPX.x) * (secondPointPX.x - centerPX.x) + (secondPointPX.y - centerPX.y) * (secondPointPX.y - centerPX.y) ));

( vvFPointは CPoint から派生したカスタム タイプです)

これは、CDC::Ellipse で「円」を描く方法です。

int up = (int)(((double)(m_p1.y-(double)originY - m_radius) / zoom) + 0.5) + offY;
int left = (int)(((double)(m_p1.x-(double)originX - m_radius) / zoom) + 0.5) + offX;
int down = (int)(((double)(m_p1.y-(double)originY + m_radius) / zoom) + 0.5) + offY;
int right = (int)(((double)(m_p1.x-(double)originX + m_radius) / zoom) + 0.5) + offX;
pDC->Ellipse( left, up, right, down);

( m_p1は円の中心、originX/Yは画像の原点、m_radiusは円の半径、zoomは倍率、offX/Yは SW のクライアント領域のオフセットです)

これは、カスタムポリラインクラスを使用して「手動で」(そして非常に簡単な方法で)円を描く方法です。

1) ポイントの配列を作成します。

point.x = centerPX.x + radiusPX;
point.y = centerPX.y;
for ( i=0; i < 3600; i++ )
{
    pt1.RotateDeg ( centerPX, (double)0.1 );
    poly->AddPoint( pt1 );
}

( RotateDegは、最初の引数をピボットとして、2 番目の引数を度単位の角度値として使用してポイントを回転させるカスタム メソッドです。AddPointは、ポイントの配列を作成するカスタム メソッドです。poly は、カスタムポリラインオブジェクトです)。

2)それを描きます:

Draw( CDC* pDC ) を呼び出すと、前の配列を使用してポリラインを描画します。

pDC->MoveTo(p);

これが私の奇妙な観察を再現するのに役立つことを願っています!

コード スニペット 2 :

void vvPoint<Tipo>::RotateDeg(const vvPoint<Tipo> &center, double angle)
{
vvPoint<Tipo> ptB;

angle *= -(M_PI / 180);

*this -= center;
ptB.x = ((this->x * cos(angle)) - (this->y * sin(angle)));
ptB.y = ((this->x * sin(angle)) + (this->y * cos(angle)));
*this = ptB + center;
}

しかし、私の観察をよりよく理解できるように、いくつかの画像を追加して、私の質問全体がどこから始まったかを確認できるようにしたいと思います...問題は、評判が10になる必要があるため、画像を追加できないことです. Dropbox に .zip ファイルをアップロードしました。必要に応じて、このファイルの URL をお送りします。これがこの問題を回避するための正しい (そして安全な..) 方法であるかどうか教えてください。

ありがとう!

4

1 に答える 1

2

これは可能な説明かもしれません。MSDNが述べているようにCDC::Ellipse(強調して):

楕円の中心は、x1y1x2、およびy2、またはlpRectによって指定された外接する四角形の中心です。楕円は現在のペンで描画され、その内部は現在のブラシで塗りつぶされます。

この関数によって描画される図形は、右と下の座標まで拡張されますが、含まれません。これは、図の高さがy2 – y1で、図の幅がx2 – x1であることを意味します。

外接する四角形を計算する方法を説明した方法は完全には明確ではありませんが (一部のソース コードが役に立ちます)、上記の 2 番目の段落を考えると、x2y2の値に 1 を追加して、希望の半径の円。

また、境界ボックスのサイズが奇数の場合 (つまり、中心点が論理的に 1/2 ピクセルになる場合)、2 つの描画方法の間にわずかな丸めの違いがある可能性があることにも注意してください。

アップデート

コード スニペットを使用して (感謝)、ズームやゼロ オフセットなどを想定すると、半径 750.704 ピクセルと楕円の次のパラメーターが得られます。

pDC->Ellipse(629, 94, 2131, 1596);

MSDN によると、これは楕円が次の寸法の図形で描画されることを意味します。

width  = (2131 - 629) = 1502
height = (1596 -  94) = 1502

私が見る限り、これは楕円ではなく円を生成するはずです。

次に行うことは、ポリゴンをどのように描画しているかを調べることです。そのためには、実装を確認する必要がありRotateDegます。そのコードを投稿できますか? ここでは、単純な丸め誤差が疑われます。ズームすると拡大される可能性があります。

更新 2

このコードを見るだけで:

for ( i=0; i < 3600; i++ )
{
    pt1.RotateDeg ( centerPX, (double)0.1 );
    poly->AddPoint( pt1 );
}

ポリゴン ポイントを毎回 0.1 度ずつ回転させています。これによりエラーが蓄積される可能性があるため、代わりに次のようにすることをお勧めします。

for ( i=0; i < 3600; i++ )
{
    vvFPoint ptNew = pt1;
    ptNew.RotateDeg ( centerPX, (double)i * 0.1 );
    poly->AddPoint( ptNew );
}

RotateDegおそらくこれは、正しい象限を処理するために関数を変更する必要があることを意味します.

もう1つのポイントとして、画像を拡大すると問題が発生するとおっしゃいました。これが変数を使用していることを意味する場合はzoom、次の行を確認する価値があります...:

pDC->Ellipse( left, up, right, down);

...パラメータはまだ正方形を形成しているため、(right - left) == (down - up).

更新 3

関数を現在の形式で実行RotateDegして、エラーがどのように蓄積されるかを確認しました (前の結果を次の反復にフィードすることにより)。各ステップで、新しいポイントと中心の間の距離を計算し、これを必要な半径と比較しました。

以下のグラフは結果を示しており、ポイントが計算されるまでに 4 ピクセルの誤差が見られます。

エラー

これは少なくとも違いの一部を説明していると思います(つまり、ポリゴンの描画に欠陥があります)。場合によってはzoom、楕円パラメーターに非対称性を導入することができます。これは、上記のように幅と高さを比較することでデバッグできます。

于 2013-11-01T09:20:55.447 に答える