Skip to content

Commit 0f92713

Browse files
committed
[delta_q] make all expression elements interactive
1 parent bbce7d5 commit 0f92713

File tree

3 files changed

+95
-27
lines changed

3 files changed

+95
-27
lines changed

delta_q/index.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
hist.fXaxis.fXmin = 0;
2929
hist.fXaxis.fXmax = max;
3030
hist.fTitle = name;
31-
console.log(hist);
31+
hist.fXaxis.fTitle = 'time';
32+
hist.fYaxis.fTitle = 'cumulative distribution';
3233
cleanup('output');
3334
draw.draw('output', hist, 'nostat minimum:0 maximum:1.1');
3435
}

delta_q/src/bin/editor-web.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,32 @@ fn app_main() -> HtmlResult {
2828
// epoch counter to trigger recomputation when the context changes
2929
let epoch = use_state(|| 0);
3030

31+
let agent_status = use_state(|| "".to_owned());
3132
let agent = use_oneshot_runner::<CalcCdf>();
3233
use_future_with(
3334
(selected.clone(), epoch.clone()),
34-
cloned!(ctx; move |deps| async move {
35+
cloned!(agent_status, ctx; move |deps| async move {
3536
if let Some(name) = deps.0.as_deref() {
36-
let cdf = agent.run((name.to_string(), (*ctx).clone())).await?;
37+
let cdf = match agent.run((name.to_string(), (*ctx).clone())).await {
38+
Ok(cdf) => cdf,
39+
Err(e) => {
40+
let init = MessageEventInit::new();
41+
init.set_data(&wasm_bindgen::JsValue::null());
42+
let _ = window().dispatch_event(&*MessageEvent::new_with_event_init_dict("rootjs", &init).unwrap());
43+
agent_status.set(format!("evaluation error: {e}"));
44+
return;
45+
}
46+
};
3747
let data = js_sys::Object::new();
3848
Reflect::set(&data, &"bins".into(), &cdf.iter().map(|x| JsValue::from(x.0)).collect::<js_sys::Array>()).unwrap();
3949
Reflect::set(&data, &"values".into(), &cdf.iter().map(|x| JsValue::from(x.1)).collect::<js_sys::Array>()).unwrap();
4050
Reflect::set(&data, &"max".into(), &(cdf.width() * 1.2).max(0.1).into()).unwrap();
4151
Reflect::set(&data, &"name".into(), &name.into()).unwrap();
4252
let init = MessageEventInit::new();
4353
init.set_data(&data);
44-
let _ = window().dispatch_event(&*MessageEvent::new_with_event_init_dict("rootjs", &init).map_err(|e| format!("{e:?}"))?);
54+
let _ = window().dispatch_event(&*MessageEvent::new_with_event_init_dict("rootjs", &init).unwrap());
55+
agent_status.set("OK".to_owned());
4556
}
46-
Ok::<_, String>(())
4757
}),
4858
)?;
4959

@@ -104,6 +114,7 @@ fn app_main() -> HtmlResult {
104114
</ContextProvider<DeltaQContext>>
105115
</div>
106116
}
117+
<p>{ "agent status: " }{ &*agent_status }</p>
107118
</div>
108119
})
109120
}

delta_q/src/render.rs

+78-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{delta_q::DeltaQ, EvaluationContext};
1+
use crate::{delta_q::DeltaQ, EvaluationContext, CDF};
22
use std::{rc::Rc, sync::Arc};
33
use web_sys::HtmlInputElement;
44
use yew::prelude::*;
@@ -37,7 +37,7 @@ pub fn delta_q_component(props: &Props) -> Html {
3737
html! { <NameComponent name={(&**name).to_owned()} rec={*rec} {on_change} /> }
3838
}
3939
DeltaQ::CDF(cdf) => {
40-
html! { <div class={classes!("cdf")}>{ format!("{}", cdf) }</div> }
40+
html! { <CdfComponent cdf={cdf.clone()} {on_change} /> }
4141
}
4242
DeltaQ::Seq(first, second) => {
4343
html!(<Seq first={(**first).clone()} second={(**second).clone()} {on_change} />)
@@ -148,6 +148,56 @@ pub fn name_component(props: &NameProps) -> Html {
148148
}
149149
}
150150

151+
#[derive(Properties, Clone, PartialEq)]
152+
pub struct CdfProps {
153+
pub cdf: CDF,
154+
pub on_change: Callback<(String, Option<DeltaQ>)>,
155+
}
156+
157+
#[function_component(CdfComponent)]
158+
pub fn cdf_component(props: &CdfProps) -> Html {
159+
let cdf = props.cdf.clone();
160+
let on_change = props.on_change.clone();
161+
let popup = use_state(|| false);
162+
let ctx = use_context::<DeltaQContext>().unwrap();
163+
164+
let abstract_name = use_state(|| "".to_string());
165+
let abstract_input = Callback::from(cloned!(abstract_name;
166+
move |e: InputEvent| abstract_name.set(e.target_unchecked_into::<HtmlInputElement>().value())));
167+
let abstract_submit = Callback::from(cloned!(abstract_name, on_change, ctx, cdf;
168+
move |e: SubmitEvent| {
169+
e.prevent_default();
170+
on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&abstract_name))));
171+
on_change.emit(((*abstract_name).clone(), Some(DeltaQ::CDF(cdf.clone()))));
172+
}
173+
));
174+
175+
html! {
176+
<div class={classes!("cdf", "anchor")} onclick={cloned!(popup; move |_| if !*popup { popup.set(true) })}>
177+
{ format!("{}", props.cdf) }
178+
if *popup {
179+
<div class={classes!("popup")}>
180+
<button onclick={cloned!(popup; move |_| popup.set(false))}>{ "abort" }</button>
181+
<button onclick={cloned!(on_change, cdf, ctx;
182+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::seq(DeltaQ::CDF(cdf.clone()), DeltaQ::BlackBox))))) }>{ "append" }</button>
183+
<button onclick={cloned!(on_change, cdf, ctx;
184+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::choice(DeltaQ::CDF(cdf.clone()), 1.0, DeltaQ::BlackBox, 1.0))))) }>{ "make choice" }</button>
185+
<button onclick={cloned!(on_change, cdf, ctx;
186+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::for_all(DeltaQ::CDF(cdf.clone()), DeltaQ::BlackBox))))) }>{ "make forAll" }</button>
187+
<button onclick={cloned!(on_change, cdf, ctx;
188+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::for_some(DeltaQ::CDF(cdf.clone()), DeltaQ::BlackBox))))) }>{ "make forSome" }</button>
189+
<button onclick={cloned!(on_change, ctx;
190+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::BlackBox))))}>{ "black box" }</button>
191+
<form onsubmit={abstract_submit}>
192+
<input type="submit" value="abstract" />
193+
<input type="text" value={(*abstract_name).clone()} oninput={abstract_input} />
194+
</form>
195+
</div>
196+
}
197+
</div>
198+
}
199+
}
200+
151201
#[derive(Properties, Clone, PartialEq)]
152202
pub struct SeqProps {
153203
pub first: DeltaQ,
@@ -213,6 +263,8 @@ pub fn seq(props: &SeqProps) -> Html {
213263
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::for_some(DeltaQ::seq(first.clone(), second.clone()), DeltaQ::BlackBox)))))}> { "make forSome" } </button>
214264
<button onclick={cloned!(on_change, first, second, popup, ctx;
215265
move |_| { popup.set(false); on_change.emit((ctx.name.clone(), Some(DeltaQ::seq(second.clone(), first.clone())))) })}>{ "switch" }</button>
266+
<button onclick={cloned!(on_change, first, second, ctx;
267+
move |_| on_change.emit((ctx.name.clone(), Some(DeltaQ::seq(first.clone(), second.clone())))))}>{ "append" }</button>
216268
<button onclick={cloned!(popup, on_change, first, ctx;
217269
move |_| { popup.set(false); on_change.emit((ctx.name.clone(), Some(first.clone()))) })}>{ "keep left" }</button>
218270
<button onclick={cloned!(popup, on_change, second, ctx;
@@ -257,7 +309,8 @@ impl BranchKind {
257309

258310
#[function_component(BranchKindComponent)]
259311
pub fn branch_kind_component(props: &BranchProps) -> Html {
260-
let kind = match &props.kind {
312+
let kind = props.kind;
313+
let kind_html = match kind {
261314
BranchKind::Choice(first_weight, second_weight) => html! {
262315
<div class={classes!("column", "center")}>
263316
<div>{first_weight}</div>
@@ -293,19 +346,17 @@ pub fn branch_kind_component(props: &BranchProps) -> Html {
293346
let abstract_name = use_state(|| "".to_string());
294347
let abstract_input = Callback::from(cloned!(abstract_name;
295348
move |e: InputEvent| abstract_name.set(e.target_unchecked_into::<HtmlInputElement>().value())));
296-
let abstract_submit = Callback::from(
297-
cloned!(abstract_name, on_change, ctx, bottom, bottom_frac, top, top_frac;
298-
move |e: SubmitEvent| {
299-
e.prevent_default();
300-
on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&abstract_name))));
301-
on_change.emit(((*abstract_name).clone(), Some(DeltaQ::choice(top.clone(), *top_frac, bottom.clone(), *bottom_frac))));
302-
}
303-
),
304-
);
349+
let abstract_submit = Callback::from(cloned!(abstract_name, on_change, ctx, bottom, top;
350+
move |e: SubmitEvent| {
351+
e.prevent_default();
352+
on_change.emit((ctx.name.clone(), Some(DeltaQ::name(&abstract_name))));
353+
on_change.emit(((*abstract_name).clone(), Some(mk_branch(kind, top.clone(), bottom.clone()))));
354+
}
355+
));
305356

306357
html!(
307358
<div class={classes!("row", "center", "branchKind", "anchor")} onclick={cloned!(popup; move |_| if !*popup { popup.set(true) })}>
308-
{ kind }
359+
{ kind_html }
309360
if *popup {
310361
<div class={classes!("popup")}>
311362
<button onclick={cloned!(popup; move |_| popup.set(false))}>{ "abort" }</button>
@@ -324,8 +375,10 @@ pub fn branch_kind_component(props: &BranchProps) -> Html {
324375
})}>{ "make forSome" }</button>
325376
<button onclick={cloned!(popup, on_change, top, bottom, ctx; move |_| {
326377
popup.set(false);
327-
on_change.emit((ctx.name.clone(), Some(DeltaQ::choice(bottom.clone(), *bottom_frac, top.clone(), *top_frac))))
378+
on_change.emit((ctx.name.clone(), Some(mk_branch(kind, bottom.clone(), top.clone()))))
328379
})}>{ "switch" }</button>
380+
<button onclick={cloned!(popup, on_change, top, bottom, ctx;
381+
move |_| { popup.set(false); on_change.emit((ctx.name.clone(), Some(DeltaQ::seq(mk_branch(kind, top.clone(), bottom.clone()), DeltaQ::BlackBox)))) })}>{ "append" }</button>
329382
<button onclick={cloned!(popup, on_change, top, ctx;
330383
move |_| { popup.set(false); on_change.emit((ctx.name.clone(), Some(top.clone()))) })}>{ "keep top" }</button>
331384
<button onclick={cloned!(popup, on_change, bottom, ctx;
@@ -341,6 +394,14 @@ pub fn branch_kind_component(props: &BranchProps) -> Html {
341394
</div>)
342395
}
343396

397+
fn mk_branch(kind: BranchKind, top: DeltaQ, bottom: DeltaQ) -> DeltaQ {
398+
match kind {
399+
BranchKind::Choice(l, r) => DeltaQ::Choice(Arc::new(top), l, Arc::new(bottom), r),
400+
BranchKind::ForAll => DeltaQ::ForAll(Arc::new(top), Arc::new(bottom)),
401+
BranchKind::ForSome => DeltaQ::ForSome(Arc::new(top), Arc::new(bottom)),
402+
}
403+
}
404+
344405
/// A component that renders a branch of a DeltaQ tree.
345406
///
346407
/// The HTML representation consists of two DIV, with the left showing the branch kind and the right showing the branch content.
@@ -352,20 +413,15 @@ fn branch(props: &BranchProps) -> Html {
352413
let top = props.top.clone();
353414
let bottom = props.bottom.clone();
354415
let kind = props.kind;
355-
let constructor: Arc<dyn Fn(Arc<DeltaQ>, Arc<DeltaQ>) -> DeltaQ> = match kind {
356-
BranchKind::Choice(l, r) => Arc::new(move |dql, dqr| DeltaQ::Choice(dql, l, dqr, r)),
357-
BranchKind::ForAll => Arc::new(DeltaQ::ForAll),
358-
BranchKind::ForSome => Arc::new(DeltaQ::ForSome),
359-
};
360416
let ctx = use_context::<DeltaQContext>().unwrap();
361417

362-
let on_top_change = Callback::from(cloned!(bottom, on_change, constructor, ctx;
418+
let on_top_change = Callback::from(cloned!(bottom, on_change, ctx;
363419
move |(name, delta_q)| {
364420
// if the name matches our context, edit the DeltaQ; otherwise just bubble up
365421
if name != ctx.name {
366422
on_change.emit((name, delta_q));
367423
} else if let Some(delta_q) = delta_q {
368-
on_change.emit((name, Some(constructor(Arc::new(delta_q), Arc::new(bottom.clone())))));
424+
on_change.emit((name, Some(mk_branch(kind, delta_q, bottom.clone()))));
369425
}
370426
}
371427
));
@@ -376,7 +432,7 @@ fn branch(props: &BranchProps) -> Html {
376432
if name != ctx.name {
377433
on_change.emit((name, delta_q));
378434
} else if let Some(delta_q) = delta_q {
379-
on_change.emit((name, Some(constructor(Arc::new(top.clone()), Arc::new(delta_q)))));
435+
on_change.emit((name, Some(mk_branch(kind, top.clone(), delta_q))));
380436
}
381437
}
382438
));

0 commit comments

Comments
 (0)