引越しでおトクなインターネット回線は?>>

画像を作ってるのですが、円描画のまともな方法が分かりません。

現在は、数学っぽいサイトを検索した所円の方程式は
r2乗 = X2乗 + Y2乗
と言うのが出てきたのでそれを元に作ってます。

具体的には、y軸に近い部分はx軸から。x軸に近い部分はy軸から描画を始め、
それそれ8回に分けて一つの円を描画しまてす。

イメージ的には rを定数とし、y軸の直下や真上付近ならx軸を一つづつずらし、yの値を求めます。
x軸の右側付近とかならy軸をずらします。
数学風に書くと
y ** 2 = (x + n) ** 2 + r ** 2
みたいなイメージです。(n = 何個目のドットを描画しているか。**はPerlなどでべき乗)。
これの平方根を求めて端数を四捨五入してます。
この方法だと軸から遠ざかると塗りつぶすドットが飛び飛びになるので8回(もしくは4回)の描画が必要です。



問題はまず、
それが一般的な方法かどうかと言う点です。8回に分けるもんだから処理が汚いです。
さらになぜか、円も汚いです。
いや理由はだいたい分かるんですけど、ドット数に少数点とかないから四捨五入とかしてます。
すると、どうもちゃんとした円にならないです。そこドットの描画一個ズレてるだろコレ?
おまえそれ、明らかに1ドットずらしたほうが綺麗な円になるぞ?
みたいことが起きます。


もう一つ、他の疑問なんですがちょっと数学的な疑問で、
円を1/8づつ描画させるにはnのループ回数を
n = r / (1.4140 < m < 1.4146) くらいの値に設定しなきゃならないことが分かりました。
なぜ割り算させたのかはもう覚えてません。
一つづつ手作業で確かめて絞り込んだ数値なのですが、
どうもmが2の平方根に似ている気がします。
なぜこうなるんでしょう。たぶん数学的な何かだと思うのですが。

なお数学的な知識は全然ないです。学校では一日中寝るのが特技でした。
まさかこんな技術が必要になるとは…。

このQ&Aに関連する最新のQ&A

A 回答 (5件)

ミッチェナーの円の描画アルゴリズムというよく知られた手法があるので調べてみてください。

この回答への補足

ありがとうございました。
今まであった使ってた処理を整理して書き換えた所で納得がつきました。

選びにくいのですが最初の返信をベストアンサーの理由にしようと思います。

補足日時:2012/05/27 00:16
    • good
    • 0
この回答へのお礼

ありがとうございます。
本日の昼に検索した所、出てきました。

すごく平たく言うと、基本の式は今のままで構わないが、
普通は1ドットずつ全部計算しないと言うことなのでしょうか。
そうなのでしょうね。

お礼日時:2012/05/26 19:59

#1です。

おっしゃる通り、基本の式は同じです。

ミッチェナーのアルゴリズムの特徴の一つは、三角関数や絶対値の計算を使わず、整数の範囲で実現できるので比較的に高速です。

また、誤差がより小さい画素が選択されるので
> 1ドットずらしたほうが綺麗な円になるぞ?
ということが軽減されます。

ただし、やはり1/8円弧ずつ描くので、その点で処理が入り組んでしまうのは改善しませんけれども。
    • good
    • 0
この回答へのお礼

よく見てよく理解して作ろうと思います。
ただ最近のPCは恐ろしく高性能なので、ある程度は処理(記述)のスマートさが優先になるかもしれません。

ありがとうございました。

お礼日時:2012/05/27 00:20

訂正


色々へんだった。。。

y = r * sinθ

θを -π/2~π/2 まで、幾つかに(3*r程度)分割して、順に y を求めて、

その y をしたの式にあてはめて、x を求める。

x^2 + y^2 = r^2
    • good
    • 0
この回答へのお礼

ご丁寧訂正にありがとうございました。


-----------------------------------------------------

今気づいたのですが、私も書いた式が間違ってるのに今気づきました。
y ** 2 = (x + n) ** 2 + r ** 2
こんな式自分のプログラムのどこにも書いてなかったです。

お礼日時:2012/05/26 20:40

三角関数を使うといいよ!



x = r * sinθ

θを 0π~2π まで、幾つかに分割して、順に x を求めて、

その x をしたの式にあてはめて、y を求める。

x^2 + y^2 = r

θをなん分割するかだけど、

r * 6 ぐらいあれば十分。。。かな?

この回答への補足

回答頂いた皆様には申しわけないですが、
ベストアンサーをどうするかの判別が付かないので
自分が納得できる描画の処理を書けてから選びたいを所存でございます。

すみませんがお待ちください。

補足日時:2012/05/26 20:43
    • good
    • 0
この回答へのお礼

ありがとうございます。
遠い昔に三角関数と言う言葉を聞いたような聞かないような記憶があります。
この計算法だと1ドットずつ出すと言うことでしょうか。

最初画像を作るとなった時、画像のフォーマットを知ればそれでOKと思ったんですが、
よくよく考えると何か描画すると言うのは全部2次元平面の数式が必要なんですね。
ナメてました。

rからループ回数を出すと言うのは塗りつぶすドット数の数を出すと言う解釈でいいですよね。
二乗とかなるとイメージが掴みにくいのですが。

お礼日時:2012/05/26 20:12

ブレゼンハムのアルゴリズムという線分を描画するアルゴリズムがあります。


このアルゴリズムが成り立つ理屈を応用すると楕円を描画するアルゴリズムができます。
円は楕円の長辺と短辺を同じにしたものですから、この方法でも可能です。
    • good
    • 0
この回答へのお礼

ありがとうございます。
こちらも見つける事ができました。

もう少ししっかり考えた上で記述を書き直そうかと思います。

お礼日時:2012/05/26 20:04

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QC++にて、塗りつぶされた円を描くプログラムを描くにはどうすればいいで

C++にて、塗りつぶされた円を描くプログラムを描くにはどうすればいいでしょう?

Aベストアンサー

中心x0,y0、半径rの円を塗りつぶすには、いろいろと方法があると思います。
・ベタな方法
全座標(x,y)について、 (x-x0)*(x-x0)+(y-y0)*(y-y0) を計算、 <= r*r なら円の中なので色を付ける。
実際には、円に外接する正方形よりだけ確認すればよいで 、 x0-r<=x<=x0+r,y0-r<=y<=y0+rの範囲を調べる。

・y0-r<=y<=y0+r のyについて、dy = y - y0 とする。
dx=x-x0 とすると、r,dx,dyからなる直角三角形ができるので、値のわかっているr,dyからdxが求まる。
左右の座標がそれぞれ x0-dx,y と x0+dx,yになるので、その間に色を付ける。

・(塗りつぶさない)円をかくアルゴリズムをつかって、円を書く
→囲まれた領域を塗りつぶす方法(ペイントアルゴリズムと言われたりする)を使って、円の中を塗る。

Q円弧の描画方法

 円(または1/8円弧)の描画アルゴリズムとして
ミッチェナーの円のアルゴリズムが知られています。
しかし、任意の円弧(例えば、長方形に内接するような→MFCのライブラリに
あるような指定方法や3点を指定して円弧を描画する)を高速に描画する
方法はありますか?ミッチェナーの方法の変形(制限)でもいいので
教えてください。

Aベストアンサー

問題を2つに分けましょう。

(1) 3点から円弧を求める方法:

とりあえず約束として、

P0:円弧始点
P1:円弧通過点
P2:円弧終点
O:求める円弧中心
R:円弧半径

とします。時計回りか、反時計回りかはここでは問いません。
線分P0P1、線分P1P2 にOから下ろした垂線の足は線分の中点ですから、線分長を

L01 = dist(P0, P1)
L12 = dist(P1, P2)
として、

<P1-P0, O> - 0.5*L01^2 = 0
<P2-P1, O> - 0.5*L12^2 = 0

です。ここに、<, >は内積を表わします。

これは、Oの成分、Ox,Oyに関する連立一次方程式になり、

det = (P1x-P0x)*(P2y-P1y) – (P1y-P0y)*(P2x-P1x)

として、

Ox = 0.5* {L01^2*(P2y-P1y) – L12^2*(P1y-P0y)} / det
Oy = 0.5* {-L01^2*(P2x-P1x) + L12^2*(P1x-P0x)} / det……………Equ.1)

が求まります。但し、3点の内2点が重なるとか、3点が一直線上に並ぶ場合はdet=0になるので注意が必要です。「円弧は求まらない」としてエラー処理しなくてはなりません。
半径は以下のように容易に求まります。

R = |P0 - O|…………………………………………………………………Equ.2)

始終角を求めます。先ず、

θ0 = atan2(P0y-Oy, P0x-Ox)
θ1 = atan2(P1y-Oy, P1x-Ox)
θ2 = atan2(P2y-Oy, P2x-Ox)…………………………………………………….Equ.3)

として、円弧中心から見た3点の角度が-π~+πの範囲で求まります。
これを

θ0’ = 0
θ1’ =θ1 -θ0
θ2’ =θ2 -θ0………………………………………………………………………Equ.4)

と始点基準にします。

これを、0~2πの値に変換して、

・θ2’ < θ1’ なら反時計回り
・θ1’ < θ2’ なら時計回り

なのです。あと処理の都合のため、時計回り回りなら始終点を入れ替わると、必ず反時計回りになります。以下、「円弧」と言うと、

・中心(上のOですが、あなたのプログラムでは、「x0, y0」です)
・半径(上のRですが、あなたのプログラムでは「r」です)
・始角(反時計回り)
・終角(反時計回り)

で表現されるものとします。ただ、Equ.3)で分かるとおり、既に、コストのかかる逆三角関数を3回も用いています。

(2) 円弧ベクター->ラスター変換:

先ず、「ミッチェナーのアルゴリズム」ですが、ご存知のように、

1) 円を8分割し、対称性を利用して基本1パタン(東->北、0°~45°)を利用。
2) 基本パタンでは、y:必ず1Pixelずつ増加、x:0または1Pixel減少。
3) x: 0または-1を判断するのに、「真円からのx方向差分」を巧妙な計算で求める。
ただし、この差分は、x: 0または-1で異なる。

という非常に巧妙な仕掛けになっている訳です。三角関数は勿論、sqrt()も一度も使われていません。しかも近似計算ではなく、誤差は1/2Pixel以内というか、可能な限りでの正確な計算になっています。

本題に入ると、whileループに入る前に、(1)で求めた始終角から、

0) この1/8セルの処理区分, 開始y, 終了y
1) この1/8セルの処理区分, 開始y, 終了y 時計回り
2) この1/8セルの処理区分, 開始y, 終了y 時計回り
3) この1/8セルの処理区分, 開始y, 終了y
4) この1/8セルの処理区分, 開始x, 終了x 時計回り
5) この1/8セルの処理区分, 開始x, 終了x
6) この1/8セルの処理区分, 開始x, 終了x
7) この1/8セルの処理区分, 開始x, 終了x 時計回り

なる8つの行からなるテーブルを作ったら如何でしょうか?8個固定ですから配列でなくてもいいです。

・ 1/8セルの処理区分:
(1) 全域無効、描かない、
(2) 全域有効、描く。
(3) 一部有効、開始~終了範囲だけ描く。

・開始・終了y(x):必ず開始<終了になります。
0)~7)何れも、最大で=r、最小で=0.707*rです。ここは気を付けて下さい。

No.2で「forループにして、forループインデックスの範囲」てなこと言いましたが、whieループでいいと思います。「開始終了y(x)」を角度から求めるには始終点位置の角度に関しては残念ながら、三角関数を一度ずつ使わなくてはいけないでしょう。それ以外の場合は、r0.707*rです。三角関数計算は不要です。

問題を2つに分けましょう。

(1) 3点から円弧を求める方法:

とりあえず約束として、

P0:円弧始点
P1:円弧通過点
P2:円弧終点
O:求める円弧中心
R:円弧半径

とします。時計回りか、反時計回りかはここでは問いません。
線分P0P1、線分P1P2 にOから下ろした垂線の足は線分の中点ですから、線分長を

L01 = dist(P0, P1)
L12 = dist(P1, P2)
として、

<P1-P0, O> - 0.5*L01^2 = 0
<P2-P1, O> - 0.5*L12^2 = 0

です。ここに、<, >は内積を表わします。

これは、Oの成分、Ox,Oyに...続きを読む

Q円の描画の太さを指定できるCコード

円の描画を行うCコードを記述していますが
Michenerのアルゴリズムにより円を下記サイトを参考に記述できました。
しかし、アルゴリズムの理解不足の為、円の太さを指定できるようにしたいのですが、なかなか上手くいっておりません。
もし、円の太さを指定できるCコードなどありましたら教えていただけないでしょうか?

Aベストアンサー

座標を記録しなくても描画可能です。
単純ループではなく、以下のような2重ループ構成にします。
外: yを(R+0.5d)から0まで1づつ減らしていくループ
中: yがその座標に等しい間、xを増やしていくループ
これで、各y座標に対応するx座標が求まります。
あとは、
・yが(R+0.5d)~(R-0.5d)の間
→外周のMichenerについてだけ中ループを実施、求まったx座標から線を引く
・yが(R-0.5d)~0の間
 →外周・内周両方のMichenerについて中ループを実行、内周と外周両方のx座標を求めて、その間を結ぶ線分を描画
といった形にすればOK。

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。

Q読み込み中にアクセス違反が発生しました、と出ます。これを回避することは

読み込み中にアクセス違反が発生しました、と出ます。これを回避することはできますか?

C++でプログラミングをしています。
現在、ポインタ(p)に入れたアドレスをずらしていって、メモリの任意の位置の値を表示する、というプログラムを制作しています。

具体的に言うと、上記のプログラムではない別のプログラムを実行し、そのプログラムで宣言した変数のアドレスを表示させます。そしてそのプログラムが動いている最中に、上記のプログラムを起動させ、ポインタpにその変数のアドレスを代入して、表示させようとしました。

しかし、止まってしまいます。デバッグしてみると、「test.exe の 0x00024ad5 でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x0014fde8 を読み込み中にアクセス違反が発生しました」と出ます。

なんとかこれを回避して、ポインタに代入したアドレスを表示さすことはできませんか?
宜しくお願いします。

ちなみに、自分がかなり妙なことをしているのは自覚しているつもりです。

Aベストアンサー

#2です。
もう少し突っ込んで話をすると、今のOSはたいてい仮想アドレス方式で各プロセスのアドレス空間はそのプロセス内でのみ有効なものです。
つまり、プロセスAの0番地とプロセスBの0番地は値は同じでも全く別の箇所を指しているということです。

ですが、プログラムの中には複数プロセスで協調動作させる等の理由により質問のような他プロセスが扱うメモリの中身を参照・変更したい場合があります。
それを実現するのが共有メモリ等の機構である訳です。

Qブレゼンハムのアルゴリズムについて(プログラミング初心者です)

ブレゼンハムのアルゴリズムについて
C言語で直線描画のプログラミングをしています。
複数の点P_1=(x1,y1),P_2=(x2,y2).... を結ぶ直線を描画するにあたり、
ブレゼンハムのアルゴリズムを用いようと思ったのですが、
描きたい直線の傾きが負の場合、また、傾きが1をこえる場合にうまく描画できず困っております。
P1とP2の位置関係に関係なく線を引くにはどうすればよいでしょうか。

画像にソースコードの一部を添付します。
当方プログラミングに関しては初心者なので改善点などがあれば合わせて指摘していただけると嬉しいです。
配列vx[i]、vy[i]には線で結びたい点P_i(i=1,2,3...)点のx座標、y座標が格納されており、
座標に点を打つ関数は作成済みです。

よろしくお願いします。

Aベストアンサー

> 描きたい直線の傾きが負の場合、また、傾きが1をこえる場合にうまく描画できず困っております。

傾きが正の1未満になるように変換して計算すれば良いのでは?
具体的には
・傾きが1より大きいとき
 直線y=xに対象な点に変換、すなわちxとyの入れ替えをして計算し、描画時に戻す。
・傾きが1未満の負のとき
 直線x=0に対象な点に変換、すなわちy=-yの入れ替えをして計算し、描画時に戻す。
・傾きが1より大きい負のとき
 上記2つを組み合わせる。


人気Q&Aランキング

おすすめ情報