backend/gsap API

パッケージ Luna-Flow/geometry3d/backend/gsap は、フロントエンドの DrawList を SVG ポリゴンとして描き、GSAP のタイムラインでアニメーションを駆動します。三角形は遠いものから近いものへ並べられ(ペインターズアルゴリズム)、<svg> 要素内の再利用可能な <polygon> ノードに書き込まれます。GsapPlayer は、現在時刻で MoonBit にコールバックする GSAP タイムラインを包みます。対応ターゲットは js のみで、moonbit-community/rabbita/dom を使い、GSAP 3 が globalThis.gsap にあることを前提とします。

import {
  "Luna-Flow/geometry3d/frontend",
  "Luna-Flow/geometry3d/backend/gsap",
  "moonbit-community/rabbita/dom",
}

supported_targets = "js"

並べ方とその限界は GSAP の設計で、プレーヤーページの作り方は GSAP チュートリアルで説明しています。

設定

GsapSvgColor

GsapSvgColor は各チャネルが [0,255][0, 255] の RGB 色です。

pub struct GsapSvgColor {
  red : Int
  green : Int
  blue : Int
}

GsapSvgColor::rgb

GsapSvgColor::rgb(r, g, b) は各チャネルを [0,255][0, 255] にクランプして色を作ります。

pub fn GsapSvgColor::rgb(Int, Int, Int) -> Self

GsapSvgRenderConfig

GsapSvgRenderConfig は、SVG のサイズ、背景色、前景色、濃淡の段階数をまとめたものです。

pub struct GsapSvgRenderConfig {
  width : Int
  height : Int
  background_color : GsapSvgColor
  foreground_color : GsapSvgColor
  shade_levels : Int
}

フィールドはパッケージ外からは読み取り専用です。描画のたびに設定は正規化されます。正でないサイズは 640 × 480 になり、チャネルはクランプされ、shade_levels は [2,256][2, 256] にクランプされます。

GsapSvgRenderConfig::default, GsapSvgRenderConfig::sized

GsapSvgRenderConfig::default() は 640 × 480、GsapSvgRenderConfig::sized(w, h) は指定したサイズを使います(正でない値は 640 または 480 になります)。どちらも背景色 rgb(7, 12, 22)、前景色 rgb(112, 226, 255)、256 段階の濃淡を使います。

pub fn GsapSvgRenderConfig::default() -> Self
pub fn GsapSvgRenderConfig::sized(Int, Int) -> Self
test "svg config" {
  let config = @gsap.GsapSvgRenderConfig::sized(-1, 360)
  debug_inspect((config.width, config.height), content="(640, 360)")
  let fg = config.foreground_color
  debug_inspect([fg.red, fg.green, fg.blue], content="[112, 226, 255]")
}

描画

render_draw_list

render_draw_list(svg, list, config) は <svg> 要素を更新して描画リストを表示します。

pub fn render_draw_list(@dom.Element, @frontend.DrawList, GsapSvgRenderConfig) -> Unit

三角形を 3 頂点の平均深度で並べ替え、最も遠いものを先にし、深度が等しいものはリストの順序を保ちます。要素の width、height、viewBox(0 0 W H)、role="img"、data-geometry3d-backend="gsap-svg" 属性を設定します。要素の直接の子として背景の <rect data-gsap-svg-background> とグループ <g data-gsap-svg-triangles> があることを保証します(初回はすべての子を置き換えます)。次にグループを三角形ごとに 1 つの <polygon> になるよう増減させ、各ポリゴンの points と fill を書き込みます。塗り色は、Canvas バックエンドと同様に、前景色を量子化された輝度 q/(L−1)q/(L - 1)(q=round⁡(clamp⁡(I)(L−1))q = \operatorname{round}(\operatorname{clamp}(I)(L - 1)))で拡大縮小したものです。ポリゴンは呼び出しの間で再利用されるため、繰り返し描画しても DOM ノードは作り直されません。

render_scene

render_scene(svg, scene, view, config) は @frontend.build_draw_list と render_draw_list を続けて実行します。

pub fn render_scene(@dom.Element, @frontend.Scene, @frontend.RenderView, GsapSvgRenderConfig) -> Unit
fn draw_on(svg : @dom.Element, angle : Double) -> Unit {
  let view = @frontend.RenderView::perspective(
    @view.Camera3::default(4.5),
    @view.PerspectiveProjection::new(@view.Viewport::new(640, 480), 400.0),
  )
  let scene = @frontend.Scene::single(
    @core.cube_mesh(1.0),
    @core.Transform3::rotation(0.4, angle, 0.0),
    @frontend.Light::default(),
  )
  @gsap.render_scene(svg, scene, view, @gsap.GsapSvgRenderConfig::sized(640, 480))
}

再生

GsapTimeline

GsapTimeline は JavaScript の GSAP タイムラインオブジェクトを指す不透明なハンドルです。

type GsapTimeline

GsapPlayer

GsapPlayer は、既定で一時停止状態にある固定長の GSAP タイムラインで、時刻を MoonBit のコールバックに通知します。

pub struct GsapPlayer {
  timeline : GsapTimeline
  duration_seconds : Double
}

GsapPlayer::new

GsapPlayer::new(duration, on_frame, repeat?) は、時計を 00 から duration 秒まで線形イージングでトゥイーンし、更新のたびに on_frame(t) を呼ぶ一時停止中のタイムラインを作ります。作成時にすぐ on_frame(0.0) も 1 回呼びます。

pub fn GsapPlayer::new(Double, (Double) -> Unit, repeat? : Int) -> Self

正でない長さは 1 秒になります。repeat は GSAP の繰り返し回数で、-1(既定)は無限に繰り返し、0 は 1 回だけ再生します。globalThis.gsap.timeline が使えない場合は JavaScript のエラーで中断します。

GsapPlayer::play, GsapPlayer::pause, GsapPlayer::reverse, GsapPlayer::restart, GsapPlayer::kill

これらのメソッドはタイムラインの play()、pause()、reverse()、restart()、kill() に転送されます。

pub fn GsapPlayer::play(Self) -> Unit
pub fn GsapPlayer::pause(Self) -> Unit
pub fn GsapPlayer::reverse(Self) -> Unit
pub fn GsapPlayer::restart(Self) -> Unit
pub fn GsapPlayer::kill(Self) -> Unit

kill の後でプレーヤーを再び使ってはいけません。

GsapPlayer::seek, GsapPlayer::time

player.seek(t) は再生ヘッドを t 秒([0,duration][0, \mathit{duration}] にクランプ)へ移動し、player.time() は再生ヘッドのローカル時刻を返します。

pub fn GsapPlayer::seek(Self, Double) -> Unit
pub fn GsapPlayer::time(Self) -> Double

GsapPlayer::progress, GsapPlayer::set_progress

player.progress() は再生ヘッドの位置を 1 回の反復に対する割合で返し、player.set_progress(p) は p を [0,1][0, 1] にクランプしてそれを設定します。

pub fn GsapPlayer::progress(Self) -> Double
pub fn GsapPlayer::set_progress(Self, Double) -> Unit

GsapPlayer::time_scale, GsapPlayer::set_time_scale

player.time_scale() は再生速度の倍率を返し、player.set_time_scale(k) はそれを設定します。正でない k は 1.0 に置き換えられます。

pub fn GsapPlayer::time_scale(Self) -> Double
pub fn GsapPlayer::set_time_scale(Self, Double) -> Unit

逆再生には負の倍率ではなく reverse を使ってください。

GsapPlayer::set_repeat

player.set_repeat(n) は繰り返し回数を設定します。-1 未満の値は -1(無限に繰り返す)に引き上げられます。

pub fn GsapPlayer::set_repeat(Self, Int) -> Unit

GsapPlayer::is_paused

player.is_paused() はタイムラインが一時停止中かどうかを返します。

pub fn GsapPlayer::is_paused(Self) -> Bool
fn start_player(svg : @dom.Element) -> @gsap.GsapPlayer {
  let player = @gsap.GsapPlayer::new(8.0, fn(t) { draw_on(svg, t * 0.8) }, repeat=-1)
  player.set_time_scale(0.5)
  player.play()
  player
}