Draw a rotating cube: Difference between revisions
Added Kotlin |
|||
Line 350:
f.setVisible(true);
});
}
}</lang>
=={{header|Kotlin}}==
{{trans|Java}}
<lang scala>// version 1.0.6
import java.awt.*
import java.awt.event.ActionEvent
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.setResizable(false)
f.add(RotatingCube(), BorderLayout.CENTER)
f.pack()
f.setLocationRelativeTo(null)
f.setVisible(true)
}
}</lang>
|
Revision as of 19:11, 5 January 2017
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
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
FutureBasic
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.
<lang futurebasic> 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 </lang>
Haskell
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.
<lang Haskell>{-# 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 "h1" $ 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 "http://www.w3.org/2000/svg") a2 a3 a4 return ()</lang>
Link to live demo: https://dc25.github.io/drawRotatingCubeHaskell/
Java
![](http://static.miraheze.org/rosettacodewiki/thumb/0/0e/Rotating_cube_java.png/200px-Rotating_cube_java.png)
<lang java>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); }); }
}</lang>
Kotlin
<lang scala>// version 1.0.6
import java.awt.* import java.awt.event.ActionEvent 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.setResizable(false) f.add(RotatingCube(), BorderLayout.CENTER) f.pack() f.setLocationRelativeTo(null) f.setVisible(true) }
}</lang>
Maple
<lang maple>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 );</lang>
Phix
Included in the distribution as demo\pGUI\iup3Dgl.exw <lang Phix>-- -- iup3Dgl.exw -- -- Simple example of use of 3D OpenGL and IUP. -- -- Creates a dialog with one canvas and draws a rotating cube in it. -- -- Author: Marcelo Gattass, Nov 9 2009. -- Translated to Phix by Pete Lomax, May 25 2016 --
include pGUI.e include opengl.e include glu.e
--#withtype Ihandle
Ihandle canvas; /* canvas handle */ integer width = 640, /* width and height of the canvas */
height = 480;
atom t = 0; /* animation time */
/*------------------------------------------*/ /* Auxiliary functions to draw a color cube */ /*------------------------------------------*/
constant vertices = {{-1,-1, 1}, {-1, 1, 1}, {1, 1, 1}, {1,-1, 1},
{-1,-1,-1}, {-1, 1,-1}, {1, 1,-1}, {1,-1,-1}};
procedure polygon(integer a, integer b, integer c, integer d)
glBegin(GL_POLYGON); glVertex3d(vertices[a+1]); glVertex3d(vertices[b+1]); glVertex3d(vertices[c+1]); glVertex3d(vertices[d+1]); glEnd();
end procedure
procedure colorCube()
glColor3f({1,0,0}); glNormal(1,0,0); polygon(2,3,7,6);
glColor3f({0,1,0}); glNormal(0,1,0); polygon(1,2,6,5);
glColor3f({0,0,1}); glNormal(0,0,1); polygon(0,3,2,1);
glColor3f({1,0,1}); glNormal(0,-1,0); polygon(3,0,4,7);
glColor3f({1,1,0}); glNormal(0,0,-1); polygon(4,5,6,7);
glColor3f({0,1,1}); glNormal(-1,0,0); polygon(5,4,0,1);
end procedure
/* function called when the canvas is exposed in the screen */ function repaint_cb(Ihandle self)
IupGLMakeCurrent(self); glClearColor(0.3, 0.3, 0.3, 1.0); /* White */ glClear(or_bits(GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT)); glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW); glPushMatrix(); /* saves current model view in a stack */ glTranslate(0.0, 0.0 , 0.0); glScalef({1.0, 1.0, 1.0}); glRotate(t,1,0,1); colorCube();
glPopMatrix();
IupGLSwapBuffers(self); /* change the back buffer with the front buffer */
return IUP_DEFAULT; /* returns the control to the main loop */
end function
/* function called in the event of changes in the width or in the height of the canvas */ function resize_cb(Ihandle self, integer new_width, integer new_height)
IupGLMakeCurrent(self); /* Make the canvas current in OpenGL */
/* define the entire canvas as the viewport */ glViewport(0,0,new_width,new_height);
/* transformation applied to each vertex */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* identity, i. e. no transformation */
/* projection transformation (orthographic in the xy plane) */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60,4/3,1,15); gluLookAt({3,3,3}, {0,0,0}, {0,0,1});
/* update canvas size and repaint */ width = new_width; height = new_height; return repaint_cb(canvas);
end function
function idle_cd()
t += 1; return repaint_cb(canvas);
end function
function exit_cb()
printf(1,"Function to free memory and do finalizations...\n"); return IUP_CLOSE;
end function
function esc_close(Ihandle /*ih*/, atom c) -- (I like all my demos to close when escape is keyed)
if c=K_ESC then return IUP_CLOSE end if return IUP_CONTINUE
end function
function initDialog()
Ihandle dialog; /* dialog containing the canvas */
canvas = IupGLCanvas("repaint_cb", Icallback("repaint_cb")); /* create _canvas and define its repaint callback */
IupSetAttribute(canvas,"IUP_RASTERSIZE","640x480"); /* define the size in pixels */ IupSetAttribute(canvas,"IUP_BUFFER","IUP_DOUBLE"); /* define that this OpenGL _canvas has double buffer (front and back) */
/* bind callback actions with callback functions */ IupSetCallback(canvas, "RESIZE_CB",Icallback("resize_cb"));
/* create the dialog and set its attributes */ dialog = IupDialog(canvas, "SIZE=640x480"); IupSetAttribute(dialog, "TITLE", "IUP_3D_OpenGL");
IupSetCallback(dialog, "CLOSE_CB", Icallback("exit_cb")); IupSetGlobalFunction("IDLE_ACTION", Icallback("idle_cd")); IupSetCallback(dialog, "K_ANY", Icallback("esc_close"))
return dialog;
end function
procedure main() Ihandle dialog;
IupOpen(); /* opens the IUP lib */
IupGLCanvasOpen(); /* enable use of OpenGL to draw in canvas */
dialog = initDialog(); /* create the dialog and canvas */ IupShowXY(dialog, IUP_CENTER, IUP_CENTER); /* show in the center of screen */
IupMainLoop(); /* give program control to IUP until a return IUP_CLOSE */
IupClose(); /* closes the IUP lib */
end procedure
main()</lang>
PostScript
Don't send this to your printer! <lang postscript>%!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</lang>
Python
See also: Draw_a_cuboid
Short version
<lang python>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 http://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) )
</lang>
Racket
<lang racket>#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]))</lang>
Tcl
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.
<lang Tcl>
- 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 Template: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 </lang>
TI-83 BASIC
<lang ti83b>:-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</lang>
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.