Конференция "Media" » Как нарисовать спираль Fermat с отрисовкой в писелах? [D7, WinXP]
 
  • Dr. Andrew (26.08.07 15:02) [0]
    Добрый день! Мастера подскажите как нарисовать спираль Fermat с отрисовкой в писелах? Что-то вроде этого:

    procedure CreateSpiral(FWidth, FHeight : Integer; Rotangle : Double);
         var X, Y, Ox, Oy : Integer;
             Theta, Dist : Double;
             clr : Byte;
             temp : Double;
    begin
     // here there will be a centre!
     Ox := MulDiv(FWidth,  1, 2);
     Oy := MulDiv(FHeight, 1, 2);
     Theta := 0;
     for Y := 0 to Pred(FHeight) do
     begin
       for X := 0 to Pred(FWidth) do
       begin
          Dist := Sqrt(Sqr(X - Ox) + Sqr(Y - Oy));
          Theta := -ArcTan2(y - Oy, x - Ox) + Rotangle;
          temp := Theta + Dist;
          clr  := 255*Sign(Sin(temp));
          if clr <> 255 then
            если не равно белому цвету, то заполнять пикселами переднего плана (например, clBlue);
       end;
     end;
    end;

  • MBo © (26.08.07 15:33) [1]

    procedure TForm1.Button2Click(Sender: TObject);
    var
     i, x, y, cx, cy, MaxR: Integer;
     a, theta, R: Double;
    begin
      a := 20;
      cx := 200;
      cy := 200;
      maxR := 200;
      i := 0;
      repeat
        theta := i * 0.2 /(2 * pi);
        R := a * Sqrt(theta);
        if R > MaxR then
          Break;
        x := cx + Round(R * Cos(theta));
        y := cy + Round(R * Sin(theta));
        Canvas.Pixels[x, y] := clBlack;
        Inc(i);
      until False;
    end;

  • Dr. Andrew (26.08.07 15:38) [2]
    Спасибо за пример, но а если все же ближе к моему коду как можно изменить код MBo?
  • Dr. Andrew (26.08.07 15:38) [3]
    с отрисовкой поп пикселам.
  • MBo © (26.08.07 16:03) [4]
    не вижу необходимости зарисовывать каждый пиксел картинки по отдельности.
  • Dr. Andrew (26.08.07 16:08) [5]
    Необходимость в том, чтобы ускорить отрисовку и сделать листья спирали (или лист спирали) с изменяемой шириной, а не просто линией или в виде отдельных пикселей. Все как можно приблизить код к моему примеру? Спасибо!
  • Dr. Andrew (26.08.07 16:13) [6]
    И еще - это R := a*Power(theta, 1/2) или R := a*Sqrt(theta) - спираль Ферма, R :=theta - это спираль Архимеда, а так R := a*1/Sqrt(theta) должна бы быть спираль Lituus, но выпадает постоянно ошибка неверного деления на ноль. Почему?
  • MBo © (26.08.07 16:19) [7]
    >*1/Sqrt(theta) должна бы быть спираль Lituus, но выпадает постоянно ошибка неверного деления на ноль. Почему?

    проверяй, что theta не равно нулю (лучше - не меньше некой константы, скажем, 10e-3
  • Dr. Andrew (26.08.07 17:09) [8]
    1) все же остается необходимость в том, чтобы ускорить отрисовку и сделать листья спирали (или лист спирали) с изменяемой шириной, а не просто линией или в виде отдельных пикселей. Все как можно приблизить код к моему примеру?
    2) проверка theta на 0 или  10e-3:
    if theta > 0 или 10e-3 then
     R := a * 1/Sqrt(theta)
    все равно вызывает прежнюю ошибку.

    Спасибо!
  • исследователь © (27.08.07 08:58) [9]
    пиши лог, можно будет посмотреть, где падает, или юзай средства Delphi
  • Dr. Andrew (27.08.07 09:52) [10]
    Доброе утро! Мастера, пожалуйста, подскажите, как все же соединить коды двух вариантов? Пример который привел Mbo работает отлично, но мне нужна конструкция именно такая (пока она строит спираль Архимеда, а мне нужны другие типы, например Ферат или Галилея):

    procedure CreateSpiral(FWidth, FHeight : Integer; Rotangle : Double);
        var X, Y, Ox, Oy : Integer;
            Theta, Dist : Double;
            clr : Byte;
            temp : Double;
    begin
    // here there will be a centre!
    Ox := MulDiv(FWidth,  1, 2);
    Oy := MulDiv(FHeight, 1, 2);
    Theta := 0;
    for Y := 0 to Pred(FHeight) do
    begin
      for X := 0 to Pred(FWidth) do
      begin
         Dist := Sqrt(Sqr(X - Ox) + Sqr(Y - Oy));
         Theta := -ArcTan2(y - Oy, x - Ox) + Rotangle;
         temp := Theta + Dist;
         clr  := 255*Sign(Sin(temp));
         if clr <> 255 then
           если не равно белому цвету, то заполнять пикселами переднего плана (например, clBlue);
      end;
    end;
    end;



    Как можно код ниже модернизировать под мою конструкцию?

    procedure TForm1.Button2Click(Sender: TObject);
    var
    i, x, y, cx, cy, MaxR: Integer;
    a, theta, R: Double;
    begin
     a := 20;
     cx := 200;
     cy := 200;
     maxR := 200;
     i := 0;
     repeat
       theta := i * 0.2 /(2 * pi);
       R := a * Sqrt(theta);
       if R > MaxR then
         Break;
       x := cx + Round(R * Cos(theta));
       y := cy + Round(R * Sin(theta));
       Canvas.Pixels[x, y] := clBlack;
       Inc(i);
     until False;
    end;



    Все спасибо за практический совет!
  • Dr. Andrew (28.08.07 12:44) [11]
    Уважаемые мастера, может все же кто-то может подсказать хоть какую строчку надо менять в коде Вариант №1, чтобы он строил спираль Фермат? Всем спасибо!
  • Jeer © (28.08.07 14:00) [12]

    > Dr. Andrew   (28.08.07 12:44) [11]



    > хоть какую строчку надо менять


    17-ю извилину.

    Начни думать, разбираться и все получится.
  • Dr. Andrew (28.08.07 15:18) [13]
    Спасибо за совет, если бы не думал и не разбирался, то не написал бы первый код. Но, в в том-то и дело что столкнулся с проблемой и поэтому обратился к более знающим Мастерам.
  • sdubaruhnul (29.08.07 22:08) [14]

    procedure TformMain.btnDrawClick(Sender: TObject);
    var
     Centre: TPoint;
     SpiralWidth, SpiralHeight: Integer;
    var
     x, y: Integer;
     angle: Extended;
     a2, 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);

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

     // r^2 = ± a^2 * angle

     a2 := sqr(40); // уже в квадрате
     delta := 0.03;

     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 ((x = Centre.X+50) and (y = Centre.Y-50)) then
             Caption := FloatToStr(angle); }

           if (angle < 0) then angle := angle + 2 * pi;

           // Полученный угол обобщается до angle + (2 * pi * n),
           // где n должно быть натуральным, что мы и проверяем.

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

           delta := sqrt(r2) * 0.0002 + 0.01;

           // Чем ближе к центру, тем большая точная нужна.
           // Поэтому delta пропорциональная расстоянию sqrt(r2).

           if (frac(abs((r2 / a2 - angle) / (2 * pi))) < delta) then
             begin
               // Положительная часть
               Canvas.Pixels[x,y] := clBlack;
               // Отрицательная часть
               Canvas.Pixels[Centre.x - (x - Centre.x),
                 Centre.y - (y - Centre.y)] := clBlack;
             end;
         end;
    end;

    procedure TformMain.btnDraw2Click(Sender: TObject);
    var
    i, x, y, cx, cy, MaxR: Integer;
    a, theta, R: Double;
    begin
     a := 40;
     cx := ClientWidth div 2;
     cy := ClientHeight div 2;
     maxR := 200;
     i := 0;
     repeat
       theta := i * 0.1 /(2 * pi);
       R := a * Sqrt(theta);
       if R > MaxR then Break;
       // Положительная часть
       x := cx - Round(R * Cos(theta));
       y := cy + Round(R * Sin(theta));
       Canvas.Pixels[x, y] := clSilver;
       // Отрицательная часть
       x := cx + Round(R * Cos(theta));
       y := cy - Round(R * Sin(theta));
       Canvas.Pixels[x, y] := clSilver;
       Inc(i);
     until False;
    end;



    2 способа, можешь сравнить.
  • Dr. Andrew (30.08.07 07:07) [15]
    Спасибо, sdubaruhnul! Первая процедура очень близка к моему первому варианту. Вы настоящий Мастер! Спасибо! Можно ли продолжить дискуссию с Вами? Меня интресует вот еще какие вопросы:

    1) как сделать прорисовку с использованием ScanLine и сделать ширину витков регулируемой? Чтобы строилась спираль не сплошной линией в 1 пиксель, а в виде ленты, закручивающейся по спирали Фермат.
    2) И еще, это (первый вариант) можно считать базовым? Как можно, например, из него нарисовать прочие спирали - например спираль Галилея?

    Спасибо и жду продолжения дискуссии.
  • Dr. Andrew (30.08.07 08:14) [16]
    Спасибо, sdubaruhnul! Спасибо протестировал - первый вариант (btnDrawClick) то, что нужно, только почему-то первый виток рисуется утолщенным в два раза. Как это исправить? Где погрешность или ошибка в коде?
  • sdubaruhnul (30.08.07 15:40) [17]
    >Dr. Andrew   (30.08.07 07:07) [15], [16]

    Этот вариант сложно считать базовым, потому что естественная форма задания спиралей - в полярных координатах. В этих координатах спираль задаётся функцией - каждому углу Theta ставится в соответствие одно значение радиуса r (в случае ± разбиваем на две функции). И когда я говорю функция, подразумеваю явную функцию, заданную формулой, которую только и надо, что вычислить.

    В декартовой системе координат спираль задаётся уравнением, которое редко когда имеет приличную форму и которое надо заранее расчитать. Попробуй, например, вывести уравнение той же спирали Ферма.

    В моём варианте кода приходится переводить из декартовой в полярную, а однозначно перевести угол невозможно, только в общей форме (логично, что 0° это и 360° и 720° и т.д). Отсюда дополнительные сложности.

    В общем, если считать базовым такой вариант, в котором ты изменяешь всего одну строчку - формулу спирали, то нет, это не базовый вариант.

    Утолщённую линию не знаю точно как поправить, нужно варьировать коэффициенты в формуле: delta := sqrt(r2) * 0.0002 + 0.01;

    Другой способ - поменять систему проверки, а именно проверять в цикле все возможные углы:


    procedure TformMain.btnDraw3Click(Sender: TObject);
    var
     Centre: TPoint;
     SpiralWidth, SpiralHeight: Integer;
    var
     x, y: Integer;
     angle, max_angle: Extended;
     a2, 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);

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

     // r^2 = ± a^2 * angle

     a2 := sqr(40); // уже в квадрате
     delta := 0.7;

     max_angle := (sqr(SpiralWidth div 2) + sqr(SpiralWidth div 2)) / a2;

     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(sqrt(a2 * angle) - sqrt(r2)) < delta) then
                 begin
                   // Положительная часть
                   Canvas.Pixels[x,y] := clBlack;
                   // Отрицательная часть
                   Canvas.Pixels[Centre.x - (x - Centre.x),
                     Centre.y - (y - Centre.y)] := clBlack;
                 end;
               angle := angle + 2 * pi;
             end;
         end;
    end;



    Теперь delta - это погрешность расстояния пиксела до идеальной линии спирали. Варьируя delta можно изменять толщину линии.
  • Dr. Andrew (30.08.07 16:11) [18]
    Спасибо обязательно протестирую. Пожалуйста, останьтесь в диалоге со-мной, хотя бы по емайл (мой teachsoft@kharkov.ukrtel.net). Вы единственный кто так отлично разбирается в этих спиралях здесь. Спасибо!
  • Dr. Andrew (31.08.07 08:21) [19]
    Доброе утро, sdubaruhnul! Можете еще помочь со спиралью Галилея (r = a*(1 - m*θ^2)) и  Poinsot (r=sech(θ/3))? Спасибо.
 
Конференция "Media" » Как нарисовать спираль Fermat с отрисовкой в писелах? [D7, WinXP]
Есть новые Нет новых   [134431   +10][b:0][p:0.006]