10
\$\begingroup\$

We don't have a single challenge about drawing a real 3 dimensional cube, so here it goes:

Challenge

Your task is to draw a rotated, cube with perspective. It can be in a separate window or as an image.

Input

Your input is 3 separate numbers between 0 and 359.99... These represent the rotation around the x, y and z axis in degrees.

0 0 0
30 0 40
95 320 12

Output

You can either display it in a separate window or save an image. You can use any type of display (vector based, rasterized, etc.).

Edit: ASCII is allowed too, to allow golfing languages with only textual output.

The output for rasterized or ASCII graphics must be at least 50*50 (pixels for rasterization, characters for ASCII)

Additional information

The positive z axis points out from the window, the x axis is horizontal, and the y axis is vertical. Basically the OpenGL standard.

Rotations are counter-clockwise if you look at the cube in the negative direction of a specific axis, e.g looking down for the y axis.

The camera should be on the z axis at a reasonable distance from the cube in the negative z direction, the cube should be at (0;0;0). The. cube also needs to be fully visible, and take up at least 50% of the drawing frame. The camera should look in the positive z direction at the cube.

The rotations of the cube get applied in the x->y->z order.

The cube is rotated around it's center, it doesn't move.

To project a cube in 2d space, you need to divide the x and y coordinates of the cube with the distance parallel to the z-axis between the point and the camera.

Rules

Rendering libraries are allowed, but the vertices need to be defined in the code. No 3d cube model class.

Test cases

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ Does it have to be wireframe? \$\endgroup\$ – Rɪᴋᴇʀ Apr 28 '16 at 15:21
  • \$\begingroup\$ Care to include an algorithm for the points? \$\endgroup\$ – Leaky Nun Apr 28 '16 at 15:24
  • 3
    \$\begingroup\$ What order/directions are the rotations done in? Where is the camera looking from? What kind of projection do we have to use? \$\endgroup\$ – flawr Apr 28 '16 at 19:47
  • 6
    \$\begingroup\$ But as I said, the rotations will not work out. As you defined it now, the cube will be moved out of the field of view if e.g. rotated around the x axis. Please use the sandbox. \$\endgroup\$ – flawr Apr 28 '16 at 20:37
  • 6
    \$\begingroup\$ @EᴀsᴛᴇʀʟʏIʀᴋ google will tell you the formula. No, challenges should contain as much of the material and information needed to solve them as possible, included in the body of the post. I shouldn't have to go googling or Wikipedia-ing just to start understanding. \$\endgroup\$ – cat Apr 28 '16 at 20:47
2
\$\begingroup\$

Shoes (Ruby) 235 231

Everything computed from scratch.

Shoes.app{p,a,b,c=ARGV.map{|j|j.to_f/90}
k=1+i="i".to_c
p=(0..3).map{|j|y,z=(k*i**(j+a)).rect
x,z=(((-1)**j+z*i)*i**b).rect
q=(x+y*i)*i**c
[90*(k+q/(z-4)),90*(k+q/(4+z))]}
4.upto(15){|j|line *(p[j%4][0].rect+p[(j+j/4)%4][1].rect)}}

Call from commandline eg shoes cube3d.rb 0 30 0

The idea is to simultaneously generate / rotate the four vertices of a tetrahedron in 3d. Then, as these are reduced to 2d, we generate the four vertices of the inverse tetrahedron (the total 8 vertices being those of the cube.) This gives 4 pairs of vertices corresponding to the 4 body diagonals. Finally the 2d vertices are connected by lines: each vertex of the original tetrahedron must be connected to each vertex of the inverse tetrahedron forming the 12 edges and 4 body diagonals of the cube. The ordering ensures the body diagonals are not plotted.

Test cases output

Note that, to be consistent with the last two test cases, rotation about the z axis is clockwise from the POV of the viewer. This seems to be in contradiction with the spec however. Rotation direction can be reversed by modifying *i**c -> /i**c

enter image description here

ungolfed

Shoes.app{
  p,a,b,c=ARGV.map{|j|j.to_f/90}   #Throw away first argument (script name) and translate next three to fractions of a right angle.
  k=1+i="i".to_c                   #set up constants i=sqrt(-1) and k=1+i

  p=(0..3).map{|j|                 #build an array p of 4 elements (each element wil be a 2-element array containing the ends of a body diagonal in complex number format)
    y,z=(k*i**(j+a)).rect          #generate 4 sides of square: 1+i,i-1,-1-i,-i+1, rotate about x axis by a, and store in y and z as reals 
    x,z=(((-1)**j+z*i)*i**b).rect  #generate x axis displacements 1,-1,1,-1, rotate x and z about y axis by b, store in x and z as reals
    q=(x+y*i)*i**c                 #rotate x and y about z axis, store result in q as complex number
  [90*(k+q/(z-4)),90*(k+q/(4+z))]} #generate "far" vertex q/(4+z) and "near" vertex q/-(4-z) opposite ends of body diagonal in 2d format.

  4.upto(15){|j|                   #iterate through 12 edges, use rect and + to convert the two complex numbers into a 4 element array for line method
    line *(p[j%4][0].rect+         #cycle through 4 vertices of the "normal" tetrahedron
     p[(j+j/4)%4][1].rect)         #draw to three vertices of the "inverted" tetrahedron. j/4=1,2,3, never 0
  }                                #so the three edges are drawn but the body diagonal is not.
}

Note that for historical reasons a scale factor of 90 is applied in line 9 (chosen to be the same as 90 degrees in line 2 for golfing) but in the end there was no golfing advantage in using this particular value, so it has become an arbitrary choice.

\$\endgroup\$
3
\$\begingroup\$

HTML/CSS/JS, 739 bytes, probably non-competing

But I just wanted to show off CSS 3D transforms.

w=_=>o.style.transform=`rotateZ(${z.value}deg) rotateY(${y.value}deg) rotateX(${-x.value}deg)`
input {
  width: 5em;
}

#c{width:90px;height:90px;margin:90px;position:relative;perspective:180px}#o{position:absolute;width:90px;height:90px;transform-style:preserve-3d;transform-origin:45px 45px 0px;}#o *{position:absolute;width:90px;height:90px;border:2px solid black}#f{transform:translateZ(45px)}#b{transform:rotateX(180deg)translateZ(45px)}#r{transform:rotateY(90deg)translateZ(45px)}#l{transform:rotateY(-90deg)translateZ(45px)}#u{transform:rotateX(90deg)translateZ(45px)}#d{transform:rotateX(-90deg)translateZ(45px)}
<div oninput=w()>
  X:<input id="x" type="number" value="0" min="0" max="360">
  Y:<input id="y" type="number" value="0" min="0" max="360">
  Z:<input id="z" type="number" value="0" min="0" max="360">
</div>
<!-- Above code for ease of use of snippet. Golfed version: <div oninput=w()><input id=x><input id=y><input id=z></div> -->

<div id=c><div id=o><div id=f></div><div id=b></div><div id=r></div><div id=l></div><div id=u></div><div id=d>

\$\endgroup\$
  • \$\begingroup\$ This actually looks pretty neat. I experienced with CSS3 transformations before, but I had issues with it. \$\endgroup\$ – Bálint Apr 29 '16 at 18:26
  • \$\begingroup\$ You seem to have the rotations in the wrong order. It should be x then y then z. You have z then y then x. @Bálint may confirm. \$\endgroup\$ – Level River St Apr 29 '16 at 20:20
  • \$\begingroup\$ @LevelRiverSt When I wrote this I didn't know what the order should be, and I couldn't work it out from the test cases, so thanks for updating me. I've also flipped the direction of the X rotation, so now I match all of the test cases. \$\endgroup\$ – Neil Apr 29 '16 at 22:51
  • \$\begingroup\$ Can you still provide a byte count? Even if answers are non-competing they should always have a byte count \$\endgroup\$ – Downgoat Apr 30 '16 at 16:30
  • \$\begingroup\$ @Downgoat Golfed or ungolfed? \$\endgroup\$ – Neil Apr 30 '16 at 19:32
0
\$\begingroup\$

Maple, 130+14 (in progress)

with(plots):f:=(X,Y,Z)->plot3d(0,x=0..1,y=0..1,style=contour,tickmarks=[0,0,0],labels=["","",""],axes=boxed,orientation=[Z,-X,Y]);

enter image description here

This plots a constant function inside a box, then uses plot options to hide ticks, labels and the function itself. Adding projection=.5 to the options brings the camera closer, enabling perspective view.
I wrote this before the specs were finalized and the rotation order is x, y', z'' instead of x, y, z. Until I fix the angles, here is another solution

POV-Ray, 182

#include"a.txt"
#include"shapes.inc"
camera{location 9*z look_at 0}
light_source{9*z color 1}
object{Wire_Box(<-2,-2,-2>,<2,2,2>,.01,0)texture{pigment{color rgb 1}}rotate<R.x,-R.y,-R.z>}

reads input through the a.txt file which should contain
#declare R=<xx,yy,zz>;
with xx,yy,zz being the rotation angles

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ Yay for using POV-ray, it's a great program. Unfortunately, the rules now state that no 3d cube class can be used. \$\endgroup\$ – miles Apr 29 '16 at 16:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.