最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python使用SymPy解決Manim曲線繪制速度不均的問題

 更新時間:2026年06月03日 08:34:00   作者:databook  
這段文章詳細講解了使用SymPy進行弧長參數化以實現(xiàn)參數曲線均勻繪制的技術,通過計算弧長函數、反解參數值及數值求解,實現(xiàn)曲線繪制節(jié)奏均勻,提升視覺體驗,關鍵代碼示例及效果展示進一步說明了方法的有效性,需要的朋友可以參考下

如果你想繪制一個參數曲線,比如:極坐標玫瑰線 r = cos(5θ)

那么思路很簡單:算出曲線上的點,用 Create 一筆畫出來。

但是,寫完代碼一渲染,問題來了:

花瓣尖端“唰”地一下就過去了,中間部分卻慢吞吞的。整條曲線的繪制節(jié)奏忽快忽慢,看起來十分別扭。

問題的根源:我讓參數 θ\thetaθ均勻遞增,但曲線上點移動的實際距離并不是均勻的。參數變化快的地方,點就“飛”過去;參數變化慢的地方,點就“爬”過去。要讓畫筆勻速移動,必須讓參數按照弧長均勻分布——這就是弧長參數化。

手工做這件事幾乎不可能:求弧長得積分,反解參數得解方程,曲線稍微復雜一點就算不動了。

好在,我們有 SymPy

1. 痛點場景還原:一個具體的例子

先看一段“有毛病”的代碼,直觀感受一下問題:

from manim import *
import numpy as np

class BadRoseCurve(Scene):
    def construct(self):
        axes = Axes(x_range=[-3, 3], y_range=[-3, 3])
        self.add(axes)

        # 用累積密度法生成非均勻 theta:尖端稀疏(快),中部密集(慢)
        n_points = 500
        t = np.linspace(0, 2 * PI, n_points)
        # 密度函數:在 cos(5t)=0 處(花瓣中部)密度高,在 |cos(5t)|=1 處(尖端)密度低
        density = 1 + 3 * np.sin(5 * t) ** 2
        theta = np.cumsum(density)
        theta = theta / theta[-1] * PI  # 歸一化到 [0, π](k為奇數時只需π即可畫完)
        r = np.cos(5 * theta)
        x = r * 2 * np.cos(theta)
        y = r * 2 * np.sin(theta)

        points = [axes.c2p(x[i], y[i]) for i in range(n_points)]
        curve = VMobject(color=PINK, stroke_width=3)
        curve.set_points_as_corners(points)

        # 用 Create 畫曲線——速度明顯不均勻!
        self.play(Create(curve), run_time=5, rate_func=linear)
        self.wait()

運行這個動畫,你會看到:花瓣尖端幾幀就畫完了,而花瓣中部卻畫得很慢。

rate_func=linear 控制的是動畫進度的均勻,但曲線本身點的分布就是疏密不均的,所以視覺速度必然忽快忽慢。

2. SymPy 解決方案:弧長參數化三步走

要讓繪制速度均勻,核心思路是:生成一組讓相鄰點之間實際距離相等的參數值。具體分三步:

  1. 用積分算出弧長函數 L(θ)——從起點到參數 θ的弧長
  2. 算總弧長,等分出一組目標弧長值 s1,s2,...s_1, s_2, ...s1?,s2?,...
  3. 對每個Si,反解方程L(θ)=Si?,得到對應的 θi?

這三步,SymPy 都能幫上忙。

2.1 用 SymPy 推導弧長函數

import sympy as sp

theta = sp.Symbol('theta', real=True)
n = 5  # 五瓣玫瑰線

# 極坐標方程(注意這里放大了2倍,與痛點代碼中的 r*2 保持一致)
r = 2 * sp.cos(n * theta)

# 極坐標 → 直角坐標(符號推導,零誤差)
x = r * sp.cos(theta)  # x = r(θ)·cos(θ)
y = r * sp.sin(theta)  # y = r(θ)·sin(θ)

# 弧長微元:ds/dθ = sqrt((dx/dθ)2 + (dy/dθ)2)
dx_dtheta = sp.diff(x, theta)
dy_dtheta = sp.diff(y, theta)

# 弧長微元表達式(注意:這里不算積分,只保留被積函數)
ds_dtheta = sp.sqrt(dx_dtheta**2 + dy_dtheta**2)
print("弧長微元 ds/dθ =", sp.simplify(ds_dtheta))

# 運行結果:
'''
弧長微元 ds/dθ = 2*sqrt((2*sin(4*theta) + 
                     3*sin(6*theta))**2 + 
                     (2*cos(4*theta) -
                     3*cos(6*theta))**2)
'''

SymPy 會輸出一個橢圓積分形式的表達式——這是正常的,很多曲線的弧長都沒有初等表達式。

沒關系,數值求解一樣好用。

2.2 用 nsolve 反解等弧長參數點

import numpy as np
from scipy.integrate import quad
from scipy.optimize import bisect  # 數值求根,穩(wěn)定且快

# 把弧長微元轉成可數值計算的函數
ds_func = sp.lambdify(theta, ds_dtheta, 'numpy')

# 數值弧長函數:L(t) = ∫?^t ds
def arc_length(t):
    """計算從 0 到 t 的弧長"""
    result, _ = quad(ds_func, 0, t, limit=100)
    return result

# 計算總弧長
total_length = arc_length(2 * np.pi)
print(f"總弧長: {total_length:.4f}")

# 等分弧長,用數值求根反解對應的 theta
N = 500
s_values = np.linspace(0, total_length, N)
theta_vals = []

# 輔助函數:f(t) = L(t) - s,我們要找 f(t)=0 的根
def f(t, s):
    return arc_length(t) - s

for i, s in enumerate(s_values):
    if i == 0:
        theta_vals.append(0.0)  # s=0 對應 theta=0
        continue

    # 初始搜索區(qū)間:用上一個 theta 作為左邊界
    # 弧長是單調遞增的,所以解一定在 [左邊界, 右邊界] 之間
    left = theta_vals[-1]  # 上一個已解出的 theta
    right = left + 0.5     # 向右擴展,足夠覆蓋下一個等分點

    # 擴展右邊界直到 f(right, s) > 0(確保根在區(qū)間內)
    while f(right, s) < 0:
        right += 0.5

    # 二分法求根(穩(wěn)定、快速)
    sol = bisect(f, left, right, args=(s,), xtol=1e-8)
    theta_vals.append(float(sol))

theta_vals = np.array(theta_vals)

代碼要點

  • sp.lambdify 把符號表達式編譯成 NumPy 函數,求值快
  • sp.nsolve 數值解方程,給定一個好猜測值能顯著加速
  • 猜測值用“弧長占比 × 總參數范圍”做線性估計,足夠接近真實解

3. Manim 聯(lián)動實戰(zhàn)

把上面的計算和 Manim 動畫串起來,就是一份完整可運行的代碼:

from manim import *
import numpy as np
import sympy as sp
from scipy.integrate import quad
from scipy.optimize import bisect

class UniformRoseCurve(Scene):
    def construct(self):
        # ===== SymPy + 數值積分計算等弧長參數點 =====
        theta = sp.Symbol("theta", real=True)
        n = 5

        # 極坐標方程 r = 2*cos(5θ)
        r = 2 * sp.cos(n * theta)

        # 極坐標轉直角坐標
        x_expr = r * sp.cos(theta)
        y_expr = r * sp.sin(theta)

        # 弧長微元
        dx = sp.diff(x_expr, theta)
        dy = sp.diff(y_expr, theta)
        ds_dtheta = sp.sqrt(dx**2 + dy**2)

        # 數值弧長函數
        ds_func = sp.lambdify(theta, ds_dtheta, "numpy")

        def arc_length(t):
            val, _ = quad(ds_func, 0, t, limit=100)
            return val

        # 總弧長(k為奇數時只需π即可畫完)
        total_len = arc_length(np.pi)
        print(f"總弧長: {total_len:.4f}")

        # 用二分法反解等弧長 theta(速度快)
        N = 500
        s_vals = np.linspace(0, total_len, N)
        theta_vals = [0.0]

        def f(t, s):
            return arc_length(t) - s

        for i in range(1, N):
            s = s_vals[i]
            left = theta_vals[-1]
            right = left + 0.5
            # 擴展右邊界直到 f(right) > 0
            while f(right, s) < 0:
                right += 0.5
            sol = bisect(f, left, right, args=(s,), xtol=1e-8)
            theta_vals.append(float(sol))

        theta_vals = np.array(theta_vals)

        # 計算直角坐標點
        x_func = sp.lambdify(theta, x_expr, "numpy")
        y_func = sp.lambdify(theta, y_expr, "numpy")

        # ===== Manim 動畫 =====
        axes = Axes(x_range=[-3, 3], y_range=[-3, 3])
        self.add(axes)

        points = []
        for t in theta_vals:
            px = float(x_func(t))
            py = float(y_func(t))
            points.append(axes.c2p(px, py))

        curve = VMobject(color=PINK, stroke_width=3)
        curve.set_points_as_corners(points)

        self.play(Create(curve), run_time=5, rate_func=linear)
        self.wait()

4. 效果展示說明

運行 UniformRoseCurve,對比之前的 BadRoseCurve,差異一目了然:

  • 繪制過程均勻流暢:粉色線條以恒定速度從起點生長到終點,花瓣尖端不再“閃現(xiàn)”,花瓣中部也不再“拖沓”。整條曲線在 10 秒內被平穩(wěn)地畫完,節(jié)奏非常舒服。
  • 同一曲線,兩種體驗BadRoseCurve 由于刻意在花瓣中部堆了 3 倍密度,視覺上畫筆會在那里明顯減速;而弧長參數化版本抹平了所有速度波動,讓 rate_func=linear 真正發(fā)揮作用。
  • 弧長參數化的本質:雖然 theta_vals 的數值分布是不均勻的(花瓣尖端附近 θ 變化慢,中部變化快),但映射到平面后,相鄰點之間的實際距離完全相等。這就是“均勻”的真正含義——空間上的均勻,而非參數上的均勻。

5. 本期小結

  • 問題本質:均勻參數 ≠ 均勻弧長,直接用均勻參數畫曲線必然速度不均。
  • 解決思路:弧長參數化——先算弧長函數,再反解出等弧長分布的參數點。
  • SymPy 的角色
    • sp.diff → 求導得弧長微元
    • sp.integrate → 算弧長函數
    • sp.nsolve → 反解參數值
    • sp.lambdify → 表達式轉數值函數,高效求值
  • Manim 中的用法:把等弧長點集喂給 VMobject.set_points_as_corners(),再用 Create + rate_func=linear,即可實現(xiàn)真正勻速的曲線繪制。

以上就是Python使用SymPy解決Manim曲線繪制速度不均的問題的詳細內容,更多關于Python SymPy解決Manim繪制速度不均的資料請關注腳本之家其它相關文章!

相關文章

最新評論

通化市| 东丽区| 桃江县| 阿克苏市| 文化| 长春市| 永修县| 肥东县| 雷州市| 桃江县| 婺源县| 揭阳市| 那坡县| 玉环县| 成武县| 乐安县| 潜江市| 黄冈市| 安康市| 龙游县| 吕梁市| 临夏县| 塔河县| 闽侯县| 大城县| 马关县| 黄梅县| 拜泉县| 河池市| 论坛| 宁夏| 五大连池市| 民乐县| 宝鸡市| 乌拉特中旗| 杭锦旗| 陆河县| 搜索| 龙海市| 西充县| 澎湖县|