Skip to content

Commit 5c8f1f2

Browse files
committed
improved formula rendering
1 parent 22a901d commit 5c8f1f2

13 files changed

Lines changed: 747 additions & 344 deletions

Cargo.lock

Lines changed: 353 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ egui_extras = { version = "0.34.1", default-features = false, features = [
2525
] }
2626
egui_plot = "0.35.0"
2727
ndarray = "0.16.1"
28+
ratex-layout = "0.1.2"
29+
ratex-parser = "0.1.2"
30+
ratex-svg = { version = "0.1.2", features = ["embed-fonts"] }
31+
ratex-types = "0.1.2"
2832
serde = { version = "1.0.228", features = ["derive"] }
2933
serde_json = "1.0.149"
3034
stochastic_optimizers = "0.3.0"

src/app.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ mod result_export;
4545
mod ui;
4646

4747
use self::diagnostics::{IterationDiagnostics, diagnostics_plot_y_axis_width};
48-
use self::formula::formula_plain_text;
4948
use self::formula::model_formula_info;
50-
#[cfg(not(target_arch = "wasm32"))]
5149
use self::formula::{formula_svg_bytes, formula_svg_uri};
5250
#[cfg(not(target_arch = "wasm32"))]
5351
use self::i18n::file_import_icon_image;
@@ -94,12 +92,12 @@ use self::state::{FitRunUiSeed, ParametricIterationTraceEntry, SplineIterationTr
9492
#[cfg(target_arch = "wasm32")]
9593
use self::state::{WasmFitJob, WasmFitRunner};
9694
use self::status::StatusMessage;
95+
#[cfg(not(target_arch = "wasm32"))]
96+
use self::types::dialog_directory_from_path;
9797
use self::types::{
98-
ExtendedMetrics, ModelFormulaInfo, ParamInitMethod, PlotTool, SampledCurveCache, SprayBrush,
99-
UiLanguage, params_to_input_strings,
98+
ExtendedMetrics, FormulaSvgCache, ModelFormulaInfo, ParamInitMethod, PlotTool,
99+
SampledCurveCache, SprayBrush, UiLanguage, params_to_input_strings,
100100
};
101-
#[cfg(not(target_arch = "wasm32"))]
102-
use self::types::{FormulaSvgCache, dialog_directory_from_path};
103101
use crate::domain::{
104102
AdamConfig, CurveFamily, CurveParams, FitResult, LbfgsConfig, NelderMeadConfig, NewtonCgConfig,
105103
OptimizerConfig, OptimizerMethod, Point, Points, SgdConfig, SteepestDescentConfig,

src/app/bootstrap.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ impl Default for CurveFitApp {
121121
result_metrics: None,
122122
residual_plot_points: Vec::new(),
123123
spline_plot_curve: None,
124-
#[cfg(not(target_arch = "wasm32"))]
125124
formula_svg_cache: None,
126125
sampled_curve_cache: None,
127126
iteration_diagnostics: IterationDiagnostics::default(),

src/app/formula.rs

Lines changed: 316 additions & 230 deletions
Large diffs are not rendered by default.

src/app/state.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ pub struct CurveFitApp {
300300
pub(super) result_metrics: Option<ExtendedMetrics>,
301301
pub(super) residual_plot_points: Vec<PlotPoint>,
302302
pub(super) spline_plot_curve: Option<Arc<[PlotPoint]>>,
303-
#[cfg(not(target_arch = "wasm32"))]
304303
pub(super) formula_svg_cache: Option<FormulaSvgCache>,
305304
pub(super) sampled_curve_cache: Option<SampledCurveCache>,
306305
pub(super) iteration_diagnostics: IterationDiagnostics,
@@ -555,28 +554,37 @@ impl CurveFitApp {
555554
)
556555
}
557556

558-
#[cfg(not(target_arch = "wasm32"))]
559557
pub(super) fn cached_formula_svg(
560558
&mut self,
561559
formula: &str,
562560
dark_mode: bool,
563-
) -> (String, Arc<[u8]>) {
561+
) -> Result<(String, Arc<[u8]>), String> {
564562
if let Some(cache) = &self.formula_svg_cache
565563
&& cache.formula == formula
566564
&& cache.dark_mode == dark_mode
567565
{
568-
return (cache.uri.clone(), Arc::clone(&cache.bytes));
566+
return cache
567+
.render_result
568+
.as_ref()
569+
.map(|bytes| (cache.uri.clone(), Arc::clone(bytes)))
570+
.map_err(|error| error.clone());
569571
}
570572

571573
let uri = formula_svg_uri(formula, dark_mode);
572-
let bytes: Arc<[u8]> = formula_svg_bytes(formula, dark_mode).into();
574+
let render_result = match formula_svg_bytes(formula, dark_mode) {
575+
Ok(bytes) => Ok(Arc::<[u8]>::from(bytes)),
576+
Err(error) => {
577+
eprintln!("Failed to render formula SVG: {error}");
578+
Err(error)
579+
}
580+
};
573581
self.formula_svg_cache = Some(FormulaSvgCache {
574582
formula: formula.to_string(),
575583
dark_mode,
576584
uri: uri.clone(),
577-
bytes: Arc::clone(&bytes),
585+
render_result: render_result.clone(),
578586
});
579-
(uri, bytes)
587+
render_result.map(|bytes| (uri, bytes))
580588
}
581589

582590
pub(super) fn cached_sampled_curve(

src/app/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use std::time::{Duration, Instant};
2929
// Все import-ы специально поднимаются в этот модуль,
3030
// чтобы подмодули `app/tests/*` брали их через `use super::*;`.
3131
mod fit_lifecycle;
32+
mod formula_rendering;
3233
mod import_export;
3334
mod init_optimizer;
3435
mod points_editing;

src/app/tests/formula_rendering.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use super::*;
2+
3+
#[test]
4+
fn formula_svg_cache_distinguishes_formula_and_theme() {
5+
let mut app = CurveFitApp::default();
6+
7+
let (light_uri, _) = app
8+
.cached_formula_svg(r"y = \frac{1}{x}", false)
9+
.expect("light formula SVG must render");
10+
let (dark_uri, _) = app
11+
.cached_formula_svg(r"y = \frac{1}{x}", true)
12+
.expect("dark formula SVG must render");
13+
let (other_uri, _) = app
14+
.cached_formula_svg(r"y = x^{2} + 1", false)
15+
.expect("different formula SVG must render");
16+
17+
assert_ne!(light_uri, dark_uri);
18+
assert_ne!(light_uri, other_uri);
19+
}

src/app/types.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,18 @@ impl ParamInitMethod {
109109
/// Полная формула модели и сопутствующие заметки для UI.
110110
#[derive(Debug, Clone)]
111111
pub(super) struct ModelFormulaInfo {
112-
pub(super) full_formula: String,
112+
pub(super) render_latex: String,
113+
pub(super) plain_text: String,
113114
pub(super) notes: String,
114115
}
115116

116-
#[cfg(not(target_arch = "wasm32"))]
117117
/// Кэш уже отрендеренной SVG-формулы, зависящий от темы и текста формулы.
118118
#[derive(Debug, Clone)]
119119
pub(super) struct FormulaSvgCache {
120120
pub(super) formula: String,
121121
pub(super) dark_mode: bool,
122122
pub(super) uri: String,
123-
pub(super) bytes: Arc<[u8]>,
123+
pub(super) render_result: Result<Arc<[u8]>, String>,
124124
}
125125

126126
/// Кэш уже сэмплированной параметрической кривой для текущего диапазона `x`.

src/app/ui.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Общие UI-хелперы и разбиение интерфейса по специализированным панелям.
22
33
use super::*;
4-
use std::borrow::Cow;
54

65
mod diagnostics_panel;
76
mod family_params;
@@ -346,22 +345,6 @@ fn paint_enlarged_collapsing_icon(ui: &mut egui::Ui, openness: f32, response: &e
346345
egui::containers::collapsing_header::paint_default_icon(ui, openness, &enlarged_response);
347346
}
348347

349-
fn formula_preview_text(formula: &str, max_chars: usize) -> Cow<'_, str> {
350-
if max_chars <= 3 {
351-
return Cow::Borrowed("...");
352-
}
353-
354-
let total_chars = formula.chars().count();
355-
if total_chars <= max_chars {
356-
return Cow::Borrowed(formula);
357-
}
358-
359-
let mut preview = String::with_capacity(max_chars + 3);
360-
preview.extend(formula.chars().take(max_chars - 3));
361-
preview.push_str("...");
362-
Cow::Owned(preview)
363-
}
364-
365348
fn diagnostics_hidden_non_loss_series_ids() -> [egui::Id; 6] {
366349
[
367350
egui::Id::new(DIAGNOSTICS_SERIES_ID_MSE),

0 commit comments

Comments
 (0)