9
\$\begingroup\$

Output coordinates of the vertices of a cube. Then, output a list of twelve triangles that will cover the cube, each triangle being a list of three vertex-indexes, consistently oriented. Output must be an ASCII string of distinct decimal numbers. This golf has no input. Winner is the fewest characters, where the character set is Unicode.

For an example, consider a 1x1x1 cube cornered at 0,0,0. The eight vertices of the cube can be described by the following xyz coordinates on a 3d Cartesian grid:

x y z = (0,0,1) (1,0,1) (1,1,1) (0,1,1) (0,0,0) (1,0,0) (1,1,0) (0,1,0)

Each vertex can be given an index: x y z->index: 0 0 1->0, 1 0 1->1, 1 1 1->2, 0 1 1->3, 0 0 0->4, 1 0 0->5, 1 1 0->6, 0 1 0->7

Now consider the top face, vertexes indexed zero to three. The two covering triangles can be described by three indices each:

[0,1,2] [2,3,0]

Here is a picture of this top face, viewed from above the cube:

 3_____2
 |    /| 
 |   / |                  
 |  /  |
 | /   |
 0_____1                

And here is a view from an angle.

    3____2
   / __-/|
 0/_`__1 |
  |    | /6
  |____|/
 4     5

Note the orientation, or 'winding', of both of these triangles is 'counterclockwise' when viewed from 'outside' the cube directly looking at the face in question (imagine visiting each vertex as listed, it goes counterclockwise). Now imagine this done for all six sides of the cube.

vertices: (0,0,1) (1,0,1) (1,1,1) (0,1,1) (0,0,0) (1,0,0) (1,1,0) (0,1,0)
triangles as indices: [0,1,2], [2,3,0], [6,5,4], [4,7,6], 
  [5,2,1], [2,5,6], [0,3,4], [4,3,7], [2,6,3], [3,6,7], [0,4,1], [1,4,5]

You can output any size of cube located at any coordinates. You can number and order the vertex coordinates however you wish. Indices can be 0 based or 1 based. The triangle's orientation can be either clockwise or counterclockwise when viewed from outside the cube as long as it is consistent for all triangles.

The output can be formatted however you wish, as long as each ASCII decimal number is separated by at least one non-numeric ASCII character. For instance the above example could also be output as follows:

0 0 1 1 0 1 1 1 1 0 1 1 0 0 0 1 0 0 1 1 0 0 1 0 
0 1 2 2 3 0 6 5 4 4 7 6 5 2 1 2 5 6 0 3 4 4 3 7 2 6 3 3 6 7 0 4 1 1 4 5

This golf is inspired by various 3d graphics systems and formats, including OpenGL, OBJ, OFF, AMF, CGAL, etc. This golf is similar to the golf by Calvin's Hobbies named Output a Face on a Numbered Cube , the big difference being you need to output the x y z coordinates of the vertices yourself and output triangle indices. Thanks for reading.

Per user inspiration here is a "helper" validation program in python2 (non-golfy) that will print 'ok' or 'not ok' for test output data in variables vertstr and idxstr. It doesn't work perfectly... but it can catch some errors.

Edit: fixed typo in example and bugs in validation code.

    

#vertstr = '0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1'
#idxstr = '1 2 0 2 1 3 7 5 6 4 6 5 2 4 0 4 2 6 7 3 5 1 5 3 4 1 0 1 4 5 7 6 3 2 3 6'
vertstr = '0 0 1 1 0 1 1 1 1 0 1 1 0 0 0 1 0 0 1 1 0 0 1 0'
idxstr = '0 1 2 2 3 0 6 5 4 4 7 6 5 2 1 2 5 6 0 3 4 4 3 7 2 6 3 3 6 7 0 4 1 1 4 5'

class Vector:
    def __init__(self,v):
        self.x,self.y,self.z=v[0],v[1],v[2]
    def __add__(self,v):
        return Vector([self.x+v.x,self.y+v.y,self.z+v.z])
    def __sub__(self,v):
        return Vector([self.x-v.x,self.y-v.y,self.z-v.z])
    def __str__(self):
        return str(self.x)+','+str(self.y)+','+str(self.z)

def cross(v1,v2):
    x = v1.y*v2.z-v2.y*v1.z
    z = v1.x*v2.y-v2.x*v1.y
    y = v1.z*v2.x-v2.z*v1.x
    return Vector([x,y,z])

# http://mathforum.org/library/drmath/view/55343.html & http://sympy.org
def winding(v1,v2,v3,obs):
    x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=v1.x,v1.y,v1.z,v2.x,v2.y,v2.z,v3.x,v3.y,v3.z,obs.x,obs.y,obs.z
    d =     x1*(y2*z3 - y2*z4 - y3*z2 + y3*z4 + y4*z2 - y4*z3) 
    d = d + y1*(-x2*z3 + x2*z4 + x3*z2 - x3*z4 - x4*z2 + x4*z3) 
    d = d + z1*(x2*y3 - x2*y4 - x3*y2 + x3*y4 + x4*y2 - x4*y3)
    d = d - x2*y3*z4 + x2*y4*z3 + x3*y2*z4 - x3*y4*z2 - x4*y2*z3 + x4*y3*z2 
    return d

def normals(v1,v2,v3):
    va = v2-v1
    vb = v3-v2
    vc = v1-v3
    n1 = cross(va,vb)
    n2 = cross(vb,vc)
    n3 = cross(vc,va)
    return [n1,n2,n3]


def triplify(str):
    nums,triples=[],[]
    for num in str.split(' '): nums+=[int(num)]
    for i in range(0,len(nums),3):
        triples += [[nums[i],nums[i+1],nums[i+2]]]
    return triples

verts = triplify(vertstr)
indices = triplify(idxstr)
nsum = Vector([0,0,0])
windsum = 0
xs,ys,zs=[],[],[]
for v in verts:
    xs+=[v[0]]
    ys+=[v[1]]
    zs+=[v[2]]
#print xs,ys,zs,len(xs)
center=Vector([float(sum(xs))/len(xs),float(sum(ys))/len(ys),float(sum(zs))/len(zs)])
for triangle in indices:
    v1 = Vector(verts[triangle[0]])
    v2 = Vector(verts[triangle[1]])
    v3 = Vector(verts[triangle[2]])
    norms = normals(v1,v2,v3)
    print v1,v2,v3,norms[0],norms[1],norms[2]
    for n in norms:
        nsum += n
    w = winding(v1,v2,v3,center)
    print 'winding',w
    if w<0: windsum-=1
    elif w>0: windsum+=1
if abs(windsum)==12: print 'winding ok'
else: print 'winding not ok'
if (nsum.x==0 and nsum.y==0 and nsum.z==0): print 'normal sum ok'
else: print 'normal sum not ok'
\$\endgroup\$
4
  • 1
    \$\begingroup\$ It's clear from the example, but just to make it perfectly unambiguous, you may want to mention that the indices are 0-based. This is not a given, since at least one of the formats you list as example (OBJ) uses 1-based indices. \$\endgroup\$ Oct 19, 2015 at 16:37
  • \$\begingroup\$ That will work as well. I think one difficulty with this challenge is that it's moderately painful to verify the correctness of the output. You pretty much have to sketch a cube with the chose vertex ordering on a piece of paper, and manually validate all 12 triangles. Well, you could write a validation program. That might actually be another challenge idea... more difficult than this one, I think. \$\endgroup\$ Oct 19, 2015 at 16:54
  • \$\begingroup\$ i really like that idea of another golf for a validator. i updated the example to give a full dataset. thanks again. \$\endgroup\$
    – don bright
    Oct 19, 2015 at 17:22
  • \$\begingroup\$ ok I added a very quick and dirty validation program that takes cross products of each pair of vectors in each triangle, adds them all, and if 0 says 'ok'. \$\endgroup\$
    – don bright
    Oct 20, 2015 at 0:20

7 Answers 7

2
\$\begingroup\$

CJam, 35 bytes

YZm*`3{[XY4]m<)\0+_:+1$f-+_@f+W%}%`

Try it online

The output is:

[[0 0 0] [0 0 1] [0 1 0] [0 1 1] [1 0 0] [1 0 1] [1 1 0] [1 1 1]][[1 2 0 2 1 3] [7 5 6 4 6 5] [2 4 0 4 2 6] [7 3 5 1 5 3] [4 1 0 1 4 5] [7 6 3 2 3 6]]

Triangle orientation is clockwise from the outside. I checked this manually, and it looks correct to me.

Explanation:

YZ      Push 2 and 3 on stack.
m*      Cartesian power, creates the coordinates of the 8 vertices.
`       Convert to string for output. Done with vertices.
3{      Start loop over 3 coordinate directions.
  [XY4]   Push [1 2 4], which are the vertex index offsets for the 3 directions.
  m<      Rotate by loop counter. So the remaining loop body will be executed once
          with [1 2 4], once with [2 4 1], once with [4 1 2].
  )       Pop off last offset. Will use this as index offset between the two
          parallel faces.
  \       Swap pair of remaining two offsets to top. These are the index offsets
          within the face.
  0+      Add a 0 to the list. These 3 indices define the first triangle.
  _:+     Calculate the sum. This is the vertex index of the opposite corner.
  1$      Copy first triangle to the top.
  f-      Subtract all indices from the index of the opposite corner, producing
          the second triangle of the face.
  +       Concatenate the indices of the two triangles, resulting in a list with
          the 6 vertex indices for the face.
  _       Copy the list.
  @       Bring the offset between the two faces to the top.
  f+      Add the offset to each index in the copied list.
  W%      Revert the order, resulting in the properly oriented list of the 6 vertex
          indices for the parallel face.
}%      End of loop over 3 coordinate directions.
`       Convert to string for output. Done with triangles.
\$\endgroup\$
2
  • \$\begingroup\$ this is really cool. . . love the symmetry... \$\endgroup\$
    – don bright
    Oct 20, 2015 at 15:42
  • \$\begingroup\$ this is clearly the most fun answer, but i messed up my problem definition to have static description and "no input", so i have to keep the agreement and award the lowest character count, below (which is also a fun answer but in a different way), the Answer checkmark. thanks for participating. \$\endgroup\$
    – don bright
    Nov 22, 2015 at 22:31
1
\$\begingroup\$

Pyth, 18 characters

j`CM"⭧勛囃勦⾽仵ᶌﻘꚱ쥎➡˻ì

Same idea as my Haskell answer; prints:

[
1
1
1
1
1
,

2
1
2
1
1
...
\$\endgroup\$
2
  • \$\begingroup\$ i love that you used the same unicode string in 3 different languages \$\endgroup\$
    – don bright
    Oct 20, 2015 at 15:15
  • 1
    \$\begingroup\$ What is this unicode magic? \$\endgroup\$
    – RK.
    Oct 20, 2015 at 20:29
1
\$\begingroup\$

JavaScript (ES6) 78

alert([...'1010011100101110111:120213756465240426735153410145763236'].join` `)

Sorry but I really don't understand these challenges with no input.

\$\endgroup\$
2
  • \$\begingroup\$ sorry, it was my first golf question. i guess it is too late to change it now... \$\endgroup\$
    – don bright
    Oct 20, 2015 at 15:37
  • \$\begingroup\$ Better next time. You have my vote anyway. \$\endgroup\$
    – edc65
    Oct 20, 2015 at 16:11
1
\$\begingroup\$

Ruby, 98106

Fixed error spotted by Reto Koradi.

s=sprintf'%024b',342391
6.times{|i|t='15462315'[i,3];t+=t.reverse;t[1+i%2*3]='07'[i%2];s+=t}
p s.split(//)

Given that coordinates are required, the only corner numbering scheme that made sense seemed to be the one where each corner is the binary representation of its coordinates. That's quite different from the linked question, where various different numbering schemes were tried. In the end I decided to print the coordinates with a dirty hardcode: s is initialised to the string version of 24-bit number000001010011100101110111 whose decimal representation is 342391. Actually with this method of printing coordinates, the numbering of the vertices is flexible, so I may do another answer.

Going round the equator of the cube, we find the vertices 1,5,4,6,2,3 and we can define one triangle for each face from any 3 consecutive numbers on this list (wrapping back to the beginning at the end.) The other triangle on each face is defined by reversing the digits, and substituting the middle digit with 0 or 7 as appropriate.

This gives all the required output, but without any separating characters. To achieve that, I simply convert to an array of characters and print the array, like this (linebreaks inserted to prevent scrolling):

["0", "0", "0", "0", "0", "1", "0", "1", "0", "0", "1", "1", "1", "0", "0",
 "1", "0", "1", "1", "1", "0", "1", "1", "1", "1", "0", "4", "4", "5", "1",
 "5", "4", "6", "6", "7", "5", "4", "0", "2", "2", "6", "4", "6", "2", "3",
 "3", "7", "6", "2", "0", "1", "1", "3", "2", "3", "1", "5", "5", "7", "3"]
\$\endgroup\$
2
  • \$\begingroup\$ Are you sure that the winding order is consistent? Based on my sketch, 1, 5, 4 is CCW, 5, 4, 6 is CW. \$\endgroup\$ Oct 20, 2015 at 6:26
  • \$\begingroup\$ @RetoKoradi fixed at a cost of 8 bytes. Thanks. Also, I've realised I may do better with a different numbering scheme. \$\endgroup\$ Oct 20, 2015 at 10:20
1
\$\begingroup\$

Haskell, 38 characters

f=mapM(mapM print.show)"⭧勛囃勦⾽仵ᶌﻘꚱ쥎➡˻ì"

Prints the right numbers, separated by a whole lot of junk:

'\''
'\\'
'1'
'1'
'1'
'1'
'1'
'\''
'\''
'\\'
'2'
'1'
'2'
'1'
'1'
...

The cube's diagonal is from (1, 1, 1) to (2, 2, 2).

\$\endgroup\$
1
\$\begingroup\$

CJam, 20 characters

"⭧勛囃勦⾽仵ᶌﻘꚱ쥎➡˻ì":isS*

Same idea as my Haskell answer; prints:

1 1 1 1 1 2 1 2 1 1 2 2 2 1 1 2 1 2 2 2 1 2 2 2 1 2 0 2 1 3 7 5 6 4 6 5 2 4 0 4 2 6 7 3 5 1 5 3 4 1 0 1 4 5 7 6 3 2 3 6
\$\endgroup\$
1
\$\begingroup\$

Ruby, Rev 1 62

29.downto(0){|c|p c>5?73888640>>c&1:[c,c^1,c|6,c|6,(c+3)%6,c]}

Got rid of the c-6 by multiplying the magic number by 64.

The assignment of coordinates is below. It's odd that I assigned 100 to number 1. I could have saved a byte in rev 0 by exchanging the axes and assigning 001 to number 1. The reason it was that way was because originally I had up counting in the loop, which would have meant I had to put everything in reverse in the magic string. Anyway, with the change I've made now, there's no additional saving to be made, so i'll leave the coordinates as they are

Cube rotated with 0163 face at back
Top layer from above
01   000 100
74   010 110    
Bottom layer from above
36   001 101   
25   011 111

Ruby, Rev 0 63

29.downto(0){|c|p c>5?1154510>>c-6&1:[c,c^1,c|6,c|6,(c+3)%6,c]}

Using hardcoding of the coordinates data to give flexibility in picking the corners. There are 54 digits in the output, meaning that the naive solution would have 63-54=9 bytes available for code. As I can't think of a way to insert spaces in 9 bytes, I believe this is shorter than the naive solution.

Numbering scheme (adapted from my Ruby answer to the linked question https://codegolf.stackexchange.com/a/48867/15599)

4---7
|  /|
| / |
|/  |
1---0---7
|  /|  /|
| / | / |
|/  |/  |
6---3---2---7
    |  /|  /|
    | / | / |
    |/  |/  |
    6---5---4
        |  /|
        | / |
        |/  |
        6---1

Output

0
0
0
1
0
0
0
1
1
0
0
1
1
1
0
1
1
1
0
0
1
1
1
0
[5, 4, 7, 7, 2, 5]
[4, 5, 6, 6, 1, 4]
[3, 2, 7, 7, 0, 3]
[2, 3, 6, 6, 5, 2]
[1, 0, 7, 7, 4, 1]
[0, 1, 6, 6, 3, 0]
\$\endgroup\$
3
  • \$\begingroup\$ I really like the incorporation of @Runer112 's method \$\endgroup\$
    – don bright
    Oct 20, 2015 at 15:35
  • \$\begingroup\$ @donbright I was the first one to think of putting the first 6 vertices on the equator and the last 2 on the poles in that previous question, which is why my C answer is the most popular answer. I had the 6 vertices in sequential order. Runer112 deserves some credit for his reordering of the 6 vertices on the equator. I had to modify the face order for Ruby on the previous question, but the vertex order is indeed identical to Runer112's. Phinotphi's alternative reordering of the 6 vertices on the equator would have given me equal length on the previous question, but would be longer on this one \$\endgroup\$ Oct 20, 2015 at 20:37
  • \$\begingroup\$ wow awesome... thanks for the detailed explanation... very interesting. i should have allowed input then it would have been a better challenge. \$\endgroup\$
    – don bright
    Oct 21, 2015 at 1:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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