backend/canvas API
パッケージ Luna-Flow/geometry3d/backend/canvas は、フロントエンドの DrawList を 2D コンテキストで HTML <canvas> に描画します。描画リストをフロントエンドのソフトウェア深度バッファにラスタライズし、各ピクセルの輝度を単一の前景色の濃淡段階に量子化し、同じ濃淡が続く水平ランを fillRect で塗ります。対応ターゲットは js のみで、moonbit-community/rabbita の DOM バインディングを使います。
import {
"Luna-Flow/geometry3d/frontend",
"Luna-Flow/geometry3d/backend/canvas",
"moonbit-community/rabbita/dom",
}
supported_targets = "js"
モジュールに moonbit-community/rabbita を追加してください(moon add moonbit-community/rabbita@0.12.4)。シェーディングとランの結合は Canvas の設計で、これを使ったページの作り方は Canvas チュートリアルで説明しています。
設定
CanvasColor
CanvasColor は各チャネルが の RGB 色です。
pub struct CanvasColor {
red : Int
green : Int
blue : Int
}
CanvasColor::rgb
CanvasColor::rgb(r, g, b) は各チャネルを にクランプして色を作ります。
pub fn CanvasColor::rgb(Int, Int, Int) -> Self
CanvasRenderConfig
CanvasRenderConfig は、キャンバスのサイズ、背景色、前景色、濃淡の段階数をまとめたものです。
pub struct CanvasRenderConfig {
width : Int
height : Int
background_color : CanvasColor
foreground_color : CanvasColor
shade_levels : Int
}
フィールドはパッケージ外からは読み取り専用なので、設定は以下のコンストラクタから得ます。描画のたびに設定は正規化されます。正でないサイズは 640 × 480 になり、チャネルはクランプされ、shade_levels は にクランプされます。
CanvasRenderConfig::default, CanvasRenderConfig::sized
CanvasRenderConfig::default() は 640 × 480、CanvasRenderConfig::sized(w, h) は指定したサイズを使います(正でない値は 640 または 480 になります)。どちらも背景色 rgb(7, 12, 22)、前景色 rgb(112, 226, 255)、256 段階の濃淡を使います。
pub fn CanvasRenderConfig::default() -> Self
pub fn CanvasRenderConfig::sized(Int, Int) -> Self
default は通常のコンストラクタであり、Default トレイトの実装ではありません。
test "canvas config" {
let config = @canvas.CanvasRenderConfig::sized(320, 0)
debug_inspect((config.width, config.height, config.shade_levels), content="(320, 480, 256)")
let c = @canvas.CanvasColor::rgb(-5, 128, 300)
debug_inspect([c.red, c.green, c.blue], content="[0, 128, 255]")
}
描画
render_draw_list
render_draw_list(context, list, config) は描画リストを 2D コンテキストに描きます。
pub fn render_draw_list(@dom.CanvasRenderingContext2D, @frontend.DrawList, CanvasRenderConfig) -> Unit
まず設定された領域全体を背景色で塗り、@frontend.draw_list_to_luma で設定サイズのままリストをラスタライズし、覆われた各ピクセルについて濃淡段階数 のもとで を計算します。次に、 が等しい覆われたピクセルの極大な水平ランごとに、高さ 1 ピクセルの fillRect を色 で 1 回呼びます。塗りつぶしスタイルは、連続するランの間で濃淡が変わるときだけ設定します。輝度 0 の覆われたピクセルは背景色ではなく黒で塗られます。描画リストは設定サイズと同じビューポートに投影されている必要があります。
render_scene
render_scene(context, scene, view, config) は @frontend.build_draw_list と render_draw_list を続けて実行します。
pub fn render_scene(@dom.CanvasRenderingContext2D, @frontend.Scene, @frontend.RenderView, CanvasRenderConfig) -> Unit
render_canvas
render_canvas(canvas, list, config) は canvas 要素の width と height 属性を設定サイズにし、2D コンテキストを取得して render_draw_list を呼びます。
pub fn render_canvas(@dom.HTMLCanvasElement, @frontend.DrawList, CanvasRenderConfig) -> Unit
要素に 2D コンテキストがない場合は中断(abort)します。
fn draw_cube_on(canvas : @dom.HTMLCanvasElement, angle : Double) -> Unit {
let config = @canvas.CanvasRenderConfig::sized(320, 240)
let view = @frontend.RenderView::perspective(
@view.Camera3::default(4.5),
@view.PerspectiveProjection::new(@view.Viewport::new(320, 240), 200.0),
)
let scene = @frontend.Scene::single(
@core.cube_mesh(1.0),
@core.Transform3::rotation(0.4, angle, 0.0),
@frontend.Light::default(),
)
@canvas.render_canvas(canvas, @frontend.build_draw_list(scene, view), config)
}