diff --git a/src/web_page/scene.gleam b/src/web_page/scene.gleam index 85900f9..825e84e 100644 --- a/src/web_page/scene.gleam +++ b/src/web_page/scene.gleam @@ -5,11 +5,13 @@ import gleam/int import gleam/list import gleam/string import gleam/time/duration +import gleam_community/maths import lustre/attribute import lustre/effect import lustre/element.{type Element} import lustre/element/html import lustre/event +import prng/random import tiramisu import tiramisu/camera import tiramisu/material @@ -36,9 +38,20 @@ const hover_bump_amplitude = 0.38 const hover_bump_spread = 1.25 -const graph_x = -4.0 +// Randomize around the original composition without drifting under portfolio copy. +const spike_origin_x = -5.0 -const graph_z = -6.0 +const spike_origin_y = -5.0 + +const spike_minimum_offset = 0.25 + +const spike_maximum_offset = 1.0 + +const full_rotation = 6.283185307179586 + +const graph_offset_x = 1.0 + +const graph_offset_y = -1.0 const graph_bottom = -2.0 @@ -49,7 +62,7 @@ const graph_spine_width = 0.1 const graph_color = 0xff4f45 pub type Spike { - Spike(age_seconds: Float) + Spike(age_seconds: Float, x: Float, y: Float) NoSpike } @@ -76,6 +89,7 @@ type PortfolioLibrary { pub type Model { Model( seed: Int, + spike_seed: random.Seed, time: duration.Duration, spike: Spike, hover_bump: HoverBump, @@ -92,9 +106,12 @@ pub type Msg { } pub fn init(_flags: Nil) -> #(Model, effect.Effect(Msg)) { + let seed = int.random(1_000_000) + #( Model( - seed: int.random(1_000_000), + seed:, + spike_seed: random.new_seed(seed + 104_729), time: duration.seconds(0), spike: NoSpike, hover_bump: NoHover, @@ -141,10 +158,27 @@ pub fn update(model: Model, msg: Msg) -> #(Model, effect.Effect(Msg)) { #(Model(..model, time:, spike:, lines:), effect.none()) } - OpenPanel(panel) -> #( - Model(..model, spike: Spike(age_seconds: 0.0), open_panel: panel), - effect.none(), - ) + OpenPanel(panel) -> { + let #(angle, spike_seed) = + random.step(random.float(0.0, full_rotation), model.spike_seed) + let #(offset, spike_seed) = + random.step( + random.float(spike_minimum_offset, spike_maximum_offset), + spike_seed, + ) + let x = spike_origin_x +. maths.cos(angle) *. offset + let y = spike_origin_y +. maths.sin(angle) *. offset + + #( + Model( + ..model, + spike_seed:, + spike: Spike(age_seconds: 0.0, x:, y:), + open_panel: panel, + ), + effect.none(), + ) + } TerrainHovered(x:, y:) -> #( Model(..model, hover_bump: Hovering(x:, y:)), @@ -169,8 +203,7 @@ pub fn view(model: Model) -> Element(Msg) { ) }) - let terrain_children = case - model.spike, + let show_graph = { model.time |> duration.to_seconds() @@ -179,10 +212,13 @@ pub fn view(model: Model) -> Element(Msg) { } % 2 == 0 - { - Spike(value), True if value >. 0.1 && value <. 1.9 -> - list.append(terrain_lines, graph_bar_lines()) - _, _ -> terrain_lines + + let terrain_children = case model.spike { + Spike(age_seconds:, x:, y:) + if age_seconds >. 0.1 && age_seconds <. 1.9 && show_graph + -> list.append(terrain_lines, graph_bar_lines(x, y)) + Spike(..) -> terrain_lines + NoSpike -> terrain_lines } html.div( @@ -782,7 +818,7 @@ fn classes(groups: List(String)) -> String { string.join(groups, with: " ") } -fn graph_bar_lines() -> List(Element(Msg)) { +fn graph_bar_lines(spike_x: Float, spike_y: Float) -> List(Element(Msg)) { [ tiramisu.primitive( "graph-bar", @@ -796,9 +832,9 @@ fn graph_bar_lines() -> List(Element(Msg)) { material.opacity(1.0), material.side(material.Double), transform.position(vec3.Vec3( - x: graph_x, + x: spike_x +. graph_offset_x, y: graph_bottom +. graph_height /. 2.0, - z: graph_z, + z: spike_y +. graph_offset_y, )), transform.rotation(vec3.Vec3(0.0, 1.57, 0.0)), ], @@ -910,11 +946,11 @@ fn hover_bumped_field( fn advance_spike(spike: Spike, delta_time: duration.Duration) -> Spike { case spike { NoSpike -> NoSpike - Spike(age_seconds:) -> { + Spike(age_seconds:, x:, y:) -> { let age_seconds = age_seconds +. duration.to_seconds(delta_time) case age_seconds >=. spike_duration_seconds { True -> NoSpike - False -> Spike(age_seconds:) + False -> Spike(age_seconds:, x:, y:) } } } @@ -923,9 +959,9 @@ fn advance_spike(spike: Spike, delta_time: duration.Duration) -> Spike { fn spiked_field(base_field: field.Field, spike: Spike) -> field.Field { case spike { NoSpike -> fn(x, y) { base_field(x, y) +. basic_shape(x, y) } - Spike(age_seconds) -> fn(x, y) { + Spike(age_seconds:, x: center_x, y: center_y) -> fn(x, y) { base_field(x, y) - +. spike_height(x +. 5.0, y +. 5.0, age_seconds) + +. spike_height(x -. center_x, y -. center_y, age_seconds) +. basic_shape(x, y) } }