[英]Scaling tangent lines in a bezier curve
我目前正在研究一個關於貝塞爾曲線及其屬性的項目。 我正在努力理解一個概念。 我似乎無法理解為什么你需要一個貝塞爾曲線中切線的比例。
這是我做動畫的代碼。
from tkinter import Tk, Canvas
from graphics_template import *
import math, time
vp_width, vp_height = 1024, 768
w_xmin, w_ymin, w_xmax = -3, -3, 10
w_ymax = w_ymin + (w_xmax - w_xmin)/vp_width * vp_height
B2 = [[0.0, 0.0], # point 0
[7.0, 0.0], # point 1
[1.0, 4.0]] # point 2
V_pos = [] # position
V_vec = [] # velocity vector
animation_done = False
DELTA_TDRAW = 0.02 # 50 fps
def eval_Bezier2(P, t):
# P(t) = (1-t)^2P[0] + 2t(1-t) P[1] + t^2P[2]
res = [0.0, 0.0]
for xy in range(2):
res[xy] = (1-t)*(1-t)*P[0][xy] + 2*t*(1-t)*P[1][xy] + t*t*P[2][xy]
return res
def eval_dBezier2(P, t):
# P'(t) = -2(1-t)P[0] + 2(1-t)P[1]-2tP[1] + 2tP[2]
res = [0.0, 0.0]
for xy in range(2):
res[xy] = -2*(1-t)*P[0][xy] + 2*(1-t)*P[1][xy]-2*t*P[1][xy] + 2*t*P[2][xy]
return res
def draw_Bezier (P, nsteps):
xi = P[0][0]
yi = P[0][1]
t_delta = 1/nsteps
t = t_delta
for ti in range(nsteps):
p = eval_Bezier2(P, t)
draw_line(canvas, xi, yi, p[0], p[1], rgb_col(255, 0 ,0))
draw_small_square(canvas, xi, yi, rgb_col(255, 255, 0))
xi = p[0]
yi = p[1]
t += t_delta
for i in range(len(P)):
draw_small_square(canvas, P[i][0], P[i][1], rgb_col(0, 255, 0))
def do_animation (t):
global animation_done
v_factor = 5 # reparameterization
u = t/v_factor
if (t > v_factor): #animation stops at t = v_factor
animation_done = True
else:
current_pos = eval_Bezier2(B2, u);
V_pos[0] = current_pos[0]
V_pos[1] = current_pos[1]
current_vel = eval_dBezier2(B2, u);
V_vec[0] = current_vel[0]
V_vec[1] = current_vel[1]
def draw_scene ():
draw_grid(canvas)
draw_axis(canvas)
draw_Bezier(B2, 20)
draw_dot(canvas, V_pos[0], V_pos[1], rgb_col(0,255,0))
draw_line(canvas, V_pos[0], V_pos[1], (V_pos[0] + V_vec[0]), (V_pos[1] + V_vec[1]), rgb_col(0,255,0))
def init_scene ():
#no data inits needed
V_pos.append(0.0)
V_pos.append(0.0)
V_vec.append(0.0)
V_vec.append(0.0)
do_animation(0.0)
draw_scene()
window = Tk()
canvas = Canvas(window, width=vp_width, height=vp_height, bg=rgb_col(0,0,0))
canvas.pack()
init_graphics (vp_width, vp_height, w_xmin, w_ymin, w_xmax)
# time.perf_counter() -> float. Return the value (in fractional seconds)
# of a performance counter, i.e. a clock with the highest available resolution
# to measure a short duration. It does include time elapsed during sleep and
# is system-wide. The reference point of the returned value is undefined,
# so that only the difference between the results of consecutive calls is valid.
init_time = time.perf_counter()
prev_draw_time = 0
init_scene ()
while (not animation_done):
draw_dt = time.perf_counter() - init_time - prev_draw_time
if (draw_dt > DELTA_TDRAW): # 50 fps
prev_draw_time += DELTA_TDRAW
do_animation(prev_draw_time)
canvas.delete("all")
draw_scene()
canvas.update()
如您所見,這些公式有效且正確。 您還可以在 do_animation() 函數中看到,有一個 v_factor 使 t 更小。 這是因為動畫以每秒 50 幀的速度進行。 傳入的每個 t 為 0.02,除以 5 以使點在曲線上移動 5 秒,然后達到 u = 1。
您可能會注意到,我將速度矢量除以 v_factor,我不理解這個概念。 我知道我的 v_factor 是一個比例,但我不明白我為什么需要它。 貝塞爾曲線的導數不只是在曲線中的每個點輸出正確的速度嗎? 我只知道當我從那里刪除 v_factor 時,我的速度矢量會變得太大而無法適應我的屏幕。 正如我所說,我知道這是一個規模,但我為什么需要它? 為什么 V_pos 向量不需要它? 我試圖從這個stackoverflow帖子中理解這個概念: 如何計算三次貝塞爾曲線的切線? ,但不幸的是沒有成功。
任何點B(t)
的速度是導數向量B'(t)
所以你得到了正確的部分,但是導數(記住不要稱它為切線:切線是沒有起點或終點的真線)在 a點告訴您“一個單位時間內”的速度。 而且不方便的是,整個貝塞爾曲線只涵蓋一個單位時間( t
參數從 0 到 1)...
因此,如果我們使用小於 1 的值來分隔t
值,就像您的曲線使用 20 個點一樣(因此t
間隔為1/20
),我們需要將所有這些導數按1/(point count)
縮放也。
如果我們看一下未縮放的衍生品,它們既笨重又龐大:
但是,如果我們將它們縮放 1/20,以便它們代表我們實際使用的時間間隔的速度矢量,事情看起來真的很好:
我們看到基於曲率的“真正的下一個點在哪里”和“前一個點加上它的速度矢量說它會在哪里”之間存在誤差:曲率越強,離“真實點”越遠 a “前一點 + 時間間隔內的速度”將是,我們可以通過使用更多點來緩解。
如果我們在曲線中只使用 8 個步驟,將導數按 1/8 縮放,事情看起來就不是那么好:
如果我們將其提高到 15,縮放比例為 1/15,事情開始看起來更好:
雖然你的 20 點曲線看起來不錯,但讓我們看看 1/50 縮放的 50 點給我們帶來了什么:
這很好。
其他答案非常好,但我只想指出,縮放導數的公式實際上是導數階數的倒數。
如果時間間隔從 0 到 5,則實際速度為v<sub>5</sub>(t) = v(t) / 5²
,實際加速度為a<sub>5</sub>(t) = a(t) / 5³
。 或更一般地說,如果您的區間從 0 到 x,則v(x, t) = v(t) / x²
和a(x, t) = a(t) / x³
。
因此,如果您的目標是縮放速度曲線,而不是將線分成幾段,您可以延長時間間隔。 請注意,對於 [0,1] 的常規間隔,在兩種情況下都將其除以 1,結果是一樣的! 這具有一些直觀的意義,因為您想要完成曲線的速度越快,點需要走的越快。
我沒有看到您將速度矢量除以v_factor
。 我猜你把那部分刪掉了?
無論如何,這個問題可能只是一些初等微積分。 您正在根據參數u
評估貝塞爾曲線,當您計算導數時,您會得到dx/du
和dy/du
。
似乎您想顯示速度 wrt 時間,因此您需要dx/dt
和dy/dt
。 根據鏈式法則, dx/dt = dx/du * du/dt
和du/dt
= 1/v_factor
。 這就是為什么你需要划分速度矢量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.