円グラフの書き方

解決


でるでる  2010-01-04 13:54:00  No: 36709

時計の12時開始で時計回りに円グラフを書くプログラムを作成しています。
一応希望通りの動きにはなったのですが理解していない箇所があります。

時計の12時開始なので、開始角度を90度(π/2) ※① としましたが、
時計の6時開始になってしまいます。

開始角度を-90度?(-π/2) ※②とするときちんと12時開始になるのですが
-を付加する意味が理解できていません。

どなたか宜しくお願いします。

procedure TForm1.Button1Click(Sender: TObject);
const
  PAI: Double = 3.1416;
  DataAry: array[0..5] of Integer = (10,20,30,40,50,60);
var
  i, Total, x1, y1: Integer;
  Theta, Delta: Double;
begin
  //合計を計算する
  Total := 0;
  for i:=0 to High(DataAry) do
    Total := Total + DataAry[i];

//Theta :=  PAI / 2.0; //※①90度(π/2)
  Theta := -PAI / 2.0; //※②-90度(-π/2)

  //円を描く
  Image.Canvas.Ellipse(0, 0, 160 ,160);

  //円グラフを描く
  for i:=0 to High(DataAry) do begin
    Delta := 2.0 * PAI * DataAry[i] / Total;
    x1 := Round(80.0 + 80.0 * Cos(Theta));
    y1 := Round(80.0 + 80.0 * Sin(Theta));
    Image.Canvas.MoveTo(80, 80);
    Image.Canvas.LineTo(x1, y1);
    Theta := Theta + Delta;
  end;
end;


tor  2010-01-04 17:50:48  No: 36710

数学で使う座標系は上がプラス、下がマイナスですが
コンピュータの座標系は一般的に左上が(0,0)で下にいくほど増えるので、Y軸がひっくり返ります。
辻褄を合わせるためsinを-sinにしてもいいですが、角度をマイナスにしても同じ結果になりますね。

ちなみに、πはわざわざ自分で定義しなくても、SystemユニットにPiが用意されていますよ。


でるでる  2010-01-04 20:12:36  No: 36711

なるほど、コンピュータ座標系で上の方向にY軸を増加させるなら-Yにすればいいってことですね。
torさん、とても分かり易い説明ありがとうございました。

それから本質問と外れて申し訳ありませんが、もう一つ教えてください。
π はsystem.pasに定義されていると教えて頂いたので
試しに、自分で定義した PAI を削除して PI にしても確かにコンパイルは通るのですが、
system.pas を検索してみても、定義らしき箇所が見当たりませんでした。
てっきり、const PI = 3.141592 のような定義があると思っていたのですが。。
どこに定義されているのでしょうか?


tor  2010-01-04 21:43:28  No: 36712

> system.pas を検索してみても、定義らしき箇所が見当たりませんでした。
> てっきり、const PI = 3.141592 のような定義があると思っていたのですが。。
> どこに定義されているのでしょうか?

まず、ヘルプを見ると分かりますが、Piは関数です。
(Pascalでは引数のない関数や手続きは括弧なしで呼び出せます。
一見定数やプロパティに見えるけれど実体は関数、というのはよく使われる手法です。
Piが定数ではなく関数で実装されている理由まではわかりませんが……)

で、System.pasの冒頭には「一部の定数・型・関数はコンパイラに組み込まれていて
実際の定義はないけど、あたかもSystemで定義されているかのように扱われるよ」
という旨の注意書きがあるので、Piもその一種なんじゃないでしょうか。


Mr.XRAY  2010-01-04 23:47:37  No: 36713

Mr.XRAYです.横から失礼します.

>実際の定義はないけど、あたかもSystemで定義されているかのように扱われるよ」

Piの値は実行時に(正確にはコンパイル時)にインライン展開されます.
その他にも,このような動作をする関数類があります.

Piのインライン展開を,Delphiが実際にはどのようにするのかは不明ですが,
以下のPiValueのような関数で(作成して)FPUから直接値を取得することもできます.
(FPU  CPUの浮動小数点演算ユニット)

//  円周率をFPU内蔵関数(命令)で取得する関数

function PiValue: Double;
asm
  FLDPI;
end;

//  関数PiValueのテスト

procedure TForm1.Button1Click(Sender: TObject);
var
  X : Double;
  S : String;
begin
  X := PiValue;
  S := FormatFloat('####.000000000000000', X);

  ShowMessage(S);
end;

Buttonクリック内にアセンブラコードを書くのであれば,
おそらくDelphiではこのように展開しているのでは.. (未確認)

procedure TForm1.Button2Click(Sender: TObject);
var
  X : Double;
  S : String;
begin
  // 関数Piのインライン展開か?
  asm
    FLDPI;
    fstp X;
  end;
  S := FormatFloat('####.000000000000000', X);

  ShowMessage(S);
end;


でるでる  2010-01-05 10:23:10  No: 36714

torさん、Mr.XRAYさん、回答ありがとうございました。
本質問とは関係なかったのに、とても親切に教えて頂いて感謝しております。

PIは定数ではなく、関数で、しかもコンパイル時に組み込まれるのですね。
また、PI関数のようなものが他にもある。
とても勉強になりました。
でもインラインアセンブリはよく分かりません^^;

一応これで解決とさせて頂きます。
有難うございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加