简体   繁体   English

无法对带有 paper.js 的“组”使用“符号”

[英]Unable to use a `Symbol` for a `Group` with paper.js

I want to do an animation of a reaction-diffusion system with paper.js .我想用paper.js做一个反应扩散系统的paper.js

Here is a code which generates just one image:这是仅生成一张图像的代码:

 <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script> <style> canvas { width: 400px; height: 400px; border: black 3px solid; } </style> </head> <body> <script> function subm_index(M, x){ if (x<0) return x+M; if(x >= M) return xM; return x; } function update_concentrations(X, L, DA, DB, f, k){ var sum_a, sum_b, x1, y1, t; var m = XAlength; var n = XA[0].length; var A = new Array(m); var B = new Array(m); for(var i = 0; i < m; i++){ A[i] = new Array(n); B[i] = new Array(n); } for(var x = 0; x < m; x++) { for(var y = 0; y < n; y++){ sum_a = 0.0; sum_b = 0.0; for(var i = -1; i <= 1; i++){ for(var j = -1; j <= 1; j++){ x1 = subm_index(m, x - i); y1 = subm_index(n, y - j); sum_a = sum_a + L[i+1][j+1] * XA[x1][y1]; sum_b = sum_b + L[i+1][j+1] * XB[x1][y1]; } } t = XA[x][y]*XB[x][y]*XB[x][y]; A[x][y] = XA[x][y] + DA*sum_a - t + f*(1-XA[x][y]); B[x][y] = XB[x][y] + DB*sum_b + t - (k+f)*XB[x][y]; } } return {A: A, B: B}; } function iterate_Gray_Scott(X, L, DA, DB, f, k, n){ for(var i = 0; i < n; i++){ X = update_concentrations(X, L, DA, DB, f, k); } return X; } var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]]; var DA = 1; var DB = 0.5; var f = 0.0545; var k = 0.062; </script> <script type="text/paperscript" canvas="quad"> var pixels = 200; var gridSize = 2; var rectSize = 2; var A = new Array(pixels); var B = new Array(pixels); for(var i = 0; i < pixels; i++){ A[i] = new Array(pixels); B[i] = new Array(pixels); for(var j = 0; j < pixels; j++){ A[i][j] = 1; B[i][j] = Math.random() < 0.98 ? 0 : 1; } } var X = {A: A, B: B}; X = iterate_Gray_Scott(X, L, DA, DB, f, k, 1000); for(var y = 0; y < pixels; y++){ for(var x = 0; x < pixels; x++){ var color = { hue: XB[x][y] * 360, saturation: 1, brightness: 1 }; var path = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize)); path.fillColor = color; } } project.activeLayer.position = view.center; </script> <canvas id="quad" resize></canvas> </body> </html>

Now, here is a code which generates the animation:现在,这是一个生成动画的代码:

 <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script> <style> canvas { width: 400px; height: 400px; border: black 3px solid; } </style> </head> <body> <script> function subm_index(M, x){ if (x<0) return x+M; if(x >= M) return xM; return x; } function update_concentrations(X, L, DA, DB, f, k){ var sum_a, sum_b, x1, y1, t; var m = XAlength; var n = XA[0].length; var A = new Array(m); var B = new Array(m); for(var i = 0; i < m; i++){ A[i] = new Array(n); B[i] = new Array(n); } for(var x = 0; x < m; x++) { for(var y = 0; y < n; y++){ sum_a = 0.0; sum_b = 0.0; for(var i = -1; i <= 1; i++){ for(var j = -1; j <= 1; j++){ x1 = subm_index(m, x - i); y1 = subm_index(n, y - j); sum_a = sum_a + L[i+1][j+1] * XA[x1][y1]; sum_b = sum_b + L[i+1][j+1] * XB[x1][y1]; } } t = XA[x][y]*XB[x][y]*XB[x][y]; A[x][y] = XA[x][y] + DA*sum_a - t + f*(1-XA[x][y]); B[x][y] = XB[x][y] + DB*sum_b + t - (k+f)*XB[x][y]; } } return {A: A, B: B}; } function iterate_Gray_Scott(X, L, DA, DB, f, k, n){ for(var i = 0; i < n; i++){ X = update_concentrations(X, L, DA, DB, f, k); } return X; } var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]]; var DA = 1; var DB = 0.5; var f = 0.0545; var k = 0.062; </script> <script type="text/paperscript" canvas="quad"> var pixels = 200; var gridSize = 2; var rectSize = 2; var A = new Array(pixels); var B = new Array(pixels); var Paths = new Array(pixels); for(var i = 0; i < pixels; i++){ A[i] = new Array(pixels); B[i] = new Array(pixels); Paths[i] = new Array(pixels); for(var j = 0; j < pixels; j++){ A[i][j] = 1; B[i][j] = Math.random() < 0.99 ? 0 : 1; } } var X = {A: A, B: B}; for(var y = 0; y < pixels; y++){ for(var x = 0; x < pixels; x++){ var color = { hue: XB[x][y] * 360, saturation: 1, brightness: 1 }; Paths[x][y] = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize)); Paths[x][y].fillColor = color; } } var nframes = 100; var XX = new Array(nframes); XX[0] = X; for(var frm = 1; frm < nframes; frm++){ XX[frm] = iterate_Gray_Scott(XX[frm-1], L, DA, DB, f, k, frm); } project.activeLayer.position = view.center; function onFrame(event){ console.log(event.count); if(event.count < nframes){ for(var y = 0; y < pixels; y++){ for(var x = 0; x < pixels; x++){ var color = { hue: XX[event.count].B[x][y] * 360, saturation: 1, brightness: 1 }; Paths[x][y].fillColor = color; } } } } </script> <canvas id="quad" resize></canvas> </body> </html>

It works but the animation is not fluid enough.它有效,但动画不够流畅。 This is due to the double loop in onFrame , which consumes some time.这是由于onFrame的双循环,它消耗了一些时间。

So I firstly tried to create an array containing nframes Group elements, each group containing pixels*pixels Rectangle elements.所以我首先尝试创建一个包含nframes Group元素的数组,每个组包含pixels*pixels Rectangle元素。 But this generated an out-of-memory.但这产生了内存不足。

So I tried to use a Symbol to save some memory.所以我尝试使用Symbol来节省一些内存。 The code is below but it does not work, there is not a single image appearing.代码在下面,但它不起作用,没有出现单个图像。

 <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script> <style> canvas { width: 400px; height: 400px; border: black 3px solid; } </style> </head> <body> <script> function subm_index(M, x){ if (x<0) return x+M; if(x >= M) return xM; return x; } function update_concentrations(X, L, DA, DB, f, k){ var sum_a, sum_b, x1, y1, t; var m = XAlength; var n = XA[0].length; var A = new Array(m); var B = new Array(m); for(var i = 0; i < m; i++){ A[i] = new Array(n); B[i] = new Array(n); } for(var x = 0; x < m; x++) { for(var y = 0; y < n; y++){ sum_a = 0.0; sum_b = 0.0; for(var i = -1; i <= 1; i++){ for(var j = -1; j <= 1; j++){ x1 = subm_index(m, x - i); y1 = subm_index(n, y - j); sum_a = sum_a + L[i+1][j+1] * XA[x1][y1]; sum_b = sum_b + L[i+1][j+1] * XB[x1][y1]; } } t = XA[x][y]*XB[x][y]*XB[x][y]; A[x][y] = XA[x][y] + DA*sum_a - t + f*(1-XA[x][y]); B[x][y] = XB[x][y] + DB*sum_b + t - (k+f)*XB[x][y]; } } return {A: A, B: B}; } function iterate_Gray_Scott(X, L, DA, DB, f, k, n){ for(var i = 0; i < n; i++){ X = update_concentrations(X, L, DA, DB, f, k); } return X; } var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]]; var DA = 1; var DB = 0.5; var f = 0.0545; var k = 0.062; </script> <script type="text/paperscript" canvas="quad"> var pixels = 50; var gridSize = 2; var rectSize = 2; var A = new Array(pixels); var B = new Array(pixels); var Paths = new Array(pixels); for(var i = 0; i < pixels; i++){ A[i] = new Array(pixels); B[i] = new Array(pixels); Paths[i] = new Array(pixels); for(var j = 0; j < pixels; j++){ A[i][j] = 1; B[i][j] = Math.random() < 0.99 ? 0 : 1; } } var X = {A: A, B: B}; var nframes = 50; var XX = new Array(nframes); XX[0] = X; for(var frm = 1; frm < nframes; frm++){ XX[frm] = iterate_Gray_Scott(XX[frm-1], L, DA, DB, f, k, frm); } var Rects = []; for(var x = 0; x < pixels; x++){ for(var y = 0; y < pixels; y++){ var rect = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize)); var color = { hue: 1, saturation: 1, brightness: 1 }; rect.fillColor = color; rect.visible = false; Rects.push(rect); } } group = new Group(Rects); symbolGroup = new Symbol(group); var Groups = new Array(nframes); for(var frm = 0; frm < nframes; frm++){ Groups[frm] = symbolGroup.place(view.center); var k = 0; for(var x = 0; x < pixels; x++){ for(var y = 0; y < pixels; y++){ Groups[frm].definition.definition.children[k].fillColor = { hue: XX[frm].B[x][y] * 360, saturation: 1, brightness: 1 }; k = k+1; } } XX[frm] = null; // to free some memory } project.activeLayer.position = view.center; function onFrame(event){ if(event.count < nframes){ console.log(event.count); Groups[event.count].visible = true; if(event.count > 0){ Groups[event.count-1].visible = false; // to free some memory } } } </script> <canvas id="quad" resize></canvas> </body> </html>

Can you help me to fix this last code?你能帮我修复这最后一个代码吗?

Syntactically, I think there's a small issue here:在语法上,我认为这里有一个小问题:

Groups[frm].definition.definition.children[k].fillColor
|           |          |          |           ^ changing fill color of Rect
|           |          |          ^ accessing children of group
|           |          ^ should be item*
|           ^ SymbolDefinition
^ array of placed symbols

* [SymbolDefinition.item] http://paperjs.org/reference/symboldefinition/ * [SymbolDefinition.item] http://paperjs.org/reference/symboldefinition/

But I wouldn't spend a lot of time trying to get Symbols to render anyway.但无论如何我都不会花很多时间来尝试让 Symbols 进行渲染。

Reading about Symbols, it seems that making changes to the definition of a SymbolDefinition will update all placed instances as they point to a common source.阅读有关 Symbols 的文章,似乎对 SymbolDefinition 的定义进行更改将更新所有放置的实例,因为它们指向一个公共源。 As a technique to speed up your operations I think this will backfire.作为一种加速操作的技术,我认为这会适得其反。 You are still making the same number of fill operations as you were with the 2D array.您仍然进行与使用二维数组相同数量的填充操作。

An alternative approach is to use a sort of pseudo double-buffering.另一种方法是使用一种伪双缓冲。 Do your fill operations on a set of rectangles that are not on screen, then cache that group to bitmap and display that.在一组不在屏幕上的矩形上执行填充操作,然后将该组缓存到位图并显示它。 Each refresh will avoid unnecessary draw operations and will happen as a unit.每次刷新将避免不必要的绘制操作,并将作为一个单元发生。 Caching as bitmap will speed up the pixel rendering as well.缓存为位图也会加速像素渲染。

Paper.js doesn't seem to give you an obvious way to implement this, but you could probably use 2 layers to accomplish it. Paper.js 似乎没有给你一个明显的方法来实现它,但你可能使用 2 层来完成它。 Since only 1 layer will be "active" at a time, you could draw your rectangles on one, cache it as bitmap, then activate.由于一次只有 1 个图层处于“活动”状态,因此您可以在一个图层上绘制矩形,将其缓存为位图,然后激活。 Then draw the next set of rectangles on the 2nd layer, cache it as bitmap, and activate.然后在第二层上绘制下一组矩形,将其缓存为位图,然后激活。 Toggling back and forth might get you the performance enhancement you need.来回切换可能会使您获得所需的性能增强。

Alternatively, there's a thing called PaperScope which you might be able to leverage for the same technique.或者,有一种叫做PaperScope的东西,您可以利用它来实现相同的技术。 PaperScope uses a second canvas element. PaperScope 使用第二个画布元素。 You'll need to manually activate them as you toggle in the same way as layers.您需要在以与图层相同的方式切换时手动激活它们。

If you are aiming for performance, I that that in this case, you should get rid of Paper.js which adds an overhead to the rendering part.如果您的目标是性能,我认为在这种情况下,您应该摆脱 Paper.js,这会增加渲染部分的开销。
As a replacement, you can directly work with the Canvas API to render your drawing.作为替代,您可以直接使用 Canvas API 来渲染您的绘图。
Here is a quick example, based on your code, showing how you could use it.这是一个基于您的代码的快速示例,展示了如何使用它。

 <html> <head> <style> canvas { width : 400px; height : 400px; } </style> </head> <body> <canvas></canvas> <script> function subm_index(M, x) { if (x < 0) { return x + M; } if (x >= M) { return x - M; } return x; } function update_concentrations(X, L, DA, DB, f, k) { var sum_a, sum_b, x1, y1, t; var m = XAlength; var n = XA[0].length; var A = new Array(m); var B = new Array(m); for (var i = 0; i < m; i++) { A[i] = new Array(n); B[i] = new Array(n); } for (var x = 0; x < m; x++) { for (var y = 0; y < n; y++) { sum_a = 0.0; sum_b = 0.0; for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { x1 = subm_index(m, x - i); y1 = subm_index(n, y - j); sum_a = sum_a + L[i + 1][j + 1] * XA[x1][y1]; sum_b = sum_b + L[i + 1][j + 1] * XB[x1][y1]; } } t = XA[x][y] * XB[x][y] * XB[x][y]; A[x][y] = XA[x][y] + DA * sum_a - t + f * (1 - XA[x][y]); B[x][y] = XB[x][y] + DB * sum_b + t - (k + f) * XB[x][y]; } } return { A: A, B: B }; } function iterate_Gray_Scott(X, L, DA, DB, f, k, n) { for (var i = 0; i < n; i++) { X = update_concentrations(X, L, DA, DB, f, k); } return X; } var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]]; var DA = 1; var DB = 0.5; var f = 0.0545; var k = 0.062; var pixels = 200; var gridSize = 2; var rectSize = 2; var A = new Array(pixels); var B = new Array(pixels); var Paths = new Array(pixels); for (var i = 0; i < pixels; i++) { A[i] = new Array(pixels); B[i] = new Array(pixels); Paths[i] = new Array(pixels); for (var j = 0; j < pixels; j++) { A[i][j] = 1; B[i][j] = Math.random() < 0.99 ? 0 : 1; } } var X = { A: A, B: B }; var nframes = 50; var XX = new Array(nframes); XX[0] = X; for (var frm = 1; frm < nframes; frm++) { XX[frm] = iterate_Gray_Scott(XX[frm - 1], L, DA, DB, f, k, frm); } // // New code // // Get a reference to the canvas element. const canvas = document.querySelector('canvas'); // Make sure that canvas internal size fits its display size. canvas.width = 400; canvas.height = 400; // Get canvas context to be able to draw directly on it. const ctx = canvas.getContext('2d'); // Counter used to swicth between frames. let currentFrame = 0; // Launch the animation. animate(); function animate() { // Draw current frame. draw(); // Update frame counter (make it loop after last frame). currentFrame = currentFrame < nframes - 1 ? currentFrame + 1 : 0; // Do a recursive call to render next frame. requestAnimationFrame(animate); } function draw() { // For each pixel... for (var y = 0; y < pixels; y++) { for (var x = 0; x < pixels; x++) { // ...get the color... const hue = Math.round(XX[currentFrame].B[x][y] * 360); const color = `hsl(${hue}, 100%, 50%)`; // ...use it as canvas fill color... ctx.fillStyle = color; // ...and draw a scaled rectangle. ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize); } } } </script> </body> </html>

Another improvement idea could be to map your current data source to an array of prepared ImageData that you will later load in the canvas rather than looping over the pixels.另一个改进想法可能是将您当前的数据源映射到一个准备好的ImageData数组,您稍后将在画布中加载该数组,而不是在像素上循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM