murrelet_livecode 0.1.2

livecode base functions for murrelet, a livecode framework
Documentation
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
use evalexpr::HashMapContext;
use murrelet_common::*;

use crate::{
    expr::{ExprWorldContextValues, IntoExprWorldContext, MixedEvalDefs},
    types::{AdditionalContextNode, LivecodeResult},
    unitcells::UnitCellContext,
};

#[derive(Debug, Clone)]
enum LivecodeWorldStateStage {
    Timeless,
    World(LiveCodeTimeInstantInfo),
    Unit(LiveCodeTimeInstantInfo),
    Lazy(LiveCodeTimeInstantInfo),
}
impl LivecodeWorldStateStage {
    fn add_step(&self, stage: LivecodeWorldStateStage) -> LivecodeWorldStateStage {
        // todo, i could start to represent the tree of steps.. but right now, just do the latest one
        stage
    }
}

#[derive(Debug, Clone)]
pub struct LivecodeWorldState {
    context: HashMapContext,
    stage: LivecodeWorldStateStage,
    assets: AssetsRef,
}
impl LivecodeWorldState {
    fn clone_ctx_and_add_world(
        evalexpr_func_ctx: &HashMapContext,
        livecode_src: &LivecodeSrc,
        maybe_time: Option<LiveCodeTimeInstantInfo>,
        maybe_node: Option<AdditionalContextNode>,
    ) -> LivecodeResult<HashMapContext> {
        let mut ctx = evalexpr_func_ctx.clone();

        let mut w = livecode_src.to_world_vals();
        if let Some(time) = maybe_time {
            w.extend(time.to_exec_funcs());
        }
        // add the world to the ctx
        let vals = ExprWorldContextValues::new(w);
        vals.update_ctx(&mut ctx)?;

        // add the node to the context
        if let Some(node) = maybe_node {
            node.eval_raw(&mut ctx)?;
        }

        Ok(ctx)
    }

    pub fn new<'a>(
        evalexpr_func_ctx: &HashMapContext,
        livecode_src: &LivecodeSrc,
        time: LiveCodeTimeInstantInfo,
        node: AdditionalContextNode,
        assets: AssetsRef,
    ) -> LivecodeResult<LivecodeWorldState> {
        let context =
            Self::clone_ctx_and_add_world(evalexpr_func_ctx, livecode_src, Some(time), Some(node))?;

        Ok(LivecodeWorldState {
            context,
            stage: LivecodeWorldStateStage::World(time),
            assets: assets.clone(),
        })
    }

    pub fn new_timeless(
        evalexpr_func_ctx: &HashMapContext,
        livecode_src: &LivecodeSrc,
    ) -> LivecodeResult<LivecodeWorldState> {
        let context = Self::clone_ctx_and_add_world(evalexpr_func_ctx, livecode_src, None, None)?;

        Ok(LivecodeWorldState {
            context,
            stage: LivecodeWorldStateStage::Timeless,
            assets: Assets::empty_ref(),
        })
    }

    // this should use the cached one if it exists, or return an error
    pub(crate) fn ctx(&self) -> &HashMapContext {
        &self.context
    }

    pub fn time(&self) -> LiveCodeTimeInstantInfo {
        // basically always world should have time, except when computing
        // the time component.
        match self.stage {
            LivecodeWorldStateStage::Timeless => panic!("checking time in a timeless world"),
            LivecodeWorldStateStage::World(t) => t,
            LivecodeWorldStateStage::Unit(t) => t,
            LivecodeWorldStateStage::Lazy(t) => t,
        }
    }

    pub fn actual_frame(&self) -> f32 {
        self.time().actual_frame()
    }

    pub fn actual_frame_u64(&self) -> u64 {
        self.time().actual_frame_u64()
    }

    pub fn should_debug(&self) -> bool {
        self.time().seconds_since_updated_realtime() < 0.1
    }

    pub fn is_on_bar(&self) -> bool {
        self.time().is_on_bar()
    }

    pub(crate) fn ctx_mut(&mut self) -> &mut HashMapContext {
        &mut self.context
    }

    pub fn update_with_defs(&mut self, more_defs: &MixedEvalDefs) -> LivecodeResult<()> {
        more_defs.update_ctx(self.ctx_mut())
    }

    pub fn clone_with_vals(
        &self,
        expr: ExprWorldContextValues,
        prefix: &str,
    ) -> LivecodeResult<LivecodeWorldState> {
        let mut lazy = self.clone_to_lazy(); // eh just need to clone

        expr.with_prefix(prefix).update_ctx(lazy.ctx_mut())?;

        Ok(lazy)
    }

    pub fn clone_to_unitcell(
        &self,
        unit_cell_ctx: &UnitCellContext,
        prefix: &str,
    ) -> LivecodeResult<LivecodeWorldState> {
        let mut context = self.context.clone();
        unit_cell_ctx
            .as_expr_world_context_values()
            .with_prefix(prefix)
            .update_ctx(&mut context)?;

        let r = LivecodeWorldState {
            context,
            stage: self
                .stage
                .add_step(LivecodeWorldStateStage::Unit(self.time())),
            assets: self.assets.clone(),
        };

        Ok(r)
    }

    pub fn clone_to_lazy(&self) -> Self {
        let context = self.context.clone();
        LivecodeWorldState {
            context,
            stage: self
                .stage
                .add_step(LivecodeWorldStateStage::Lazy(self.time())),
            assets: self.assets.clone(),
        }
    }

    pub fn asset_layer(&self, key: &str, layer_idx: usize) -> Option<Vec<Polyline>> {
        self.assets.asset_layer(key, layer_idx).cloned()
    }

    pub fn asset_layers_in_key(&self, key: &str) -> &[String] {
        self.assets.layer_for_key(key)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct LivecodeTimingConfig {
    pub bpm: f32,
    pub fps: f32,
    pub realtime: bool,
    pub beats_per_bar: f32,
}
impl LivecodeTimingConfig {
    fn seconds_from_config(&self, system_timing: LiveCodeTiming) -> f32 {
        if self.realtime {
            self.current_time_seconds_realtime(system_timing)
        } else {
            self.current_time_seconds_frame(system_timing)
        }
    }

    fn beat_from_config(&self, system_timing: LiveCodeTiming) -> f32 {
        let seconds = self.seconds_from_config(system_timing);
        self.seconds_to_beats(seconds)
    }

    fn seconds_to_beats(&self, s: f32) -> f32 {
        let minutes = s / 60.0;
        minutes * self.bpm
    }

    fn current_time_seconds_realtime(&self, system_timing: LiveCodeTiming) -> f32 {
        (MurreletTime::now() - system_timing.start).as_secs_f32()
    }

    fn current_time_seconds_frame(&self, system_timing: LiveCodeTiming) -> f32 {
        system_timing.frame as f32 / self.fps
    }
}

#[derive(Copy, Clone, Debug)]
pub struct LiveCodeTimeInstantInfo {
    timing_config: LivecodeTimingConfig,
    system_timing: LiveCodeTiming,
}
impl LiveCodeTimeInstantInfo {
    pub fn new(
        timing_config: LivecodeTimingConfig,
        system_timing: LiveCodeTiming,
    ) -> LiveCodeTimeInstantInfo {
        LiveCodeTimeInstantInfo {
            timing_config,
            system_timing,
        }
    }

    pub fn debug(&self) -> String {
        format!(
            "realtime: {}\nseconds: {:.01}\nbeat: {:.01}\nbar: {:.01} ({})\nframe: {}\nlast updated {:?}",
            self.timing_config.realtime,
            self.seconds(),
            self.beat(),
            self.bar(),
            self.is_on_bar(),
            self.actual_frame(),
            self.system_timing.last_config_update
        )
    }

    pub fn actual_frame_u64(&self) -> u64 {
        self.system_timing.frame
    }

    pub fn actual_frame(&self) -> f32 {
        self.system_timing.frame as f32
    }

    // magical
    pub fn beat(&self) -> f32 {
        self.timing_config.beat_from_config(self.system_timing)
    }

    pub fn bar(&self) -> f32 {
        self.beat() / self.timing_config.beats_per_bar
    }

    pub fn seconds(&self) -> f32 {
        self.timing_config.seconds_from_config(self.system_timing)
    }

    pub fn is_on_bar(&self) -> bool {
        // okay so, we want to know the prev beat.
        let prev_time = if self.timing_config.realtime {
            let render_time = self.system_timing.last_render_time;
            render_time.as_secs_f32()
        } else {
            let prev_frame = self.system_timing.frame - 1;
            prev_frame as f32 / self.timing_config.fps
        };

        // check if this beat rounds differently than the curr one
        let prev_beat = self.timing_config.seconds_to_beats(prev_time);

        let curr_beat_bar = self.bar().floor();
        let prev_beat_bar = (prev_beat / self.timing_config.beats_per_bar).floor();

        curr_beat_bar as i32 > prev_beat_bar as i32
    }

    pub fn seconds_since_updated_realtime(&self) -> f32 {
        // check when last updated
        let last_update_time = self.system_timing.last_config_update;
        // check how much time has passed since it was last updated
        let time = MurreletTime::now() - last_update_time;
        time.as_secs_f32()
    }

    fn seconds_since_updated_frame(&self) -> f32 {
        // check when last updated
        let last_update_time = self.system_timing.last_config_update_frame;
        // check how much time has passed since it was last updated
        let frames_since_updated = self.system_timing.frame - last_update_time;

        (frames_since_updated as f32) / self.timing_config.fps
    }

    pub fn seconds_between_render_times(&self) -> f32 {
        if self.timing_config.realtime {
            (self.system_timing.last_render_time - self.system_timing.prev_render_time)
                .as_secs_f32()
        } else {
            1.0 / self.timing_config.fps
        }
    }

    pub fn seconds_since_reload(&self) -> f32 {
        if self.timing_config.realtime {
            self.seconds_since_updated_realtime()
        } else {
            self.seconds_since_updated_frame()
        }
    }
}

impl IsLivecodeSrc for LiveCodeTimeInstantInfo {
    fn update(&mut self, input: &murrelet_common::LivecodeSrcUpdateInput) {
        self.system_timing.frame = input.app().elapsed_frames();
    }

    fn to_exec_funcs(&self) -> Vec<(String, LivecodeValue)> {
        let time = self.beat();
        let frame = self.actual_frame_u64();

        vec![
            ("t".to_owned(), LivecodeValue::Float(time as f64)),
            (
                "tease".to_owned(),
                LivecodeValue::Float(ease(
                    time.into(),
                    (1.0 / self.timing_config.beats_per_bar).into(),
                    0.0,
                )),
            ),
            (
                "stease".to_owned(),
                LivecodeValue::Float(ease(time.into(), 0.01, 0.0)),
            ),
            ("ti".to_owned(), LivecodeValue::Int(time as i64)),
            ("f".to_owned(), LivecodeValue::Float(frame as f64)),
            ("fi".to_owned(), LivecodeValue::Int(frame as i64)),
        ]
    }
}

pub struct LiveCodeConfigInfo {
    pub config_next_check: MurreletTime,
    updated: bool,
}

impl Default for LiveCodeConfigInfo {
    fn default() -> Self {
        Self::new()
    }
}

impl LiveCodeConfigInfo {
    pub fn new() -> LiveCodeConfigInfo {
        LiveCodeConfigInfo {
            config_next_check: MurreletTime::in_one_sec(),
            updated: true,
        }
    }

    pub fn should_check(&self) -> bool {
        MurreletTime::now() > self.config_next_check
    }

    pub fn reset(&mut self) {
        self.updated = false;
    }

    pub fn update(&mut self, updated: bool, config_next_check: MurreletTime) {
        self.updated = updated;
        self.config_next_check = config_next_check;
    }

    pub fn updated(&self) -> bool {
        self.updated
    }
}

#[derive(Copy, Clone, Debug)]
pub struct LiveCodeTiming {
    start: MurreletTime,
    frame: u64,
    start_frame: u64,
    true_start: MurreletTime,         // not updated when reset
    last_config_update: MurreletTime, // i don't know, config could also have it
    last_config_update_frame: u64,
    last_render_time: MurreletTime, // used for realtime bar update, also to measure time between frames for simulations
    prev_render_time: MurreletTime, // used for simulations
}

impl Default for LiveCodeTiming {
    fn default() -> Self {
        Self::new()
    }
}

impl LiveCodeTiming {
    pub fn new() -> LiveCodeTiming {
        LiveCodeTiming {
            start: MurreletTime::now(),
            frame: 0,
            start_frame: 0,
            true_start: MurreletTime::now(),
            last_config_update: MurreletTime::now(),
            last_config_update_frame: 0,
            last_render_time: MurreletTime::now(),
            prev_render_time: MurreletTime::now(),
        }
    }

    pub fn frame(&self) -> u64 {
        self.frame
    }

    pub fn true_start(&self) -> MurreletTime {
        self.true_start
    }

    pub fn config_updated(&mut self) {
        self.last_config_update = MurreletTime::now();
        self.last_config_update_frame = self.frame; // copy over curr frame
    }

    pub fn reset_time(&mut self) {
        self.start = MurreletTime::now();
        self.start_frame = self.frame;
    }

    pub fn set_last_render_time(&mut self) {
        self.prev_render_time = self.last_render_time;
        self.last_render_time = MurreletTime::now();
    }

    pub fn set_frame(&mut self, frame: u64) {
        self.frame = frame - self.start_frame;
    }
}