Test if a point is in an Icosahedron

6

1

Take as input 3 floating point numbers, which represent the x, y, z coordinates of a point. Return a truthy or falsey value indicating whether the point is inside the regular icosahedron centred at the origin, with top and bottom vertices at (0, 0, 1) and (0, 0, -1), and with one of the upper ring of middle vertices in the +X+Z quarterplane. Input/output formats can be anything reasonable. To allow for rounding error, Your code does not have to give correct answers for points within 10^-6 units of the boundary

Example inputs / outputs:

0, 0, 0 => true
1, 1, 1 => false
0.7, 0.5, -0.4 => true
-0.152053, -0.46797, 0.644105 => false
-0.14609, -0.449618, 0.618846 => true

To be absolutely clear on the orientation of the icosahedron, the coordinates of the vertices are approximately

{{0., 0., -1.}, 
{0., 0., 1.}, 
{-0.894427190999916, 0., -0.447213595499958}, 
{0.894427190999916, 0., 0.447213595499958}, 
{0.723606797749979, -0.5257311121191336, -0.447213595499958}, 
{0.723606797749979, 0.5257311121191336, -0.447213595499958}, 
{-0.723606797749979, -0.5257311121191336, 0.447213595499958}, 
{-0.723606797749979,  0.5257311121191336, 0.447213595499958}, 
{-0.27639320225002106, -0.85065080835204, -0.447213595499958}, 
{-0.27639320225002106,  0.85065080835204, -0.447213595499958}, 
{0.27639320225002106, -0.85065080835204, 0.447213595499958}, 
{0.27639320225002106,  0.85065080835204, 0.447213595499958}}

Scoring is standard ; the fewest bytes wins.

QuadmasterXLII

Posted 2016-01-31T20:03:22.507

Reputation: 881

6Nitpick: Standard code golf is fewest bytes, not fewest characters. The latter option allows compressing code by using all 2^32 Unicode characters. – Dennis – 2016-01-31T20:12:21.067

Answers

5

Ruby,87

->c,z{a=g=3-5**0.5
10.times{x=(c*="i".to_c**0.4).real
z=-z
a&&=z<1+x*g&&x*4>g-4+g*z}
a}

An anonymous function taking the x and y coordinates as a complex number and the z coordinate as a real. (The question says "any reasonable" format which is a bit vague. If the format is not acceptable I can change it.)

Explanation

Below you can see a view looking down the z axis at the icosahedron's vertices. (for simplicity I have just drawn 2 pentagons that are perpendicular to the z axis.)

I have labelled all the points with positive z with their approximate coordinates. I have given the exact coordinates sqrt 0.8, 0, sqrt 0.2 for the point with positive z that lies in the xz plane (y=0), and I have also labelled the opposing point.

It can be seen that there is an edge parallel to the y axis whose equation is approximately z=0.4472, x=-0.7236. This edge is common to two faces of the icosahedron. I will describe these as a polar face and an equatorial face.

The equation of the polar face is z=1+x*g where g=3-sqrt(5) (this value was arrived at by studying Wikipedia and Wolfram Alpha, plus a bit of my own maths.) So z must be less than 1+x*g if the point is inside the icosahedron.

The equation of the equatorial face is x*4=g-4+g*z so x*4 must be greater than
g-4+g*z if the point is inside the icosahedron.

We could rotate these faces through 72 degrees to test if the point is inside the icosahedron, but it is easier to rotate the point about the origin. In order to cover both the top and bottom of the icosahedron, the symmetry operation applied to the point is a rotation of 36 degrees combined with a reflection in the xy plane (flip the z axis.) This operation is applied 10 times to cover all possibilities.

enter image description here

Ungolfed in test program

f=->c,z{
  a=g=3-5**0.5                    #g is slope of polar face. It is truthy, so can also be used to initialize a
  10.times{                       #generate 10 images of the point
    x=(c*="i".to_c**0.4).real     #rotate the point 36 degrees, assign the real value to x              
    z=-z                          #flip the z axis
    a &&= z<1+x*g && x*4>g-4+g*z  #check the point is inside the two test planes and update a if necessary
  }
a}                                #return a true or false

p f["0+0i".to_c,0]                     #true
p f["1+i".to_c,1]                      #false
p f["-0.152053-0.46797i".to_c,0.644105]#false
p f["-0.14609-0.449618i".to_c,0.618846]#true

p f["-0.8944+0i".to_c,-0.4472]         #true, just inside vertex
p f["-0.8944+0i".to_c,0.4472]          #false, outside midpoint of an edge
p f["-0.8945+0i".to_c,-0.4473]         #false, just outside vertex

Level River St

Posted 2016-01-31T20:03:22.507

Reputation: 22 049

Taking 2 of the coordinates as a complex number strikes me as reasonable, and also very clever :) – QuadmasterXLII – 2016-02-01T23:09:29.617