# Tutorial 4 Part 1 - Rubik's Cube Solver

October 8, 2018

Welcome back! Today we're going over how to create the confetti cube effect that you can view here.

The cube has a few elements to it. Firstly, there's the VEX heavy Rubik's cube that solves itself. After that we have to convert it into an rbd object. Once that's done, we emit points from the high velocity areas of the cube that we convert to pieces of confetti which gets packed and used in a rigid body solver. The rigid body aspects are covered in part 2 which will be released soon.

As for part 1, we're covering the creation of the cube and its solver. Below is the video tutorial and all of the explanations follow that.

Below we cover most of the VEX code used in the above tutorial. Each snippet is explained with diagrams and other visual aids to prevent this from getting clumsy.

Color Setting Attribute Wrangle:

float iter = detail(1,"iteration");

if(iter==0){

@Cd=set(1,1,1);

}

if(iter==1){

@Cd=set(1,0,0);

}

if(iter==2){

@Cd=set(0,1,0);

}

if(iter==3){

@Cd=set(1,1,0);

}

if(iter==4){

@Cd=set(0,0,1);

}

if(iter==5){

@Cd=set(1,0.25,0);

}

We first create an iteration variable.  That variable is set by the current iteration of the for-each loop. A for-each loop runs over inputs based on the type of for-each loop. In this case, we're running over primitives. Each time it runs over a primitive, the iteration value increases (starting at 0). It then moves onto the next face. So, if we assign values based on iterations then each primitive will have a value based on its own unique iteration.

Group Setting Attribute Wrangle:

if(@P.y>=@yMax){

setpointgroup(0, 'top', @ptnum,1);

}

if(@P.y<=-@yMax){

setpointgroup(0, 'bot', @ptnum,1);

}

if(@P.x>=@xMax){

setpointgroup(0, 'right', @ptnum,1);

}

if(@P.x<=-@xMax){

setpointgroup(0, 'left', @ptnum,1);

}

if(@P.z>=@zMax){

setpointgroup(0, 'front', @ptnum,1);

}

if(@P.z<=-@zMax){

setpointgroup(0, 'back', @ptnum,1);

}

This wrangle sets each side's group based on the position of the points of the cube. The illustration below shows exactly what is being done:

This illustration shows how a cube of size {1,1,1} and position of {0,0,0} can be split by a value of 0.25 to create a group in either direction when being viewed from the front.

The yMax value is a quarter of the total height. Any points above that height, are in the top group. Any points below the negative of the yMax value are part of the bottom group. The same thing is done for each other side.

Solver Wrangle Side Chooser:

float side = rint(rand(@iteration*123)*5);

s@group;

v@dir;

if(side==0){

@group = 'top';

@dir={0,1,0};

}

if(side==1){

@group = 'right';

@dir={1,0,0};

}

if(side==2){

@group = 'left';

@dir={1,0,0};

}

if(side==3){

@group = 'bot';

@dir={0,1,0};

}

if(side==4){

@group = 'front';

@dir={0,0,1};

}

if(side==5){

@group = 'back';

@dir={0,0,1};

}

NOTE: I made a mistake in the video tutorial, you do not need to multiply the random number by 6. We are including 0 as an option so only multiply by 5. It doesn't affect the outcome negatively but it is an error.

In this wrangle we are generating a random number based on our iteration variable that we created outside of the solver (rand(@iteration)). This gives us a value between 0 and 1. We then multiply it by 5, making it a value between 0 and 5. We then use rint() to round the entire value to a whole number so instead of, for example, 3.4 we end up with a value of 3.

We also add a direction value which is a vector based upon which axis a side would pivot around. Keep in mind that vectors can be viewed in a number of ways, eg: {0,1,2}, {R,G,B}, {X,Y,Z}. Thus, when we write a vector value of {0,0,1}, it means that we have a Z value of 1. For our @dir attribute, we use it to decide how a particular group of points rotate. For example, we want top and bottom groups to rotate around the Y-Axis, right and left to rotate around the X-Axis and the front and back groups to rotate around the Z-Axis. The illustration below shows how each side pivots:

The above illustration shows how each side rotates along a different axis which needs to be specified by the @dir attribute. This helps visualize each axis as a pivot.

Cube Rotation and Reset Wrangle:

float rot=90/chf('duration');

if(@pause==0){

if(inpointgroup(0,s@group,@ptnum)){

matrix3 i = ident ( );

rotate ( i, radians ( rot ), v@dir );

@P *= i;

}

@rotation+=rot;

}

else{

@pause- =1;

}

if(@rotation>90-rot){

@iteration+=1;

@rotation=0;

@pause=chi('pause_duration');

setpointgroup(0,'top',@ptnum,0);

setpointgroup(0,'bot',@ptnum,0);

setpointgroup(0,'front',@ptnum,0);

setpointgroup(0,'back',@ptnum,0);

setpointgroup(0,'right',@ptnum,0);

setpointgroup(0,'left',@ptnum,0);

s@prevGroup = s@group;

if(@P.y>=@yMax){

setpointgroup(0, 'top', @ptnum,1);

}

if(@P.y<=-@yMax){

setpointgroup(0, 'bot', @ptnum,1);

}

if(@P.x>=@xMax){

setpointgroup(0, 'right', @ptnum,1);

}

if(@P.x<=-@xMax){

setpointgroup(0, 'left', @ptnum,1);

}

if(@P.z>=@zMax){

setpointgroup(0, 'front', @ptnum,1);

}

if(@P.z<=-@zMax){

setpointgroup(0, 'back', @ptnum,1);

}

}

This is the wrangle that drives the entire Rubik's cube motion. Keep in mind that this wrangle sits in a solver and this causes it to be executed once every frame. This allows us to increment values over the frames until they meet a required value.

Our first line creates a rot variable which controls the per-frame rotation. We set it to 90 divided by the number of frames we want the group to rotate over. We use 90 because if you rotate the face of a Rubik's cube once, it will have moved 90 degrees. This value ensures that we do not over-turn or under-turn.

Next, we check if the current @pause value is greater than 0. If it is, then we're in a paused state so we shouldn't rotate anything right now. Instead, we lower the pause value. To understand the logic behind the pause value, see the flowchart below:

The above diagram shows how the above code determines whether to rotate the cube-face or pause during the current frame. This happens every frame because the wrangle is within a solver.

If the code runs the rotation part, it adds the rotation to a @rotation attribute. This attribute is checked every frame.

if(@rotation>90-rot)...

If we have rotated 90 degrees in total, this value is set back to 0, our iteration is increased and every other value is reset. This includes the groups. The reason we reset all groups is because their positions have changed and thus the groups no longer apply. We reassign the groups after they have been removed, except now the groups are based on the new positions of the points after rotation. The illustration below shows why we remove and reassign groups:

When we rotate a side, each cube changes which groups it is a part of. Every cube is part of at least 2 groups and 1 group will always change upon rotation. We reassign to avoid incorrect usage of groups in the following frames.

Hopefully we covered any concerns you may have had. Thank you for taking the time to make use of our content. That's all for this tutorial, see you soon for part 2!

If you would like to find documentation on VEX code

If you think we're awesome then go like our Facebook

or subscribe to our YouTube Channel

If you would like us to recreate any movie effects or have any ideas of your own then please comment down below and we'll try our best to do it.

We're open to criticism, feel free to tell us what you would like to have us do differently or more of.

Tags: