当前位置:   article > 正文

c++字符动画代码_P5.js动画中131个字符的代码说明

c++动画代码

c++字符动画代码

There is a tweet from Naoki Tsutae which has some nice looking graphics and some rather cryptic looking code.

Naoki Tsutae发了一条推文,其中有一些漂亮的图形和一些相当隐秘的代码。

https://twitter.com/ntsutae/status/1268820823952916486 https://twitter.com/ntsutae/status/1268820823952916486

The visuals are from a sketch Noaki created on OpenProcessing:

视觉效果来自在OpenProcessing上创建的草图Noaki:

The code to make all of this happen is this:

实现所有这些功能的代码是这样的:

That's just 131 characters. Pretty amazing!

只有131个字符 相当了不起!

那么它是怎样工作的? (So how does it work?)

As is to be expected (as this is rather terse and complex code), not everyone understands how this code works.

可以预料的是(这是非常简洁和复杂的代码),并不是每个人都了解此代码的工作方式。

The author isn’t much inclined to explain:

作者不太愿意解释:

As I enjoy puzzles, graphics, and teaching, I thought it might attempt to explain what is going on myself.

当我喜欢拼图,图形和教学时,我认为它可能试图解释自己的状况。

解压代码 (Unpacking the code)

Before we can look at how this works, we need to make the code more readable. You see, the code uses some tricks and shorthand notations to shave off extra bytes.

在查看它如何工作之前,我们需要使代码更具可读性。 您会看到,代码使用了一些技巧和速记符号来删除多余的字节。

Spreading things out gives us more of an idea about what is going on:

散布事物使我们对发生的事情有了更多的了解:

The things that make this code so terse are the same things that makes it harder to read.

使这段代码如此简洁的原因与难以阅读的原因相同。

  1. Hardly any variables are used. In fact, there are only three. To make matters worse, t,w, and x aren’t really descriptive names.

    几乎没有使用任何变量。 实际上,只有三个 。 更糟的是, twx并不是真正的描述性名称。

  2. The Ternary Operator is used. It works like this: condition ? expresionIfTrue : expresionIfFalse. This is a shorthand alternative to more verbose (and often more readable) if and else statements.

    使用三元运算符 。 它的工作方式如下: condition ? expresionIfTrue : expresionIfFalse condition ? expresionIfTrue : expresionIfFalse 。 这是更冗长(且通常更易读)的ifelse语句的简写方式。

If we unpack the code further and rename some variables, it looks like this:

如果我们进一步解压缩代码并重命名一些变量,则如下所示:

This code has exactly the same functionality. That in itself should give you an idea of how ingenious it is to fold all of that into so few characters.

此代码具有完全相同的功能。 这本身应该使您知道将所有内容折叠成很少的字符是多么的巧妙。

The verbose version, however, should make it easier to see what’s going on. Let's walk through it in more detail…

但是,冗长的版本应该可以更轻松地查看正在发生的情况。 让我们更详细地介绍一下…

开始 (Making a start)

The first things that happen are easy enough.

首先发生的事情很容易。

Image for post
  • A counter t is declared.

    声明一个计数器t

  • The draw() function is created. This is a special function used by P5.js (The library used by OpenProcessing to draw sketches). It is called in a continuous loop (unless noloop() is called).

    创建了draw ()函数。 这是P5.js (OpenProcessing用于绘制草图的库)所使用的特殊功能。 在连续循环中调用它(除非noloop () )。

  • The counter is incremented. This will happen each time draw() is called in the loop provided by P5.js.

    计数器增加。 每当在P5.js提供的循环中调用draw()时,就会发生这种情况。

  • A variable w that sets the canvas width is defined.

    定义了设置画布宽度的变量w

  • A canvas is created.

    创建一个画布。

The trick here is that the value of t is used to decide the outcome of the ternary operator before t is incremented. This makes sure the canvas is only created once. (As t is only equal to zero the first time draw() is called).

这里的技巧是价值t使用决定三元运算符的结果t递增。 这样可以确保画布仅创建一次。 (由于t仅等于零,因此第一次调用draw() )。

Comparing this to the verbose code, it would look like this:

将其与冗长的代码进行比较,它看起来像这样:

Here createCanvas() has been placed in setup() , which also a special function defined by P5.js. It is only called once in order to set everything up.

这里createCanvas()已放置在setup ()setup ()也是P5.js定义的特殊功能。 为了设置所有内容,只调用一次。

画一条线 (Drawing a line)

The next thing that happens is that a row (or line) is drawn.

接下来发生的事情是绘制了一行(或一行)。

Image for post

The line is drawn one pixel at a time using the point() function. The stroke() function determines which color the dot will be.

使用point ()函数一次将线绘制一个像素。 stroke ()函数确定点将是哪种颜色。

Each point is drawn in the forloop. The point() function draws a point at the given X and Y coordinates. As only one line is drawn, the Y is always zero. The X starts at the right-hand side of the image, where X is equal to the image width. It then moves to the right with each iteration of the for loop (as X keeps being decreased by one each iteration).

每个点都在for循环中绘制。 point()函数在给定的X和Y坐标处绘制一个点。 由于只画了一条线,所以Y始终为零。 X从图像的右侧开始,其中X等于图像的宽度。 然后,它在for循环的每次迭代中向右移动(因为X每次迭代都会减少一个)。

This continues until X is zero.

这一直持续到X为零为止。

That is a small trick being used here… The final-expression of the for loop is omitted.

这是在这里使用的一个小技巧... for 循环最终表达式被省略。

Adding this to our more verbose version of the code gives:

将其添加到我们更详细的代码版本中可以得到:

So how is the stroke (color) calculated?

那么笔划(颜色)如何计算?

第一块魔法 (The first piece of magic)

The first thing to understand is that the color will always be either black or white. This is because the value passed to stroke() is always either 0 or 720 (the value of w ). The zero value is interpreted as black, any value larger than 255 is interpreted as white.

首先要了解的是,颜色将始终为黑色或白色。 这是因为传递给stroke()值始终为0720 ( w的值)。 零值解释为黑色,大于255的任何值解释为白色。

Image for post

Here things get a bit complicated. This is the part most people will find hard to understand, as it requires a firm grasp of mathematics, especially algebra.

在这里,事情变得有些复杂。 这是大多数人难以理解的部分,因为它需要牢固地掌握数学,尤其是代数

The logic that decides which color is picked does so using:

决定选择哪种颜色的逻辑使用以下方法:

To understand the “how”, we’ll have to break the equation down into smaller parts. The full equation looks like this:

要理解“如何”,我们必须将等式分解为更小的部分。 完整的方程式如下所示:

Image for post

异或 (Xor)

Let’s we take the innermost part first, the “Xor”.

让我们首先介绍最里面的部分“ Xor”。

Image for post

For each line in the image, the x will be between 0 and 720. The t will start at 0 and just go up, and up, and up for each consecutive line. So the “minus” side of things will keep going further down as t rises and the “plus” will just keep rising.

对于图像中的每一行, x都将在0720之间。 t将从0开始,并且在每一行连续上升。 因此,事物的“负”面将随着t上升而进一步下降,而“正”数将不断上升。

So what happens ?

那么会发生什么呢?

Remember, “Xor” means that the outcome will only be true when one side of the equation true (or 1) and the other isfalse (or 0). The thing to know, though, is that this does not work on integers (like 720 or 97) but on their binary representation (like 1011010000 or 1100001). So for instance (using 720 for xand 97 for t), we get 350.

请记住,“异或”表示仅当方程式的一侧为true (或1 )而另一侧为false (或0 )时,结果才为true 。 但是,要知道的是,它不适用于整数(如720或97),但不适用于整数的二进制表示形式 (如1011010000或1100001)。 因此,例如(对于x使用720,对于t使用97),我们得到350

(720 - 97) XOR (720 + 97) = (623) XOR (817) = 350

How? Convert the decimals to binary and you will see:

怎么样? 将小数转换为二进制,您将看到:

  1001101111      623^ 1100110001    ^ 817============    =====  0101011110      350

This creates some of the randomness you see in the image, but it is also fundamental to the structure in the image.

这会创建您在图像中看到的一些随机性,但这也是图像结构的基础。

求幂 (Exponentiation)

The next thing that happens is pretty straightforward. The integer created by the Xor is raised to the power 3:

接下来发生的事情非常简单。 由Xor创建的整数被提高为幂3:

Image for post

So, using 350 from our previous example, this would give us:

因此,使用上一个示例中的350,可以得出:

350 * 350 * 350 = 42875000

That’s a pretty big number! Not to worry. That’s what the modulo is there for. But we’ll get to that later. First, something that might not be obvious…

这是一个很大的数字! 不要担心。 那就是模的用途。 但是我们稍后再讲。 首先,有些事情可能并不明显……

When the input is a positive integer, nothing special happens. The outcome will always be a positive integer. However, when the input is a negative number, the outcome will be negative for odd and positive for even exponents.

当输入为正整数时,不会发生任何特殊情况。 结果将始终为正整数。 但是,当输入为负数时,结果对奇数将为负,对偶数指数将为正。

Allow me to demonstrate:

请允许我演示:

-1^2 =                -1 * -1 =  1-1^3 =           -1 * -1 * -1 = -1-1^4 =      -1 * -1 * -1 * -1 =  1-1^5 = -1 * -1 * -1 * -1 * -1 = -1

And so on.

等等。

Because the number is raised an odd amount of times (in this case 3), the outcome could also be a negative number! So now you can probably already gues why there is an abs() coming up soon.

由于该数字的增加是奇数次(在这种情况下为3),因此结果也可能是负数! 因此,现在您可能已经猜出了为什么很快就会出现abs()

加成 (Addition)

The next part just adds t to the outcome of the exponentiation:

下一部分仅将t添加到取幂的结果中:

Image for post

So continuing our example of x being 720 and t being 97, that gives us:

因此,继续我们的x为720而t为97的示例,我们得到:

97 + 42875000 = 42875097

赦免 (Absolution)

The next part is another simple one. Remember that (because of the exponentation) we might have a negative number at this point?

下一部分是另一个简单的部分。 还记得(因为有指数)我们此时可能为负数?

The next bit fixes that for us:

下一点为我们解决了这个问题:

Image for post

This is the Math.abs() I mentioned before. Now we finally reach that modulo I mentioned before, too.

这是我之前提到的Math.abs() 。 现在,我们终于也达到了我之前提到的模数。

模数 (Modulo)

I have noticed that a lot of people know the modulo (or “remainder”) operator, but not everyone knows what it is called.

我注意到很多人都知道 (或“余数”)运算符,但并不是每个人都知道它什么。

Image for post

What it does, however, is easy enough. It divides the first number by the second and returns the remainder. So we get: 42875097 % 997 = 109

但是,它所做的很容易。 它将第一个数字除以第二个数字,然后返回余数。 因此我们得到: 42875097 % 997 = 109

If you want to be real strict about it, it would be:

如果您想对此严格一点,那就是:

42875097 % 997 = 42875097

Only one more mathematical step to go!

只需再走一个数学步骤!

少于 (Less than)

The very last thing that happens here is a comparison.

最后发生的是一个比较。

Image for post

This is what makes the final desicion whether we get a black or white pixel on our current line. If the output of the equation is less than 97 the result is black (or to be precise 0) otherwise it is white (through the value of w).

这就是最终决定我们在当前行上获得黑色还是白色像素的原因。 如果方程的输出小于97,则结果为黑色(准确地说是0 ),否则为白色(通过w的值)。

Pfew. That sure was a lot of math just to get one pixel, right?

f 可以肯定,要获得一个像素就需要很多数学运算,对吗?

If we were to put this in verbose code as a separate function, it would look like this:

如果我们将其作为详细的函数放在详细代码中,它将看起来像这样:

The fact that there is so much math involved, is why the image gets the structure is has. Now you might be thinking, if all of that was for just one line how the hell much further do we have to go?

涉及大量数学的事实图像具有结构的原因。 现在您可能在想,如果所有这些仅是一行 ,我们还需要走多远?

Well, with all of that hard math out of the way, there is only one more line of code to explain…

好吧,尽管所有这些困难的数学都无法解决,但仅需再解释一行代码即可……

创建下一行 (Creating the next line)

Once the line has been drawn, the loop provided by P5.js calls the draw() function again. The t++ happens and, from now on, createCanvas() will no longer be called. Instead, copy() is called.

绘制线条后,P5.js提供的循环将再次调用draw()函数。 t++发生了,从现在开始,将不再调用createCanvas() 。 相反, copy()调用copy()

Image for post

So what does copy do? As the name implies, it copies something. What it copies depends on the parameters it is given:

那么复制有什么作用? 顾名思义,它复制了某些内容。 它复制的内容取决于给定的参数:

copy(    sourceX, sourceY, sourceWidth, sourceHeight,    destinationX, destinationY, destinationWidth, destinationHeight)

Can you guess what copy(0, 0, w, w, 0, 1, w, w) does?

您能猜出copy(0, 0, w, w, 0, 1, w, w)是什么吗?

As you might have guessed, it copies the entire canvas from X and Y 0 up to X and Y w which is equal to the entire height and width (as the image is 720 square). So where does it copy it to? One line down.

您可能已经猜到了,它会将整个画布从X和Y 0复制到X和Y w ,这等于整个高度和宽度(因为图像为720平方)。 那么它将复制到哪里呢? 向下一行。

And then the next line is draw in the first row and the cycle repeats.

然后在第一行绘制下一行,并重复该循环。

This mechanism is also a neat trick to produce the “scrolling” effect.

这种机制也是产生“滚动”效果的巧妙技巧。

结语 (Wrapping up)

Now that we’ve gone through it, line by line, it should be clear what everything does. Did I miss anything? Do you have a question about something in this article? You are welcome to leave a comment or contact me on Twitter.

现在我们逐行进行了研究,现在应该清楚所有操作。 我想念什么吗? 您是否对本文有任何疑问? 欢迎您在Twitter上发表评论或与我联系。

I’ll be happy to explain!

我很乐意解释!

In closing, I would like to leave you this slightly modified version of the verbose code. It has all the variables in a config object, so you can more easily play around with it:

最后,我想向您介绍此冗长代码的经过稍微修改的版本。 它在config对象中具有所有变量,因此您可以更轻松地使用它:

I’ve made a sketch on OpenProcessing with this code, so you can have some fun with it! 本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】

推荐阅读
相关标签