backend/canvas API
包 Luna-Flow/geometry3d/backend/canvas 使用 2D 上下文把前端的 DrawList 绘制到 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 trait 的实现。
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 绘制,颜色为 。只有当相邻像素段的明暗级别变化时才设置填充样式。亮度为 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 上下文,它会中止。
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)
}