Showing a Range of Pickover's Biomorphs: Auto Multi Colouring lazarus / 39-Graphics ---------------------------------------------------------------------- Boleeman · 2024-09-26 11:32 Miguel Diaz Kusztrich at https://www.codeproject.com/Articles/1217543/The-astounding-Pickovers- biomorphs created an article about The astounding Pickover's biomorphs and supplied some CSharp code. Now Seeing the Correct Biomorph Curves thanks to Hero Member Tetrastes Go down to later replies for working versions of Random Biomorphs. ---------------------------------------------------------------------- Zvoni · 2024-09-26 12:10 Is there a specific reason you use CAdd/CSub/CMul instead of the "standard" operators? ---------------------------------------------------------------------- Boleeman · 2024-09-26 12:21 Zvoni, I was getting an error saying that + and - were not overloaded, so I took a punt and used CAdd/CSub/CMul. ---------------------------------------------------------------------- Zvoni · 2024-09-26 12:41 Quote from: Boleeman on September 26, 2024, 02:21:02 pmZvoni, I was getting an error saying that + and - were not overloaded, so I took a punt and used CAdd/CSub/CMul. This is not making any sense. Looking at the source of ucomplex, the standard operators (+-*/ etc.) are definitely defined there. Precedence of units in Uses? later declared unit beats earlier declared unit? ---------------------------------------------------------------------- tetrastes · 2024-09-26 13:15 Quote from: Zvoni on September 26, 2024, 02:41:36 pmQuote from: Boleeman on September 26, 2024, 02:21:02 pmZvoni, I was getting an error saying that + and - were not overloaded, so I took a punt and used CAdd/CSub/CMul. This is not making any sense. Looking at the source of ucomplex, the standard operators (+-*/ etc.) are definitely defined there. They are defined in ucomplex from lmath package, but in ucomplex from dmath there are only functions. ---------------------------------------------------------------------- Zvoni · 2024-09-26 13:23 Quote from: tetrastes on September 26, 2024, 03:15:53 pmThey are defined in ucomplex from lmath package, but in ucomplex from dmath there are only functions. *facepalm* ---------------------------------------------------------------------- TRon · 2024-09-26 13:39 Quote from: tetrastes on September 26, 2024, 03:15:53 pmThey are defined in ucomplex from lmath package, but in ucomplex from dmath there are only functions. uhm...  I assume those are not the same as rtl-extra unit ucomplex ? ---------------------------------------------------------------------- Zvoni · 2024-09-26 13:46 Quote from: TRon on September 26, 2024, 03:39:44 pmQuote from: tetrastes on September 26, 2024, 03:15:53 pmThey are defined in ucomplex from lmath package, but in ucomplex from dmath there are only functions. uhm...  I assume those are not the same as rtl-extra unit ucomplex ? "Praise the Lord"...... ---------------------------------------------------------------------- tetrastes · 2024-09-26 14:34 Quote from: TRon on September 26, 2024, 03:39:44 pmQuote from: tetrastes on September 26, 2024, 03:15:53 pmThey are defined in ucomplex from lmath package, but in ucomplex from dmath there are only functions. uhm...  I assume those are not the same as rtl-extra unit ucomplex ? uhm...  The third... I compared them out of curiosity. The one from lmath is hybrid of two others. ---------------------------------------------------------------------- TRon · 2024-09-26 14:45 @tetrastes Great !, Not that it was already a complex thing-a-ma-bob with those 2 units, now we have 3  Thank you for having looked into that and having shared your findings. fwiw: as far as I can remember the rtl-extra ucomplex had some complex functions missing (not anything that couldn't be solved using the other available functions though). ---------------------------------------------------------------------- Boleeman · 2024-09-26 15:00 I tried another version of function TForm1.Fz(z, c: Complex; f: Integer): Complex;    but it still gives incorrect curve shapes (Even though I used this in my previous working Biomorphs program that gave the correct curve:     3: // z^3 + c     begin       z.X := t.X * t.X * t.X - 3 * t.X * t.Y * t.Y + c.X;       z.Y := 3 * t.X * t.X * t.Y - t.Y * t.Y * t.Y + c.Y;     end;  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  function TForm1.Fz(z, c: Complex; f: Integer): Complex;var  t: Complex;begin  t := z;  case f of    0: // CSin(z) + z^2 + c    begin      z := CSin(z);      z.X := z.X + t.X * t.X - t.Y * t.Y + c.X;      z.Y := z.Y + 2 * t.X * t.Y + c.Y;    end;     1: // z^2 + z^6 + c    begin    z.X := t.X * t.X - t.Y * t.Y + Power(t.X, 6) - Power(t.Y, 6) + c.X;      z.Y := 2 * t.X * t.Y + 6 * t.X * t.X * t.X * t.X * t.X * t.Y + c.Y;    end;     2: // z^2 + z^5 + c    begin      z.X := t.X * t.X - t.Y * t.Y + Power(t.X, 5) - Power(t.Y, 5) + c.X;      z.Y := 2 * t.X * t.Y + 5 * t.X * t.X * t.X * t.X * t.Y + c.Y;    end;     3: // z^3 + c    begin      z.X := t.X * t.X * t.X - 3 * t.X * t.Y * t.Y + c.X;    z.Y := 3 * t.X * t.X * t.Y - t.Y * t.Y * t.Y + c.Y;    end;     4: // CSin(z) + z^5 + c    begin      z := CSin(z);      z.X := z.X + Power(t.X, 5) - Power(t.Y, 5) + c.X;      z.Y := z.Y + 5 * t.X * t.X * t.X * t.X * t.Y + c.Y;    end;     5: // CSin(z) - CCos(z) + z^2 + c  begin      z := CSin(z);      z.X := z.X - CCos(t).X + t.X * t.X - t.Y * t.Y + c.X;      z.Y := z.Y - CCos(t).Y + 2 * t.X * t.Y + c.Y;  end;     6: // z^3 - CCos(z) + z^2 + c    begin      z.X := t.X * t.X * t.X - 3 * t.X * t.Y * t.Y - CCos(t).X + c.X;      z.Y := 3 * t.X * t.X * t.Y - t.Y * t.Y * t.Y - CCos(t).Y + c.Y;    end;     7: // z^6 - z^4 + CSinh(z) + c    begin      z := CSinh(z);      z.X := z.X + Power(t.X, 6) - Power(t.Y, 6) - Power(t.X, 4) + Power(t.Y, 4) + c.X;    z.Y := z.Y + 6 * t.X * t.X * t.X * t.X * t.X * t.Y - 4 * t.X * t.X * t.X * t.Y + c.Y;    end;     8: // z^10 - CSinh(z) + c    begin    z := CSinh(z);      z.X := z.X + Power(t.X, 10) - Power(t.Y, 10) + c.X;      z.Y := z.Y + 10 * t.X * t.X * t.X * t.X * t.X * t.X * t.X * t.X * t.Y + c.Y;    end;   else // Default: CCos(z) + z^4 + c    begin      z := CCos(z);      z.X := z.X + Power(t.X, 4) - Power(t.Y, 4) + c.X;      z.Y := z.Y + 4 * t.X * t.X * t.X * t.Y + c.Y;    end;  end;  Result := z;end;    procedure TForm1.DrawBiomorph(c: complex; f: Integer);var  p, q, k, index: Integer;  z: complex;  xmin, xmax, ymin, ymax, px, py: Double;  BitmapData: PByte;  AWidth, AHeight, DataSize: Integer;begin   xmin := myLimits[f, 0];  xmax := myLimits[f, 1];  ymin := myLimits[f, 2];  ymax := myLimits[f, 3];  px := (xmax - xmin) / (BGRAGraphicControl1.Width - 1);  py := (ymax - ymin) / (BGRAGraphicControl1.Height - 1);   BGRAGraphicControl1.Bitmap.Fill(BGRABlack);   AWidth := BGRAGraphicControl1.Bitmap.Width;  AHeight := BGRAGraphicControl1.Bitmap.Height;    DataSize := AWidth * AHeight * 4; // 4 bytes per pixel   if (AWidth > 0) and (AHeight > 0) then  begin     BitmapData := BGRAGraphicControl1.Bitmap.DataByte;     for p := 0 to AWidth - 1 do    begin      for q := 0 to AHeight - 1 do      begin        z := cmplx(xmin + p * px, ymin + q * py);        for k := 0 to 50 do        begin          z := Fz(z, c, f);          if CAbs(z) > 100 then Break;        end;         if (CAbs(z) < 100) then        begin           index := (p + (AHeight - 1 - q) * AWidth) * 4; // Correctly using AWidth and AHeight           if (index >= 0) and (index + 3 < DataSize) then          begin            // Set color to white for valid points            BitmapData[index] := 255;          BitmapData[index + 1] := 255;            BitmapData[index + 2] := 255;            BitmapData[index + 3] := 255;          end;        end;      end;    end;            BGRAGraphicControl1.Invalidate;  end;end;     Not sure why the correct curves are not being drawn. Frustrated. Anyway thanks everyone for having a look. Got to go to bed now. ---------------------------------------------------------------------- tetrastes · 2024-09-26 20:59 Try this (note my comments in (*  *) ): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.DrawBiomorph(c: complex; f: Integer);var  p, q, k, index: Integer;  z: complex;  xmin, xmax, ymin, ymax, px, py: Double;  BitmapData: PByte; // Pointer to the bitmap data  AWidth, AHeight, DataSize: Integer;begin  // Set up the limits and increments  xmin := _limits[f, 0];  xmax := _limits[f, 1];  ymin := _limits[f, 2];  ymax := _limits[f, 3];  px := (xmax - xmin) / (BGRAGraphicControl1.Width - 1);  py := (ymax - ymin) / (BGRAGraphicControl1.Height - 1);   BGRAGraphicControl1.Bitmap.Fill(BGRABlack);   AWidth := BGRAGraphicControl1.Bitmap.Width;  AHeight := BGRAGraphicControl1.Bitmap.Height;   // Calculate the size of the bitmap data  DataSize := AWidth * AHeight * 4; // 4 bytes per pixel   if (AWidth > 0) and (AHeight > 0) then  begin    // Get a pointer to the bitmap data for direct manipulation    BitmapData := BGRAGraphicControl1.Bitmap.DataByte;     // Loop through each pixel and calculate the color    for p := 0 to AWidth - 1 do    begin      for q := 0 to AHeight - 1 do      begin        z := cmplx(xmin + p * px, ymin + q * py);(*    Just in case to be exactly like in C#:    for (int k = 1; k < 50; k++)*)        for k := 1 to 49 do        begin        z := Fz(z, c, f);          if CAbs(z) > 100 then Break;        end;         // Set the pixel color directly using the byte array(*  Here is the main logic error: in line 43 above there is        if CAbs(z) > 100 then Break;    and now         if (CAbs(z) < 100) then  ??    Let it be like in C# code:*)        //if (CAbs(z) < 100) then      if ( (Abs(CReal(z)) < 100) or (Abs(CImag(z)) < 100) ) then      begin          // Calculate the index for the pixel          index := (p + (AHeight - 1 - q) * AWidth) * 4; // Correctly using AWidth and AHeight           // Check if the index is within bounds          if (index >= 0) and (index < DataSize (*- 4 - little arithmetic inaccuracy*) ) then // Ensure there's space for 4 bytes          begin            // Set pixel to red (BGRA)            BitmapData[index] := 0;       // Blue            BitmapData[index + 1] := 0;   // Green            BitmapData[index + 2] := 255; // Red          BitmapData[index + 3] := 0; // Alpha          end         else        begin            // Optionally log or handle the out-of-bounds case            ShowMessage('Index out of bounds: ' + IntToStr(index));          end;        end;      end;    end;     BGRAGraphicControl1.Bitmap.NotifyBitmapChange;  end  else  begin    ShowMessage('Bitmap not initialized or size is zero.');  end;end;  Also you didn't have to invent function ComplexPower, as there is CIntPower in ucomplex unit (dmath and lmath). ---------------------------------------------------------------------- Boleeman · 2024-09-27 05:10 Thanks tetrastes for your great help (and Zvoni and TRon). I learnt about complex numbers during my university days many years ago. I am still learning about dmath and the inbuilt ucomplex unit for Lazarus. Good to know about Abs(CReal(z)) < 100) or (Abs(CImag(z)) < 100  , as I tried a few other ways but it was not working. and for k := 1 to 49 do    for Lazarus one less index on canvas like for AHeight - 1 and AWidth - 1 and if (index >= 0) and (index < DataSize   index range. Attached is the working tetrastes  version and another version of mine (which works faster but gives weird curves) Also attaching some animated gifs showing the difference. Lastly I did not know CIntPower was in ucomplex unit (dmath and lmath). I will perhaps install lmath, as well. Many thanks  tetrastes ---------------------------------------------------------------------- Boleeman · 2024-09-27 09:24 Stop Start the Rendering. Set the Curve Colour and the Back Colour ---------------------------------------------------------------------- Boleeman · 2024-09-27 11:17 Now with Auto Multi-Colouring Ooooo so .....  Groovy. ---------------------------------------------------------------------- TRon · 2024-09-29 01:52 Thx to Boleeman a modified version using FPC's RTL complex unit (no need for lmath, dmath or whatever 3th-party unit for complex numbers) with some reshuffling and attempting to stay as close to the original code. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface // conversion from https://www.codeproject.com/Articles/1217543/The-astounding-Pickovers- biomorphs// oiginally by Miguel Diaz Kusztrich uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,  BGRABitmap, BGRABitmapTypes, BGRAGraphicControl; type   { TForm1 }   TForm1 = class(TForm)    BGRAGraphicControl1: TBGRAGraphicControl;    Timer1: TTimer;    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);    procedure BGRAGraphicControl1Redraw(Sender: TObject; Bitmap: TBGRABitmap);    procedure Timer1Timer(Sender: TObject);  private    stopBio: Boolean;  end; var  Form1: TForm1;  implementation {$R *.lfm} uses  ucomplex;  var  _limits    : array[0..9] of array[0..3] of real =  (   (  2, 4, -5, -3        ),   ( -2, 2, -2, 2         ),   ( -1.7, 1.7, -1.7, 1.7 ),   ( -3, 3, -3, 3         ),   ( -5, 5, -5, 5         ),   ( -10, 10, -10, 10     ),   ( -3, 3, -3, 3         ),   ( -1.5, 1.5, -1.5, 1.5 ),   ( -1.2, 1.2, -1.2, 1.2 ),   ( -8, 8, -8, 8         )  );   csinh      : function(z: complex) : complex = @csh;  cmagnitude : function(z: complex) : real    = @cmod; function CPow(value: complex; pwr: real): complex;begin  result := value ** pwr;end; function Fz(z, c: complex; f: Integer): complex;begin  case f of    0: result := CSin(z)     + CPow(z, 2) + c;    1: result := CPow(z, 2)  + CPow(z, 6) + c;    2: result := CPow(z, 2)  + CPow(z, 5) + c;    3: result := CPow(z, 3)  + c;    4: result := CSin(z)     + CPow(z, 5) + c;    5: result := CSin(z)     - CCos(z)    + CPow(z, 2) + c;    6: result := CPow(z, 3)  - CCos(z)    + CPow(z, 2) + c;    7: result := CPow(z, 6)  - CPow(z, 4) + CSinh(z)   + c;    8: result := CPow(z, 10) - CSinh(z)   + c;    otherwise       result := CCos(z)     + CPow(z, 4) + c;  end;end; procedure DrawBiomorph(Bitmap: TBGRABitmap; c: complex; f: Integer);var  bmpbits    : PBGRAPixel;  xmin, xmax : real;  ymin, ymax : real;  px  , py   : real;  p, q, k    : Integer;  z          : complex;  PixelCol   : TBGRAPixel;begin  // Get a pointer to the bitmap data for direct manipulation  bmpbits := Pointer(Bitmap.DataByte);  Bitmap.Fill(BGRAWhite);   // Set up the limits and increments  xmin := _limits[f, 0];  xmax := _limits[f, 1];  ymin := _limits[f, 2];  ymax := _limits[f, 3];  px := (xmax - xmin) / (Bitmap.Width - 1);  py := (ymax - ymin) / (Bitmap.Height - 1);   // additional check  if (Bitmap.Width > 0) and (Bitmap.Height > 0) then  begin    // Loop through each pixel and calculate the color    for p := 0 to Bitmap.Width-1 do    begin      for q := 0 to Bitmap.Height-1 do      begin        z := Cinit(xmin + p * px, ymin + q * py);       for k := 1 to 50-1 do        begin          z := Fz(z, c, f);        if (CMagnitude(z) > 100)            then break;        end;         // Check if the index is within bounds        if ((Abs(z.Re) < 100) or (Abs(z.Im) < 100)) then        begin          // Calculate the index for the pixel and set RGBAcolor directly using the RGBA array        PixelCol := BGRABlack; // CSSRed;          bmpbits[p + Bitmap.Width * (pred(Bitmap.Height) - q)] := PixelCol;        end        else        begin          // Optionally log or handle the out-of-bounds case        // Please don't do this because it will "hang" the application on          // Linux and requires a manual termination.          // ShowMessage('Index out of bounds: ' + IntToStr(p + Bitmap.Width * (pred(Bitmap.Height) - q)));        end;      end;    end;     // notify BGRAgraphicControl to update the bitmap    Bitmap.NotifyBitmapChange;  end  else    ShowMessage('Bitmap not initialized or size is zero.');end;  { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin  Self.Width  := 640;  Self.Height := 480;   stopBio := False;  Randomize;   Timer1.Interval := 1000;  Timer1.Enabled  := True;end; procedure TForm1.FormDestroy(Sender: TObject);begin  stopBio := True;  Timer1.Enabled := False;end; procedure TForm1.BGRAGraphicControl1Redraw(Sender: TObject; Bitmap: TBGRABitmap);var  c : complex;  f : Integer;begin  if stopBio then Exit;   // Generate random complex number and function index  c := cinit(Random * (Random(3) + 1), Random * (Random(3) + 1));  f := Random(10);   DrawBiomorph(Bitmap, c, f);end; procedure TForm1.Timer1Timer(Sender: TObject);begin  BGRAGraphicControl1.RedrawBitmap;end; end. Thank you all and have fun ! ---------------------------------------------------------------------- Boleeman · 2024-09-29 06:27 No 3rd-party unit for complex numbers. Had to see you you did it. Many thanks TRon. ...  Let the Biomorph Curves "Roll on"