Placeholder Image

字幕列表 影片播放

  • (whistle whistles)

  • - Hello and welcome to part two

  • of the Rubik's Cube coding challenge.

  • In this version of the coding challenge,

  • I just want to take my existing Rubik's Cube simulation

  • and be able to make some turns, I want to shift

  • the faces of one side of the cubies,

  • like the yellow faces, to turn maybe clockwise

  • and be on the other side and everything follows suit.

  • So I'm going to do that with matrix transformations.

  • Here's the thing, I have to be honest with you,

  • this is my second attempt at doing this.

  • I did a previous livestream where I went down

  • a different road and I was keeping track of arrays

  • and different arrays and trying to shift things

  • and have custom hard coded things for every possible way

  • the Rubik's Cube could turn and it was a total disaster.

  • In the end I actually got it to work, you can see it

  • spinning around and working here right now.

  • I had some really useful suggestions,

  • most notably from Michael Raphael Panganiban,

  • who suggested that I use matrix transformations.

  • And I'm going to try this again, I'm going to try to really think

  • about refactoring that gobbledygook code I wrote before

  • and try to set myself up for success in the future.

  • I also want to highlight this website, I Am the Cube,

  • made by Stewart Smith and other collaborators from Google.

  • This was made for the Google Doodle about the Rubik's Cube

  • a bunch of years ago, and it's really phenomenal.

  • The code is available.

  • I really should just not bother doing what I'm doing

  • and just play with this, but I have a mission.

  • And my mission is to make this Rubik's Cube turn and move.

  • And so I'm going to do that.

  • This is what I left off with in Part One.

  • And one of the things that I did in Part One

  • that was a little bit weird is I really

  • was thinking in terms of cube notation.

  • And if you're a cuber, you look at the cube

  • in a certain direction, you're thinking about what's up,

  • what's down, what's left, what's right,

  • what's clockwise, what's counter-clockwise.

  • And that really led me in some strange directions.

  • I think it's useful maybe to double back

  • and add that in once I have the cube working.

  • But the thing that I'm actually working with

  • here in this code, x-axis, y-zxis, z-axis.

  • And it's going to be much simpler for me I think

  • to keep track of things and think in those terms.

  • And the rotation might not be clockwise

  • or counter-clockwise, it might be

  • in the positive direction or in the negative direction.

  • The first thing that I want to do to really figure this out

  • and to have a more effective way is actually keep track

  • of the cube's location not in a vector, but in a matrix.

  • Dun dun dun.

  • So I have done a lot of videos about matrices

  • and matrix math and some of that's going to come up here.

  • The ones that you might want to check out if that's

  • a totally new concept to you is the video where I made

  • my own 3D renderer using matrix transformations.

  • And I made a little like bunch of functions to do

  • to store matrices and to multiply them.

  • I also used matrices in a bunch

  • of videos I made about neural networks.

  • The matrices pop up there in the math

  • of machine learning with neural networks.

  • But here, what I want to do is I want to have

  • a transformation matrix for each one of the cubies

  • to keep track of where it is in the world.

  • And so, the way that I'm going to do that is I'm going to

  • change the cubie to receive a matrix and for its position

  • to be that matrix rather than a PVector.

  • Luckily for me, Processing already

  • has built into it matrix classes.

  • So I don't have to write my own matrix 3D object,

  • I don't have to write a matrix 2D object,

  • I've got one in the Processing.

  • So, it's going to be a PMatrix3D object.

  • The other thing I want to do now, so this is how I made

  • the cubes before, I made each cubie with an x, y, and z.

  • But now I want to do this.

  • I want to make each cubie

  • with a matrix,

  • and then what I want to do is translate that matrix.

  • So in other words, each cube, it's a way each cube

  • is going to keep track of its own 3D world,

  • and it has the full matrix of what is the translation,

  • what is the rotation, what is the scale.

  • And so if I translate that matrix to its x, y, z location,

  • then I'm initializing the cubie with that particular matrix.

  • Some other things, while this nested loop is useful,

  • it's going to be much simpler for me in the end I think

  • if I actually keep all of the cubies in just a single array.

  • The multidimensional array is interesting, but let me,

  • so let me change that to, I'm just going to make it, no,

  • I'm not going to make this i, I'm going to make this like index,

  • oh actually, let me make this, I'll make this index.

  • And I'm gnona say cube index equals new cubie

  • with the matrix, index plus plus, so that I'm counting.

  • And then, this will now be a single array

  • that has dimensions times dimensions times dimensions.

  • Three times three times three, or 27.

  • Of course, there really isn't a cubie in the center, but eh,

  • you can't see it anyway, so we can imagine it's there.

  • Now the other thing that's really important here,

  • and I'm going to, I think this will be helpful.

  • I'm going to change this to x, y, and z.

  • X, y, and z.

  • I'm going to change this to x, y, and z.

  • I'm not going to worry about this figuring out

  • the actual position of the cube with its length.

  • I just want its position in a three-dimensional world.

  • And what are those positions?

  • So if I just think about one face of the cube, for example,

  • maybe this is a face of the cube where z equals one.

  • If we're looking at the cube with green facing us,

  • facing you, this could be the z-axis, this is z equals one.

  • So we've got the x-axis along the horizontal

  • and the y-axis along the vertical.

  • So what I could think about this is really the location

  • x, y, zero, zero, so it's zero, zero, one.

  • So all of these have a z location of one.

  • But this one is negative one, negative one.

  • Sorry, zero, negative one, one, negative one.

  • The y is negative one, the x is negative one, zero, one.

  • So this is negative one, zero, and this is one, zero.

  • This is negative one, one.

  • This is zero, one, and this is one, one.

  • So these are all the locations, and these

  • can actually be the x, y, z values that I apply

  • to translate each individual cubelet's matrix.

  • So if I come back to the code,

  • what I'm doing here is I'm saying,

  • have the x goes from negative one all the way to one.

  • Same thing for the y.

  • Same thing for z.

  • Create a matrix for each one.

  • Create the cubie, I'm not going to

  • worry about this leg thing right now.

  • And then say index plus plus.

  • The cubie

  • then is just a thing with its matrix.

  • And this should say PMatrix3D.

  • I've got to specifically use, I'm going to be using a 2D matrix

  • in a little bit, you'll see this is crazy.

  • But right now I'm going to use the (mumbles).

  • Alright, so, I still have this length variable here.

  • The other thing I want to do is, while it was really useful

  • that I had this whole way of doing these quads

  • and coloring them, I'm going to get rid of that right now.

  • Because I think that what I want to do

  • is think of each of these cubies as a box.

  • And then it's also going to have faces.

  • And I think I'll draw the faces

  • as a separate object with a quad.

  • So right now to keep things very simple,

  • I'm going to show you something kind of nuts.

  • I am going to take out all of this.

  • I'm going to get rid of this translate.

  • I am actually going to call this

  • function called apply matrix.

  • So what am I doing here, I'm taking the matrix,

  • and I probably shouldn't call it pos as in position.

  • Let's actually call it matrix.

  • Let's call it matrix, each cubie has a matrix

  • which really contains all the information about where,

  • its position and rotation in the world of the cube itself.

  • And this is actually the same

  • as just calling translate at this point.

  • But because I'm storing it in a matrix instead of calling

  • translate directly, it's going to give me some more

  • possibilities later as I need to keep track of all these

  • cubies separately or in smaller groups and that kind of thing.

  • So if I do this, and then if I just say box,

  • and I'm going to say box one, it's going to be a small box,

  • pop matrix, then, and here, the nice thing

  • about this also is I can change this to cube dot length

  • and get rid of this nested loop.

  • I can say cube index I dot show,

  • and hope that we see the Rubik's Cube.

  • Ah, where is it?

  • Oh look, I think it's there.

  • I think that's the Rubik's Cube.

  • Oh, look, I visualized the black hole on the day of the,

  • this is the second image of a black hole, no, okay.

  • So the reason why it's so tiny is because scale.

  • So let me say scale 100,

  • and let's see what happens now.

  • Where's that Rubik's Cube?

  • So let's think about, ah, stroke weight eight.

  • Now that's lunacy here.

  • so let's make the stroke zero, let's make

  • the stroke weight small because I'm scaling up.

  • And let's also make sure we say

  • fill to (mumbles) that's there already.

  • Oh, that's a really big one, but there it is.

  • So I don't want to scale that much.

  • Let's scale it by 50 and let's just say stroke weight .1.

  • And there we go.

  • So now, I have exactly the same thing.

  • I have exactly what I had before,

  • but I just have a matrix for each one of the cubies.

  • Now I need to figure out, how am I going to debug this?

  • Right now if I move them all around,

  • I could move them around, they're all white,

  • they're all going to appear in the same location.

  • So what I'm going to do is I am going to make a variable.

  • I'll just call it Highlight false.

  • And I'm going to say,

  • if highlight fill zero.

  • And then let me say,

  • let me say cube index zero highlight.

  • So there we go, we can see, ah, that one.

  • See look, this one is now highlighted.

  • I can address any individual cubie.

  • It's not really highlighted 'cause it's black,

  • it sort of looks like it's removed.

  • Maybe I should make it like red or something

  • just so it's a bit more obvious.

  • 'Cause everything right now is just white, okay.

  • But let me actually highlight the one that's in the front.

  • (bell dings)

  • Alright, the answer by the way,

  • in case you were wondering, was two.

  • I'm counting with z first, so it's going zero, one, two.

  • So perfect.

  • Boy, that was a lot of time I spent trying to figure out

  • that it was two, you're lucky you didn't have to watch that.

  • Okay.

  • So now I can see that I'm highlighting a given cubie.

  • The next thing I want to do is take one of these faces,

  • each one has its own 3D matrix

  • with an x, y, and z, and rotate It.

  • I want to apply a 2D rotation to this particular face.

  • So any face, whether it's the front,

  • the back, the top, the bottom, if we look at it,

  • it ends up becoming two-dimensional.

  • So I just want to rotate two, I just want to

  • rotate along one particular axis.

  • Any given face can become suddenly

  • a 2D world that I want to tdo a rotation to.

  • So if I suddenly have this point here,

  • which is at negative one, negative one,

  • and I want to rotate that relative to the center 90 degrees,

  • or half pi, then it's going to end up where?

  • In one, comma negative one.

  • It's going to move and rotate there.

  • So how can I do that?

  • I can apply a 2D rotation.

  • Let's write a function called turn z.

  • And I want to avoid using rotate z,

  • because even though I'm doing a z-axis rotation,

  • rotate z is the built-in function in Processing.

  • So if I say turn z, first let me look at all of the cubes.

  • So I always need this particular,

  • this particular loop.

  • Just realized something, I now, I sort of regret,

  • I'm regretting taking out that nested loop

  • because I just want all the faces that are in the z-axis.

  • And if I had that nested loop,

  • I would be able to keep it that way.

  • But actually, I'm going to do something slightly redundant,

  • which is that I am also going

  • to keep track in a separate variable.

  • I am going to keep track of, I'm going back,

  • I'm going to keep track of its x, y, and z index values.

  • I'm going to keep track of what those values are

  • within what would be a three-dimensional array.

  • And so let me actually add those as arguments here.

  • And I'm going to say x equals x, y equals y.

  • Z equals z.

  • And then when I create each cube, I'm going to say x, y, z.

  • Great.

  • So I have those extra variables because now I can say

  • if a cube dot z equals one, I want only the cubes

  • that are in the z-axis one, negative, zero, one,

  • those are the ones, and this is cube index i dot z,

  • these are the ones that I want to rotate.

  • So now I need to make a 2D matrix, this is what

  • I was saying, 2D matrix out of that face.

  • So the 2D matrix is just a new 2D matrix.

  • I want to do a 90 degree or a half pi rotation of that matrix.

  • And then, I want to translate out to the cubes x,

  • Spongeman on Code Pen who made a version of this.

  • I saw used actually a really cute

  • variable name which I'm going to use right now.

  • qb, like the letters qb for the cubie, equals cube index i.

  • So I can actually just use qb, it's qb z

  • equals one, translate by qb x and qb y.

  • Essentially what I want to do here is

  • I need to rotate before I translate.

  • I have this thing here, I make this matrix,

  • I rotate the matrix, and then I translate out to here

  • and I have my new location where x and y should be.

  • So, I think in order to make this more clear,

  • I want to do a little bit of console logging.

  • 'Cause with this matrix, what's actually in there?

  • So let me first make,

  • I'm going to call this matrix one

  • and I'm going to call this matrix two.

  • And this is just for debugging purposes.

  • And I'm going to say matrix one dot translate qb x qb y.

  • So I want to show you, what does it look like

  • when I just apply this translation to x and y

  • without a rotation, and what happens

  • when I rotate and then apply that translation?

  • So to show you that, I could do matrix one print

  • and matrix two print.

  • Processing has a nice print function associated with the,

  • a nice print function associated with matrices.

  • And let me also just do print line

  • something like this, and let's also,

  • just so we can see, do print line qb x qb y.

  • So we can see what those values are.

  • So let me run this.

  • Oh, I need to call this function.

  • I'm going to call turn z right here in setup.

  • Look at this.

  • When the xy location is negative one, negative one,

  • you can see it as the third column

  • in rows one and two of this matrix.

  • This is the matrix that keeps track of translations

  • and rotations for a 2D world in Processing.

  • When I rotate it 90 degrees, where does it end up?

  • One, negative one.

  • Negative one, negative one ends up

  • at one, negative one, ha ha.

  • When it's negative one, zero,

  • it ends up at zero, negative one.

  • Negative one, zero, rotating 90 degrees,

  • it ends up at, where did it end up?

  • Zero, negative one, which is right here.

  • So you can see this rotates to here.

  • So actually, in those matrices are the new index values

  • of where it is in my 3D array that I'm imagining.

  • So here we go.

  • So I, and these values are all stored

  • in properties of the matrix object

  • which are indexed by its row and column.

  • So I can basically say, hey, qb, you have a new location.

  • Update your location to matrix two dot M, it's row, column.

  • So zero, two, matrix.

  • Matrix two, M two.

  • Basically I'm getting, and I could've

  • just done the matrix math with sine and cosine probably,

  • but I kind of like the idea that I'm using

  • the built-in matrix math of Processing.

  • So you can see here that these are these two values.

  • Update, oh, sorry.

  • M one, two.

  • And then, qb dot z.

  • 'Cause I'm not affecting the z.

  • There's no update function yet.

  • That's a new thing I have to write,

  • but this is what I'm doing.

  • I'm taking the x, y, actually,

  • I want to get rid of the debugging stuff,

  • because this makes it so much simpler to look at.

  • And I'm just going to call this matrix.

  • I don't need matrix one or matrix two.

  • What I'm doing is I am taking, I'm making a 2D matrix

  • out of the xy, then I am rotating it, and then I am getting

  • the new xy and replacing it in theory in my qb object.

  • And in my qb object then, I need an update function,

  • which does something like,

  • first of all, I'm just going to,

  • there might be a more thoughtful way of doing this.

  • But I'm just going to reset the matrix.

  • And this needs an x, a y, and a z.

  • And I'm going to, and somebody in the chat was telling me,

  • and I should probably get in the habit of doing this,

  • if I have the same variable names of things,

  • I can make sure I'm referring

  • to the object instance variables with this dot,

  • which is of course the thing I like to use on this channel.

  • Let's be consistent and do it here.

  • So I can say matrix, I don't have

  • to say reset matrix, matrix reset,

  • matrix translate x, y, z.

  • And then let me update, again,

  • I've got a serious bit of redundancy here,

  • but let me just update those x, y, z values as well.

  • So now,

  • this should update,

  • why is update not working?

  • Oh, you know what?

  • These values in the matrix are floats.

  • And qb wants an in, so I'm going to, I could just convert it

  • to an in, but I'm going to use round, because I want to

  • make sure, I ran into this in another project,

  • just in case it for some reason gives me

  • like 0.999999999, if I used casting it

  • to an integer or floor, it would make it zero.

  • So this should work.

  • Let's get rid of turn z here.

  • And I'm going to add a key pressed.

  • And I'm just going to say if key equals,

  • just the key one, turn z.

  • So now, alright, ready?

  • (drum roll pattering)

  • When I press the key one,

  • that cubie should move over to the right.

  • (horns trumpeting) Anticlimactic, but it works.

  • Now here's the interesting thing.

  • I should now be able to say give this turn z an index,

  • which would be here.

  • So for example, now, if I were to say turn z zero,

  • I know you want me to use a switch statement, everybody.

  • Turn z one.

  • The middle is technically, while I could program it

  • with a rotation of the middle of the cube,

  • that's not really a thing.

  • So I'm actually just going to use one

  • and two to rotate zero and two.

  • Let's highlight both cube two

  • and cube zero, and let's see what happens.

  • So I should now be able to, if I press one, woops.

  • Oh, what happened here?

  • Oh, sorry everybody, the indices are negative one and one.

  • Negative one, zero, and one, of course, ah.

  • Alright, let's try this again.

  • Right, oh, is it going around?

  • Look there, it's going around, look at that.

  • That one's going around.

  • It's going around, look at that.

  • It's going around, yay. (hands clap)

  • Yes, okay, now look at this.

  • Now there's got to be some sort of way I could

  • make this function into any, just like keep this

  • functiOn generic and do any given axis.

  • But that's too hard for me, I'm going to,

  • this will definitely.

  • ("Refactor" by Espen Sande Larsen)

  • ♪ I will refactor this later

  • You know I will refactor

  • - Right now I'm just going to copy, paste.

  • And I'm going to say turn y.

  • And so now if y equals index,

  • then what I want is x and z qb y.

  • I don't need to actually round that.

  • So this should be exactly the same for a y rotation, right?

  • And then, oh, and I need, mm, I'm going to add one more thing.

  • And then x, I'm going to just check the x-axis.

  • And this should be y and z.

  • Then I need to keep qb x

  • and adjust the y and z.

  • So I'm turning any face, like this here with turn y.

  • Now it was kind of obvious to us that when I have the x-axis

  • that I turn this face into a 2D matrix.

  • But when I'm using the y-axis, eh,

  • let's just take this face and turn it into a 2D matrix.

  • I need the y values and the z value,

  • I mean sorry, the x values and the z values.

  • That's what's changing, y is fixed at negative one, one.

  • Alright, alright.

  • I'm going to use a switch statement.

  • Probably going to have to come back to my if statement,

  • but let me, I know it's just too painful,

  • I can't bear to deal with the comments.

  • Switch key, I'm looking at it

  • over here on my invisible computer.

  • Case one.

  • No, no, case one.

  • Turn z negative one, break.

  • Right, and then I just keep doing this, right?

  • Case two, turn z, one.

  • Right?

  • And then, alright.

  • Now, actually, here's the thing.

  • Let's see if I can get clockwise and counter-clockwise.

  • There's how many moves?

  • I could rotate in this direction or the other direction.

  • So I'm going to consider that to be a direction in my rotate.

  • Let me just get this to work first, then I'll add direction.

  • Too many things.

  • Case one and two.

  • Case

  • three and four,

  • which is turn y.

  • Case five and six, which is turn x.

  • Is that actually a switch statement?

  • What's the chance that's actually

  • a switch statement that works?

  • So one is just turning the back row.

  • That still works.

  • Two is turning the front row.

  • Oh my god.

  • Three, that's not right.

  • Is that?

  • So let's do something a little different.

  • Let's give these a color.

  • And let's do the following.

  • Let's set the color equal to red.

  • And the color equal to, oh, this just C.

  • The color equal to blue.

  • And then in the cubie itself,

  • let's just fill it with its color.

  • That makes much more sense.

  • So now, I think it'll look right to us now.

  • So if I go and do a y rotation,

  • right, that's correct.

  • Now here's the thing.

  • Will this all keep working, will this continue to work,

  • if I do multiple combinations?

  • So let's do my z rotation

  • where now, that's there.

  • Now let me also rotate that there,

  • but let me put it down here.

  • Or let's just leave that there.

  • Now let's do a y rotation.

  • Yes, that still works.

  • Let's do x.

  • Yep, that one's going around there.

  • And then the red one is going around.

  • The cubes move correctly!

  • Now that I have all this working,

  • I need to actually color their faces correctly.

  • So if I put the actual colors of the cube on the faces,

  • then I can start to move those as well.

  • So in order to do that, I have an idea.

  • What I would like to do is

  • I would like to make a face class.

  • The idea of how I'm going to keep track of a face,

  • so every single cubie will have,

  • well, in an actual Rubik's Cube, there are

  • center pieces which just have one face and they never move,

  • they spin, but they just stay permanent.

  • There are these corner pieces which have three faces.

  • And I don't know what these

  • are called, these little like middle pieces,

  • I'm sure there's a tech name for them, that have two faces.

  • I'm going to be simple about this for now

  • and I'm just going to give every single cubie six faces.

  • And there's going to be a lot of redundancy, like you will

  • never see the fact that this cubie does not actually have

  • a white face on the side, but I'm going to give it that.

  • And the way that I'm going to define

  • each face, each face is going to have a color.

  • Like what is its actual color.

  • And it is going to have a normal.

  • So its normal is a vector that points

  • perpendicularly, perpendicular from the face.

  • So if it's the front-facing face,

  • it's normal will be zero, zero, one.

  • It's pointing forward in the z-axis.

  • If it's the backwards-facing face, zero, zero, negative one,

  • to the right, one, zero, zero, so on and so forth.

  • So here in the face class,

  • we're going to have a Pvector called normal.

  • And then we're going to have a color called C for color.

  • And when I make the face,

  • I will give it a normal and a color.

  • And I will say this dot normal equals normal.

  • And this dot C equals C.

  • Then every single cubie, in addition to having

  • all this information, will have an array of faces

  • with six faces, again, a thing that I would want to do later,

  • and maybe you can do this in your version,

  • is to have the correct number of faces for each cubie.

  • But let's just do them all with six.

  • And then in that case, I'm also just going to hard code this.

  • So each face, faces index zero would be,

  • so I'm going to consider the z-axis

  • as green.

  • Forward, so the back is blue.

  • Negative one of is zero.

  • So that's going to be a new face with a new Pvector

  • that is zero, zero, negative one.

  • And the color is zero, 0255.

  • So this is the blue face.

  • This is the,

  • green face.

  • This is (laughs) wait, up,

  • up would be zero one zero.

  • This would be white.

  • Yellow, how do I do yellow?

  • It's a little red and a little green, something like that.

  • No, no, a lot of red and red and green,

  • green, yeah, but this is zero.

  • It's yellow.

  • Yellow, so then left and right,

  • positive right is orange.

  • One, zero, zero is,

  • I think that should be orangeish.

  • Somebody in the chat will give me some better colors.

  • And then the other side is red.

  • So this is now me making all of the faces, great.

  • And then, guess what I could do here in show.

  • After I apply that matrix,

  • I can draw the faces.

  • For face f in faces, f dot show.

  • And now, all I need is a function here

  • that knows how to draw a rectangle

  • pointed perpendicular to the normal with a particular color.

  • So the first thing I know I need to do is fill the color.

  • Then I need to draw a quad or a rectangle.

  • Let's just use rectangle, let's just try,

  • I'm just drawing a 2D rectangle.

  • And I need to rotate it according to the normal.

  • Let's just draw the rectangle.

  • Rectangle at zero, zero, one comma one.

  • Actually it's a square,

  • I can use Processing square function.

  • I think its size is just one.

  • So what if I do this?

  • Oh.

  • Okay, first of all, let me go back to my cubie.

  • And let's just say,

  • no fill.

  • There's the red one.

  • It's in the wrong place.

  • Oh, they're all just on top of each other.

  • Right, of course.

  • Oh, I do need to translate out,

  • I do need to translate, I can translate by the normal.

  • Oh, of course.

  • Okay, so I can translate

  • by the normal dot x, normal dot y, normal dot z.

  • And I'm going to want to add push matrix and pop matrix.

  • And let's do no stroke here.

  • So now, that should be, there they are.

  • There're all the faces, sort of.

  • Are they in the right place?

  • They kind of are, right?

  • White is back there, yellow's there, then blue,

  • then red, but they're all, the problem is

  • they're all, I need a rotation.

  • Can I rotate along the normal or something?

  • Can I do something like say rotate normal dot x,

  • normal dot y, normal dot z, half pi?

  • Is that like going to work?

  • No.

  • Oh, maybe it's the other way, maybe half pi goes first.

  • That's the angle and then this is the axis of rotation.

  • Oh, oh look at this.

  • Something's right sort of.

  • First of all, they're, (laughs)

  • I should be translating by normal x, half of that.

  • So let's multiply all these by .5.

  • Okay.

  • (laughs) Green and blue, are those on opposite sides?

  • Yes.

  • White and yellow are in,

  • the right orientation,

  • everything's in the right orientation,

  • but they're in the wrong place.

  • Ah. (bell dings)

  • Rrrrr. (laughs)

  • negative one, that's a negative one, right?

  • Ah.

  • Okay, now I'm really close.

  • Alright, well let me, I know there's a way I could do this.

  • I was trying to avoid this.

  • But just so I get it right.

  • If I were to say, if a normal dot x,

  • if the absolute value of normal dot z is greater than zero,

  • right, if the normal is along the z-axis,

  • I need to rotate,

  • I actually don't need to rotate at all.

  • The rectangle is drawn correctly.

  • So I could say like rotate just,

  • I could say rotate z, by half pi.

  • Else if the absolute value of normal dot x

  • is greater than zero,

  • then I could rotate, I think it's by x.

  • Else if absolute value of normal dot y

  • is greater than zero, there's got to be a better way,

  • but this will work, pretty sure.

  • Then I can draw the face.

  • (laughs) That's the same thing.

  • (bell dings)

  • Oops.

  • Okay, so of course, of course, if I'm, blegh,

  • if I'm along the x-axis, I want to do,

  • I'm drawing the quad like,

  • I'm always drawing the quad face forward.

  • So if this is my quad, if I'm along here, along the x-axis,

  • I need to actually do a y rotation.

  • If I'm along the, yes, of course, so that's it.

  • So what I needed to change here was not,

  • and this doesn't, since I'm drawing it forward,

  • I can just basically skip the whole z thing.

  • So I only need to do a rotation

  • if I'm on the x-axis of a y, if I'm on,

  • if the normal is along the y-axis, rotate by x.

  • And then,

  • here we go, now, (bell dings)

  • we have the Rubik's Cube.

  • And, guess what, I can turn it.

  • Wait, huh?

  • So now I'm turning it.

  • And nothing's happening.

  • Guess what.

  • Now that I have these faces,

  • what happens when I actually turn a Rubik's Cube?

  • The faces also rotate.

  • The normals need to rotate.

  • This is the last piece.

  • I want to animate it turning, but this is the last piece

  • to at least have it so that I can do all the moves.

  • Actually I need to add directions and maybe make

  • the keyboard controls make a little bit more sense.

  • But.

  • So, now what I need is I need inside the face,

  • I need a turn x.

  • I need a turn y.

  • And I need a turn z.

  • So I need to be able to rotate any given face.

  • Once again I can use matrix transformations.

  • Only this time, and I've done this before.

  • I did this in my video about doing 3D rendering

  • without the 3D renderer, with our own matrix math.

  • And so what I need to do here,

  • let me pull up the Wikipedia page.

  • So here they are, these are the three rotation matrices

  • for doing an x rotation, a y rotation, and a z rotation.

  • And all I need to do is apply

  • those rotations to the normal vector.

  • So, I'm sure I could use PMatrix for this,

  • but for whatever reason, I think I might

  • have ane asier time just writing this out.

  • Because ultimately what I'm saying is,

  • let me make a new Pvector.

  • I'll just call this V two.

  • And the V two dot x equals the normal dot x times sine.

  • So I need an angle here.

  • Normal dot x times cosine of the angle

  • minus normal dot y times sine of the angle.

  • And then v two dot y equals normal dot x times sine

  • of the angle plus normal dot y times cosine of the angle.

  • And then v two dot z equals z.

  • And then I could say normal equals v two.

  • So I have just rotated the normal according to the x-axis.

  • Sorry, this should be normal dot z.

  • Now here's the thing, I'm a little worried we're

  • going to run into some rounding weirdness.

  • So what I probably should do is say,

  • let's just put round around all of these.

  • So this should be turning the face.

  • And this math I sort of did by looking at the formula

  • a little bit by memory, but I go through this particular

  • formula in detail in the other matrix transformation video.

  • So now, and somebody in the chat

  • will point out if I may wrong.

  • So now I should be able to take exactly this same thing

  • and put it in turn Y.

  • And the difference is, with turn y,

  • I am changing x and z.

  • So this is x times cosine,

  • this should have also a float angle.

  • This would be x, and this will be z.

  • This is z, this is x, and this is z.

  • And this is y.

  • So that's just the matrix take and applied

  • to x and z, because y, if I'm turning

  • along the y-axis, y stays the same.

  • Oh wait, this was z, by the way.

  • I've got them in the wrong place.

  • This was z, because z is fixed.

  • This is y because y is fixed.

  • And now, let me copy this into here and this will be x.

  • I think this should turn all the normals.

  • And if it's turning the normals, they'll display correctly.

  • So what's a rotation that I'm doing?

  • So how do I have this working?

  • When I say turn z, for example,

  • I say qb update.

  • So I should also say here probably qb turn faces z.

  • Let's just do that, this is, yeah,

  • I mean this is awkward naming, but I need to,

  • yeah, I need to turn the faces.

  • I'll do that afterwards, it doesn't really matter which.

  • And so then I would add a function here, turn faces z.

  • And I would say for every face F, faces, and faces.

  • F dot turn z half pi.

  • What's the chance this works?

  • Very little.

  • Oh, wait a sec.

  • Wait a sec.

  • That's right.

  • Orange goes to there goes to there

  • goes to there goes to there, yes.

  • Oh, that one's working too.

  • Right, one, two, one two, one, two.

  • One, two.

  • (bell dings)

  • So close, alright.

  • Turn faces y.

  • And again, I think, there's probably a way

  • to refactor this so that these functions turn faces x.

  • Okay, let's do y, which was,

  • oh, wait wait wait wait.

  • I need to call those.

  • Turn faces z.

  • Turn faces y.

  • Turn faces x.

  • And I forget what my key commands are.

  • By the way, guess what, we can get rid of this thing.

  • All this nonsense that I had before,

  • I'm not using this anymore.

  • My code is so much nicer.

  • So now, where's my key commands?

  • Let's just put this in its own tab.

  • So right now if I do y, this should be the top four.

  • Oh, that's the bottom, that's fine.

  • Five

  • is x.

  • Three is the top.

  • And then, can I start combining these?

  • Yeah.

  • I'm shuffling the cube.

  • I think this is good.

  • I got to do some like real testing of this

  • to make sure this is really working.

  • One thing I need to do is I need to add direction.

  • So I need to be able to add a direction for turning here.

  • So in these turn functions, I should also have a direction.

  • So each one should get a direction.

  • Then, the direction is half pi, direction times half pi.

  • And turned face is z, with that direction.

  • So rotate according to the direction.

  • And then rotate according to the direction.

  • So now in turn faces,

  • these should all also have a direction.

  • And then now, alright, so let's think about this.

  • So z is forward.

  • So let's use F.

  • Hm.

  • What's a good set of key commands for all the moves?

  • So all the moves that I have are forward,

  • back,

  • up,

  • down,

  • right, left.

  • So I'll use those keys.

  • And lower case will be in a positive direction,

  • upper case will mean negative direction.

  • So the case f is turn z one, one.

  • The case capital F is turn z negative one, negative one.

  • That's forward.

  • Then, case b would be also turn z, but one.

  • One.

  • And B would be one, negative one.

  • Alright, I'm going to do this silently

  • and this could get fast-forwarded if it needed to.

  • (laid-back music)

  • (bell dings)

  • Alright, I have now put in every possible move.

  • Front is turn z with a z of one either direction.

  • Back is a z of negative one, either direction.

  • Then y, then x, so then up then down is y.

  • Then left and right is turn x.

  • It does occur to me that I could just have a generic

  • turn function and I give it the normal vector of the axis.

  • Or the vector of the axis itself.

  • But anyway, this I believe should work.

  • Let's see.

  • So if I want to do a front, I should just see

  • that front face, which is the green face, turn.

  • Which I am seeing.

  • Back should be the back face.

  • Up, oh, the up, is down.

  • So up is down, whatever,

  • 'cause the y points in the other direfction.

  • Down is the top, ah, you know, spin it around.

  • Up is the top, as long as I'm consistent.

  • Down goes this way.

  • Right, okay, now this is right.

  • And left.

  • Now are my clockwise and counter-clockwise things working?

  • So front, which was the green face,

  • goes this way, and capital F should go the other way.

  • Yep, so now let's see.

  • If I turn front, and then now let me do the left,

  • which is there, that's the right.

  • Doesn't matter, long as it's one side.

  • It's working.

  • I think this is good.

  • Here's a way that I could test this.

  • What I'm going to do now to finish off this video

  • is run a set of moves shuffling it

  • and then run those moves backwards.

  • So to do that, let me get, let me make a char array.

  • All moves is just f,

  • front, back, up, down,

  • left,

  • right.

  • And I could deal with the capitals, you know what,

  • I should make these individual strings.

  • So what I'm going to do, and I'm going to

  • make a string called sequence.

  • And what I'm going to do is in setup,

  • I'm going to say for int i equals zero,

  • i is less than, let's just do 10 moves.

  • A sequence

  • index i equals int.

  • So let me get an index.

  • Which is a random number between all,

  • that's an index into the all moves array.

  • Sequence plus equals,

  • oh, I'm already using index.

  • So let me just say r.

  • Sequence plus equals all moves r.

  • So if I do that, all movies, all moves,

  • this would be, you can see, there should be a sequence here.

  • You can't really see that.

  • This is my sequence.

  • Up, forward, right right right, left, up, right, down, back.

  • And just for plausible deniability here, let's just say

  • if random, I'm going to, random is less than .5.

  • We'll do this.

  • Otherwise we'll do that to upper case.

  • So now I should have my sequence.

  • And there you go, there's my sequence, down, up,

  • right, up, down, forward, up, back, left, left.

  • Okay.

  • So now what I want to do is I want to run through that sequence.

  • So I'm going to use, I'm actually going to use that int,

  • I'm just going to use counter equals zero.

  • And I'm going to say in draw,

  • char move equals sequence char

  • at counter, counter plus plus.

  • And I'm just going to do this,

  • if counter is less than sequence dot length.

  • Okay.

  • Counter is less than sequence dot length, I'm going to get

  • the move, and then I'm going to do apply move, move.

  • And now in controls, this should

  • really be apply move char move.

  • And then switch move.

  • So I could if I want to control it by key pressed,

  • I could still say key, apply move key.

  • But I don't want to control it by key press,

  • I want to control it by these moves, so let's run this.

  • (laughs) It did it really fast.

  • Because see, normally the rendering engine

  • takes a minute to spin up.

  • So I'm going to do this.

  • Boolean started equals false.

  • And I'm going to say in key pressed,

  • if key equals the space, then started equals true.

  • So if I press the space bar, start things going.

  • And then I'm going to say here in draw,

  • if started, so it won't do this shuffling now.

  • It won't do the shuffling right now

  • until I press the space bar.

  • See, it shuffled it.

  • But let's slow that down just so we can see it.

  • If frame count modulus, you know, 20 equals zero,

  • that's going to only do that every 20 frames.

  • So I hit the space bar.

  • And you can see it should be, I mean,

  • it should be doing this sequence right now.

  • It should be doing that sequence.

  • So when it gets to the end of the sequence,

  • I should go backwards through the sequence.

  • So sequence is less than, so what actually,

  • here's what I'm going to do.

  • After I make this sequence, now, I'm going to say

  • for int i equals zero, i is less than sequence dot length.

  • I plus plus.

  • Now what I want to do is say,

  • so now I need to say that move, but the other direction.

  • So char move, or actually, so,

  • string next move equals sequence char at i.

  • Oh, but I want to go backwards.

  • So I want to go from sequence dot length

  • minus one

  • all the way down to zero, i minus minus.

  • And then I want to get each one of those characters

  • what's wrong with char at i, it's not a string.

  • So I'll just convert it into a string.

  • (laughs) Well, that.

  • Can I do this to, oh, wait.

  • Oh, how do I flip the case?

  • Rrr.

  • Thank you to MC Seem in the chat

  • who just gave me a really nice suggestion.

  • I'm going to say flip case.

  • Sequence char at.

  • So I'm going to write my own flip case function.

  • It's going to be kind of ridiculous.

  • Flip case of char, any given character c.

  • And what I'm going to do is if,

  • so first I need to make it a string.

  • Sure I could just do that.

  • If s dot low two, I know it's lower case.

  • If it equals itself.

  • Right?

  • If the lower case version of a string is the same

  • as the string, and I think I want to say it this way,

  • if s equals s, this is just a little clearer,

  • if s equals s to lower case, then return s to upper case.

  • Otherwise, return s to lower case.

  • And people were giving good suggestion about using x

  • or flipping bits or adding the ASCII values.

  • That would be a nice way of doing it.

  • Apparently that's not a way you can make a string.

  • I'll just do this, think that works.

  • That's one way to make a string.

  • Plus equals flip case.

  • So, oh no, next move, yes.

  • Next move equals, flip the case,

  • and then sequence plus equals next move, alright?

  • Let's have it shuffle pretty fast.

  • Like every five frames.

  • And then let's see what happens.

  • (drum roll pattering)

  • Ah, it got an error.

  • Oh, plus equals, (laughs)

  • we don't need press two there.

  • Shuffle, shuffle, shuffle, shuffle, shuffle.

  • Reverse.

  • Ah.

  • (horns trumpeting)

  • Let's do, let's finish this off

  • with just doing like

  • 200 moves.

  • Let's forget about slowing it down.

  • And let's just enjoy this beauty.

  • Let's make it full screen.

  • Thank you for watching Part Two of my Rubik's Cube

  • coding challenge, where I am now shuffling the cube,

  • moving all the pieces around, then unshuffling it backwards

  • to make it appear as if it's solving, of course

  • it's not actually solving itself, it's just doing a sequence

  • and then turning it back, and there we go.

  • So the next step that I need to do is I need to be,

  • I want to see it animate, I think it would be much more

  • interesting to actually watch the faces themselves turn.

  • So that's just a little

  • animation thing, that'll come in part three.

  • Once I have that, then I can start to think about,

  • are there different techniques that I can try to apply

  • to have the cube solve itself automatically?

  • Thanks for watching this very long Part Two.

  • If you make your own version of this, if you've

  • figured out clever ways to refactor my code,

  • to visualize it in interesting different ways,

  • to make it more generic with different scales,

  • maybe you could make one of those pyramid,

  • it's not a cube, those Rubik's pyramids.

  • That would be great.

  • And I'll see you in a future video, goodbye.

  • (bell dings)

  • (upbeat music)

(whistle whistles)

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

編碼挑戰#142.2。魔術方塊第二部分 (Coding Challenge #142.2: Rubik's Cube Part 2)

  • 3 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字