summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/InkReveal.fs
blob: 04c1675c3407fb1c484e535121b35fb34d17317f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
namespace LivingVillage.Desktop

open System

/// P74 启动开场过渡:纯程序实现的水墨式渐显。
/// 启动最初数帧用若干柔和墨滴(预乘 alpha 椭圆行,绝无整屏纯色/大黑块)从中心向外
/// 晕开,同时整体墨幕强度随帧 ease-out 消退,露出后方的天色/水面/屋脊/标题。
/// 全部为帧计数与画布尺寸的纯函数,确定性可复现;Kernel 不参与。
module InkReveal =

    /// 墨幕完全消散所需帧数(< LaunchScreen.durationFrames,先散墨再见标题)。
    let dissolveFrames = 96L

    /// 单滴墨的最大 alpha:始终 < 255,保证是柔光墨晕而非实心黑块。
    let maxAlpha = 200

    /// 墨滴种子:归一化中心 + 归一化基准半径 + 相位(纯数据,确定性布局)。
    let private seeds : (float32 * float32 * float32 * float32 * int) list =
        [ (0.22f, 0.30f, 0.14f, 0.10f, 0)
          (0.46f, 0.22f, 0.18f, 0.12f, 1)
          (0.74f, 0.34f, 0.12f, 0.10f, 2)
          (0.58f, 0.62f, 0.20f, 0.13f, 3)
          (0.30f, 0.70f, 0.16f, 0.11f, 4)
          (0.83f, 0.67f, 0.11f, 0.08f, 5) ]

    let private clamp01 (v: float32) = if v < 0.0f then 0.0f elif v > 1.0f then 1.0f else v

    /// 整体墨幕强度:起手 1,随帧 ease-out 退到 0(dissolveFrames 后为 0)。
    let inkStrength (frame: int64) : float32 =
        if frame <= 0L then 1.0f
        elif frame >= dissolveFrames then 0.0f
        else
            let t = clamp01 (float32 frame / float32 dissolveFrames)
            1.0f - t |> fun remaining -> remaining * remaining * remaining

    /// 一滴墨:像素中心/半径/alpha。alpha 已含墨幕强度,绘制方按 alpha 预乘 rgb。
    type Dab =
        { X: int
          Y: int
          Rx: int
          Ry: int
          Alpha: int }

    /// 当前帧的墨滴集合:消散过程中半径外扩、alpha 下降,读起来像墨在水中晕开。
    let dabs (frame: int64) (width: int) (height: int) : Dab list =
        let strength = inkStrength frame
        let expand = 1.0f + 0.9f * (1.0f - strength)
        seeds
        |> List.map (fun (cx, cy, brx, bry, phase) ->
            let wobble = 1.0f + 0.06f * float32 (sin (float (phase) * 1.7))
            { X = int (cx * float32 width)
              Y = int (cy * float32 height)
              Rx = max 1 (int (brx * float32 width * expand * wobble))
              Ry = max 1 (int (bry * float32 height * expand * wobble))
              Alpha = int (float32 maxAlpha * strength) })

    /// 墨色(深靛而非纯黑),绘制时与 alpha 预乘。
    let inkRgb : int * int * int = (26, 36, 52)