Show the loss history from the last realization
This introduces a dependency on the Charming crate, which we use to plot the loss history, and the ECharts JavaScript library, which Charming depends on. Now that there's more than one canvas on the page, we have to pick out the display by ID rather than by element type in our style sheet.
This commit is contained in:
parent
d4302d237b
commit
6d2e3d776b
10 changed files with 660 additions and 11 deletions
|
@ -3,8 +3,9 @@ use sycamore::prelude::*;
|
|||
use web_sys::{console, wasm_bindgen::JsValue};
|
||||
|
||||
use crate::{
|
||||
engine,
|
||||
AppState,
|
||||
engine,
|
||||
engine::DescentHistory,
|
||||
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
|
||||
};
|
||||
|
||||
|
@ -195,6 +196,7 @@ pub fn AddRemove() -> View {
|
|||
assembly.regulators.update(|regs| regs.clear());
|
||||
assembly.elements.update(|elts| elts.clear());
|
||||
assembly.elements_by_id.update(|elts_by_id| elts_by_id.clear());
|
||||
assembly.descent_history.set(DescentHistory::new());
|
||||
state.selection.update(|sel| sel.clear());
|
||||
|
||||
// load assembly
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
sphere,
|
||||
ConfigSubspace,
|
||||
ConstraintProblem,
|
||||
DescentHistory,
|
||||
Realization,
|
||||
RealizationResult
|
||||
},
|
||||
|
@ -549,7 +550,10 @@ pub struct Assembly {
|
|||
pub tangent: Signal<ConfigSubspace>,
|
||||
|
||||
// indexing
|
||||
pub elements_by_id: Signal<BTreeMap<String, Rc<dyn Element>>>
|
||||
pub elements_by_id: Signal<BTreeMap<String, Rc<dyn Element>>>,
|
||||
|
||||
// realization diagnostics
|
||||
pub descent_history: Signal<DescentHistory>
|
||||
}
|
||||
|
||||
impl Assembly {
|
||||
|
@ -558,7 +562,8 @@ impl Assembly {
|
|||
elements: create_signal(BTreeSet::new()),
|
||||
regulators: create_signal(BTreeSet::new()),
|
||||
tangent: create_signal(ConfigSubspace::zero(0)),
|
||||
elements_by_id: create_signal(BTreeMap::default())
|
||||
elements_by_id: create_signal(BTreeMap::default()),
|
||||
descent_history: create_signal(DescentHistory::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,6 +708,9 @@ impl Assembly {
|
|||
console_log!("Steps: {}", history.scaled_loss.len() - 1);
|
||||
console_log!("Loss: {}", history.scaled_loss.last().unwrap());
|
||||
|
||||
// record realization diagnostics
|
||||
self.descent_history.set(history);
|
||||
|
||||
if let Ok(Realization { config, tangent }) = result {
|
||||
/* DEBUG */
|
||||
// report the tangent dimension
|
||||
|
|
65
app-proto/src/diagnostics.rs
Normal file
65
app-proto/src/diagnostics.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use charming::{
|
||||
Chart,
|
||||
WasmRenderer,
|
||||
component::{Axis, Grid},
|
||||
element::AxisType,
|
||||
series::Line,
|
||||
theme::Theme
|
||||
};
|
||||
use sycamore::prelude::*;
|
||||
|
||||
use crate::AppState;
|
||||
|
||||
// a plot of the loss history from the last realization
|
||||
#[component]
|
||||
pub fn Diagnostics() -> View {
|
||||
const CONTAINER_ID: &str = "loss-history";
|
||||
let state = use_context::<AppState>();
|
||||
let renderer = WasmRenderer::new_opt(None, Some(180)).theme(Theme::Walden);
|
||||
|
||||
on_mount(move || {
|
||||
create_effect(move || {
|
||||
// get the loss history
|
||||
let scaled_loss = state.assembly.descent_history.with(
|
||||
|history| history.scaled_loss.clone()
|
||||
);
|
||||
let step_cnt = scaled_loss.len();
|
||||
|
||||
// initialize the chart axes and series
|
||||
const MIN_INTERVAL: f64 = 0.01;
|
||||
let mut step_axis = Axis::new()
|
||||
.type_(AxisType::Category)
|
||||
.boundary_gap(false);
|
||||
let scaled_loss_axis = Axis::new()
|
||||
.type_(AxisType::Value)
|
||||
.min(0)
|
||||
.min_interval(MIN_INTERVAL);
|
||||
|
||||
// load the chart data. when there's no history, we load the data
|
||||
// point (0, None) to clear the chart. it would feel more natural to
|
||||
// load empty data vectors, but that turns out not to clear the
|
||||
// chart: it instead leads to previous data being re-used
|
||||
let mut scaled_loss_series = Line::new();
|
||||
if step_cnt > 0 {
|
||||
step_axis = step_axis.data(
|
||||
(0..step_cnt).map(|step| step.to_string()).collect()
|
||||
);
|
||||
scaled_loss_series = scaled_loss_series.data(scaled_loss);
|
||||
} else {
|
||||
step_axis = step_axis.data(vec![0.to_string()]);
|
||||
scaled_loss_series = scaled_loss_series.data(vec![None::<f64>]);
|
||||
}
|
||||
let chart = Chart::new()
|
||||
.animation(false)
|
||||
.x_axis(step_axis)
|
||||
.y_axis(scaled_loss_axis)
|
||||
.grid(Grid::new().top(20).right(40).bottom(30).left(60))
|
||||
.series(scaled_loss_series);
|
||||
renderer.render(CONTAINER_ID, &chart).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
view! {
|
||||
div(id=CONTAINER_ID)
|
||||
}
|
||||
}
|
|
@ -806,6 +806,7 @@ pub fn Display() -> View {
|
|||
// again
|
||||
canvas(
|
||||
ref=display,
|
||||
id="display",
|
||||
width="600",
|
||||
height="600",
|
||||
tabindex="0",
|
||||
|
|
|
@ -262,7 +262,7 @@ pub struct DescentHistory {
|
|||
}
|
||||
|
||||
impl DescentHistory {
|
||||
fn new() -> DescentHistory {
|
||||
pub fn new() -> DescentHistory {
|
||||
DescentHistory {
|
||||
config: Vec::<DMatrix<f64>>::new(),
|
||||
scaled_loss: Vec::<f64>::new(),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod add_remove;
|
||||
mod assembly;
|
||||
mod diagnostics;
|
||||
mod display;
|
||||
mod engine;
|
||||
mod outline;
|
||||
|
@ -13,6 +14,7 @@ use sycamore::prelude::*;
|
|||
|
||||
use add_remove::AddRemove;
|
||||
use assembly::{Assembly, Element};
|
||||
use diagnostics::Diagnostics;
|
||||
use display::Display;
|
||||
use outline::Outline;
|
||||
|
||||
|
@ -60,6 +62,7 @@ fn main() {
|
|||
div(id="sidebar") {
|
||||
AddRemove {}
|
||||
Outline {}
|
||||
Diagnostics {}
|
||||
}
|
||||
Display {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue