Task: Kronecker product based fractals


C++: #include <cassert>
#include <vector>

#include <QImage>

template <typename scalar_type> class matrix {
public:
    matrix(size_t rows, size_t columns)
        : rows_(rows), columns_(columns), elements_(rows * columns) {}
    matrix(size_t rows, size_t columns,
        const std::initializer_list<std::initializer_list<scalar_type>>& values)
        : rows_(rows), columns_(columns), elements_(rows * columns) {
        assert(values.size() <= rows_);
        size_t i = 0;
        for (const auto& row : values) {
            assert(row.size() <= columns_);
            std::copy(begin(row), end(row), &elements_[i]);
            i += columns_;
        }
    }
    size_t rows() const { return rows_; }
    size_t columns() const { return columns_; }

    const scalar_type& operator()(size_t row, size_t column) const {
        assert(row < rows_);
        assert(column < columns_);
        return elements_[row * columns_ + column];
    }
    scalar_type& operator()(size_t row, size_t column) {
        assert(row < rows_);
        assert(column < columns_);
        return elements_[row * columns_ + column];
    }
private:
    size_t rows_;
    size_t columns_;
    std::vector<scalar_type> elements_;
};


template <typename scalar_type>
matrix<scalar_type> kronecker_product(const matrix<scalar_type>& a,
                                      const matrix<scalar_type>& b) {
    size_t arows = a.rows();
    size_t acolumns = a.columns();
    size_t brows = b.rows();
    size_t bcolumns = b.columns();
    matrix<scalar_type> c(arows * brows, acolumns * bcolumns);
    for (size_t i = 0; i < arows; ++i)
        for (size_t j = 0; j < acolumns; ++j)
            for (size_t k = 0; k < brows; ++k)
                for (size_t l = 0; l < bcolumns; ++l)
                    c(i*brows + k, j*bcolumns + l) = a(i, j) * b(k, l);
    return c;
}

bool kronecker_fractal(const char* fileName, const matrix<unsigned char>& m, int order) {
    matrix<unsigned char> result = m;
    for (int i = 0; i < order; ++i)
        result = kronecker_product(result, m);

    size_t height = result.rows();
    size_t width = result.columns();
    size_t bytesPerLine = 4 * ((width + 3)/4);
    std::vector<uchar> imageData(bytesPerLine * height);

    for (size_t i = 0; i < height; ++i)
        for (size_t j = 0; j < width; ++j)
            imageData[i * bytesPerLine + j] = result(i, j);

    QImage image(&imageData[0], width, height, bytesPerLine, QImage::Format_Indexed8);
    QVector<QRgb> colours(2);
    colours[0] = qRgb(0, 0, 0);
    colours[1] = qRgb(255, 255, 255);
    image.setColorTable(colours);
    return image.save(fileName);
}

int main() {
    matrix<unsigned char> matrix1(3, 3, {{0,1,0}, {1,1,1}, {0,1,0}});
    matrix<unsigned char> matrix2(3, 3, {{1,1,1}, {1,0,1}, {1,1,1}});
    matrix<unsigned char> matrix3(2, 2, {{1,1}, {0,1}});
    kronecker_fractal("vicsek.png", matrix1, 5);
    kronecker_fractal("sierpinski_carpet.png", matrix2, 5);
    kronecker_fractal("sierpinski_triangle.png", matrix3, 8);
    return 0;
}










Ada: with SDL.Video.Windows.Makers;
with SDL.Video.Renderers.Makers;
with SDL.Events.Events;
with SDL.Events.Mice;

procedure Kronecker_Fractals is

   Width  : constant := 800;
   Height : constant := 800;
   Order  : constant := 6;

   Window   : SDL.Video.Windows.Window;
   Renderer : SDL.Video.Renderers.Renderer;

   type Matrix is array (Positive range <>, Positive range <>) of Integer;

   function "*" (Left, Right : in Matrix) return Matrix is
      Result : Matrix
        (1 .. Left'Length (1) * Right'Length (1),
         1 .. Left'Length (2) * Right'Length (2));
      LI : Natural := 0;
      LJ : Natural := 0;
   begin
      for I in 0 .. Result'Length (1) - 1 loop
         for J in 0 .. Result'Length (2) - 1 loop
            Result (I + 1, J + 1) :=
              Left (Left'First (1) + (LI), Left'First (2) + (LJ))
              * Right
              (Right'First (1) + (I mod Right'Length (1)),
               Right'First (2) + (J mod Right'Length (2)));
            if (J + 1) mod Right'Length (2) = 0 then
               LJ := LJ + 1;
            end if;
         end loop;
         if (I + 1) mod Right'Length (1) = 0 then
            LI := LI + 1;
         end if;
         LJ := 0;
      end loop;
      return Result;
   end "*";

   function "**" (Base : Matrix; Exp : Positive) return Matrix is
      (case Exp is
         when 1      => Base,
         when 2      => Base * Base,
         when others => Base * Base ** (Exp - 1));

   procedure Draw_Matrix (LX, LY : Integer; M : Matrix) is
      use SDL.C;
   begin
      for Y in M'Range (1) loop
         for X in M'Range (2) loop
            if M (Y, X) /= 0 then
               Renderer.Draw (Point => (int (LX + X), int (LY + Y)));
            end if;
         end loop;
      end loop;
   end Draw_Matrix;

   type Fractals is (Cross, H, X, Sierpinski, U);
   Base : Fractals := Fractals'First;

   M : constant array (Fractals) of Matrix (1 .. 3, 1 .. 3) :=
     (Cross      => ((0, 1, 0), (1, 1, 1), (0, 1, 0)),
      H          => ((1, 0, 1), (1, 1, 1), (1, 0, 1)),
      X          => ((1, 0, 1), (0, 1, 0), (1, 0, 1)),
      Sierpinski => ((1, 1, 1), (1, 0, 1), (1, 1, 1)),
      U          => ((1, 0, 1), (1, 0, 1), (1, 1, 1)));

   procedure Draw is
   begin
      Renderer.Set_Draw_Colour ((0, 0, 0, 255));
      Renderer.Fill (Rectangle => (0, 0, Width, Height));

      Renderer.Set_Draw_Colour (Colour => (0, 220, 0, 255));
      Draw_Matrix (10, 10, M (Base) ** Order);
      Window.Update_Surface;
      Base := (if Base = Fractals'Last
                    then Fractals'First
                    else Fractals'Succ (Base));
   end Draw;

   procedure Event_Loop is
      use type SDL.Events.Event_Types;
      Event : SDL.Events.Events.Events;
   begin
      loop
         SDL.Events.Events.Wait (Event);
         case Event.Common.Event_Type is
            when SDL.Events.Quit             => return;
            when SDL.Events.Mice.Button_Down => Draw;
            when others                      => null;
         end case;
      end loop;
   end Event_Loop;

begin
   if not SDL.Initialise (Flags => SDL.Enable_Screen) then
      return;
   end if;

   SDL.Video.Windows.Makers.Create (Win      => Window,
                                    Title    => "Kronecker fractals (Click to cycle)",
                                    Position => SDL.Natural_Coordinates'(X => 10, Y => 10),
                                    Size     => SDL.Positive_Sizes'(Width, Height),
                                    Flags    => 0);
   SDL.Video.Renderers.Makers.Create (Renderer, Window.Get_Surface);

   Draw;
   Event_Loop;
   Window.Finalize;
   SDL.Finalise;
end Kronecker_Fractals;