summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/InkReveal.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 11:08:32 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 11:08:32 +0800
commit837fd81a2bd857011855ca1dc32e646629f14236 (patch)
tree896e33d5948bb5457cf0dd17494792f9aa631cd2 /src/LivingVillage.Desktop/InkReveal.fs
parentf3ad37b1d057566a9e5e01a7ee9850eb1c4598f4 (diff)
downloadliving-village-837fd81a2bd857011855ca1dc32e646629f14236.tar.gz
p74: 启动开场过渡(水墨式渐显)+ 开始界面布局回归
新增纯函数 InkReveal.fs(inkStrength ease-out 消散 + 6 滴预乘墨滴 dabs),DrawSplash 在场景层之上、标题之下绘制,与既有 reveal/veil 淡入叠加,形成可辨识水墨开场;单滴 alpha<255 且椭圆行落成,无实心黑块。开始界面沿用既有 开始/读档/退出(有档 继续)。新增 P74LaunchTransitionTests(4 例)与 LV_P74_SHOT/-analyze-p74 证据(5 帧墨幕非增、两两 diff>0,verdict=PASS)。HUD 结构未动;Desktop 350/350、Kernel 120/120、digest 与 p70 一致。
Diffstat (limited to 'src/LivingVillage.Desktop/InkReveal.fs')
-rw-r--r--src/LivingVillage.Desktop/InkReveal.fs58
1 files changed, 58 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/InkReveal.fs b/src/LivingVillage.Desktop/InkReveal.fs
new file mode 100644
index 0000000..04c1675
--- /dev/null
+++ b/src/LivingVillage.Desktop/InkReveal.fs
@@ -0,0 +1,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)