跳轉到

場景與動作

場景是不可變的節點集合,節點有穩定的編號,另外還有一個相機。動作則是讓節點的屬性隨時間變化。兩者都不知道繪圖端的存在。

2D 場景

from kinetickit import (
    Animation,
    Bounds2D,
    Group2D,
    Line2D,
    Path2D,
    PathNode2D,
    Scene2D,
    Shape2D,
    Transform2D,
    Vec2,
    ease,
)

x_axis = Line2D(id="x-axis", start=Vec2(0, 0), end=Vec2(10, 0), width=1.5, color="#000000")
y_axis = Line2D(id="y-axis", start=Vec2(0, 0), end=Vec2(0, 8), width=1.5, color="#000000")
curve = PathNode2D.polyline(
    "curve", [(0, 7), (2, 4.5), (4, 3), (7, 1.6), (10, 1)], width=3, color="#000000", draw_progress=0.0
)
area = Shape2D(id="area", path=Path2D.rect(0, 0, 4, 3), fill="#bbbbbb", opacity=0.0)
plot = Group2D(id="plot", children=(x_axis, y_axis, area, curve))

scene = Scene2D(plot)
scene = scene.bind({scene.camera.ref("view"): Bounds2D(Vec2(-1, -1), Vec2(12, 9))})

draw = curve.animate.draw_progress.to(1.0, duration=1.5, easing=ease.in_out_cubic)
fade = area.animate.opacity.to(1.0, duration=0.8)
move = plot.animate.transform.to(Transform2D(translate=Vec2(1, 0.6)), duration=0.8)

Animation(scene, draw.then(fade).then(move)).save("scene2d.svg", width=455, height=315)

路徑逐段畫出,圖形淡入,整個群組移動;接著動作倒著播放。

路徑逐段畫出,圖形淡入,整個群組移動;接著動作倒著播放。

每種節點都能查到哪些屬性可以製作動畫:Point2D.animatable_properties() 回傳屬性名稱與數值型別,node.animate.<屬性> 則為其中一個屬性建立動作。顏色、向量、角度與變換各依自己的規則插補(參見插補與變換)。

Line2D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    width=2.0,
    cap='round',
    join='round',
    dash=(),
    draw_progress=1.0,
    start=Vec2(x=0.0, y=0.0),
    end=Vec2(x=1.0, y=0.0),
)

兩點之間的描邊線段。端點、顏色、線寬、虛線樣式與繪製進度都能製作動畫。

PathNode2D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    width=2.0,
    cap='round',
    join='round',
    dash=(),
    draw_progress=1.0,
    path=...,
)

開放或封閉的描邊 Path2D。它保留直線與 Bézier 命令,並支援路徑變形。PathNode2D.polyline(id, points) 是畫直線折線的捷徑。

Shape2D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    path=...,
    fill=...,
    fill_rule='nonzero',
    stroke=None,
    stroke_width=1.0,
    join='round',
    dash=(),
)

填色的路徑,可另外加描邊。用於區域、封閉輪廓與複合圖形。

Group2D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    children=(),
    transform=...,
    clip=None,
)

對一串有順序的子節點,統一套用變換、透明度與裁切的節點。

Text2D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    asset=None,
    position=Vec2(x=0.0, y=0.0),
    rotation=0.0,
    scale=1.0,
    color=...,
)

由排版轉接器產生、已定位的 TextAsset,縮放、變換與透明度都能製作動畫(參見文字排版)。

Camera2D(
    *,
    id='camera',
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    view=None,
    pan=Vec2(x=0.0, y=0.0),
    zoom=1.0,
)

定義 2D 世界中可見的矩形,以及它如何對應到輸出視窗。用 scene.bind({scene.camera.ref("view"): Bounds2D(...)}) 設定。視野會維持長寬比,所以輸出尺寸的比例不同時,會出現留白。

3D 場景

import numpy as np

from kinetickit import Animation, Camera3D, DirectionalLight3D, Point3D, Scene3D, Surface3D, Vec3
from kinetickit.styles import ColorScale, Material

xs = ys = np.linspace(-2, 2, 28)
X, Y = np.meshgrid(xs, ys)
Z = np.sin(np.hypot(X, Y) * 2.2) / (1 + np.hypot(X, Y))
surface = Surface3D.height_field(
    "surface", xs, ys, Z, colorscale=ColorScale.viridis_like(), material=Material(0.4, 0.6, 0.2, 24)
)
scene = Scene3D(
    surface,
    Point3D(id="peak", position=Vec3(0, 0, 1.0), size=6, color="#d62728"),
    DirectionalLight3D(id="sun"),
    camera=Camera3D(position=Vec3(5.5, -6.5, 4.2), target=Vec3(0, 0, 0.2)),
)
orbit = scene.camera.animate.position.to(Vec3(-6, -5.5, 4.2), duration=2.0)
Animation(scene, orbit).save("surface.gif", width=320, height=240, fps=10)

相機繞著打光的高度場曲面轉動。

相機繞著打光的高度場曲面轉動。

Scene3D(*nodes, overlay=(), camera=None)

不可變的 3D 場景,包含幾何、光源、群組與一個相機。

Point3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    position=Vec3(x=0.0, y=0.0, z=0.0),
    size=4.0,
    symbol='circle',
    color=...,
)

3D 空間中的點標記,位置、大小、顏色與透明度都能製作動畫。

Line3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    width=2.0,
    cap='round',
    join='round',
    dash=(),
    draw_progress=1.0,
    start=Vec3(x=0.0, y=0.0, z=0.0),
    end=Vec3(x=1.0, y=0.0, z=0.0),
)

單一 3D 線段,由目前的相機投影。

Polyline3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    width=2.0,
    cap='round',
    join='round',
    dash=(),
    draw_progress=1.0,
    points=...,
    closed=False,
)

由有順序的 3D 點組成的折線,畫成相連的線段。

Mesh3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    material=...,
    edge_color=None,
    edge_width=0.5,
    two_sided=True,
    mesh=...,
)

帶索引的頂點與面,拓撲會經過驗證,可選用每個面的顏色與材質設定。

Surface3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    color=...,
    material=...,
    edge_color=None,
    edge_width=0.5,
    two_sided=True,
    grid=...,
    colorscale=None,
    value_range=None,
)

由 \(x,y\) 網格與高度值建立的規則高度場曲面。常用的建構方式是 Surface3D.height_field(id, xs, ys, zs)。

Group3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    children=(),
    transform=...,
)

一組有順序的 3D 節點,共用同一個變換與透明度。

Camera3D(
    *,
    id='camera',
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    position=Vec3(x=5.0, y=-7.0, z=4.0),
    target=Vec3(x=0.0, y=0.0, z=0.0),
    up=Vec3(x=0.0, y=0.0, z=1.0),
    projection='perspective',
    fov=0.6981317007977318,
    ortho_height=6.0,
    near=0.1,
    far=1000.0,
)

透視或正交相機,有位置、目標、裁切平面與投影設定。它的屬性和一般節點一樣可以製作動畫,所以繞行只是一個 position 動作。

Vec3(x, y, z)
xfloat

x 分量。

yfloat

y 分量。

zfloat

z 分量。

3D 的點或方向。

ColorScale(stops)
stopstuple[tuple[float, Color | str], ...]

由 0 到 1、依序排列的 (位置, 顏色) 配對。

透過有序的色標,把數值對應到顏色。

Material(ambient=0.35, diffuse=0.65, specular=0.0, shininess=32.0)
ambientfloat預設值: 0.35

永遠被照亮的比例。

diffusefloat預設值: 0.65

隨光線變化的比例。

specularfloat預設值: 0.0

高光強度。

shininessfloat預設值: 32.0

高光的集中程度。

表面的環境光、漫射與鏡面反應。

DirectionalLight3D(
    *,
    id,
    opacity=1.0,
    z=0.0,
    visible=True,
    semantics=None,
    direction=Vec3(x=-0.4, y=0.6, z=-0.7),
    color=...,
    intensity=1.0,
)
idstr

穩定的編號。

光線行進的方向。

intensityfloat預設值: 1.0

亮度。

朝單一方向照射的光源,像太陽那樣。

組合時間

Motion

不可變的變化描述。動作可以依序、重疊、延遲、重複、反轉或裁切。

Timeline(motion, base, registry=...)

動作樹編譯後的形式,可以直接求出任意有效時間的屬性值。

Keyframe(time, value, easing=None, hold=False)

某個時間點的屬性值,也可指定通往下一個區間的緩動規則。hold=True 會讓這個值維持到下一個關鍵影格。

Keyframes(target, frames, easing=...)

同一屬性的關鍵影格序列,會檢查端點與時間順序是否合法。

motion = (
    a.animate.position.to(Vec2(5, 5), duration=1.0)
    .then(b.animate.opacity.to(1.0, duration=0.5))
    .with_(c.animate.size.to(9, duration=0.5), offset=1.0)
    .mark("settled")
)

then 在前一段結束時開始下一段。with_ 則在前一段內部的某個偏移時間開始。兩者的總長度見時間與緩動。

場景之間的轉場

為兩個場景建立動作。編號相同的節點會配成一對,並對相容的屬性做插補。

TransitionPolicy(enter='fade', exit='fade')

決定沒有對象或結構不相容的節點要如何進場、退場、交叉淡化或報錯。

路徑、網格與曲面都有專用的變形工具。自動正規化推不出你要的拓撲時,請明確提供對應方式(參見變形)。

留言