Rotate a hypercube
Postscript 1075 732 683 640 631 601 590 545 542 526 514 478 470
Uses mat.ps and G.
Edit:-343 Applied binary-encoding generation of vectors and Eulerian circuit stolen borrowed from other answers. And applied binary-token-strings from the G library.
Edit:-49 Redefined sin
cos
and neg
to shorter names.
Edit:-43 Defined short names for sequences 0 0
0 1
1 0
.
Edit:-9 al
(ie. aload
) is shorter than (")@
. Factored 3 calls to idi
(ie. idiv
) at the cost of a do-nothing 1 idiv
.
Edit:-30 Applied implicit definition block from G.
Edit:-10 A few more triply-used sequences.
Edit:-45 Remove variables i
j
k
l
m
n
for the angles and always define the current angle as t
and functions of angles use the value of the (global) t
variable. Defer execution of the code-description of the rotation matrix until its t
value is ready.
Edit:-3 Remove <16>$
ie. closepath
. And a space.
Edit:-16 Factor-out array brackets from unit vectors in the rotation matrices (J
K
L
and M
). Re-apply dropped mo
for mod
and su
for sub
.
Edit:-12 In-line the project-and-draw function and remove (now empty) enclosing dictionary.
Edit:-36 Encoded the circuit (ie. the faces) in a string.
Edit:-8 Remove definition of vertices array V
. Instead, leave on stack and dup
working copies as needed (once, at first, and again at the end of the loop). Also, translated a few operators from binary-token-strings back to abbreviated names where the BTS gave no savings, so (I)$
is now fora
(ie. forall
). if du
could be (T8)$
, but if du
is clearly a better choice (it's golf, not obfuscation per se). Also, perform the scale
before translate
, so translated coordinates can be 3
and 4
instead of 300
and 400
.
(mat.ps)run 3(G)run $
t sin
A neg
t cos
0 0
0 1
1 0
2 mu Z 2(!V)@
idi 2 mo .5 su
(>8)$
[F D]
[D E]
[E D]
[D F]
3 4 100(&>88)$(,)# div(<N)#[E 15{[I 1 H I 2 H I 4 H ex 8 H]}fo]E
5{ARGUMENTS 1(XK/)$/t ex d{{J[0 C B 0][0 A C 0]K}{[C 0 A 0]L[B 0
C 0]K}{[C B D][A C D]M K}{[C D A]L M[B D C]}{J[0 C 0 B]M[0 A 0
C]}{J L[D C B][D A C]}}(>K)$[(>?)$]transpose matmul}fo
du(019;:89=?;37?>:26><804<=576451320){48 su get al po{W
Z Y X}{(>3)$}fora X G Y G{li}(D)#{mov}if du}fora(HB)#
The 3
4
and 100
in the first line of the second block are parameters representing center-x, center-y and scale, respectively, of the drawing on the page (center coordinates are scaled by scale
). (300,400) is roughly the center of US letter-sized paper (612,792) in PS units.
If you can roughly follow postscript, the important bizarre things are the implicit procedure block and the encoded operator strings. As shown by comments in the workfile, below, each line of the first block is implicitly named by A, B, C, etc. So, eg. F E D
would produce 1 0 0 1 0 0
. For the encoded operator strings, anything that is an argument to $
#
or @
is a sequence of operator calls, using the bytes to select operators from the system name table, PLRM 3ed Appendix F. These features and more are available for PostScript with the G library (now includes the mat.ps functions too).
Workfile:
(mat.ps)run 3(G)run $
t sin %/A
A neg %/B
t cos %/C
0 0 %/D
0 1 %/E
1 0 %/F
2 mu Z 2(!V)@ %/G %ad div %add div %108 1 54
idi 2 mo .5 su %idiv mod sub %/H %106 169 51
(>8)$ %/I %exch dup
[F D] %/J
[D E] %/K
[E D] %/L
[D F] %/M
3 4
100(&>88)$ %currentlinewidth exch dup dup %38
(,)# %scale %139-95=44
div(<N)# %div setlinewidth %54 155-95=60 %translate %173-95=78
%/V
[E 15{[ I
1 H I
2 H I
4 H ex
8 H]}fo]
E 5{ARGUMENTS 1(XK/)$ %index get cvr %88 75 47
/t ex d %exch def %62 51
{{J[0 C B 0][0 A C 0]K}
{[C 0 A 0]L[B 0 C 0]K}
{[C B D][A C D]M K}
{[C D A]L M[B D C]}
{J[0 C 0 B]M[0 A 0 C]}
{J L[D C B][D A C]}}
(>K)$ %exch get %62 75
[
(>?)$ %exch exec %62 63
]
transpose matmul
}fo %for
du %dup
%d %def
%{transpose matmul}fora d
%[E 9 11 10 8 9 13 15 11 3 7 15 14 10 2 6 14 12 8 0 4 12 13 5 7 6 4 5 1 3 2 0]
%<0001090b0a08090d0f0b03070f0e0a02060e0c0800040c0d050706040501030200>
% abcdef
%0123456789:;<=>?
(019;:89=?;37?>:26><804<=576451320)
{48 su get % 169 75 %V (>K)$ %sub %exch get
al po %aload pop %2 117
{W Z Y X}{(>3)$ %exch def
}fora %forall %2 117 62 51 73
X G
Y G
{li}(D)# %stopped
{mov}
if du%(T8)$ %if %84 du %dup 56
}
%<49a7a1>$ %forall stroke showpage %73 167-95=72 161-95=66
fora(HB)#
Ungolfed and lightly commented:
300 400 translate %roughly center of letter paper
currentlinewidth
100 dup dup scale
div setlinewidth %scale x100, reduce line-width/100
(mat.ps)run %load matrix library
ARGUMENTS aload pop{f e d c b a}{exch cvr def}forall %define args as
% a,b,etc and convert to real numbers
/m{2 mod .5 sub}def
/P{aload pop{w z y x}{exch def}forall %P: [x y z w] project-and-draw -
x 2 mul z 2 add div
y 2 mul z 2 add div
{lineto}stopped{moveto}if %catch(&handle!) nocurrentpoint error in lineto
}bind def
/V[0 1 15{ % generate vectors with a for-loop
[ exch
dup m
1 index 2 idiv m
2 index 4 idiv m
4 3 roll 8 idiv m
]
}for]
[[[1 0 0 0][0 a cos a sin neg 0][0 a sin a cos 0][0 0 0 1]]
[[b cos 0 b sin 0][0 1 0 0][b sin neg 0 b cos 0][0 0 0 1]]
[[c cos c sin neg 0 0][c sin c cos 0 0][0 0 1 0][0 0 0 1]]
[[d cos 0 0 d sin][0 1 0 0][0 0 1 0][d sin neg 0 0 d cos]]
[[1 0 0 0][0 e cos 0 e sin neg][0 0 1 0][0 e sin 0 e cos]]
[[1 0 0 0][0 1 0 0][0 0 f cos f sin neg][0 0 f sin f cos]]]
{transpose matmul} forall def % apply array of rotations and define
%Eulerian circuit (borrowed and adjusted for 0-based indexing)
[0 1 9 11 10 8 9 13 15 11 3 7 15 14 10 2 6 14 12 8 0 4 12 13 5 7 6 4 5 1 3 2 0]
% the main program!
% on the stack is the Eulerian circuit array
{
V exch get %lookup index in (sextuply-transformed) vertex array
P %call project-and-draw
} forall
closepath stroke %draw it, don't just think about it
showpage % gs's cmd-line-args option automatically sets -dBATCH,
% so without a showpage, gs will immediately exit before you
% can look at the picture :(
Some of my outputs are mirror images of the question's examples.
For gs -- hc.ps 0 0 0 0 0 0
, I get:
gs -- hc.ps 0 0 0 0 0 30
gs -- hc.ps 30 0 0 0 0 30
gs -- hc.ps 0 0 0 30 30 30
gs -- hc.ps 45 45 45 0 0 0
gs -- hc.ps 45 45 45 45 45 45
Bonus animation I just made with this program. This image corresponds to the rotation sequence 0 30 60 0 i i, where i ranges from 0 to 360 by 2.
Octave, 474 433 429 bytes
function H(a,b,c,d,e,f) C=@cosd;S=@sind;R=[1,0,0,0;0,C(e),0,-S(e);0,-S(e)*S(f),C(f),-C(e)*S(f);0,S(e)*C(f),S(f),C(e)*C(f)]*[C(c)*C(d),-S(c)*C(d),0,S(d);S(c),C(c),0,0;0,0,1,0;-C(c)*S(d),S(c)*S(d),0,C(d)]*[C(b),S(a)*S(b),C(a)*S(b),0;0,C(a),-S(a),0;-S(b),S(a)*C(b),C(a)*C(b),0;0,0,0,1]*(dec2bin(0:15)'-48.5);Z=R(3,:)+2;R=2*R./Z;Q=[1,2,10,12,11,9,10,14,16,12,4,8,16,15,11,3,7,15,13,9,1,5,13,14,6,8,7,5,6,2,4,3,1];plot(R(1,Q),R(2,Q));
Rotated:
function H(a,b,c,d,e,f)
C=@cosd;S=@sind;
R=[1,0,0,0;0,C(e),0,-S(e);0,-S(e)*S(f),C(f),-C(e)*S(f);0,S(e)*C(f),S(f),C(e)*C(f)]*
[C(c)*C(d),-S(c)*C(d),0,S(d);S(c),C(c),0,0;0,0,1,0;-C(c)*S(d),S(c)*S(d),0,C(d)]*
[C(b),S(a)*S(b),C(a)*S(b),0;0,C(a),-S(a),0;-S(b),S(a)*C(b),C(a)*C(b),0;0,0,0,1]*
(dec2bin(0:15)'-48.5);
Z=R(3,:)+2;
R=2*R./Z;
Q=[1,2,10,12,11,9,10,14,16,12,4,8,16,15,11,3,7,15,13,9,1,5,13,14,6,8,7,5,6,2,4,3,1];
plot(R(1,Q),R(2,Q));
The rotation matrices still consume a lot of bytes, but the Eulerian cycle worked out quite well, reducing the number of vertices visited from 96 120 down to 33.
Vertices are generated by taking the 4-bit binary representation of [0:15]
and considering the msb to be the x-coordinate and the lsb the w-coordinate.
Edit: Pre-multiplying all of the rotation matrices was a nightmare, which is why I didn't use it initially, but pre-multiplying them in pairs saved 41 bytes. Now to look for the optimum combination. :) Multiplying the matrices by threes was worse than no pre-multiplication at all, so I'll be happy with the pair-wise approach.
Output:
H(0,0,0,0,0,0)
H(0,0,0,0,0,30)
H(30,0,0,0,0,30)
H(0,0,0,30,30,30)
H(45,45,45,0,0,0)
H(45,45,45,45,45,45)
C# + Unity, 1060 845 835 bytes
C# ≈ Java
Assumes that this function is in a script placed on MainCamera
.
Edit:
Thanks to @TuukkaX for the suggestions to save 19 bytes
Saved ~200 bytes using the Eulerian cycle.
Golfed:
void d(float[]r){transform.position=Vector3.back*2;GetComponent<Camera>().backgroundColor=Color.black;Vector4[]p=new Vector4[16];Matrix4x4[]m=new Matrix4x4[6];int i=0;for(;i<16;i++)p[i]=new Vector4(i%2,i/2%2,i/4%2,i/8%2)-new Vector4(.5f,.5f,.5f,.5f);int[,]X={{6,8,1,12,7,11},{5,0,0,0,5,10},{10,10,5,15,15,15}};for(i=0;i<6;i++){m[i]=Matrix4x4.identity;r[i]=Mathf.Deg2Rad*r[i];float c=Mathf.Cos(r[i]),s=Mathf.Sin(r[i]);m[i][X[1,i]]=c;m[i][X[2,i]]=c;m[i][X[0,i]]=s;m[i][X[0,i]%4*4+X[0,i]/4]=-s;}for(i=0;i<16;i++)foreach(Matrix4x4 x in m)p[i]=x*p[i];int[]F={0,1,9,11,10,8,9,13,15,11,3,7,15,14,10,2,6,14,12,8,0,4,12,13,5,7,6,4,5,1,3,2,0};LineRenderer l=new GameObject().AddComponent<LineRenderer>();l.SetVertexCount(33);l.material=new Material(Shader.Find("Sprites/Default"));l.SetWidth(.03f,.03f);for(i=0;i<33;i++)l.SetPosition(i,p[F[i]]);
Newlines + indentation + Full shell:
using UnityEngine;
using System.Collections;
public class h : MonoBehaviour {
void d(float[]r)
{
transform.position=Vector3.back*2.5f;
GetComponent<Camera>().backgroundColor=Color.black;
Vector4[]p=new Vector4[16];
Matrix4x4[]m=new Matrix4x4[6];
int i=0;
for(;i<16;i++)p[i]=new Vector4(i%2,i/2%2,i/4%2,i/8%2)-new Vector4(.5f,.5f,.5f,.5f);
int[,]X={{6,8,1,12,7,11},{5,0,0,0,5,10},{10,10,5,15,15,15}};
for (i=0;i<6;i++){
m[i]=Matrix4x4.identity;
r[i]=Mathf.Deg2Rad*r[i];
float c=Mathf.Cos(r[i]);
float s=Mathf.Sin(r[i]);
m[i][X[1,i]]=c;
m[i][X[2,i]]=c;
m[i][X[0,i]]=s;
m[i][X[0,i]%4*4+X[0,i]/4]=-s;
}
for (i=0;i<16;i++)foreach(Matrix4x4 x in m)p[i]=x*p[i];
int[]F={0,1,9,11,10,8,9,13,15,11,3,7,15,14,10,2,6,14,12,8,0,4,12,13,5,7,6,4,5,1,3,2,0};
LineRenderer l=new GameObject().AddComponent<LineRenderer>();
l.SetVertexCount(33);
l.material=new Material(Shader.Find("Sprites/Default"));
l.SetWidth(.03f,.03f);
for (i=0;i<33;i++)
l.SetPosition(i,p[F[i]]);
l.gameObject.tag = "Player";
}
public float[] input;
void Start()
{
d(input);
}
}
I couldn't figure out a simple formula for constructing the rotation matrices nor the "faces" which to draw, so that cost a lot of bytes to hard-code. I borrowed the Eulerian cycle from @beaker. Also, Unity built-ins are extremely verbose.
You can verify all test cases online.