1

所以我在玩一些贝塞尔曲线的动画——只是学习如何使用 ipycanvas (0,10,2) 的一部分——我制作的动画真的让我头疼。我期望看到的是 4 个贝塞尔控制点之间的一组直线,它们在画布周围“弹跳”,贝塞尔曲线也随之移动。

我确实得到了移动的贝塞尔曲线——但控制点保持静止。更奇怪的是,他们在最后的位置上是静止的,曲线来迎接他们。

现在有时 Python 的结构和引用会变得有点棘手,所以如果你没有真正考虑清楚,有时会得到令人困惑的结果——这完全可能是正在发生的事情——但我不知所措。

因此,为了确保我不感到困惑,我在开始时打印了控制点(pts),然后将它们显示到画布上。这证实了我的猜想。通过量子隧道或其他一些神奇的时间旅行,这条线canvas.stroke_lines(pts)到达未来并抓住未来pts存在的阵列并将控制点保持在其最终状态。

其他所有使用都pts使用当前时间状态。

所以我需要知道的是 A) 物理定律是安全的,我只是太笨了,无法理解我自己的代码。B)我应该报告 ipycanvas 中有一些奇怪的错误。C)如何通过这种时间旅行代码获利——比如,我们可以用它来以某种方式分解大量数字吗?

from ipycanvas import Canvas, hold_canvas
import numpy as np

def rgb_to_hex(rgb):
    if len(rgb) == 3:
        return '#%02x%02x%02x' % rgb
    elif len(rgb) == 4:
        return '#%02x%02x%02x%02x' % rgb

def Bezier4(t, pts):
    p = t**np.arange(0, 4,1)
    M=np.matrix([[0,0,0,1],[0,0,3,-3],[0,3,-6,3],[1,-3,3,-1]])
    return np.asarray((p*M*pts))

canvas = Canvas(width=800, height=800)
display(canvas) # display the canvas in the output cell..
pts = np.random.randint(50, 750, size=[4, 2])   #choose random starting point
print(pts) #print so we can compare with ending state
d =  np.random.uniform(-4,4,size=[4,2])  #some random velocity vectors
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3)))  #some random color
canvas.font = '16px serif'  #font for displaying the changing pts array
with hold_canvas(canvas):
    for ani in range(300):
        #logic to bounce the points about...
        for n in range(0,len(pts)):
            pts[n]=pts[n] + d[n]
            if pts[n][0] >= 800 or pts[n][0] <= 0 :
                d[n][0] = - d[n][0]
            if pts[n][1] >= 800 or pts[n][1] <= 0 :
                d[n][1] = - d[n][1]
        #calculate the points needed to display a bezier curve
        B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,15)]
        #begin display output....
        canvas.clear()
        #first draw bezier curve...
        canvas.stroke_style = c
        canvas.stroke_lines(B)
        
        #Now draw control points
        canvas.stroke_style = rgb_to_hex((255,255,128, 50))
        canvas.stroke_lines(pts)
        
        #print the control points to the canvas so we can see them move
        canvas.stroke_style = rgb_to_hex((255,255,128, 150))
        canvas.stroke_text(str(pts), 10, 32)
        
        canvas.sleep(20)

说真的,我试图思考可能发生的事情,但我却一头雾水。由于 ipycanvas 正在与浏览器/javascript 对话,因此可能首先呈现帧的所有数据,并且用于保存pts数据的数组stroke_lines最终得到最终值......而 B 数组在每个循环中重新创建...... . 这是一个猜测。

4

1 回答 1

0

有两种方法可以使代码按预期运行并避免难看的时间旅行代码。第一种方法是将线的位置切换with hold_canvas(canvas):到循环内部。然而,这使得这canvas.sleep(20)条线相当无用。

canvas = Canvas(width=800, height=800)
display(canvas)
pts = np.random.randint(50, 750, size=[4, 2])
print(pts)
d =  np.random.uniform(-8,8,size=[4,2])
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3)))
canvas.font = '16px serif'
#with hold_canvas(canvas):
for ani in range(300):
    with hold_canvas(canvas):
        for n in range(0,len(pts)):
            if pts[n][0] > 800 or pts[n][0] < 0 :
                d[n][0] = -d[n][0]
            if pts[n][1] > 800 or pts[n][1] < 50 :
                d[n][1] = -d[n][1]
            pts[n]=pts[n] + d[n]

        B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,25)]
        canvas.clear()
        canvas.stroke_style = c
        canvas.stroke_lines(B)
        canvas.stroke_style = rgb_to_hex((255,255,128, 50))
        #pts2 = np.copy(pts)
        canvas.stroke_lines(pts)
        canvas.fill_style = rgb_to_hex((255,255,255, 150))
        canvas.fill_circles(pts.T[0], pts.T[1],np.array([4]*4))
        canvas.stroke_style = rgb_to_hex((255,255,128, 150))
        canvas.fill_text(str(pts), 10, 32)
        sleep(20/1000)
        #canvas.sleep(20)

在这个版本中,控制线按预期更新。这个版本更“实时”,因此sleep(20/1000)需要

另一种方法是确保pts制作副本并将其传递给canvas.stroke_lines

canvas = Canvas(width=800, height=800)
display(canvas)
pts = np.random.randint(50, 750, size=[4, 2])
print(pts)
d =  np.random.uniform(-8,8,size=[4,2])
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3)))
canvas.font = '16px serif'
with hold_canvas(canvas):
    for ani in range(300):
    #with hold_canvas(canvas):
        for n in range(0,len(pts)):
            if pts[n][0] > 800 or pts[n][0] < 0:
                d[n][0] = -d[n][0]
            if pts[n][1] > 800 or pts[n][1] < 50:
                d[n][1] = -d[n][1]
            pts[n]=pts[n] + d[n]

        B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,35)]
        canvas.clear()
        canvas.stroke_style = c
        canvas.stroke_lines(B)
        canvas.stroke_style = rgb_to_hex((255,255,128, 50))
        pts2 = np.copy(pts)
        canvas.stroke_lines(pts2)
        canvas.fill_style = rgb_to_hex((255,255,255, 150))
        canvas.fill_circles(pts.T[0], pts.T[1],np.array([4]*4))
        canvas.stroke_style = rgb_to_hex((255,255,128, 150))
        canvas.fill_text(str(pts), 10, 32)
        #sleep(20/1000)
        canvas.sleep(20)

我实际上找不到在 python 和浏览器之间传递的数据,但似乎很合乎逻辑的是,发生的事情是 pythonani在发送关于绘制内容的小部件指令之前完成了它的工作(和循环),并且pts发送的值是最后的。

(是的,我知道弹跳逻辑中有一个错误)

于 2022-01-09T07:47:02.530 回答