Task
Draw a rotating cube.

It should be oriented with one vertex pointing straight up, and its opposite vertex on the main diagonal (the one farthest away) straight down. It can be solid or wire-frame, and you can use ASCII art if your language doesn't have graphical capabilities. Perspective is optional.




Related tasks
 Draw a cuboid
 write language name in 3D ASCII


with Ada.Numerics.Elementary_Functions;
 
with SDL.Video.Windows.Makers;
with SDL.Video.Renderers.Makers;
with SDL.Events.Events;
 
procedure Rotating_Cube is
 
   Width  : constant := 500;
   Height : constant := 500;
   Offset : constant := 500.0 / 2.0;
 
   Window   : SDL.Video.Windows.Window;
   Renderer : SDL.Video.Renderers.Renderer;
   Event    : SDL.Events.Events.Events;
   Quit     : Boolean := False;
 
   type Node_Id   is new Natural;
   type Point_3D  is record X, Y, Z : Float;   end record;
   type Edge_Type is record A, B    : Node_Id; end record;
 
   Nodes : array (Node_Id range <>) of Point_3D :=
     ((-100.0, -100.0, -100.0), (-100.0, -100.0, 100.0), (-100.0, 100.0, -100.0),
      (-100.0, 100.0, 100.0),   (100.0, -100.0, -100.0), (100.0, -100.0, 100.0),
      (100.0, 100.0, -100.0),   (100.0, 100.0, 100.0));
   Edges : constant array (Positive range <>) of Edge_Type :=
     ((0, 1), (1, 3), (3, 2), (2, 0), (4, 5), (5, 7),
      (7, 6), (6, 4), (0, 4), (1, 5), (2, 6), (3, 7));
 
   use Ada.Numerics.Elementary_Functions;
 
   procedure Rotate_Cube (AngleX, AngleY : in Float) is
      SinX : constant Float := Sin (AngleX);
      CosX : constant Float := Cos (AngleX);
      SinY : constant Float := Sin (AngleY);
      CosY : constant Float := Cos (AngleY);
      X, Y, Z : Float;
   begin
      for Node of Nodes loop
         X := Node.X;
         Y := Node.Y;
         Z := Node.Z;
         Node.X := X * CosX - Z * SinX;
         Node.Z := Z * CosX + X * SinX;
         Z := Node.Z;
         Node.Y := Y * CosY - Z * SinY;
         Node.Z := Z * CosY + Y * SinY;
      end loop;
   end Rotate_Cube;
 
   function Poll_Quit return Boolean is
      use type SDL.Events.Event_Types;
   begin
      while SDL.Events.Events.Poll (Event) loop
         if Event.Common.Event_Type = SDL.Events.Quit then
            return True;
         end if;
      end loop;
      return False;
   end Poll_Quit;
 
   procedure Draw_Cube (Quit : out Boolean) is
      use SDL.C;
      Pi : constant := Ada.Numerics.Pi;
      Xy1, Xy2 : Point_3D;
   begin
      Rotate_Cube (Pi / 4.0, Arctan (Sqrt (2.0)));
      for Frame in 0 .. 359 loop
         Renderer.Set_Draw_Colour ((0, 0, 0, 255));
         Renderer.Fill (Rectangle => (0, 0, Width, Height));
 
         Renderer.Set_Draw_Colour ((0, 220, 0, 255));
         for Edge of Edges loop
            Xy1 := Nodes (Edge.A);
            Xy2 := Nodes (Edge.B);
            Renderer.Draw (Line => ((int (Xy1.X + Offset), int (Xy1.Y + Offset)),
                                    (int (Xy2.X + Offset), int (Xy2.Y + Offset))));
         end loop;
         Rotate_Cube (Pi / 180.0, 0.0);
         Window.Update_Surface;
         Quit := Poll_Quit;
         exit when Quit;
         delay 0.020;
      end loop;
   end Draw_Cube;
 
begin
   if not SDL.Initialise (Flags => SDL.Enable_Screen) then
      return;
   end if;
 
   SDL.Video.Windows.Makers.Create (Win      => Window,
                                    Title    => "Rotating cube",
                                    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);
 
   while not Quit loop
      Draw_Cube (Quit);
   end loop;
 
   Window.Finalize;
   SDL.Finalise;
end Rotating_Cube;