How do you draw a cube in python?

Draw a rotating cube
You are encouraged to solve this task according to the task description, using any language you may know.

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

Ada[edit]

Translation of: Go

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;

AutoHotkey[edit]

Requires Gdip Library

; --------------------------------------------------------------- cubeSize := 200 deltaX := A_ScreenWidth/2 deltaY := A_ScreenHeight/2 keyStep := 1 mouseStep := 0.2 zoomStep := 1.1 playSpeed := 1 playTimer := 10 penSize := 5 /* HotKeys: !p:: Play/Stop !x:: change play to x-axis !y:: change play to y-axis !z:: change play to z-axis !NumpadAdd:: Zoom in !WheelUp:: Zoom in !NumpadSub:: Zoom out !WheelDown:: Zoom out !LButton:: Rotate X-axis, follow mouse !Up:: Rotate X-axis, CCW !Down:: Rotate X-axis, CW !LButton:: Rotate Y-axis, follow mouse !Right:: Rotate Y-axis, CCW !Left:: Rotate Y-axis, CW !RButton:: Rotate Z-axis, follow mouse !PGUP:: Rotate Z-axis, CW !PGDN:: Rotate Z-axis, CCW +LButton:: Move, follow mouse ^esc:: Exitapp */ visualCube = ( 1+--------+5 |\ \ | 2+--------+6 | | | 3+ | 7+ | \ | | 4+--------+8 ) SetBatchLines, -1 coord := cubeSize/2 nodes :=[[-coord, -coord, -coord] , [-coord, -coord, coord] , [-coord, coord, -coord] , [-coord, coord, coord] , [ coord, -coord, -coord] , [ coord, -coord, coord] , [ coord, coord, -coord] , [ coord, coord, coord]] edges := [[1, 2], [2, 4], [4, 3], [3, 1] , [5, 6], [6, 8], [8, 7], [7, 5] , [1, 5], [2, 6], [3, 7], [4, 8]] faces := [[1,2,4,3], [2,4,8,6], [1,2,6,5], [1,3,7,5], [5,7,8,6], [3,4,8,7]] CP := [(nodes[8,1]+nodes[1,1])/2 , (nodes[8,2]+nodes[1,2])/2] rotateX3D(-30) rotateY3D(30) Gdip1() draw() return ; -------------------------------------------------------------- draw() { global D := "" for i, n in nodes D .= Sqrt((n.1-CP.1)**2 + (n.2-CP.2)**2) "`t:" i ":`t" n.3 "`n" Sort, D, N p1 := StrSplit(StrSplit(D, "`n", "`r").1, ":").2 p2 := StrSplit(StrSplit(D, "`n", "`r").2, ":").2 hiddenNode := nodes[p1,3] < nodes[p2,3] ? p1 : p2 ; Draw Faces loop % faces.count() { n1 := faces[A_Index, 1] n2 := faces[A_Index, 2] n3 := faces[A_Index, 3] n4 := faces[A_Index, 4] if (n1 = hiddenNode) || (n2 = hiddenNode) || (n3 = hiddenNode) || (n4 = hiddenNode) continue points := nodes[n1,1]+deltaX "," nodes[n1,2]+deltaY . "|" nodes[n2,1]+deltaX "," nodes[n2,2]+deltaY . "|" nodes[n3,1]+deltaX "," nodes[n3,2]+deltaY . "|" nodes[n4,1]+deltaX "," nodes[n4,2]+deltaY Gdip_FillPolygon(G, FaceBrush%A_Index%, Points) } ; Draw Node-Numbers ;~ loop % nodes.count() { ;~ Gdip_FillEllipse(G, pBrush, nodes[A_Index, 1]+deltaX, nodes[A_Index, 2]+deltaY, 4, 4) ;~ Options := "x" nodes[A_Index, 1]+deltaX " y" nodes[A_Index, 2]+deltaY "c" TextColor " Bold s" size ;~ Gdip_TextToGraphics(G, A_Index, Options, Font) ;~ } ; Draw Edges loop % edges.count() { n1 := edges[A_Index, 1] n2 := edges[A_Index, 2] if (n1 = hiddenNode) || (n2 = hiddenNode) continue Gdip_DrawLine(G, pPen, nodes[n1,1]+deltaX, nodes[n1,2]+deltaY, nodes[n2,1]+deltaX, nodes[n2,2]+deltaY) } UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height) } ; --------------------------------------------------------------- rotateZ3D(theta) { ; Rotate shape around the z-axis global theta *= 3.141592653589793/180 sinTheta := sin(theta) cosTheta := cos(theta) loop % nodes.count() { x := nodes[A_Index,1] y := nodes[A_Index,2] nodes[A_Index,1] := x*cosTheta - y*sinTheta nodes[A_Index,2] := y*cosTheta + x*sinTheta } Redraw() } ; --------------------------------------------------------------- rotateX3D(theta) { ; Rotate shape around the x-axis global theta *= 3.141592653589793/180 sinTheta := sin(theta) cosTheta := cos(theta) loop % nodes.count() { y := nodes[A_Index, 2] z := nodes[A_Index, 3] nodes[A_Index, 2] := y*cosTheta - z*sinTheta nodes[A_Index, 3] := z*cosTheta + y*sinTheta } Redraw() } ; --------------------------------------------------------------- rotateY3D(theta) { ; Rotate shape around the y-axis global theta *= 3.141592653589793/180 sinTheta := sin(theta) cosTheta := cos(theta) loop % nodes.count() { x := nodes[A_Index, 1] z := nodes[A_Index, 3] nodes[A_Index, 1] := x*cosTheta + z*sinTheta nodes[A_Index, 3] := z*cosTheta - x*sinTheta } Redraw() } ; --------------------------------------------------------------- Redraw(){ global gdip2() gdip1() draw() } ; --------------------------------------------------------------- gdip1(){ global If !pToken := Gdip_Startup() { MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system ExitApp } OnExit, Exit Width := A_ScreenWidth, Height := A_ScreenHeight Gui, 1: -Caption +E0x80000 +LastFound +OwnDialogs +Owner +AlwaysOnTop Gui, 1: Show, NA hwnd1 := WinExist() hbm := CreateDIBSection(Width, Height) hdc := CreateCompatibleDC() obm := SelectObject(hdc, hbm) G := Gdip_GraphicsFromHDC(hdc) Gdip_SetSmoothingMode(G, 4) TextColor:="FFFFFF00", size := 18 Font := "Arial" Gdip_FontFamilyCreate(Font) pBrush := Gdip_BrushCreateSolid(0xFFFF00FF) FaceBrush2 := Gdip_BrushCreateSolid(0xFF0000FF) ; blue FaceBrush2 := Gdip_BrushCreateSolid(0xFFFF0000) ; red FaceBrush3 := Gdip_BrushCreateSolid(0xFFFFFF00) ; yellow FaceBrush4 := Gdip_BrushCreateSolid(0xFFFF7518) ; orange FaceBrush5 := Gdip_BrushCreateSolid(0xFF00FF00) ; lime FaceBrush6 := Gdip_BrushCreateSolid(0xFFFFFFFF) ; white pPen := Gdip_CreatePen(0xFF000000, penSize) } ; --------------------------------------------------------------- gdip2(){ global Gdip_DeleteBrush(pBrush) Gdip_DeletePen(pPen) SelectObject(hdc, obm) DeleteObject(hbm) DeleteDC(hdc) Gdip_DeleteGraphics(G) } ; Viewing Hotkeys ---------------------------------------------- ; HotKey Play/Stop --------------------------------------------- !p:: SetTimer, rotateTimer, % (toggle:=!toggle)?playTimer:"off" return rotateTimer: axis := !axis ? "Y" : axis rotate%axis%3D(playSpeed) return !x:: !y:: !z:: axis := SubStr(A_ThisHotkey, 2, 1) return ; HotKey Zoom in/out ------------------------------------------- !NumpadAdd:: !NumpadSub:: !WheelUp:: !WheelDown:: loop % nodes.count() { nodes[A_Index, 1] := nodes[A_Index, 1] * (InStr(A_ThisHotkey, "Add") || InStr(A_ThisHotkey, "Up") ? zoomStep : 1/zoomStep) nodes[A_Index, 2] := nodes[A_Index, 2] * (InStr(A_ThisHotkey, "Add") || InStr(A_ThisHotkey, "Up") ? zoomStep : 1/zoomStep) nodes[A_Index, 3] := nodes[A_Index, 3] * (InStr(A_ThisHotkey, "Add") || InStr(A_ThisHotkey, "Up") ? zoomStep : 1/zoomStep) } Redraw() return ; HotKey Rotate around Y-Axis ---------------------------------- !Right:: !Left:: rotateY3D(keyStep * (InStr(A_ThisHotkey,"right") ? 1 : -1)) return ; HotKey Rotate around X-Axis ---------------------------------- !Up:: !Down:: rotateX3D(keyStep * (InStr(A_ThisHotkey, "Up") ? 1 : -1)) return ; HotKey Rotate around Z-Axis ---------------------------------- !PGUP:: !PGDN:: rotateZ3D(keyStep * (InStr(A_ThisHotkey, "UP") ? 1 : -1)) return ; HotKey, Rotate around X/Y-Axis ------------------------------- !LButton:: MouseGetPos, pmouseX, pmouseY while GetKeyState("Lbutton", "P") { MouseGetPos, mouseX, mouseY DeltaMX := mouseX - pmouseX DeltaMY := pmouseY - mouseY if (DeltaMX || DeltaMY) { MouseGetPos, pmouseX, pmouseY rotateY3D(DeltaMX) rotateX3D(DeltaMY) } } return ; HotKey Rotate around Z-Axis ---------------------------------- !RButton:: MouseGetPos, pmouseX, pmouseY while GetKeyState("Rbutton", "P") { MouseGetPos, mouseX, mouseY DeltaMX := mouseX - pmouseX DeltaMY := mouseY - pmouseY DeltaMX *= mouseY < deltaY ? mouseStep : -mouseStep DeltaMY *= mouseX > deltaX ? mouseStep : -mouseStep if (DeltaMX || DeltaMY) { MouseGetPos, pmouseX, pmouseY rotateZ3D(DeltaMX) rotateZ3D(DeltaMY) } } return ; HotKey, Move ------------------------------------------------- +LButton:: MouseGetPos, pmouseX, pmouseY while GetKeyState("Lbutton", "P") { MouseGetPos, mouseX, mouseY deltaX += mouseX - pmouseX deltaY += mouseY - pmouseY pmouseX := mouseX pmouseY := mouseY Redraw() } return ; --------------------------------------------------------------- ^esc:: Exit: gdip2() Gdip_Shutdown(pToken) ExitApp Return ; ---------------------------------------------------------------

BASIC256[edit]

global escala global tam global zoff global cylr escala = 50 tam = 320 zoff = 0.5773502691896257645091487805019574556 cylr = 1.6329931618554520654648560498039275946 clg graphsize tam, tam dim x(6) theta = 0.0 dtheta = 1.5 dt = 1.0 / 30 dim cylphi = {PI/6, 5*PI/6, 3*PI/2, 11*PI/6, PI/2, 7*PI/6} while key = "" lasttime = msec for i = 0 to 5 x[i] = tam/2 + escala *cylr * cos(cylphi[i] + theta) next i clg call drawcube(x) while msec < lasttime + dt end while theta += dtheta*(msec-lasttime) pause .4 call drawcube(x) end while subroutine drawcube(x) for i = 0 to 2 color rgb(0,0,0) #black line tam/2, tam/2 - escala / zoff, x[i], tam/2 - escala * zoff line tam/2, tam/2 + escala / zoff, x[5-i], tam/2 + escala * zoff line x[i], tam/2 - escala * zoff, x[(i % 3) + 3], tam/2 + escala * zoff line x[i], tam/2 - escala * zoff, x[((i+1)%3) + 3], tam/2 + escala * zoff next i end subroutine

C[edit]

Rotating wireframe cube in OpenGL, windowing implementation via freeglut

#include<gl/freeglut.h> double rot = 0; float matCol[] = {1,0,0,0}; void display(){ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(30,1,1,0); glRotatef(rot,0,1,1); glMaterialfv(GL_FRONT,GL_DIFFUSE,matCol); glutWireCube(1); glPopMatrix(); glFlush(); } void onIdle(){ rot += 0.1; glutPostRedisplay(); } void reshape(int w,int h){ float ar = (float) w / (float) h ; glViewport(0,0,(GLsizei)w,(GLsizei)h); glTranslatef(0,0,-10); glMatrixMode(GL_PROJECTION); gluPerspective(70,(GLfloat)w/(GLfloat)h,1,12); glLoadIdentity(); glFrustum ( -1.0, 1.0, -1.0, 1.0, 10.0, 100.0 ) ; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void init(){ float pos[] = {1,1,1,0}; float white[] = {1,1,1,0}; float shini[] = {70}; glClearColor(.5,.5,.5,0); glShadeModel(GL_SMOOTH); glLightfv(GL_LIGHT0,GL_AMBIENT,white); glLightfv(GL_LIGHT0,GL_DIFFUSE,white); glMaterialfv(GL_FRONT,GL_SHININESS,shini); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } int main(int argC, char* argV[]) { glutInit(&argC,argV); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(600,500); glutCreateWindow("Rossetta's Rotating Cube"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(onIdle); glutMainLoop(); return 0; }

C#[edit]

Translation of: Java

using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.Windows.Threading; namespace RotatingCube { public partial class Form1 : Form { double[][] nodes = { new double[] {-1, -1, -1}, new double[] {-1, -1, 1}, new double[] {-1, 1, -1}, new double[] {-1, 1, 1}, new double[] {1, -1, -1}, new double[] {1, -1, 1}, new double[] {1, 1, -1}, new double[] {1, 1, 1} }; int[][] edges = { new int[] {0, 1}, new int[] {1, 3}, new int[] {3, 2}, new int[] {2, 0}, new int[] {4, 5}, new int[] {5, 7}, new int[] {7, 6}, new int[] {6, 4}, new int[] {0, 4}, new int[] {1, 5}, new int[] {2, 6}, new int[] {3, 7}}; public Form1() { Width = Height = 640; StartPosition = FormStartPosition.CenterScreen; SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); Scale(100, 100, 100); RotateCuboid(Math.PI / 4, Math.Atan(Math.Sqrt(2))); var timer = new DispatcherTimer(); timer.Tick += (s, e) => { RotateCuboid(Math.PI / 180, 0); Refresh(); }; timer.Interval = new TimeSpan(0, 0, 0, 0, 17); timer.Start(); } private void RotateCuboid(double angleX, double angleY) { double sinX = Math.Sin(angleX); double cosX = Math.Cos(angleX); double sinY = Math.Sin(angleY); double cosY = Math.Cos(angleY); foreach (var node in nodes) { double x = node[0]; double y = node[1]; double z = node[2]; node[0] = x * cosX - z * sinX; node[2] = z * cosX + x * sinX; z = node[2]; node[1] = y * cosY - z * sinY; node[2] = z * cosY + y * sinY; } } private void Scale(int v1, int v2, int v3) { foreach (var item in nodes) { item[0] *= v1; item[1] *= v2; item[2] *= v3; } } protected override void OnPaint(PaintEventArgs args) { var g = args.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; g.Clear(Color.White); g.TranslateTransform(Width / 2, Height / 2); foreach (var edge in edges) { double[] xy1 = nodes[edge[0]]; double[] xy2 = nodes[edge[1]]; g.DrawLine(Pens.Black, (int)Math.Round(xy1[0]), (int)Math.Round(xy1[1]), (int)Math.Round(xy2[0]), (int)Math.Round(xy2[1])); } foreach (var node in nodes) { g.FillEllipse(Brushes.Black, (int)Math.Round(node[0]) - 4, (int)Math.Round(node[1]) - 4, 8, 8); } } } }

Delphi[edit]

unit main; interface uses Winapi.Windows, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.ExtCtrls, System.Math, System.Classes; type TForm1 = class(TForm) tmr1: TTimer; procedure FormCreate(Sender: TObject); procedure tmr1Timer(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; nodes: TArray<TArray<double>> = [[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1], [1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]]; edges: TArray<TArray<Integer>> = [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]]; implementation {$R *.dfm} procedure Scale(factor: TArray<double>); begin if Length(factor) <> 3 then exit; for var i := 0 to High(nodes) do for var f := 0 to High(factor) do nodes[i][f] := nodes[i][f] * factor[f]; end; procedure RotateCuboid(angleX, angleY: double); begin var sinX := sin(angleX); var cosX := cos(angleX); var sinY := sin(angleY); var cosY := cos(angleY); for var i := 0 to High(nodes) do begin var x := nodes[i][0]; var y := nodes[i][1]; var z := nodes[i][2]; nodes[i][0] := x * cosX - z * sinX; nodes[i][2] := z * cosX + x * sinX; z := nodes[i][2]; nodes[i][1] := y * cosY - z * sinY; nodes[i][2] := z * cosY + y * sinY; end; end; function DrawCuboid(x, y, w, h: Integer): TBitmap; var offset: TPoint; begin Result := TBitmap.Create; Result.SetSize(w, h); rotateCuboid(PI / 180, 0); offset := TPoint.Create(x, y); with Result.canvas do begin Brush.Color := clBlack; Pen.Color := clWhite; Lock; FillRect(ClipRect); for var edge in edges do begin var p1 := (nodes[edge[0]]); var p2 := (nodes[edge[1]]); moveTo(trunc(p1[0]) + offset.x, trunc(p1[1]) + offset.y); lineTo(trunc(p2[0]) + offset.x, trunc(p2[1]) + offset.y); end; Unlock; end; end; procedure TForm1.FormCreate(Sender: TObject); begin ClientHeight := 360; ClientWidth := 640; DoubleBuffered := true; scale([100, 100, 100]); rotateCuboid(PI / 4, ArcTan(sqrt(2))); end; procedure TForm1.tmr1Timer(Sender: TObject); var buffer: TBitmap; begin buffer := DrawCuboid(ClientWidth div 2, ClientHeight div 2, ClientWidth, ClientHeight); Canvas.Draw(0, 0, buffer); buffer.Free; end; end.

Resource Form

object Form1: TForm1 OnCreate = FormCreate object tmr1: TTimer Interval = 17 OnTimer = tmr1Timer end end

EasyLang[edit]

Draws only the visible edges

Run it

node[][] = [ [ -1 -1 -1 ] [ -1 -1 1 ] [ -1 1 -1 ] [ -1 1 1 ] [ 1 -1 -1 ] [ 1 -1 1 ] [ 1 1 -1 ] [ 1 1 1 ] ] edge[][] = [ [ 0 1 ] [ 1 3 ] [ 3 2 ] [ 2 0 ] [ 4 5 ] [ 5 7 ] [ 7 6 ] [ 6 4 ] [ 0 4 ] [ 1 5 ] [ 2 6 ] [ 3 7 ] ] # func scale f . . for i range len node[][] for d range 3 node[i][d] *= f . . . func rotate angx angy . . sinx = sin angx cosx = cos angx siny = sin angy cosy = cos angy for i range len node[][] x = node[i][0] z = node[i][2] node[i][0] = x * cosx - z * sinx y = node[i][1] z = z * cosx + x * sinx node[i][1] = y * cosy - z * siny node[i][2] = z * cosy + y * siny . . len nd[] 3 func draw . . clear m = 999 mi = -1 for i range len node[][] if node[i][2] < m m = node[i][2] mi = i . . ix = 0 for i range len edge[][] if edge[i][0] = mi nd[ix] = edge[i][1] ix += 1 elif edge[i][1] = mi nd[ix] = edge[i][0] ix += 1 . . for ni range len nd[] for i range len edge[][] if edge[i][0] = nd[ni] or edge[i][1] = nd[ni] x1 = node[edge[i][0]][0] y1 = node[edge[i][0]][1] x2 = node[edge[i][1]][0] y2 = node[edge[i][1]][1] move x1 + 50 y1 + 50 line x2 + 50 y2 + 50 . . . . call scale 25 call rotate 45 atan sqrt 2 call draw on animate call rotate 1 0 call draw .

FreeBASIC[edit]

#define PI 3.14159265358979323 #define SCALE 50 #define SIZE 320 #define zoff 0.5773502691896257645091487805019574556 #define cylr 1.6329931618554520654648560498039275946 screenres SIZE, SIZE, 4 dim as double theta = 0.0, dtheta = 1.5, x(0 to 5), lasttime, dt = 1./30 dim as double cylphi(0 to 5) = {PI/6, 5*PI/6, 3*PI/2, 11*PI/6, PI/2, 7*PI/6} sub drawcube( x() as double, colour as uinteger ) for i as uinteger = 0 to 2 line (SIZE/2, SIZE/2-SCALE/zoff) - (x(i), SIZE/2-SCALE*zoff), colour line (SIZE/2, SIZE/2+SCALE/zoff) - (x(5-i), SIZE/2+SCALE*zoff), colour line ( x(i), SIZE/2-SCALE*zoff ) - ( x(i mod 3 + 3), SIZE/2+SCALE*zoff ), colour line ( x(i), SIZE/2-SCALE*zoff ) - ( x((i+1) mod 3 + 3), SIZE/2+SCALE*zoff ), colour next i end sub while inkey="" lasttime = timer for i as uinteger = 0 to 5 x(i) = SIZE/2 + SCALE*cylr*cos(cylphi(i)+theta) next i drawcube x(), 15 while timer < lasttime + dt wend theta += dtheta*(timer-lasttime) drawcube x(),0 wend end

FutureBasic[edit]

Among the capabilities of FutureBasic (or FB as it's called by its developers) is the ability to compile Open GL code as demonstrated here.

include "Tlbx agl.incl" include "Tlbx glut.incl" output file "Rotating Cube" local fn AnimateCube '~'1 begin globals dim as double  sRotation end globals // Speed of rotation sRotation += 2.9 glMatrixMode( _GLMODELVIEW ) glLoadIdentity() glTranslated( 0.0, 0.0, 0.0 ) glRotated( sRotation, -0.45, -0.8, -0.6 ) glColor3d( 1.0, 0.0, 0.3 ) glLineWidth( 1.5 ) glutWireCube( 1.0 ) end fn // Main program dim as GLint           attrib(2) dim as CGrafPtr        port dim as AGLPixelFormat  fmt dim as AGLContext      glContext dim as EventRecord     ev dim as GLboolean       yesOK window 1, @"Rotating Cube", (0,0) - (500,500) attrib(0) = _AGLRGBA attrib(1) = _AGLDOUBLEBUFFER attrib(2) = _AGLNONE fmt = fn aglChoosePixelFormat( 0, 0, attrib(0) ) glContext = fn aglCreateContext( fmt, 0 ) aglDestroyPixelFormat( fmt ) port = window( _wndPort ) yesOK = fn aglSetDrawable( glContext, port ) yesOK = fn aglSetCurrentContext( glContext ) glClearColor( 0.0, 0.0, 0.0, 0.0 ) poke long event - 8, 1 do glClear( _GLCOLORBUFFERBIT ) fn AnimateCube aglSwapBuffers( glContext ) HandleEvents until gFBQuit

Go[edit]

As of Go 1.9, it looks as if the only standard library supporting animated graphics is image/gif - so we create an animated GIF...

package main import ( "image" "image/color" "image/gif" "log" "math" "os" ) const ( width, height = 640, 640 offset = height / 2 fileName = "rotatingCube.gif" ) var nodes = [][]float64{{-100, -100, -100}, {-100, -100, 100}, {-100, 100, -100}, {-100, 100, 100}, {100, -100, -100}, {100, -100, 100}, {100, 100, -100}, {100, 100, 100}} var edges = [][]int{{0, 1}, {1, 3}, {3, 2}, {2, 0}, {4, 5}, {5, 7}, {7, 6}, {6, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}} func main() { var images []*image.Paletted fgCol := color.RGBA{0xff, 0x00, 0xff, 0xff} var palette = []color.Color{color.RGBA{0x00, 0x00, 0x00, 0xff}, fgCol} var delays []int imgFile, err := os.Create(fileName) if err != nil { log.Fatal(err) } defer imgFile.Close() rotateCube(math.Pi/4, math.Atan(math.Sqrt(2))) var frame float64 for frame = 0; frame < 360; frame++ { img := image.NewPaletted(image.Rect(0, 0, width, height), palette) images = append(images, img) delays = append(delays, 5) for _, edge := range edges { xy1 := nodes[edge[0]] xy2 := nodes[edge[1]] drawLine(int(xy1[0])+offset, int(xy1[1])+offset, int(xy2[0])+offset, int(xy2[1])+offset, img, fgCol) } rotateCube(math.Pi/180, 0) } if err := gif.EncodeAll(imgFile, &gif.GIF{Image: images, Delay: delays}); err != nil { imgFile.Close() log.Fatal(err) } } func rotateCube(angleX, angleY float64) { sinX := math.Sin(angleX) cosX := math.Cos(angleX) sinY := math.Sin(angleY) cosY := math.Cos(angleY) for _, node := range nodes { x := node[0] y := node[1] z := node[2] node[0] = x*cosX - z*sinX node[2] = z*cosX + x*sinX z = node[2] node[1] = y*cosY - z*sinY node[2] = z*cosY + y*sinY } } func drawLine(x0, y0, x1, y1 int, img *image.Paletted, col color.RGBA) { dx := abs(x1 - x0) dy := abs(y1 - y0) var sx, sy int = -1, -1 if x0 < x1 { sx = 1 } if y0 < y1 { sy = 1 } err := dx - dy for { img.Set(x0, y0, col) if x0 == x1 && y0 == y1 { break } e2 := 2 * err if e2 > -dy { err -= dy x0 += sx } if e2 < dx { err += dx y0 += sy } } } func abs(x int) int { if x < 0 { return -x } return x }

Haskell[edit]

This implementation compiles to JavaScript that runs in a browser using the ghcjs compiler . The reflex-dom library is used to help with svg rendering and animation.

{-# LANGUAGE RecursiveDo #-} import Reflex.Dom import Data.Map as DM (Map, lookup, insert, empty, fromList) import Data.Matrix import Data.Time.Clock import Control.Monad.Trans size = 500 updateFrequency = 0.2 rotationStep = pi/10 data Color = Red | Green | Blue | Yellow | Orange | Purple | Black deriving (Show,Eq,Ord,Enum) zRot :: Float -> Matrix Float zRot rotation = let c = cos rotation s = sin rotation in fromLists [[ c, s, 0, 0 ] ,[-s, c, 0, 0 ] ,[ 0, 0, 1, 0 ] ,[ 0, 0, 0, 1 ] ] xRot :: Float -> Matrix Float xRot rotation = let c = cos rotation s = sin rotation in fromLists [[ 1, 0, 0, 0 ] ,[ 0, c, s, 0 ] ,[ 0, -s, c, 0 ] ,[ 0, 0, 0, 1 ] ] yRot :: Float -> Matrix Float yRot rotation = let c = cos rotation s = sin rotation in fromLists [[ c, 0, -s, 0 ] ,[ 0, 1, 0, 0 ] ,[ s, 0, c, 0 ] ,[ 0, 0, 0, 1 ] ] translation :: (Float,Float,Float) -> Matrix Float translation (x,y,z) = fromLists [[ 1, 0, 0, 0 ] ,[ 0, 1, 0, 0 ] ,[ 0, 0, 1, 0 ] ,[ x, y, z, 1 ] ] scale :: Float -> Matrix Float scale s = fromLists [[ s, 0, 0, 0 ] ,[ 0, s, 0, 0 ] ,[ 0, 0, s, 0 ] ,[ 0, 0, 0, 1 ] ] -- perspective transformation; perspective :: Matrix Float perspective = fromLists [[ 1, 0, 0, 0 ] ,[ 0, 1, 0, 0 ] ,[ 0, 0, 1, 1 ] ,[ 0, 0, 1, 1 ] ] transformPoints :: Matrix Float -> Matrix Float -> [(Float,Float)] transformPoints transform points = let result4d = points `multStd2` transform result2d = (\[x,y,z,w] -> (x/w,y/w)) <$> toLists result4d in result2d showRectangle :: MonadWidget t m => Float -> Float -> Float -> Float -> Color -> Dynamic t (Matrix Float) -> m () showRectangle x0 y0 x1 y1 faceColor dFaceView = do let points = fromLists [[x0,y0,0,1],[x0,y1,0,1],[x1,y1,0,1],[x1,y0,0,1]] pointsToString = concatMap (\(x,y) -> show x ++ ", " ++ show y ++ " ") dAttrs <- mapDyn (\fvk -> DM.fromList [ ("fill", show faceColor) , ("points", pointsToString (transformPoints fvk points)) ] ) dFaceView elDynAttrSVG "polygon" dAttrs $ return () showUnitSquare :: MonadWidget t m => Color -> Float -> Dynamic t (Matrix Float) -> m () showUnitSquare faceColor margin dFaceView = showRectangle margin margin (1.0 - margin) (1.0 - margin) faceColor dFaceView -- show colored square on top of black square for outline effect showFace :: MonadWidget t m => Color -> Dynamic t (Matrix Float) -> m () showFace faceColor dFaceView = do showUnitSquare Black 0 dFaceView showUnitSquare faceColor 0.03 dFaceView facingCamera :: [Float] -> Matrix Float -> Bool facingCamera viewPoint modelTransform = let cross [x0,y0,z0] [x1,y1,z1] = [y0*z1-z0*y1, z0*x1-x0*z1, x0*y1-y0*x1 ] dot v0 v1 = sum $ zipWith (*) v0 v1 vMinus = zipWith (-) untransformedPoints = fromLists [ [0,0,0,1] -- lower left , [1,0,0,1] -- lower right , [0,1,0,1] ] -- upper left transformedPoints = toLists $ untransformedPoints `multStd2` modelTransform pt00 = take 3 $ head transformedPoints -- transformed lower left pt10 = take 3 $ transformedPoints !! 1 -- transformed upper right pt01 = take 3 $ transformedPoints !! 2 -- transformed upper left tVec_10_00 = pt10 `vMinus` pt00 -- lower right to lower left tVec_01_00 = pt01 `vMinus` pt00 -- upper left to lower left perpendicular = tVec_10_00 `cross` tVec_01_00 -- perpendicular to face cameraToPlane = pt00 `vMinus` viewPoint -- line of sight to face -- Perpendicular points away from surface; -- Camera vector points towards surface -- Opposed vectors means that face will be visible. in cameraToPlane `dot` perpendicular < 0 faceView :: Matrix Float -> Matrix Float -> (Bool, Matrix Float) faceView modelOrientation faceOrientation = let modelTransform = translation (-1/2,-1/2,1/2) -- unit square to origin + z offset `multStd2` faceOrientation -- orientation specific to each face `multStd2` scale (1/2) -- shrink cube to fit in view. `multStd2` modelOrientation -- position the entire cube isFacingCamera = facingCamera [0,0,-1] modelTransform -- backface elimination -- combine to get single transform from 2d face to 2d display viewTransform = modelTransform `multStd2` perspective `multStd2` scale size -- scale up to svg box scale `multStd2` translation (size/2, size/2, 0) -- move to center of svg box in (isFacingCamera, viewTransform) updateFaceViews :: Matrix Float -> Map Color (Matrix Float) -> (Color, Matrix Float) -> Map Color (Matrix Float) updateFaceViews modelOrientation prevCollection (faceColor, faceOrientation) = let (isVisible, newFaceView) = faceView modelOrientation faceOrientation in if isVisible then insert faceColor newFaceView prevCollection else prevCollection faceViews :: Matrix Float -> Map Color (Matrix Float) faceViews modelOrientation = foldl (updateFaceViews modelOrientation) empty [ (Purple , xRot (0.0) ) , (Yellow , xRot (pi/2) ) , (Red , yRot (pi/2) ) , (Green , xRot (-pi/2) ) , (Blue , yRot (-pi/2) ) , (Orange , xRot (pi) ) ] viewModel :: MonadWidget t m => Dynamic t (Matrix Float) -> m () viewModel modelOrientation = do faceMap <- mapDyn faceViews modelOrientation listWithKey faceMap showFace return () view :: MonadWidget t m => Dynamic t (Matrix Float) -> m () view modelOrientation = do el "h2" $ text "Rotating Cube" elDynAttrSVG "svg" (constDyn $ DM.fromList [ ("width", show size), ("height", show size) ]) $ viewModel modelOrientation main = mainWidget $ do let initialOrientation = xRot (pi/4) `multStd2` zRot (atan(1/sqrt(2))) update _ modelOrientation = modelOrientation `multStd2` (yRot (rotationStep) ) tick <- tickLossy updateFrequency =<< liftIO getCurrentTime rec view modelOrientation modelOrientation <- foldDyn update initialOrientation tick return () -- At end because of Rosetta Code handling of unmatched quotes. elDynAttrSVG a2 a3 a4 = do elDynAttrNS' (Just "//www.w3.org/2000/svg") a2 a3 a4 return ()

Link to live demo: //dc25.github.io/drawRotatingCubeHaskell/

J[edit]

Derived from J's qt shader demo:

require'gl2 gles ide/qt/opengl' coinsert'jgl2 jgles qtopengl' rotcube=: {{ if.0=nc<'sprog'do.return.end. fixosx=. 'opengl';'opengl',('DARWIN'-:UNAME)#' version 4.1' wd 'pc rot; minwh 300 300; cc cube opengl flush' rplc fixosx HD=: ".wd 'qhwndc cube' wd 'ptimer 17; pshow' }} rot_close=: {{ wd 'ptimer 0' glDeleteBuffers ::0: 2; vbo glDeleteProgram ::0: sprog erase 'sprog' wd 'pclose' }} cstr=: {{if.y do.memr y,0 _1 2 else.EMPTY end.}} gstr=: {{cstr>{.glGetString y}} diag=: {{p[echo y,': ',p=.gstr".y}} blitf=: {{ dat=. 1 fc,y NB. short floats glBindBuffer GL_ARRAY_BUFFER; x{vbo glBufferData GL_ARRAY_BUFFER; (#dat); (symdat<'dat'); GL_STATIC_DRAW }} rot_cube_initialize=: {{ erase'sprog' if.0=#diag 'GL_VERSION' do.echo 'cannot retrieve GL_VERSION' return.end. diag each;:'GL_VENDOR GL_RENDERER GL_SHADING_LANGUAGE_VERSION' GLSL=:wglGLSL'' wglPROC'' 'err program'=. gl_makeprogram VSRC ;&fixversion FSRC if.#err do. echo 'err: ', err return.end. if. GLSL>120 do.vao=: >{:glGenVertexArrays 1;,_1 end. assert _1~:vertexAttr=: >{.glGetAttribLocation program;'vertex' assert _1~:colorAttr=: >{.glGetAttribLocation program;'color' assert _1~:mvpUni=: >{.glGetUniformLocation program;'mvp' vbo=: >{:glGenBuffers 2;2#_1 0 blitf vertexData 1 blitf colorData sprog=: program }} VSRC=: {{)n #version $version $v_in $highp vec3 vertex; $v_in $lowp vec3 color; $v_out $lowp vec4 v_color; uniform mat4 mvp; void main(void) { gl_Position= mvp * vec4(vertex,1.0); v_color= vec4(color,1.0); } }} FSRC=: {{)n #version $version $f_in $lowp vec4 v_color; $fragColor void main(void) { $gl_fragColor= v_color; } }} fixversion=: {{ NB. cope with host shader language version r=. '$version';GLSL,&":;(GLSL>:300)#(*GLES_VERSION){' core';' es' f1=. GLSL<:120 r=.r, '$v_in';f1{'in';'attribute' r=.r, '$v_out';f1{'out';'varying' r=.r, '$f_in';f1{'in';'varying' r=.r, '$highp ';f1#(*GLES_VERSION)#'highp' r=.r, '$lowp ';f1#(*GLES_VERSION)#'lowp' f2=.(330<:GLSL)+.(300<:GLSL)**GLES_VERSION r=.r, '$gl_fragColor';f2{'gl_FragColor';'fragColor' r=.r, '$fragColor';f2#'out vec4 fragColor;' y rplc r }} rot_timer=: {{ try. gl_sel HD gl_paint'' catch. echo 'error in rot_timer',LF,13!:12'' wd'ptimer 0' end. }} zeroVAttr=: {{ glEnableVertexAttribArray y glBindBuffer GL_ARRAY_BUFFER; x{vbo glVertexAttribPointer y; 3; GL_FLOAT; 0; 0; 0 }} mp=: +/ .* ref=: (gl_Translate 0 0 _10) mp glu_LookAt 0 0 1,0 0 0,1 0 0 rot_cube_paint=: {{ try. if.nc<'sprog' do.return.end. wh=. gl_qwh'' glClear GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT [glClearColor 0 0 0 0+%3 glUseProgram sprog glEnable each GL_DEPTH_TEST, GL_CULL_FACE, GL_BLEND glBlendFunc GL_SRC_ALPHA; GL_ONE_MINUS_SRC_ALPHA mvp=. (gl_Rotate (360|60*6!:1''),1 0 0)mp ref mp gl_Perspective 30, (%/wh),1 20 glUniformMatrix4fv mvpUni; 1; GL_FALSE; mvp if. GLSL>120 do. glBindVertexArray {.vao end. 0 zeroVAttr vertexAttr 1 zeroVAttr colorAttr glDrawArrays GL_TRIANGLES; 0; 36 glUseProgram 0 catch. echo 'error in rot_cube_paint',LF,13!:12'' wd'ptimer 0' end. }} NB. oriented triangle representation of unit cube unitCube=: #:(0 1 2, 2 1 3)&{@".;._2 {{)n 2 3 0 1 NB. unit cube corner indices 3 7 1 5 NB. 0: origin 4 0 5 1 NB. 1, 2, 4: unit distance along each axis 6 2 4 0 NB. 3, 5, 6, 7: combinations of axes 7 6 5 4 7 3 6 2 }} NB. orient cube so diagonal is along first axis daxis=: (_1^5 6 e.~i.3 3)*%:6%~2 0 4,2 3 1,:2 3 1 vertexData=:(_1^unitCube)mp daxis NB. cube with center at origin colorData=: unitCube NB. corresponding colors rotcube''

A variation which did not use opengl would probably be much more concise.

Java[edit]

import java.awt.*; import java.awt.event.ActionEvent; import static java.lang.Math.*; import javax.swing.*; public class RotatingCube extends JPanel { double[][] nodes = {{-1, -1, -1}, {-1, -1, 1}, {-1, 1, -1}, {-1, 1, 1}, {1, -1, -1}, {1, -1, 1}, {1, 1, -1}, {1, 1, 1}}; int[][] edges = {{0, 1}, {1, 3}, {3, 2}, {2, 0}, {4, 5}, {5, 7}, {7, 6}, {6, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; public RotatingCube() { setPreferredSize(new Dimension(640, 640)); setBackground(Color.white); scale(100); rotateCube(PI / 4, atan(sqrt(2))); new Timer(17, (ActionEvent e) -> { rotateCube(PI / 180, 0); repaint(); }).start(); } final void scale(double s) { for (double[] node : nodes) { node[0] *= s; node[1] *= s; node[2] *= s; } } final void rotateCube(double angleX, double angleY) { double sinX = sin(angleX); double cosX = cos(angleX); double sinY = sin(angleY); double cosY = cos(angleY); for (double[] node : nodes) { double x = node[0]; double y = node[1]; double z = node[2]; node[0] = x * cosX - z * sinX; node[2] = z * cosX + x * sinX; z = node[2]; node[1] = y * cosY - z * sinY; node[2] = z * cosY + y * sinY; } } void drawCube(Graphics2D g) { g.translate(getWidth() / 2, getHeight() / 2); for (int[] edge : edges) { double[] xy1 = nodes[edge[0]]; double[] xy2 = nodes[edge[1]]; g.drawLine((int) round(xy1[0]), (int) round(xy1[1]), (int) round(xy2[0]), (int) round(xy2[1])); } for (double[] node : nodes) g.fillOval((int) round(node[0]) - 4, (int) round(node[1]) - 4, 8, 8); } @Override public void paintComponent(Graphics gg) { super.paintComponent(gg); Graphics2D g = (Graphics2D) gg; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawCube(g); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setTitle("Rotating Cube"); f.setResizable(false); f.add(new RotatingCube(), BorderLayout.CENTER); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); }); } }

JavaScript[edit]

Translation of: Java

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> canvas { background-color: black; } </style> </head> <body> <canvas></canvas> <script> var canvas = document.querySelector("canvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var g = canvas.getContext("2d"); var nodes = [[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1], [1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]]; var edges = [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]]; function scale(factor0, factor1, factor2) { nodes.forEach(function (node) { node[0] *= factor0; node[1] *= factor1; node[2] *= factor2; }); } function rotateCuboid(angleX, angleY) { var sinX = Math.sin(angleX); var cosX = Math.cos(angleX); var sinY = Math.sin(angleY); var cosY = Math.cos(angleY); nodes.forEach(function (node) { var x = node[0]; var y = node[1]; var z = node[2]; node[0] = x * cosX - z * sinX; node[2] = z * cosX + x * sinX; z = node[2]; node[1] = y * cosY - z * sinY; node[2] = z * cosY + y * sinY; }); } function drawCuboid() { g.save(); g.clearRect(0, 0, canvas.width, canvas.height); g.translate(canvas.width / 2, canvas.height / 2); g.strokeStyle = "#FFFFFF"; g.beginPath(); edges.forEach(function (edge) { var p1 = nodes[edge[0]]; var p2 = nodes[edge[1]]; g.moveTo(p1[0], p1[1]); g.lineTo(p2[0], p2[1]); }); g.closePath(); g.stroke(); g.restore(); } scale(200, 200, 200); rotateCuboid(Math.PI / 4, Math.atan(Math.sqrt(2))); setInterval(function() { rotateCuboid(Math.PI / 180, 0); drawCuboid(); }, 17); </script> </body> </html>

Julia[edit]

Run at the Julia REPL command line.

using Makie, LinearAlgebra N = 40 interval = 0.10 scene = mesh(FRect3D(Vec3f0(-0.5), Vec3f0(1)), color = :skyblue2) rect = scene[end] for rad in 0.5:1/N:8.5 arr = normalize([cospi(rad/2), 0, sinpi(rad/2), -sinpi(rad/2)]) Makie.rotate!(rect, Quaternionf0(arr[1], arr[2], arr[3], arr[4])) sleep(interval) end

Kotlin[edit]

Translation of: Java

// version 1.1 import java.awt.* import javax.swing.* class RotatingCube : JPanel() { private val nodes = arrayOf( doubleArrayOf(-1.0, -1.0, -1.0), doubleArrayOf(-1.0, -1.0, 1.0), doubleArrayOf(-1.0, 1.0, -1.0), doubleArrayOf(-1.0, 1.0, 1.0), doubleArrayOf( 1.0, -1.0, -1.0), doubleArrayOf( 1.0, -1.0, 1.0), doubleArrayOf( 1.0, 1.0, -1.0), doubleArrayOf( 1.0, 1.0, 1.0) ) private val edges = arrayOf( intArrayOf(0, 1), intArrayOf(1, 3), intArrayOf(3, 2), intArrayOf(2, 0), intArrayOf(4, 5), intArrayOf(5, 7), intArrayOf(7, 6), intArrayOf(6, 4), intArrayOf(0, 4), intArrayOf(1, 5), intArrayOf(2, 6), intArrayOf(3, 7) ) init { preferredSize = Dimension(640, 640) background = Color.white scale(100.0) rotateCube(Math.PI / 4.0, Math.atan(Math.sqrt(2.0))) Timer(17) { rotateCube(Math.PI / 180.0, 0.0) repaint() }.start() } private fun scale(s: Double) { for (node in nodes) { node[0] *= s node[1] *= s node[2] *= s } } private fun rotateCube(angleX: Double, angleY: Double) { val sinX = Math.sin(angleX) val cosX = Math.cos(angleX) val sinY = Math.sin(angleY) val cosY = Math.cos(angleY) for (node in nodes) { val x = node[0] val y = node[1] var z = node[2] node[0] = x * cosX - z * sinX node[2] = z * cosX + x * sinX z = node[2] node[1] = y * cosY - z * sinY node[2] = z * cosY + y * sinY } } private fun drawCube(g: Graphics2D) { g.translate(width / 2, height / 2) for (edge in edges) { val xy1 = nodes[edge[0]] val xy2 = nodes[edge[1]] g.drawLine(Math.round(xy1[0]).toInt(), Math.round(xy1[1]).toInt(), Math.round(xy2[0]).toInt(), Math.round(xy2[1]).toInt()) } for (node in nodes) { g.fillOval(Math.round(node[0]).toInt() - 4, Math.round(node[1]).toInt() - 4, 8, 8) } } override public fun paintComponent(gg: Graphics) { super.paintComponent(gg) val g = gg as Graphics2D g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) g.color = Color.blue drawCube(g) } } fun main(args: Array<String>) { SwingUtilities.invokeLater { val f = JFrame() f.defaultCloseOperation = JFrame.EXIT_ON_CLOSE f.title = "Rotating cube" f.isResizable = false f.add(RotatingCube(), BorderLayout.CENTER) f.pack() f.setLocationRelativeTo(null) f.isVisible = true } }

Lua[edit]

local abs,atan,cos,floor,pi,sin,sqrt = math.abs,math.atan,math.cos,math.floor,math.pi,math.sin,math.sqrt local bitmap = { init = function(self, w, h, value) self.w, self.h, self.pixels = w, h, {} for y=1,h do self.pixels[y]={} end self:clear(value) end, clear = function(self, value) for y=1,self.h do for x=1,self.w do self.pixels[y][x] = value or " " end end end, set = function(self, x, y, value) x,y = floor(x),floor(y) if x>0 and y>0 and x<=self.w and y<=self.h then self.pixels[y][x] = value or "#" end end, line = function(self, x1, y1, x2, y2, c) x1,y1,x2,y2 = floor(x1),floor(y1),floor(x2),floor(y2) local dx, sx = abs(x2-x1), x1<x2 and 1 or -1 local dy, sy = abs(y2-y1), y1<y2 and 1 or -1 local err = floor((dx>dy and dx or -dy)/2) while(true) do self:set(x1, y1, c) if (x1==x2 and y1==y2) then break end if (err > -dx) then err, x1 = err-dy, x1+sx if (x1==x2 and y1==y2) then self:set(x1, y1, c) break end end if (err < dy) then err, y1 = err+dx, y1+sy end end end, render = function(self) for y=1,self.h do print(table.concat(self.pixels[y])) end end, } screen = { clear = function() os.execute("cls") -- or? os.execute("clear"), or? io.write("\027[2J\027[H"), or etc? end, } local camera = { fl = 2.5 } local L = 0.5 local cube = { verts = { {L,L,L}, {L,-L,L}, {-L,-L,L}, {-L,L,L}, {L,L,-L}, {L,-L,-L}, {-L,-L,-L}, {-L,L,-L} }, edges = { {1,2}, {2,3}, {3,4}, {4,1}, {5,6}, {6,7}, {7,8}, {8,5}, {1,5}, {2,6}, {3,7}, {4,8} }, rotate = function(self, rx, ry) local cx,sx = cos(rx),sin(rx) local cy,sy = cos(ry),sin(ry) for i,v in ipairs(self.verts) do local x,y,z = v[1],v[2],v[3] v[1], v[2], v[3] = x*cx-z*sx, y*cy-x*sx*sy-z*cx*sy, x*sx*cy+y*sy+z*cx*cy end end, } local renderer = { render = function(self, shape, camera, bitmap) local fl = camera.fl local ox, oy = bitmap.w/2, bitmap.h/2 local mx, my = bitmap.w/2, bitmap.h/2 local rpverts = {} for i,v in ipairs(shape.verts) do local x,y,z = v[1],v[2],v[3] local px = ox + mx * (fl*x)/(fl-z) local py = oy + my * (fl*y)/(fl-z) rpverts[i] = { px,py } end for i,e in ipairs(shape.edges) do local v1, v2 = rpverts[e[1]], rpverts[e[2]] bitmap:line( v1[1], v1[2], v2[1], v2[2], "██" ) end end } -- bitmap:init(40,40) cube:rotate(pi/4, atan(sqrt(2))) for i=1,60 do cube:rotate(pi/60,0) bitmap:clear("··") renderer:render(cube, camera, bitmap) screen:clear() bitmap:render() end

Frame 1: ················································································ ······································██········································ ····································██████······································ ··································████····██···································· ································██··██······██·································· ······························██····██········██································ ····························██······██··········██······························ ····························██······██············██···························· ··························██······██················██·························· ························██········██··················██························ ······················██········████····················██······················ ····················██········██····██····················██···················· ··················██········██········██····················██·················· ················██········██············██····················██················ ··············██········██················██················████················ ··············████····██····················██············██····██·············· ············██····████························██········██······██·············· ··········██······████··························██····██········██·············· ··········██····██····████························████············██············ ········██····██··········██······················████············██············ ········██··██··············██················████····██··········██············ ······██··██··················████··········██··········██··········██·········· ······████························██······██··············████······██·········· ····████····························██████····················██····██·········· ····██································██························██··██·········· ··██··································██··························██··██········ ····██································██····························████········ ······████····························██························████············ ··········██··························██······················██················ ············████······················██··················████·················· ················████··················██··············████······················ ····················██················██············██·························· ······················████············██········████···························· ··························██··········██······██································ ····························████······██··████·································· ································████··████······································ ····································████········································ ················································································ ················································································ ················································································ Frame 60: ················································································ ······································██········································ ····································██████······································ ··································██··██··██···································· ······························████····██····████································ ····························██········██········██······························ ························████··········██··········████·························· ····················████··············██··············██························ ··················██··················██················██······················ ··············████····················██··················████·················· ············██························██······················██················ ········████··························██························████············ ····████······························██····························████········ ····████······························██····························████········ ······████····························████························████·········· ······██··██························██····██····················██··██·········· ········██··██····················██········████··············██····██·········· ········██····██··············████··············██··········██····██············ ········██······██··········██····················██······██······██············ ··········██······██······██························██████········██············ ··········██········██████····························████······██·············· ··········██········████····························██····██····██·············· ············██··████····██························██········████················ ············████··········████··················██············██················ ··············██··············██··············██············██·················· ················██··············██··········██············██···················· ··················██··············██······██············██······················ ····················██··············██████············██························ ······················██··············██············██·························· ························██············██············██·························· ··························██··········██··········██···························· ····························██········██········██······························ ······························██······██······██································ ································██····██····██·································· ··································██··██··██···································· ····································██████······································ ······································██········································ ················································································ ················································································ ················································································

Maple[edit]

plots:-display( seq( plots:-display( plottools[cuboid]( [0,0,0], [1,1,1] ), axes=none, scaling=constrained, orientation=[0,45,i] ), i = 0..360, 20 ), insequence=true );

Mathematica/Wolfram Language[edit]

Dynamic[ Graphics3D[ GeometricTransformation[ GeometricTransformation[Cuboid[], RotationTransform[Pi/4, {1, 1, 0}]], RotationTransform[Clock[2 Pi], {0, 0, 1}] ], Boxed -> False]]

Nim[edit]

Translation of: Ada

import math import sdl2 const Width = 500 Height = 500 Offset = 500 / 2 var nodes = [(x: -100.0, y: -100.0, z: -100.0), (x: -100.0, y: -100.0, z: 100.0), (x: -100.0, y: 100.0, z: -100.0), (x: -100.0, y: 100.0, z: 100.0), (x: 100.0, y: -100.0, z: -100.0), (x: 100.0, y: -100.0, z: 100.0), (x: 100.0, y: 100.0, z: -100.0), (x: 100.0, y: 100.0, z: 100.0)] const Edges = [(a: 0, b: 1), (a: 1, b: 3), (a: 3, b: 2), (a: 2, b: 0), (a: 4, b: 5), (a: 5, b: 7), (a: 7, b: 6), (a: 6, b: 4), (a: 0, b: 4), (a: 1, b: 5), (a: 2, b: 6), (a: 3, b: 7)] var window: WindowPtr renderer: RendererPtr event: Event endSimulation = false #--------------------------------------------------------------------------------------------------- proc rotateCube(angleX, angleY: float) = let sinX = sin(angleX) cosX = cos(angleX) sinY = sin(angleY) cosY = cos(angleY) for node in nodes.mitems: var (x, y, z) = node 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 #--------------------------------------------------------------------------------------------------- proc pollQuit(): bool = while pollEvent(event): if event.kind == QuitEvent: return true #--------------------------------------------------------------------------------------------------- proc drawCube(): bool = var rect: Rect = (cint(0), cint(0), cint(Width), cint(Height)) rotateCube(PI / 4, arctan(sqrt(2.0))) for frame in 0..359: renderer.setDrawColor((0u8, 0u8, 0u8, 255u8)) renderer.fillRect(addr(rect)) renderer.setDrawColor((0u8, 220u8, 0u8, 255u8)) for edge in Edges: let xy1 = nodes[edge.a] let xy2 = nodes[edge.b] renderer.drawLine(cint(xy1.x + Offset), cint(xy1.y + Offset), cint(xy2.x + Offset), cint(xy2.y + Offset)) rotateCube(PI / 180, 0) renderer.present() if pollQuit(): return true delay 10 #——————————————————————————————————————————————————————————————————————————————————————————————————— if sdl2.init(INIT_EVERYTHING) == SdlError: quit(QuitFailure) window = createWindow("Rotating cube", 10, 10, 500, 500, 0) renderer = createRenderer(window, -1, Renderer_Accelerated) while not endSimulation: endSimulation = drawCube() window.destroy()

Objeck[edit]

Translation of: Ada

#~ Rotating Cube ~# use Collection.Generic; use Game.SDL2; use Game.Framework; class RotatingCube { # game framework @framework : GameFramework; @initialized : Bool; @nodes : Float[,]; @edges : Int[,]; New() { @initialized := true; @framework := GameFramework->New(GameConsts->SCREEN_WIDTH, GameConsts->SCREEN_HEIGHT, "Rotating Cube"); @nodes := [[-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 := [[0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7]]; } function : Main(args : String[]) ~ Nil { RotatingCube->New()->Play(); } method : Play() ~ Nil { if(@initialized) { # initialization @framework->SetClearColor(Color->New(0, 0, 0)); RotateCube(Float->Pi(), 2.0->SquareRoot()->ArcTan()); quit := false; e := @framework->GetEvent(); while(<>quit) { @framework->FrameStart(); @framework->Clear(); # process input while(e->Poll() <> 0) { if(e->GetType() = EventType->SDL_QUIT) { quit := true; }; }; #draw DrawCube(); @framework->FrameEnd(); # render @framework->Show(); Timer->Delay(200); RotateCube (Float->Pi() / 180.0, 0.0); }; } else { "--- Error Initializing Environment ---"->ErrorLine(); return; }; leaving { @framework->FreeShapes(); }; } method : RotateCube(angleX : Float, angleY : Float) ~ Nil { sinX := angleX->Sin(); cosX := angleX->Cos(); sinY := angleY->Sin(); cosY := angleY->Cos(); node_sizes := @nodes->Size(); size := node_sizes[0]; for(i := 0; i < size; i += 1;) { x := @nodes[i, 0]; y := @nodes[i, 1]; z := @nodes[i, 2]; @nodes[i, 0] := x * cosX - z * sinX; @nodes[i, 2] := z * cosX + x * sinX; z := @nodes[i, 2]; @nodes[i, 1] := y * cosY - z * sinY; @nodes[i, 2] := z * cosY + y * sinY; }; } method : DrawCube() ~ Nil { edge_sizes := @edges->Size(); size := edge_sizes[0]; @framework->GetRenderer()->SetDrawColor(0, 220, 0, 0); for(i := 0; i < size; i += 1;) { x0y0  := @nodes[@edges[i, 0], 0]; x0y1  := @nodes[@edges[i, 0], 1]; x1y0  := @nodes[@edges[i, 1], 0]; x1y1  := @nodes[@edges[i, 1], 1]; @framework->GetRenderer()->DrawLine(x0y0 + GameConsts->DRAW_OFFSET, x0y1 + GameConsts->DRAW_OFFSET, x1y0 + GameConsts->DRAW_OFFSET, x1y1 + GameConsts->DRAW_OFFSET); }; } } consts GameConsts { SCREEN_WIDTH := 600, SCREEN_HEIGHT := 600, DRAW_OFFSET := 300 }

OxygenBasic[edit]

Using An OpenGl-based console

 % Title "Rotating Cube"  % Animated  % PlaceCentral uses ConsoleG sub main ======== cls 0.0, 0.5, 0.7 shading scale 7 pushstate GoldMaterial.act static float ang rotateX ang rotateY ang go cube popstate ang+=.5 : if ang>=360 then ang-=360 end sub EndScript

Perl[edit]

#!/usr/bin/perl use strict; # //www.rosettacode.org/wiki/Draw_a_rotating_cube use warnings; use Tk; use Time::HiRes qw( time ); my $size = 600; my $wait = int 1000 / 30; my ($height, $width) = ($size, $size * sqrt 8/9); my $mid = $width / 2; my $rot = atan2(0, -1) / 3; # middle corners every 60 degrees my $mw = MainWindow->new; my $c = $mw->Canvas(-width => $width, -height => $height)->pack; $c->Tk::bind('<ButtonRelease>' => sub {$mw->destroy}); # click to exit draw(); MainLoop; sub draw { my $angle = time - $^T; # full rotation every 2*PI seconds my @points = map { $mid + $mid * cos $angle + $_ * $rot, $height * ($_ % 2 + 1) / 3 } 0 .. 5; $c->delete('all'); $c->createLine( @points[-12 .. 1], $mid, 0, -width => 5,); $c->createLine( @points[4, 5], $mid, 0, @points[8, 9], -width => 5,); $c->createLine( @points[2, 3], $mid, $height, @points[6, 7], -width => 5,); $c->createLine( $mid, $height, @points[10, 11], -width => 5,); $mw->after($wait, \&draw); }

Phix[edit]

You can run this online here.

-- -- demo\rosetta\DrawRotatingCube.exw -- ================================= -- -- credits: //petercollingridge.appspot.com/3D-tutorial/rotating-objects -- //github.com/ssloy/tinyrenderer/wiki/Lesson-4:-Perspective-projection -- -- Aside: low CPU usage, at least when using a 30ms timer (33 FPS, which is plenty). -- with javascript_semantics include pGUI.e constant title = "Draw a Rotating Cube" Ihandle dlg, canvas cdCanvas cd_canvas -- -- First, define 8 corners equidistant from {0,0,0}: -- -- 6-----2 -- 5-----1 3 -- 8-----4 -- -- ie the right face is 1-2-3-4 clockwise, and the left face -- is 5-6-7-8 counter-clockwise (unless using x-ray vision). -- (since this is not drawing textures, clockwise-ness does -- not matter, as shown by the corrected orange face, but -- it will if you (figure out how to) apply any textures.) -- (a quick (online) study of opengl texture documentation -- should convince you that stuff is best left to opengl.) -- enum X, Y, Z constant l = 100 constant corners = {{+l,+l,+l}, -- 1 (front top right) {+l,+l,-l}, -- 2 (back top "right") {+l,-l,-l}, -- 3 (back btm "right") {+l,-l,+l}, -- 4 (front btm right) {-l,+l,+l}, -- 5 (front top left) {-l,+l,-l}, -- 6 (back top "left") {-l,-l,-l}, -- 7 (back btm "left") {-l,-l,+l}} -- 8 (front btm left) -- I put left/right in quotes for the back face as a reminder -- those match the above diagram, but of course they would be -- swapped were you looking "at" the face/rotated it by 180. constant faces = {{CD_RED, 1,2,3,4}, -- right {CD_YELLOW, 1,5,6,2}, -- top {CD_DARK_GREEN, 1,4,8,5}, -- front {CD_BLUE, 2,3,7,6}, -- back {CD_WHITE, 3,4,8,7}, -- bottom -- {CD_ORANGE, 5,6,7,8}} -- left {CD_ORANGE, 8,7,6,5}} -- left -- rotation angles, 0..359, on a timer atom rx = 45, -- initially makes cube like a H ry = 35, -- " " " italic H rz = 0 constant naxes = {{Y,Z}, -- (rotate about the X-axis) {X,Z}, -- (rotate about the Y-axis) {X,Y}} -- (rotate about the Z-axis) function rotate(sequence points, atom angle, integer axis) -- -- rotate points by the specified angle about the given axis -- atom radians = angle*CD_DEG2RAD, sin_t = sin(radians), cos_t = cos(radians) integer {nx,ny} = naxes[axis] for i=1 to length(points) do atom x = points[i][nx], y = points[i][ny] points[i][nx] = x*cos_t - y*sin_t points[i][ny] = y*cos_t + x*sin_t end for return points end function function projection(sequence points, atom d) -- -- project points from {0,0,d} onto the perpendicular plane through {0,0,0} -- for i=1 to length(points) do atom {x,y,z} = points[i], denom = (1-z/d) points[i][X] = x/denom points[i][Y] = y/denom end for return points end function function nearest(sequence points) -- -- return the index of the nearest point (highest z value) -- return largest(vslice(points,Z),true) end function procedure draw_cube(integer cx, cy) -- {cx,cy} is the centre point of the canvas sequence points = deep_copy(corners) points = rotate(points,rx,X) points = rotate(points,ry,Y) points = rotate(points,rz,Z) points = projection(points,1000) integer np = nearest(points) -- -- find the three faces that contain the nearest point, -- then for each of those faces let diag be the point -- that is diagonally opposite said nearest point, and -- order by/draw those faces furthest diag away first. -- (one or two of them may be completely obscured due -- to the effects of the perspective projection.) -- (you could of course draw all six faces, as long as -- the 3 furthest are draw first/obliterated, which -- is what that commented-out "else" would achieve.) -- sequence faceset = {} for i=1 to length(faces) do sequence fi = faces[i] integer k = find(np,fi) -- k:=2..5, or 0 if k then integer diag = mod(k,4)+2 -- {2,3,4,5} --> {4,5,2,3} -- aka swap 2<=>4 & 3<=>5 diag = fi[diag] -- 1..8, diagonally opp. np faceset = append(faceset,{points[diag][Z],i}) -- else -- faceset = append(faceset,{-9999,i}) end if end for faceset = sort(faceset) for i=1 to length(faceset) do sequence face = faces[faceset[i][2]] cdCanvasSetForeground(cd_canvas,face[1]) -- first fill sides (with bresenham edges), then -- redraw edges, but anti-aliased aka smoother sequence modes = {CD_FILL,CD_CLOSED_LINES} for m=1 to length(modes) do cdCanvasBegin(cd_canvas,modes[m]) for fdx=2 to 5 do sequence pt = points[face[fdx]] cdCanvasVertex(cd_canvas,cx+pt[X],cy-pt[Y]) end for cdCanvasEnd(cd_canvas) end for end for end procedure function canvas_action_cb(Ihandle canvas) cdCanvasActivate(cd_canvas) cdCanvasClear(cd_canvas) integer {w, h} = IupGetIntInt(canvas, "DRAWSIZE") draw_cube(floor(w/2),floor(h/2)) cdCanvasFlush(cd_canvas) return IUP_DEFAULT end function function canvas_map_cb(Ihandle canvas) IupGLMakeCurrent(canvas) if platform()=JS then cd_canvas = cdCreateCanvas(CD_IUP, canvas) else atom res = IupGetDouble(NULL, "SCREENDPI")/25.4 cd_canvas = cdCreateCanvas(CD_GL, "10x10 %g", {res}) end if cdCanvasSetBackground(cd_canvas, CD_PARCHMENT) return IUP_DEFAULT end function function canvas_resize_cb(Ihandle /*canvas*/) integer {canvas_width, canvas_height} = IupGetIntInt(canvas, "DRAWSIZE") atom res = IupGetDouble(NULL, "SCREENDPI")/25.4 cdCanvasSetAttribute(cd_canvas, "SIZE", "%dx%d %g", {canvas_width, canvas_height, res}) return IUP_DEFAULT end function function timer_cb(Ihandln /*ih*/) -- (feel free to add a bit more randomness here, maybe) rx = mod(rx+359,360) ry = mod(ry+359,360) rz = mod(rz+359,360) IupRedraw(canvas) return IUP_IGNORE end function procedure main() IupOpen() canvas = IupGLCanvas("RASTERSIZE=640x480") IupSetCallbacks(canvas, {"ACTION", Icallback("canvas_action_cb"), "MAP_CB", Icallback("canvas_map_cb"), "RESIZE_CB", Icallback("canvas_resize_cb")}) dlg = IupDialog(canvas,`TITLE="%s"`,{title}) IupShow(dlg) IupSetAttribute(canvas, "RASTERSIZE", NULL) Ihandle hTimer = IupTimer(Icallback("timer_cb"), 30) if platform()!=JS then IupMainLoop() IupClose() end if end procedure main()

PostScript[edit]

Don't send this to your printer!

%!PS-Adobe-3.0 %%BoundingBox: 0 0 400 400 /ed { exch def } def /roty { dup sin /s ed cos /c ed [[c 0 s neg] [0 1 0] [s 0 c]] } def /rotz { dup sin /s ed cos /c ed [[c s neg 0] [s c 0] [0 0 1]] } def /dot { /a ed /b ed a 0 get b 0 get mul a 1 get b 1 get mul a 2 get b 2 get mul add add } def /mmul { /v ed [exch {v dot} forall] } def /transall { /m ed [exch {m exch mmul}forall] } def /vt [[1 1 1] [-1 1 1] [1 -1 1] [-1 -1 1] [1 1 -1] [-1 1 -1] [1 -1 -1] [-1 -1 -1]] -45 roty transall 2 sqrt 1 atan rotz transall def /xy { exch get {} forall pop } def /page { /a ed /v vt a roty transall def 0 setlinewidth 100 100 scale 2 2 translate /edge { v xy moveto v xy lineto stroke } def 0 1 2 3 4 5 6 7 0 2 1 3 4 6 5 7 0 4 1 5 2 6 3 7 1 1 12 { pop edge } for showpage } def 0 {3.2 add dup page } loop %%EOF

Processing[edit]

Create a cube in Processing with box(), rotate the scene with rotate(), and drive the rotation with either the built-in millis() or frameCount timers.

void setup() { size(500, 500, P3D); } void draw() { background(0); // position translate(width/2, height/2, -width/2); // optional fill and lighting colors noStroke(); strokeWeight(4); fill(192, 255, 192); pointLight(255, 255, 255, 0, -500, 500); // rotation driven by built-in timer rotateY(millis()/1000.0); // draw box box(300, 300, 300); }

Python[edit]

[edit]

Works with: Python version 2.7.9

See also: Draw_a_cuboid

Short version[edit]

from visual import * scene.title = "VPython: Draw a rotating cube" scene.range = 2 scene.autocenter = True print "Drag with right mousebutton to rotate view." print "Drag up+down with middle mousebutton to zoom." deg45 = math.radians(45.0) # 0.785398163397 cube = box() # using defaults, see //www.vpython.org/contents/docs/defaults.html cube.rotate( angle=deg45, axis=(1,0,0) ) cube.rotate( angle=deg45, axis=(0,0,1) ) while True: # Animation-loop rate(50) cube.rotate( angle=0.005, axis=(0,1,0) )

Racket[edit]

#lang racket/gui (require math/matrix math/array) (define (Rx θ) (matrix [[1.0 0.0 0.0] [0.0 (cos θ) (- (sin θ))] [0.0 (sin θ) (cos θ)]])) (define (Ry θ) (matrix [[ (cos θ) 0.0 (sin θ)] [ 0.0 1.0 0.0 ] [(- (sin θ)) 0.0 (cos θ)]])) (define (Rz θ) (matrix [[(cos θ) (- (sin θ)) 0.0] [(sin θ) (cos θ) 0.0] [ 0.0 0.0 1.0]])) (define base-matrix (matrix* (identity-matrix 3 100.0) (Rx (- (/ pi 2) (atan (sqrt 2)))) (Rz (/ pi 4.0)))) (define (current-matrix) (matrix* (Ry (/ (current-inexact-milliseconds) 1000.)) base-matrix)) (define corners (for*/list ([x '(-1.0 1.0)] [y '(-1.0 1.0)] [z '(-1.0 1.0)]) (matrix [[x] [y] [z]]))) (define lines '((0 1) (0 2) (0 4) (1 3) (1 5) (2 3) (2 6) (3 7) (4 5) (4 6) (5 7) (6 7))) (define ox 200.) (define oy 200.) (define (draw-line dc a b) (send dc draw-line (+ ox (array-ref a #(0 0))) (+ oy (array-ref a #(1 0))) (+ ox (array-ref b #(0 0))) (+ oy (array-ref b #(1 0))))) (define (draw-cube c dc) (define-values (w h) (send dc get-size)) (set! ox (/ w 2)) (set! oy (/ h 2)) (define cs (for/vector ([c (in-list corners)]) (matrix* (current-matrix) c))) (for ([l (in-list lines)]) (match-define (list i j) l) (draw-line dc (vector-ref cs i) (vector-ref cs j)))) (define f (new frame% [label "cube"])) (define c (new canvas% [parent f] [min-width 400] [min-height 400] [paint-callback draw-cube])) (send f show #t) (send* (send c get-dc) (set-pen "black" 1 'solid) (set-smoothing 'smoothed)) (define (refresh) (send c refresh)) (define t (new timer% [notify-callback refresh] [interval 35] [just-once? #f]))

Raku[edit]

(formerly Perl 6)

Works with: Rakudo version 2018.12

Raku has no native graphics libraries built in, but makes it fairly easy to bind to third party libraries. Here we'll use bindings to Libcaca, the Color ASCII Art library to generate a rotating cube in an ASCII terminal.

use Terminal::Caca; given my $canvas = Terminal::Caca.new { .title('Rosetta Code - Rotating cube - Press any key to exit'); sub scale-and-translate($x, $y, $z) { $x * 5 / ( 5 + $z ) * 15 + 40, $y * 5 / ( 5 + $z ) * 7 + 15, $z; } sub rotate3d-x( $x, $y, $z, $angle ) { my ($cosθ, $sinθ) = cis( $angle * π / 180.0 ).reals; $x, $y * $cosθ - $z * $sinθ, $y * $sinθ + $z * $cosθ; } sub rotate3d-y( $x, $y, $z, $angle ) { my ($cosθ, $sinθ) = cis( $angle * π / 180.0 ).reals; $x * $cosθ - $z * $sinθ, $y, $x * $sinθ + $z * $cosθ; } sub rotate3d-z( $x, $y, $z, $angle ) { my ($cosθ, $sinθ) = cis( $angle * π / 180.0 ).reals; $x * $cosθ - $y * $sinθ, $x * $cosθ + $y * $sinθ, $z; } # Unit cube from polygon mesh, aligned to axes my @mesh = [ [1, 1, -1], [-1, -1, -1], [-1, 1, -1] ], # far face [ [1, 1, -1], [-1, -1, -1], [ 1, -1, -1] ], [ [1, 1, 1], [-1, -1, 1], [-1, 1, 1] ], # near face [ [1, 1, 1], [-1, -1, 1], [ 1, -1, 1] ]; @mesh.push: [$_».rotate( 1)».Array] for @mesh[^4]; # positive and @mesh.push: [$_».rotate(-1)».Array] for @mesh[^4]; # negative rotations # Rotate to correct orientation for task for ^@mesh X ^@mesh[0] -> ($i, $j) { @(@mesh[$i;$j]) = rotate3d-x |@mesh[$i;$j], 45; @(@mesh[$i;$j]) = rotate3d-z |@mesh[$i;$j], 40; } my @colors = red, blue, green, cyan, magenta, yellow; loop { for ^359 -> $angle { .color( white, white ); .clear; # Flatten 3D into 2D and rotate for all faces my @faces-z; my $c-index = 0; for @mesh -> @triangle { my @points; my $sum-z = 0; for @triangle -> @node { my ($px, $py, $z) = scale-and-translate |rotate3d-y |@node, $angle; @points.append: $px.Int, $py.Int; $sum-z += $z; } @faces-z.push: %( color => @colors[$c-index++ div 2], points => @points, avg-z => $sum-z / +@points; ); } # Draw all faces # Sort by z to draw farthest first for @faces-z.sort( -*.<avg-z> ) -> %face { # Draw filled triangle .color( %face<color>, %face<color> ); .fill-triangle( |%face<points> ); # And frame .color( black, black ); .thin-triangle( |%face<points> ); } .refresh; exit if .wait-for-event(key-press); } } # Cleanup on scope exit LEAVE { .cleanup; } }

Ring[edit]

#===================================================================# # Based on Original Sample from RayLib (//www.raylib.com/) # Ported to RingRayLib by Ring Team #===================================================================# load "raylib.ring" screenWidth = 800 screenHeight = 450 InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking") camera = Camera3D( 10, 10, 10, 0, 0, 0 , 0, 1, 0 , 45, CAMERA_PERSPECTIVE ) cubePosition = Vector3( 0, 1, 0 ) cubeSize = Vector3( 2, 2, 2 ) ray = Ray(0,0,0,0,0,0) collision = false SetCameraMode(camera, CAMERA_FREE) SetTargetFPS(60) while !WindowShouldClose() UpdateCamera(camera) if IsMouseButtonPressed(MOUSE_LEFT_BUTTON) if !collision ray = GetMouseRay(GetMousePosition(), camera) collision = CheckCollisionRayBox(ray, BoundingBox( cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2, cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2 ) ) else collision = false ok ok BeginDrawing() ClearBackground(RAYWHITE) BeginMode3D(camera) if collision DrawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, RED) DrawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, MAROON) DrawCubeWires(cubePosition, cubeSize.x + 0.2f, cubeSize.y + 0.2f, cubeSize.z + 0.2f, GREEN) else DrawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, GRAY) DrawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, DARKGRAY) ok DrawRay(ray, MAROON) DrawGrid(10, 1) EndMode3D() DrawText("Try selecting the box with mouse!", 240, 10, 20, DARKGRAY) if collision DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30)) / 2, screenHeight * 0.1f, 30, GREEN) ok DrawFPS(10, 10) EndDrawing() end CloseWindow()

Rotating a Cube

Scala[edit]

Java Swing Interoperability[edit]

Works with: Scala version 2.13

import java.awt.event.ActionEvent import java.awt._ import javax.swing.{JFrame, JPanel, Timer} import scala.math.{Pi, atan, cos, sin, sqrt} object RotatingCube extends App { class RotatingCube extends JPanel { private val vertices: Vector[Array[Double]] = Vector(Array(-1, -1, -1), Array(-1, -1, 1), Array(-1, 1, -1), Array(-1, 1, 1), Array(1, -1, -1), Array(1, -1, 1), Array(1, 1, -1), Array(1, 1, 1)) private val edges: Vector[(Int, Int)] = Vector((0, 1), (1, 3), (3, 2), (2, 0), (4, 5), (5, 7), (7, 6), (6, 4), (0, 4), (1, 5), (2, 6), (3, 7)) setPreferredSize(new Dimension(640, 640)) setBackground(Color.white) scale(100) rotateCube(Pi / 4, atan(sqrt(2))) new Timer(17, (_: ActionEvent) => { rotateCube(Pi / 180, 0) repaint() }).start() override def paintComponent(gg: Graphics): Unit = { def drawCube(g: Graphics2D): Unit = { g.translate(getWidth / 2, getHeight / 2) for {edge <- edges xy1: Array[Double] = vertices(edge._1) xy2: Array[Double] = vertices(edge._2) } { g.drawLine(xy1(0).toInt, xy1(1).toInt, xy2(0).toInt, xy2(1).toInt) g.fillOval(xy1(0).toInt -4, xy1(1).toInt - 4, 8, 8) g.setColor(Color.black) } } super.paintComponent(gg) val g: Graphics2D = gg.asInstanceOf[Graphics2D] g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) drawCube(g) } private def scale(s: Double): Unit = { for {node <- vertices i <- node.indices } node(i) *= s } private def rotateCube(angleX: Double, angleY: Double): Unit = { def sinCos(x: Double) = (sin(x), cos(x)) val ((sinX, cosX), (sinY, cosY)) = (sinCos(angleX), sinCos(angleY)) for { node <- vertices x: Double = node(0) y: Double = node(1) } { def f(p: Double, q: Double)(a: Double, b: Double) = a * p + b * q def fx(a: Double, b: Double) = f(cosX, sinX)(a, b) def fy(a: Double, b: Double) = f(cosY, sinY)(a, b) node(0) = fx(x, -node(2)) val z = fx(node(2), x) node(1) = fy(y, -z) node(2) = fy(z, y) } } } new JFrame("Rotating Cube") { add(new RotatingCube(), BorderLayout.CENTER) pack() setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE) setLocationRelativeTo(null) setResizable(false) setVisible(true) } }

Tcl[edit]

See also Draw a cuboid. This implementation uses tcllib's Linear Algebra module for some matrix ops to handle the screen transform and (animated!) rotation. Rendering is in a Tk canvas.

The *Matrix* procedure is something unique to Tcl: it's essentially a control construct that leverages *expr* to make declaring matrices much more convenient than hand-rolling lists.

There is a bit of wander in the top and bottom points, which might just be due to rounding error in the cube's initial "rotation into position".

See this wiki page (and others linked from it) for many similar examples.

# matrix operation support: package require math::linearalgebra namespace import ::math::linearalgebra::matmul namespace import ::math::linearalgebra::crossproduct namespace import ::math::linearalgebra::dotproduct namespace import ::math::linearalgebra::sub # returns a cube as a list of faces, # where each face is a list of (3space) points proc make_cube {{radius 1}} { set dirs { A { 1 1 1} B { 1 1 -1} C { 1 -1 -1} D { 1 -1 1} E {-1 1 1} F {-1 1 -1} G {-1 -1 -1} H {-1 -1 1} } set faces { {A B C D} {D C G H} {H G F E} {E F B A} {A D H E} {C B F G} } lmap fa $faces { lmap dir $fa { lmap x [dict get $dirs $dir] { expr {1.0 * $x * $radius} } } } } # a matrix constructor proc Matrix {m} { tailcall lmap row $m { lmap e $row { expr 1.0*($e) } } } proc identity {} { Matrix { {1 0 0} {0 1 0} {0 0 1} } } # some matrices useful for animation: proc rotateZ {theta} { Matrix { { cos($theta) -sin($theta) 0 } { sin($theta) cos($theta) 0 } { 0 0 1 } } } proc rotateY {theta} { Matrix { { sin($theta) 0 cos($theta) } { 0 1 0 } { cos($theta) 0 -sin($theta) } } } proc rotateX {theta} { Matrix { { 1 0 0 } { 0 cos($theta) -sin($theta) } { 0 sin($theta) cos($theta) } } } proc camera {flen} { Matrix { { $flen 0 0 } { 0 $flen 0 } { 0 0 0 } } } proc render {canvas object} { set W [winfo width $canvas] set H [winfo height $canvas] set fl 1.0 set t [expr {[clock microseconds] / 1000000.0}] set transform [identity] set transform [matmul $transform [rotateX [expr {atan(1)}]]] set transform [matmul $transform [rotateZ [expr {atan(1)}]]] set transform [matmul $transform [rotateY $t]] set transform [matmul $transform [camera $fl]] foreach face $object { # do transformations into screen space: set points [lmap p $face { matmul $p $transform }] # calculate a normal set o [lindex $points 0] set v1 [sub [lindex $points 1] $o] set v2 [sub [lindex $points 2] $o] set normal [crossproduct $v1 $v2] set cosi [dotproduct $normal {0 0 -1.0}] if {$cosi <= 0} { ;# rear-facing! continue } set points [lmap p $points { lassign $p x y list [expr {$x + $W/2}] [expr {$y + $H/2}] }] set points [concat {*}$points] $canvas create poly $points -outline black -fill red } } package require Tk pack [canvas .c] -expand yes -fill both proc tick {} { .c delete all render .c $::world after 50 tick } set ::world [make_cube 100] tick

TI-83 BASIC[edit]

:-1→Xmin:1→Xmax :-1→Ymin:1→Ymax :AxesOff :Degrees :While 1 :For(X,0,359,5 :sin(X-120→I% :sin(X→PV :sin(X+120→FV :Line(0,1,I%,.3 :Line(0,1,PV,.3 :Line(0,1,FV,.3 :Line(0,-1,-I%,-.3 :Line(0,-1,-PV,-.3 :Line(0,-1,-FV,-.3 :Line(.3,I%,-.3,-PV :Line(.3,I%,-.3,-FV :Line(.3,PV,-.3,-I% :Line(.3,PV,-.3,-FV :Line(.3,FV,-.3,-I% :Line(.3,FV,-.3,-PV :End :End

I%, PV, and FV are all finance variables that can be found in the finance menu (inside the APPS menu on TI-83+ and up). Finance variables are much faster than normal variables.

Wren[edit]

import "graphics" for Canvas, Color import "dome" for Window import "math" for Math var Nodes = [ [-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1], [ 1, -1, -1], [ 1, -1, 1], [ 1, 1, -1], [ 1, 1, 1] ] var Edges = [ [0, 1], [1, 3], [3, 2], [2, 0], [4, 5], [5, 7], [7, 6], [6, 4], [0, 4], [1, 5], [2, 6], [3, 7] ] class RotatingCube { construct new(width, height) { Window.title = "Rotating cube" Window.resize(width, height) Canvas.resize(width, height) _width = width _height = height _fore = Color.blue } init() { scale(100) rotateCube(Num.pi / 4, Math.atan(2.sqrt)) drawCube() } update() { rotateCube(Num.pi / 180, 0) } draw(alpha) { drawCube() } scale(s) { for (node in Nodes) { node[0] = node[0] * s node[1] = node[1] * s node[2] = node[2] * s } } drawCube() { Canvas.cls(Color.white) Canvas.offset(_width / 2, _height / 2) for (edge in Edges) { var xy1 = Nodes[edge[0]] var xy2 = Nodes[edge[1]] Canvas.line(Math.round(xy1[0]), Math.round(xy1[1]), Math.round(xy2[0]), Math.round(xy2[1]), _fore) } for (node in Nodes) { Canvas.rectfill(Math.round(node[0]) - 4, Math.round(node[1]) - 4, 8, 8, _fore) } } rotateCube(angleX, angleY) { var sinX = Math.sin(angleX) var cosX = Math.cos(angleX) var sinY = Math.sin(angleY) var cosY = Math.cos(angleY) for (node in Nodes) { var x = node[0] var y = node[1] var z = node[2] node[0] = x * cosX - z * sinX node[2] = z * cosX + x * sinX z = node[2] node[1] = y * cosY - z * sinY node[2] = z * cosY + y * sinY } } } var Game = RotatingCube.new(640, 640)

XPL0[edit]

The main challenge was figuring out the initial coordinates of the cube. Zometool came to the rescue. The program runs much smoother than the animated gif.

def Size=100., Speed=0.05; \drawing size and rotation speed real X, Y, Z, Farthest; \arrays: 3D coordinates of vertices int I, J, K, ZI, Edge; def R2=sqrt(2.), R3=sqrt(3.), R13=sqrt(1./3.), R23=sqrt(2./3.), R232=R23*2.; \vertex:0 1 2 3 4 5 6 7 [X:= [ 0., R2, 0., -R2, 0., R2, 0., -R2]; Y:= [ -R3, -R13, R13, -R13, -R13, R13, R3, R13]; Z:= [ 0., -R23, -R232, -R23, R232, R23, 0., R23]; Edge:= [0,1, 1,2, 2,3, 3,0, 4,5, 5,6, 6,7, 7,4, 0,4, 1,5, 2,6, 3,7]; SetVid($101); \set 640x480x8 graphics repeat Farthest:= 0.0; \find the farthest vertex for I:= 0 to 8-1 do if Z(I) > Farthest then [Farthest:= Z(I); ZI:= I]; Clear; \erase screen for I:= 0 to 2*12-1 do \for all the vertices... [J:= Edge(I); I:= I+1; \get vertex numbers for edge Move(Fix(X(J)*Size)+640/2, Fix(Y(J)*Size)+480/2); K:= Edge(I); Line(Fix(X(K)*Size)+640/2, Fix(Y(K)*Size)+480/2, if J=ZI ! K=ZI then $F001 \dashed blue\ else $0C \red\); ]; DelayUS(55000); for I:= 0 to 8-1 do [X(I):= X(I) + Z(I)*Speed; \rotate vertices about Y axis Z(I):= Z(I) - X(I)*Speed; \ (which rotates in X-Z plane) ]; until KeyHit; \run until a key is struck SetVid(3); \restore normal text mode ]

//www.xpl0.org/rotcube2.gif

Yabasic[edit]

// Rosetta Code problem: //rosettacode.org/wiki/Draw_a_rotating_cube // adapted to Yabasic by Galileo, 05/2022 // GFA Punch (code from tigen.ti-fr.com/) // Carré 3D en rotation open window 50, 70 backcolor 0,0,0 clear window color 255,255,255 do clear window x = COS(T) * 20 y = SIN(T) * 18 r = SIN(T + T) line (x + 40), (y + 40 - r), (-y + 40), (x + 40 - r) line (-y + 40), (x + 40 - r), (-x + 40), (-y + 40 - r) line (-x + 40), (-y + 40 - r), (y + 40), (-x + 40 - r) line (y + 40), (-x + 40 - r), (x + 40), (y + 40 - r) line (x + 40), (y + 20 + r), (-y + 40), (x + 20 + r) line (-y + 40), (x + 20 + r), (-x + 40), (-y + 20 + r) line (-x + 40), (-y + 20 + r), (y + 40), (-x + 20 + r) line (y + 40), (-x + 20 + r), (x + 40), (y + 20 + r) line (x + 40), (y + 40 - r), (x + 40), (y + 20 + r) line (-y + 40), (x + 40 - r), (-y + 40), (x + 20 + r) line (-x + 40), (-y + 40 - r), (-x + 40), (-y + 20 + r) line (y + 40), (-x + 40 - r), (y + 40), (-x + 20 + r) pause 0.02 T = T + 0.15 loop

How do you make a cube in Python?

Drawing Cube First draw the front square. Move to back square through one bottom left side. Draw the back square. Draw the remaining side as shown in code.

Can Python draw shapes?

Turtle is a Python feature like a drawing board, which let us command a turtle to draw all over it!

How do you make a 3d figure in Python?

3D Plotting.
import numpy as np from mpl_toolkits import mplot3d import matplotlib.pyplot as plt plt..
fig = plt. figure(figsize = (10,10)) ax = plt. axes(projection='3d') plt..
x = [1, 2, 3, 4] y = [3, 4, 5] X, Y = np. meshgrid(x, y) print(X) [[1 2 3 4] [1 2 3 4] [1 2 3 4]].

How do you make a square in Python?

Python turtle square size.
size. setup(width=850, height=650) is a function used to set the height and width of the square..
shape('square') is used for making the shape of a square..

Postingan terbaru

LIHAT SEMUA