Конференция "Media" » Как нарисовать спираль Fermat с отрисовкой в писелах? [D7, WinXP]
 
  • sdubaruhnul (03.09.07 22:58) [20]

    uses
     Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     Dialogs, StdCtrls, Math;

    type
     TDrawType = (dtMaxRadius, dtMaxAngle);

    type
     TformMain = class(TForm)
       btnDraw1: TButton;
       btnDraw2: TButton;
       rbArchimedes: TRadioButton;
       rbFermat: TRadioButton;
       rbGalilei: TRadioButton;
       rbPoinsot: TRadioButton;
       editCoeff: TEdit;
       procedure btnDraw1Click(Sender: TObject);
       procedure btnDraw2Click(Sender: TObject);
       procedure rbArchimedesClick(Sender: TObject);
       procedure rbFermatClick(Sender: TObject);
       procedure rbGalileiClick(Sender: TObject);
       procedure rbPoinsotClick(Sender: TObject);
       procedure FormCreate(Sender: TObject);
     private
       HowToDraw: TDrawType; // for plotting by angle only  
     end;

    function RoArchimedes(A, Theta: Extended): Extended;
    function RoFermat(A, Theta: Extended): Extended;
    function RoGalilei(A, Theta: Extended): Extended;
    function RoPoinsot(A, Theta: Extended): Extended;

    var
     formMain: TformMain;
     Ro: function (A, Theta: Extended): Extended;

    implementation

    {$R *.dfm}

    function RoArchimedes(A, Theta: Extended): Extended;
    begin
     Result := A * Theta;
    end;

    function RoFermat(A, Theta: Extended): Extended;
    begin
     Result := A * Power(Theta, 1/2);
    end;

    function RoGalilei(A, Theta: Extended): Extended;
    begin
     Result := A * (1 - sqr(Theta));
    end;

    function RoPoinsot(A, Theta: Extended): Extended;
    begin
     Result := A * SecH(Theta/3);
    end;

    procedure TformMain.btnDraw1Click(Sender: TObject);
    var
    i, x, y, cx, cy, MaxR, MaxT: Integer;
    a, theta, R: Double;
    begin
     Canvas.Brush.Color := clBtnFace;
     Canvas.FillRect(ClientRect);

     try
       StringReplace(editCoeff.Text, '.', DecimalSeparator, [rfReplaceAll]);
       StringReplace(editCoeff.Text, ',', DecimalSeparator, [rfReplaceAll]);
       a := StrToFloat(editCoeff.Text);
     except
       on E: EConvertError do
         begin
           MessageDlg('Cannot convert coefficient A to number!',
             mtError, [mbOK], 0);
           Exit;
         end;
     end;

     cx := ClientWidth div 2;
     cy := ClientHeight div 2;
     MaxR := 200;
     MaxT := 1000;
     i := 0;
     repeat
       theta := i * 0.001 * (2 * pi);
       R := Ro(a, theta);

       case HowToDraw of
         dtMaxRadius: if abs(R) > MaxR then Break;
         dtMaxAngle:  if abs(Theta) > MaxT then Break;
       end;

       x := cx - Round(R * Cos(theta));
       y := cy + Round(R * Sin(theta));
       Canvas.Pixels[x,y] := clRed;
       Inc(i);
     until False;
    end;

    procedure TformMain.btnDraw2Click(Sender: TObject);
    var
     Centre: TPoint;
     SpiralWidth, SpiralHeight: Integer;
    var
     x, y: Integer;
     angle, max_angle: Extended;
     a, r2, delta: Extended;
     ARect: TRect;
    begin
     Centre := Point(ClientWidth div 2, ClientHeight div 2);
     SpiralWidth := 400;  SpiralHeight := 300;

     ARect := Rect(Centre.x - SpiralWidth div 2, Centre.y - SpiralHeight div 2,
         Centre.x + SpiralWidth div 2, Centre.y + SpiralHeight div 2);

     Canvas.DrawFocusRect(ARect);

     Canvas.Brush.Color := clBtnFace;
     Canvas.FillRect(ClientRect);

     try
       StringReplace(editCoeff.Text, '.', DecimalSeparator, [rfReplaceAll]);
       StringReplace(editCoeff.Text, ',', DecimalSeparator, [rfReplaceAll]);
       a := StrToFloat(editCoeff.Text);
     except
       on E: EConvertError do
         begin
           MessageDlg('Cannot convert coefficient A to number!',
             mtError, [mbOK], 0);
           Exit;
         end;
     end;

     // Угол считается против часовой стрелки (глядя на экран).
     // Координатная ось x совпадает с углом 0°.

     delta := 0.7; // Также задаёт толщину

     // Максимальный возможный угол в данном прямоугольнике нужно
     // расчитывать отдельно для каждой спирали, поэтому для упрощения
     // здесь берётся максимальный угол для Fermat's spiral как самый
     // большой при равном A и возрастающем радиусе.
     max_angle := (sqr(SpiralWidth div 2) + sqr(SpiralWidth div 2)) / 100;

     for y := ARect.Top to ARect.Bottom do
       for x := ARect.Left to ARect.Right do
         begin
           angle := ArcTan2(Centre.y - y, x - Centre.x);
           if (angle < 0) then angle := angle + 2 * pi;

           r2 := sqr(x - Centre.x) + sqr(Centre.y - y);

           while (angle <= max_angle) do
             begin
             if (abs(abs(Ro(a, angle)) - sqrt(r2)) < delta) then
                 Canvas.Pixels[x,y] := clBlack;
               angle := angle + 2 * pi;
             end;
         end;
    end;

    procedure TformMain.rbArchimedesClick(Sender: TObject);
    begin
     Ro := RoArchimedes;
     editCoeff.Text := '
    15';
     HowToDraw := dtMaxRadius;
    end;

    procedure TformMain.rbFermatClick(Sender: TObject);
    begin
     Ro := RoFermat;
     editCoeff.Text := '
    35';
     HowToDraw := dtMaxRadius;
    end;

    procedure TformMain.rbGalileiClick(Sender: TObject);
    begin
     Ro := RoGalilei;
     editCoeff.Text := '
    1';
     HowToDraw := dtMaxRadius;
    end;

    procedure TformMain.rbPoinsotClick(Sender: TObject);
    begin
     Ro := RoPoinsot;
     editCoeff.Text := '
    200';
     HowToDraw := dtMaxAngle;  
    end;

    procedure TformMain.FormCreate(Sender: TObject);
    begin
     rbArchimedesClick(Self);
    end;



    В центре плохо прорисовывается спираль - это ущербность перевода в полярные координаты. Чем больше вожусь с этим, тем лучше мне кажется верный и простой способ MBo.

    Всё, дальше сам.
  • Dr. Andrew (04.09.07 00:08) [21]
    Спасибо!
 
Конференция "Media" » Как нарисовать спираль Fermat с отрисовкой в писелах? [D7, WinXP]
Есть новые Нет новых   [134431   +10][b:0][p:0.003]