1 (изменено: Freeman, 05.12.2020 в 17:38)

Тема: Fractal Tree

Этот пример рисует фрактальное дерево в окно.

misc.php?action=pun_attachment&item=62&download=0

Нажимайте клавишу ENTER, чтобы сгенерировать новое дерево.

Исходный код:

program FractalTree;

uses
  KolibriOS;

const
  WINDOW_BORDER_SIZE = 5;
  KEY_CODE_ENTER = #13;

var
  WndLeft, WndTop: LongInt;
  WndHeight, WndWidth: LongWord;

function Sin(Value: Extended): Extended;
asm
  FLD Value
  FSIN
  FWAIT
end;

function Cos(Value: Extended): Extended;
asm
  FLD Value
  FCOS
  FWAIT
end;

function Round(Value: Extended): Int64;
asm
  FLD Value
  SUB ESP, 8
  FISTP qword ptr [ESP]
  FWAIT
  POP EAX
  POP EDX
end;

procedure BoldLine(X1, Y1, X2, Y2: LongInt; Color: LongWord);
begin
  DrawLine(X1, Y1, X2, Y2, Color);
  DrawLine(X1 + 1, Y1, X2 + 1, Y2, Color);
  DrawLine(X1 - 1, Y1, X2 - 1, Y2, Color);
  DrawLine(X1, Y1 + 1, X2, Y2 + 1, Color);
  DrawLine(X1, Y1 - 1, X2, Y2 - 1, Color);
end;

procedure Tree(X, Y, Size: LongInt; Angle: Extended);
Var
  X2, Y2: LongInt;
  Color: LongWord;
begin
    case Size of
      0..2:   Color := $FFFF00;
      3..4:   Color := $00FF00;
      5..8:   Color := $808000;
      9..16:  Color := $008000;
      17..32: Color := $2c5400;
    else
              Color := $562a00;
    end;

  X2 := Round(X + Size * Sin(Angle));
  Y2 := Round(Y + Size * Cos(Angle));

  if Size < 8 then
    DrawLine(X, Y, X2, Y2, Color)
  else
    BoldLine(X, Y, X2, Y2, Color);

  if Size > 0 then
  begin
    Tree(X2, Y2, Size * 3 div 4, Angle + Pi / 5 / (Random + 1));
    Tree(X2, Y2, Size * 3 div 4, Angle - Pi / 5 / (Random + 1));
  end
end;

procedure On_Redraw;
begin
  BeginDraw;
  DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'FractalTree; Press key ENTER to generate new Tree', $00A0A0A0,
    WS_SKINNED_FIXED + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
  Randomize;
  Tree(WndWidth div 2, WndHeight - WINDOW_BORDER_SIZE - GetSkinHeight, WndHeight div 4, Pi);
  EndDraw;
end;

begin
  with GetScreenSize do
  begin
    WndHeight := Height div 2;
    WndWidth := Width div 2;
    WndLeft := (Width - WndWidth) div 2;
    WndTop := (Height - WndHeight) div 2;
  end;
  while True do
    case WaitEvent of
      REDRAW_EVENT:
        On_Redraw;
      KEY_EVENT:
        if GetKey.Code = KEY_CODE_ENTER then
          On_Redraw;
      BUTTON_EVENT:
        Break;
    end;
end.
Здесь используются функции Sin, Cos, Round

Они должны быть реализованы в модуле System.
На данный момент этих функций в System нет, поэтому они находятся в самом проекте.

Раньше я уже делал рисование других фрактальных деревьев вот в этой теме, но там на ассемблере.
Также прикладываю уже готовое приложение FractalTree.kex

Post's attachments

Иконка вложений FractalTree.kex 1.07 Кб, 108 скачиваний с 2020-10-24 

FractalTree.PNG, 27.23 Кб, 513 x 385
FractalTree.PNG 27.23 Кб, 82 скачиваний с 2020-10-24 

2

Re: Fractal Tree

Добавил на GitHub, но после переноса системных функций в System программа не работает. Если оставить в самой программе, норм.

3 (изменено: 0CodErr, 08.01.2021 в 05:10)

Re: Fractal Tree

Freeman пишет:

после переноса системных функций в System программа не работает. Если оставить в самой программе, норм.

Да, всё так и есть.
Вот из исходника System для Delphi 7

{ Procedures and functions that need compiler magic }

procedure _COS;
procedure _EXP;
procedure _INT;
procedure _SIN;
procedure _FRAC;
procedure _ROUND;
procedure _TRUNC;

Ключевое слово здесь — compiler magic
Эти функции, если они находятся в модуле System, то ожидают аргумент на стеке FPU в ST(0)
А в нашем случае аргумент передаётся на стеке CPU и загружается в FPU с помощью "FLD Value".
Кроме того для Sin и Cos добавлено это

@@outOfRange:
        FSTP    st(0)   { for now, return 0. result would }
        FLDZ            { have little significance anyway }

похоже, что не просто так, иначе бы компилятор просто вставлял бы инструкцию FSIN или FCOS(как он делает, например, для Abs или Sqrt) вместо вызова функции(а это ведь ещё и медленнее).
Самое интересное, что в модуле Math имеются функции, обратные к Sin и Cos

function Secant(const X: Extended): Extended;
{ Secant := 1 / Cos(X) }
asm
        FLD   X
        FCOS
        FLD1
        FDIVRP
        FWAIT
end;

function Cosecant(const X: Extended): Extended;
{ Cosecant := 1 / Sin(X) }
asm
        FLD   X
        FSIN
        FLD1
        FDIVRP
        FWAIT
end;

И заметное отличие в том, что тут нет обработки случая

@@outOfRange

но при этом в Sin и Cos из System не используется инструкция

FWAIT

зато она используется в остальных функциях из System — Exp, Int, Frac, Round и Trunc
Понимайте это как хотите smilecompiler magic же
Тут только разработчики компилятора могут сказать, почему сделано именно так, а не по-другому.
Поэтому предлагаю такие функции копировать просто из System "как есть".

4

Re: Fractal Tree

Внезапно оказалось, что программа не компилируется Delphi 2007. Ругается на отсутствие Sin, Cos и Random. В родном модуле Delphi (если он родной), объявления Sin и Cos не отличаются от наших, а для Random есть дополнительная новая функция. Объявил ее, не помогло. Походу придется полноценную версию ставить и смотреть, что и как.

5

Re: Fractal Tree

Freeman пишет:

...не компилируется Delphi 2007. Ругается на отсутствие Sin, Cos и Random. В родном модуле Delphi...

Возможно, как-то связано со способом компиляции системных модулей: System и SysInit.
Вот в этом файле указан только модуль System

dcc32 -m -y -z "%~dp0..\Lib\System" -n"%DCU%"

Можно указать ещё и SysInit

dcc32 -m -y -z "%~dp0..\Lib\System" "%~dp0..\Lib\SysInit" -n"%DCU%"

Но дело в том, что при некоторых условиях либо первый способ не работает(пишет "Fatal error"), либо, наоборот, второй("Unit SysInit was compiled with a different version of...").
Возможно, что просто модуль не пересобрался и поэтому ругается на отсутствие Sin, Cos и Random — их же в предыдущей версии и не было.
Но остаётся вопрос, какая же тогда команда компиляции System правильная, с указанием SysInit или же без?

Сама по себе пересборка в Delphi 2007 должна работать, к примеру, вот тут пишут, что работает("Hell, 4KB in Delphi 2007 .. thats nice!")
А вот тут команда сборки немного не такая, как у нас

dcc32.exe -Q system.pas sysinit.pas -M -Y -Z -$D- -0