From 09b732cfbc7fdec2b74fa84337c3972268f28918 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Sat, 21 Feb 2026 02:59:46 +0300 Subject: [PATCH] Attempt 2 to make mutability-independent UI --- utility/src/gui/const_mut_switch/const.rs | 35 +- utility/src/gui/const_mut_switch/mod.rs | 4 +- utility/src/gui/const_mut_switch/mut.rs | 39 ++- utility/src/gui/const_mut_switch/ref.rs | 49 +-- utility/src/gui/const_mut_switch/table.rs | 75 ++-- utility/src/gui/const_mut_switch/trait.rs | 30 +- .../gui/const_mut_switch/эволюция_костылей.md | 153 +++++++- utility/src/gui/lengths_table.rs | 330 ++++++++++-------- utility/src/gui/render_graph.rs | 2 +- utility/src/lambda_iterator.rs | 28 ++ utility/src/lib.rs | 2 + 11 files changed, 436 insertions(+), 311 deletions(-) create mode 100644 utility/src/lambda_iterator.rs diff --git a/utility/src/gui/const_mut_switch/const.rs b/utility/src/gui/const_mut_switch/const.rs index eb2ee99..29ae060 100644 --- a/utility/src/gui/const_mut_switch/const.rs +++ b/utility/src/gui/const_mut_switch/const.rs @@ -1,7 +1,4 @@ -use super::{ - _ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback, - _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstRef, RefType, -}; +use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, RefType, _ConstMutSwitchUiTableHeaderCallback}; use eframe::egui::{ScrollArea, Ui}; use eframe::emath::Numeric; use std::ops::RangeInclusive; @@ -52,9 +49,8 @@ impl ConstMutSwitchUi for ConstUI<'_> { self.ui.label(""); } - fn horizontal>(&mut self, clojure: &mut F::Clojure<'_>) { - self.ui - .horizontal(|ui| F::render(&mut ConstUI { ui }, clojure)); + fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback) { + self.ui.horizontal(|ui| scope.render(&mut ConstUI { ui })); } fn button(&mut self, label: &str, _storage: &mut &T, _on_click: impl FnOnce(&mut T)) { @@ -65,33 +61,22 @@ impl ConstMutSwitchUi for ConstUI<'_> { self.ui.separator(); } - fn scroll_area_2>( + fn scroll_area_2( &mut self, - clojure: &mut F::Clojure<'_>, + scope: impl _ConstMutSwitchUiCallback, ) { ScrollArea::both() .auto_shrink([false, false]) - .show_viewport(self.ui, |ui, _| F::render(&mut ConstUI { ui }, clojure)); + .show_viewport(self.ui, |ui, _|scope.render(&mut ConstUI { ui })); } - fn table< - 'a, - F: _ConstMutSwitchUiTableBaseCallback, - _B: _ConstMutSwitchUiTableRowCallback = _BC>, - _BC, - >( + + fn table( &mut self, vscroll: bool, columns: usize, - header_clojure: >::Clojure<'_>, - body_data: impl Iterator, + header: impl _ConstMutSwitchUiTableHeaderCallback, ) { - super::render_table::( - self.ui, - vscroll, - columns, - header_clojure, - body_data, - ); + super::render_table::(self.ui, vscroll, columns, header); } } diff --git a/utility/src/gui/const_mut_switch/mod.rs b/utility/src/gui/const_mut_switch/mod.rs index 6915233..0b1ffd0 100644 --- a/utility/src/gui/const_mut_switch/mod.rs +++ b/utility/src/gui/const_mut_switch/mod.rs @@ -6,9 +6,9 @@ mod r#trait; pub use r#const::ConstUI; pub use r#mut::MutUI; -pub use r#ref::{ConstRef, MutRef, RefGetter, RefType, _IteratorGetter}; +pub use r#ref::{ConstRef, MutRef, RefType}; use table::render_table; pub use table::{ - _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow, + _ConstMutSwitchUiTableHeaderCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUiTableRow, }; pub use r#trait::{_ConstMutSwitchUiCallback, ConstMutSwitchUi}; diff --git a/utility/src/gui/const_mut_switch/mut.rs b/utility/src/gui/const_mut_switch/mut.rs index f433608..0c85c19 100644 --- a/utility/src/gui/const_mut_switch/mut.rs +++ b/utility/src/gui/const_mut_switch/mut.rs @@ -1,4 +1,4 @@ -use super::{ConstMutSwitchUi, MutRef, RefType, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback}; +use super::{_ConstMutSwitchUiTableHeaderCallback, ConstMutSwitchUi, MutRef, RefType}; use crate::gui::const_mut_switch::r#trait::_ConstMutSwitchUiCallback; use eframe::egui::{ScrollArea, Ui}; use eframe::emath::Numeric; @@ -8,6 +8,12 @@ pub struct MutUI<'ui> { ui: &'ui mut Ui, } +impl<'ui> MutUI<'ui> { + pub fn wrap(ui: &'ui mut Ui) -> Self { + return Self { ui }; + } +} + impl ConstMutSwitchUi for MutUI<'_> { type RefType = MutRef; @@ -15,7 +21,12 @@ impl ConstMutSwitchUi for MutUI<'_> { self.ui.label(s); } - fn slider(&mut self, range: RangeInclusive, step: f64, storage: &mut ::Ref<'_, T>) { + fn slider( + &mut self, + range: RangeInclusive, + step: f64, + storage: &mut ::Ref<'_, T>, + ) { crate::gui::slider(self.ui, *storage, range, step); } @@ -33,9 +44,8 @@ impl ConstMutSwitchUi for MutUI<'_> { self.ui.label(""); } - fn horizontal>(&mut self, clojure: &mut I::Clojure<'_>) { - self.ui - .horizontal(|ui| I::render(&mut MutUI { ui }, clojure)); + fn horizontal(&mut self, scope: impl _ConstMutSwitchUiCallback) { + self.ui.horizontal(|ui| scope.render(&mut MutUI { ui })); } fn button(&mut self, label: &str, storage: &mut &mut T, on_click: impl FnOnce(&mut T)) { @@ -48,27 +58,18 @@ impl ConstMutSwitchUi for MutUI<'_> { self.ui.separator(); } - fn scroll_area_2>( - &mut self, - clojure: &mut F::Clojure<'_>, - ) { + fn scroll_area_2(&mut self, scope: impl _ConstMutSwitchUiCallback) { ScrollArea::both() .auto_shrink([false, false]) - .show_viewport(self.ui, |ui, _| F::render(&mut MutUI { ui }, clojure)); + .show_viewport(self.ui, |ui, _| scope.render(&mut MutUI { ui })); } - fn table< - 'a, - F: _ConstMutSwitchUiTableBaseCallback, - _B: _ConstMutSwitchUiTableRowCallback = _BC>, - _BC, - >( + fn table( &mut self, vscroll: bool, columns: usize, - header_clojure: >::Clojure<'_>, - body_data: impl Iterator, + header: impl _ConstMutSwitchUiTableHeaderCallback, ) { - super::render_table::(self.ui, vscroll, columns, header_clojure, body_data); + super::render_table::(self.ui, vscroll, columns, header); } } diff --git a/utility/src/gui/const_mut_switch/ref.rs b/utility/src/gui/const_mut_switch/ref.rs index 2ab1427..f5efee2 100644 --- a/utility/src/gui/const_mut_switch/ref.rs +++ b/utility/src/gui/const_mut_switch/ref.rs @@ -1,9 +1,8 @@ -use crate::gui::const_mut_switch::ConstMutSwitchUi; use std::ops::Deref; use std::ptr::NonNull; pub trait RefType { - type Ref<'a, T: 'a>: Deref; + type Ref<'a, T: 'a>: Deref; fn _get<'r, 'f: 'r, 's: 'f, S, M>( storge: &'f mut Self::Ref<'s, S>, @@ -13,32 +12,6 @@ pub trait RefType { unsafe fn _to_ptr(r: Self::Ref<'_, T>) -> NonNull; unsafe fn _from_ptr<'a, T>(r: NonNull) -> Self::Ref<'a, T>; - - fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>( - storge: &'f mut Self::Ref<'s, F::C>, - ) -> impl Iterator>>; - - fn _enum_iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>( - storge: &'f mut Self::Ref<'s, F::C>, - ) -> impl Iterator>>; -} - -pub trait _IteratorGetter { - type C; - type T<'a>: 'a; - fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator>; - - fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator>; -} - -pub trait _EnumeratedIteratorGetter { - type C; - type T<'a>: 'a; - fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator)>; - - fn get_mut_iterator<'a>( - cont: &'a mut Self::C, - ) -> impl Iterator)>; } pub struct ConstRef {} @@ -61,12 +34,6 @@ impl RefType for ConstRef { unsafe fn _from_ptr<'a, T>(r: NonNull) -> Self::Ref<'a, T> { return r.as_ref(); } - - fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>( - storge: &'f mut Self::Ref<'s, F::C>, - ) -> impl Iterator> { - return F::get_const_iterator(storge); - } } pub struct MutRef {} @@ -89,18 +56,4 @@ impl RefType for MutRef { unsafe fn _from_ptr<'a, T>(mut r: NonNull) -> Self::Ref<'a, T> { return r.as_mut(); } - - fn _iter<'r, 'f: 'r, 's: 'f, F: _IteratorGetter>( - storge: &'f mut Self::Ref<'s, F::C>, - ) -> impl Iterator> { - return F::get_mut_iterator(storge); - } -} - -pub trait RefGetter { - type Ref<'a, T: 'a>; -} - -impl RefGetter for C { - type Ref<'a, T: 'a> = ::Ref<'a, T>; } diff --git a/utility/src/gui/const_mut_switch/table.rs b/utility/src/gui/const_mut_switch/table.rs index a57f2f6..c98b0ab 100644 --- a/utility/src/gui/const_mut_switch/table.rs +++ b/utility/src/gui/const_mut_switch/table.rs @@ -1,30 +1,32 @@ -use super::{_ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType}; +#![allow(deprecated)] + +use super::{ + _ConstMutSwitchUiCallback, ConstMutSwitchUi, ConstRef, ConstUI, MutRef, MutUI, RefType, +}; +use crate::LambdaIterator; use eframe::egui::Ui; use egui_extras::{Column, TableBuilder, TableRow}; use std::marker::PhantomData; +use std::mem::uninitialized; -pub trait _ConstMutSwitchUiTableBaseCallback { - type Header: _ConstMutSwitchUiTableRowCallback; - type Body: _ConstMutSwitchUiTableRowCallback; +pub trait _ConstMutSwitchUiTableHeaderCallback { + type RowRender: _ConstMutSwitchUiTableRowCallback; + type RowsIterator: LambdaIterator; - fn header_to_body<'a>( - header: &mut >::Clojure<'a>, - ) -> impl Iterator>::Clojure<'a>>; + fn render_header( + self, + ctx: &mut impl ConstMutSwitchUiTableRow, + ) -> Self::RowsIterator; } pub trait _ConstMutSwitchUiTableRowCallback { - type Clojure<'a>; - - fn render_row>( - row: &mut Row, - clojure: &mut Self::Clojure<'_>, - ); + fn render_row(self, row: &mut impl ConstMutSwitchUiTableRow); } pub trait ConstMutSwitchUiTableRow { type RefType: RefType; - fn cell>(&mut self, clojure: &mut F::Clojure<'_>); + fn cell(&mut self, scope: impl _ConstMutSwitchUiCallback); } pub(super) struct ConstMutSwitchUiTableRowImpl< @@ -42,13 +44,12 @@ impl> Cons { type RefType = RefType; - fn cell>(&mut self, clojure: &mut F::Clojure<'_>) { - self.row - .col(|ui| F::render(&mut Constructor::__wrap(ui), clojure)); + fn cell(&mut self, cell: impl _ConstMutSwitchUiCallback) { + self.row.col(|ui| cell.render(&mut Constructor::__wrap(ui))); } } -trait __SwitchUiConstructor1 { +pub(super) trait __SwitchUiConstructor1 { type Ctx<'a>: ConstMutSwitchUi; fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a>; @@ -58,53 +59,47 @@ impl __SwitchUiConstructor1 for ConstUI<'_> { type Ctx<'a> = ConstUI<'a>; fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> { - todo!() + return ConstUI::wrap(ui); } } impl __SwitchUiConstructor1 for MutUI<'_> { type Ctx<'a> = MutUI<'a>; fn __wrap<'a>(ui: &'a mut Ui) -> Self::Ctx<'a> { - todo!() + return MutUI::wrap(ui); } } pub(super) fn render_table< - 'a, - Ctx: ConstMutSwitchUi, - F: _ConstMutSwitchUiTableBaseCallback, - Constructor: __SwitchUiConstructor1, + RefType: super::RefType, + Constructor: __SwitchUiConstructor1, >( ui: &mut Ui, vscroll: bool, columnsCount: usize, - headerClojure: &mut >::Clojure<'_>, + header_render: impl _ConstMutSwitchUiTableHeaderCallback, ) { + let mut rows_it = unsafe { uninitialized() }; TableBuilder::new(ui) .striped(true) // Alternating row colors .resizable(true) .vscroll(vscroll) .columns(Column::remainder(), columnsCount) .header(20.0, |row| { - F::Header::render_row( - &mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { + rows_it = + header_render.render_header(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { row, __phantom: PhantomData::default(), - }, - headerClojure, - ) + }); }) .body(|mut body| { - for mut rowData in F::header_to_body(headerClojure) { + rows_it.consume(|row_render| { body.row(20.0, |row| { - F::Body::render_row( - &mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { - row, - __phantom: PhantomData::default(), - }, - &mut rowData, - ) - }); - } + row_render.render_row(&mut ConstMutSwitchUiTableRowImpl::<_, Constructor> { + row, + __phantom: PhantomData::default(), + }) + }) + }); }); } diff --git a/utility/src/gui/const_mut_switch/trait.rs b/utility/src/gui/const_mut_switch/trait.rs index 41788c8..051a82f 100644 --- a/utility/src/gui/const_mut_switch/trait.rs +++ b/utility/src/gui/const_mut_switch/trait.rs @@ -1,6 +1,4 @@ -use super::{ - _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstRef, RefType, -}; +use super::{_ConstMutSwitchUiTableHeaderCallback, RefType}; use eframe::emath::Numeric; use std::ops::RangeInclusive; @@ -26,10 +24,7 @@ pub trait ConstMutSwitchUi { fn space(&mut self); - fn horizontal>( - &mut self, - clojure: &mut F::Clojure<'_>, - ); + fn horizontal(&mut self, clojure: impl _ConstMutSwitchUiCallback); fn button( &mut self, @@ -40,29 +35,16 @@ pub trait ConstMutSwitchUi { fn separator(&mut self); - fn scroll_area_2>( - &mut self, - clojure: &mut F::Clojure<'_>, - ); + fn scroll_area_2(&mut self, clojure: impl _ConstMutSwitchUiCallback); - fn table< - 'a, - F: _ConstMutSwitchUiTableBaseCallback - >( + fn table( &mut self, vscroll: bool, columns: usize, - header_clojure: >::Clojure< - '_, - >, + header: impl _ConstMutSwitchUiTableHeaderCallback, ); } pub trait _ConstMutSwitchUiCallback { - type Clojure<'a>; - - fn render>( - ctx: &mut SubCtx, - clojure: &mut Self::Clojure<'_>, - ); + fn render(self, ctx: &mut impl ConstMutSwitchUi); } diff --git a/utility/src/gui/const_mut_switch/эволюция_костылей.md b/utility/src/gui/const_mut_switch/эволюция_костылей.md index 9b12f7f..cf2b548 100644 --- a/utility/src/gui/const_mut_switch/эволюция_костылей.md +++ b/utility/src/gui/const_mut_switch/эволюция_костылей.md @@ -10,6 +10,9 @@ Альтернативный вариант - параметризовать тип ссылок... +В данном файле приведены только краткие выдержки из рассуждений. +![](https://www.meme-arsenal.com/memes/639e92214ce1ea090b2134dbd89c3cf1.jpg) + ## 1 Начальный интерфейс содержал ассоциированный тип с самой ссылкой и методом @@ -683,4 +686,152 @@ trait _IteratorGetter { } ``` -Но способ работает только для очень простых случаев, так что малопригоден. \ No newline at end of file +Но способ работает только для очень простых случаев, так что малопригоден. + +## 5 + +Что бы упростить функции, отвечающие за интерфейс и абстрагироваться от формата хранения данных, +работа с графом была переделана на контексты (почему-то всё всегда приходит к контекстам): +```rust +trait Ctx_Graph { + type RefType: RefType; + + type Vertex; + fn iter_vertices(&mut self, graph: &mut G) -> impl Iterator; + + type Edge; + fn iter_edges_or_nothing( + &mut self, + vertex: &mut Self::Vertex, + ) -> impl Iterator>; + + fn get_edge_len_ptr( + &mut self, + edge: &mut Self::Edge, + ) -> ::Ref<'_, f64>; + + fn set_vertex_to_update_state(&self, state: &mut UpdatePending); +} + +fn draw_lengths_table< + G, + CtxUi: ConstMutSwitchUi, + CtxGraph: Ctx_Graph, +>( + ctx_ui: &mut CtxUi, + ctx_graph: &mut CtxGraph, + graph: G, + update: ::Ref<'_, UpdatePending>, +) { + // ... +} +``` + +Тип графа передаётся параметром, а не ассоциированным типом, потому что в общем случае +контекст может работать с разными представлениями одновременно. + +## 6 +### 6.1 +В результате очередной внезапной гениальной мысли, все трейты которые должны были быть +лямбдами, вместо ассоциированного типа для параметра теперь используют `self`. Источник +озарения - необходимость сделать эту функцию деструктором для передаваемых данных. Не +то что бы это нельзя было сделать с параметром, но новое способ оказался удобнее для +имплементации: + +```rust +trait _ConstMutSwitchUiCallback { + fn render(self, ctx: &mut impl ConstMutSwitchUi); +} + +trait ConstMutSwitchUi { + // ... + + fn horizontal(&mut self, clojure: impl _ConstMutSwitchUiCallback); + + // ... +} +``` + +Такой способ более гибок корректен с точки зрения владения переданными для области данными. + +### 6.2 +Необходимость сделать этот метод деструктором возникла во время оптимизации трейтов для таблиц: +метод для отрисовки заголовка становится конвертером в итератор. + +```rust +trait _ConstMutSwitchUiTableHeaderCallback { + type RowRender: _ConstMutSwitchUiTableRowCallback; + + fn render_header<'a>( + self, + ctx: &mut impl ConstMutSwitchUiTableRow, + ) -> impl Iterator; +} +``` + +### 6.3 +Итератор, который возвращает значения не может контролировать, что возвращённое значение не будет +использоваться после выдачи следующего. Чтобы это ограничить, вместо возврата итератор будет +передавать значение в полученную лямбду. + +```rust +trait LambdaIterator { + type Item; + + fn next(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option; + + fn consume(mut self, receiver: impl FnMut(Self::Item)) where Self: Sized { + while self.next(|e| receiver(e)).is_some() {} + } +} +``` + +Метод `consume` добавлен просто для удобства. На новый трейт были заменены все итераторы в этом +модуле. + +### 6.4 +Но из-за того, что значение в параметр `self` после `_ConstMutSwitchUiTableHeaderCallback::render_header` +не существует, нельзя просто взять и вернуть что-то с ним связанное (вообще должно быть можно, но +было много красного и лень разбираться). Решение такое же как и с итератором: "возвращать" в лямбду: +```rust +trait _ConstMutSwitchUiTableHeaderCallback { + type RowRender: _ConstMutSwitchUiTableRowCallback; + type RowsIterator: LambdaIterator; + + fn render_header( + self, + ctx: &mut impl ConstMutSwitchUiTableRow, + render_body: impl FnOnce(Self::RowsIterator), + ); +} +``` + +### 6.5 +Решение передавать в метод отрисовки заголовка лямбду выглядело прекрасно, пока не выяснилось, что +для получения дескриптора отрисовки тела таблицы нужно выйти из этого метода, что случается после +того как передаётся лямбда которая его использует. Пришлось всё-таки сразиться с borrow checker и найти +ошибку, которая мешала возвращать итератор из этого метода и вернуться на этап `6.2`. + +### 6.6 +Пользоваться контекстом графа внутри лямбды, переданной в итератор от этого контекста по очевидным +причинам нельзя, поэтому возвращаемые итераторы были расширены: +```rust + +trait Ctx_Graph { + // ... + + type Vertex; + type VerticesIterator<'a>: LambdaIterator + where + Self: 'a; + fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>; + + type Edge; + fn iter_edges_or_nothing( + &mut self, + vertex: &mut Self::Vertex, + ) -> impl LambdaIterator)>; + + // ... +} +``` \ No newline at end of file diff --git a/utility/src/gui/lengths_table.rs b/utility/src/gui/lengths_table.rs index 7bc1578..b8e732e 100644 --- a/utility/src/gui/lengths_table.rs +++ b/utility/src/gui/lengths_table.rs @@ -1,173 +1,201 @@ -use crate::UpdatePending; -use crate::graph::{CompleteGraph, Edge, EdgesVec, VerticesVec}; -use crate::gui::const_mut_switch::{_ConstMutSwitchUiCallback, _ConstMutSwitchUiTableBaseCallback, _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefGetter, RefType, _IteratorGetter}; -use eframe::egui::{ScrollArea, Ui}; -use egui_extras::{Column, TableBuilder}; -use std::collections::HashSet; -use std::ops::{Index, IndexMut}; +use crate::gui::const_mut_switch::{ + _ConstMutSwitchUiCallback, _ConstMutSwitchUiTableHeaderCallback, + _ConstMutSwitchUiTableRowCallback, ConstMutSwitchUi, ConstMutSwitchUiTableRow, RefType, +}; +use crate::{LambdaIterator, UpdatePending}; use std::ptr::NonNull; -pub fn draw_lengths_table<'a, Ctx: ConstMutSwitchUi>( - ctx: &mut Ctx, - graph: ::Ref<'a, CompleteGraph<()>>, - update: ::Ref<'a, UpdatePending>, +pub trait Ctx_Graph { + type RefType: RefType; + + type Vertex; + type VerticesIterator<'a>: LambdaIterator + where + Self: 'a; + fn iter_vertices(&mut self, graph: &mut G) -> Self::VerticesIterator<'_>; + + type Edge; + fn iter_edges_or_nothing( + &mut self, + vertex: &mut Self::Vertex, + ) -> impl LambdaIterator)>; + + fn get_edge_len_ptr( + &mut self, + edge: &mut Self::Edge, + ) -> ::Ref<'_, f64>; + + fn set_vertex_to_update_state(&self, vertex: &Self::Vertex, state: &mut UpdatePending); + + fn vertices_count(&self) -> usize; +} + +pub fn draw_lengths_table< + G, + CtxUi: ConstMutSwitchUi, + CtxGraph: Ctx_Graph, +>( + ctx_ui: &mut CtxUi, + ctx_graph: &mut CtxGraph, + graph: G, + update: ::Ref<'_, UpdatePending>, ) { - ctx.scroll_area_2::(&mut (graph, update)); + ctx_ui.scroll_area_2(ScrollArea { + ctx_graph, + graph, + update, + }); +} +struct ScrollArea<'g, 'u, G, CtxGraph: Ctx_Graph> { + ctx_graph: &'g mut CtxGraph, + graph: G, + update: ::Ref<'u, UpdatePending>, +} - struct ScrollArea {} - impl _ConstMutSwitchUiCallback for ScrollArea { - type Clojure<'a> = (RT::Ref<'a, CompleteGraph<()>>, RT::Ref<'a, UpdatePending>); - - fn render>( - ctx: &mut SubCtx, - mut clojure: &mut (RT::Ref<'_, CompleteGraph<()>>, RT::Ref<'_, UpdatePending>), - ) { - ctx.table::(false, clojure.0.vertex_count(), HeaderData { - update_state: clojure.1, - edges: CompleteGraph::<()>, - vertices: (), - }) - } +impl> _ConstMutSwitchUiCallback +for ScrollArea<'_, '_, G, CtxGraph> +{ + fn render(self, ctx: &mut impl ConstMutSwitchUi) { + ctx.table( + false, + self.ctx_graph.vertices_count(), + Header { + ctx_graph: self.ctx_graph, + graph: self.graph, + update: self.update, + }, + ) } } -struct Table{} -impl _ConstMutSwitchUiTableBaseCallback for Table { - type Header = Header; - type Body = Body; +struct Header<'g, 'u, G, CtxGraph: Ctx_Graph> { + ctx_graph: &'g mut CtxGraph, + graph: G, + update: ::Ref<'u, UpdatePending>, +} - fn header_to_body<'a>(header: &mut >::Clojure<'a>) -> impl Iterator>::Clojure<'a>> { - struct ItGet{}; - impl _IteratorGetter for ItGet { - type C = VerticesVec; - type T<'a> = HashSet; +impl<'g, 'u, G, CtxGraph: Ctx_Graph> _ConstMutSwitchUiTableHeaderCallback +for Header<'g, 'u, G, CtxGraph> +{ + type RowRender = Row<'g, 'u, G, CtxGraph>; - fn get_const_iterator<'a>(cont: &'a Self::C) -> impl Iterator> { - return cont.iter_indexed() - } + type RowsIterator = RowsIterators<'g, 'u, G, CtxGraph>; - fn get_mut_iterator<'a>(cont: &'a mut Self::C) -> impl Iterator> { - return cont.iter_indexed_mut() - } - } - - return header.vertices.iter_indexed().enumerate().map(|(ri, (vi, eis))| { - return RowData{ - update_state: header.update_state, - edges: header.edges, - row_id: ri, - vertex_id: vi, - vertex_edges_ids: eis, - } - }) + fn render_header( + mut self, + row: &mut impl ConstMutSwitchUiTableRow, + ) -> Self::RowsIterator { + row.cell(CornerCell {}); + + let mut column_id = 0; + self.ctx_graph + .iter_vertices(&mut self.graph) + .consume(|(ctx_graph, v)| { + column_id += 1; + // let update = &mut self.update; + let data = TitleCell { + ctx_graph: ctx_graph, + row_id: column_id, + vertex: &v, + update: &mut self.update, + }; + row.cell(data) + }); + + return RowsIterators { + vertices: self.ctx_graph.iter_vertices(&mut self.graph), + update: self.update, + row_id: 0, + }; } } - - -struct HeaderData<'us, 'es, 'eis, RT: RefType> { - update_state: RT::Ref<'us, UpdatePending>, - edges: RT::Ref<'es, EdgesVec<()>>, - vertices: RT::Ref<'eis, VerticesVec>, -} - -struct Header {} -impl _ConstMutSwitchUiTableRowCallback for Header { - type Clojure<'a> = HeaderData<'a, 'a, 'a, RT>; - - fn render_row>( - row: &mut Row, - clojure: &mut Self::Clojure<'_>, - ) { - row.cell::(&mut ()); - for i in 0..clojure.vertices.len() { - row.cell::(&mut (i, NonNull::from_ref(&clojure.update_state))); - } - - struct CornerCell {} - impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell { - type Clojure<'a> = (); - - fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(ctx: &mut SubCtx, _: &mut ()) { - ctx.label("#"); - } - } - } -} - -struct RowData<'us, 'es, 'eis, RT: RefType> { - update_state: RT::Ref<'us, UpdatePending>, - edges: RT::Ref<'es, EdgesVec<()>>, +struct RowsIterators<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> { + vertices: CtxGraph::VerticesIterator<'g>, + update: <CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>, row_id: usize, - vertex_id: usize, - vertex_edges_ids: RT::Ref<'eis, HashSet<usize>>, } -struct Body {} -impl<RT: RefType> _ConstMutSwitchUiTableRowCallback<RT> for Body { - type Clojure<'a> = RowData<'a, 'a, 'a, RT>; +impl<'g, 'u, G, CtxGraph: Ctx_Graph<G> + 'g> LambdaIterator for RowsIterators<'g, 'u, G, CtxGraph> { + type Item = Row<'g, 'u, G, CtxGraph>; - fn render_row<Row: ConstMutSwitchUiTableRow<RefType = RT>>( - row: &mut Row, - clojure: &mut Self::Clojure<'_>, - ) { - row.cell::<Title>(&mut (clojure.row_id, NonNull::from_ref(&clojure.update_state))); - - // not map because a borrow checker doesn't allow returning from lambda. - // raw pointer because a borrow checker. - let mut local_edges = - Vec::<(usize, NonNull<f64>)>::with_capacity(clojure.vertex_edges_ids.len()); - for ei in clojure.vertex_edges_ids.iter() { - let mut e = RT::_get( - &mut clojure.edges, - |ee| ee.index(*ei), - |ee| ee.index_mut(*ei), - ); - let another_vertex = e.another(clojure.vertex_id); - let length_storage = RT::_get(&mut e, |e| &e.length, |e| &mut e.length); - let length_storage = unsafe { RT::_to_ptr(length_storage) }; - local_edges.push((another_vertex, length_storage)); - } - local_edges.sort_by_key(|(k, _)| *k); - - for (column_index, cell_data) in local_edges.iter_mut().enumerate() { - if (column_index == clojure.row_id) { - row.cell::<EmptyCell>(&mut ()); - } - row.cell::<Cell>(&mut unsafe { RT::_from_ptr(cell_data.1) }); - } - - struct Cell {} - impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Cell { - type Clojure<'a> = RT::Ref<'a, f64>; - - fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>( - ctx: &mut SubCtx, - clojure: &mut Self::Clojure<'_>, - ) { - ctx.slider(0.0..=10.0, 0.1, clojure); - } - } - - struct EmptyCell {} - impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell { - type Clojure<'a> = (); - - fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>(_: &mut SubCtx, _: &mut ()) {} - } + fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> { + return self.vertices.next(|(ctx, v)| { + self.row_id += 1; + return receiver(Row { + ctx_graph: ctx, + vertex: v, + row_id: self.row_id, + update: NonNull::from_mut(&mut self.update), + }); + }); } } -struct Title {} -impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for Title { - type Clojure<'a> = (usize, NonNull<RT::Ref<'a, UpdatePending>>); +struct Row<'g, 'u, G, CtxGraph: Ctx_Graph<G>> { + ctx_graph: &'g mut CtxGraph, + vertex: CtxGraph::Vertex, + row_id: usize, + update: NonNull<<CtxGraph::RefType as RefType>::Ref<'u, UpdatePending>>, +} - fn render<SubCtx: ConstMutSwitchUi<RefType = RT>>( - ctx: &mut SubCtx, - clojure: &mut Self::Clojure<'_>, - ) { - ctx.label(clojure.0.to_string().as_str()); - ctx.button("-", unsafe { clojure.1.as_mut() }, |s| { - *s = UpdatePending::Remove(clojure.0) - }) +impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiTableRowCallback<CtxGraph::RefType> +for Row<'_, '_, G, CtxGraph> +{ + fn render_row(mut self, row: &mut impl ConstMutSwitchUiTableRow<RefType=CtxGraph::RefType>) { + row.cell(TitleCell { + ctx_graph: self.ctx_graph, + row_id: self.row_id, + vertex: &self.vertex, + update: unsafe { self.update.as_mut() }, + }); + + self.ctx_graph + .iter_edges_or_nothing(&mut self.vertex) + .consume(|(ctx_graph, e)| { + match e { + None => row.cell(EmptyCell {}), + Some(mut e) => row.cell(LengthCell { + length: &mut ctx_graph.get_edge_len_ptr(&mut e), + }) + } + }) } } + +struct CornerCell {} +impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for CornerCell { + fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) { + ctx.label("#"); + } +} + +struct TitleCell<'g, 'v, 'rr, 'r: 'rr, G, CtxGraph: Ctx_Graph<G>> { + ctx_graph: &'g mut CtxGraph, + row_id: usize, + vertex: &'v CtxGraph::Vertex, + update: &'rr mut <CtxGraph::RefType as RefType>::Ref<'r, UpdatePending>, +} +impl<G, CtxGraph: Ctx_Graph<G>> _ConstMutSwitchUiCallback<CtxGraph::RefType> +for TitleCell<'_, '_, '_, '_, G, CtxGraph> +{ + fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=CtxGraph::RefType>) { + ctx.label(self.row_id.to_string().as_str()); + ctx.button("-", self.update, |s| { + self.ctx_graph.set_vertex_to_update_state(self.vertex, s) + }); + } +} + +struct LengthCell<'rr, 'r: 'rr, RT: RefType> { + length: &'rr mut RT::Ref<'r, f64>, +} +impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for LengthCell<'_, '_, RT> { + fn render(self, ctx: &mut impl ConstMutSwitchUi<RefType=RT>) { + ctx.slider(0.0..=10.0, 0.1, self.length); + } +} + +struct EmptyCell {} +impl<RT: RefType> _ConstMutSwitchUiCallback<RT> for EmptyCell { + fn render(self, _: &mut impl ConstMutSwitchUi<RefType=RT>) {} +} diff --git a/utility/src/gui/render_graph.rs b/utility/src/gui/render_graph.rs index 8535271..dd90396 100644 --- a/utility/src/gui/render_graph.rs +++ b/utility/src/gui/render_graph.rs @@ -1,6 +1,6 @@ use crate::graph::{Edge, EdgesVec, VerticesVec}; use eframe::egui; -use eframe::egui::{Align2, Color32, FontDefinitions, Pos2, Rect, StrokeKind, Ui, Vec2}; +use eframe::egui::{Align2, Color32, Pos2, Rect, StrokeKind, Ui, Vec2}; use eframe::epaint::{CornerRadius, Stroke}; pub fn render_graph<D>( diff --git a/utility/src/lambda_iterator.rs b/utility/src/lambda_iterator.rs new file mode 100644 index 0000000..f443534 --- /dev/null +++ b/utility/src/lambda_iterator.rs @@ -0,0 +1,28 @@ +pub trait LambdaIterator { + type Item; + + fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R>; + + fn consume(mut self, mut receiver: impl FnMut(Self::Item)) + where + Self: Sized, + { + while self.next(|e| receiver(e)).is_some() {} + } +} + +pub fn make_lambda<I: Iterator>(it: I) -> impl LambdaIterator<Item = I::Item> { + return LambdaIteratorConverter { it }; +} + +struct LambdaIteratorConverter<I: Iterator> { + it: I, +} + +impl<I: Iterator> LambdaIterator for LambdaIteratorConverter<I> { + type Item = I::Item; + + fn next<R>(&mut self, receiver: impl FnOnce(Self::Item) -> R) -> Option<R> { + return self.it.next().map(receiver); + } +} diff --git a/utility/src/lib.rs b/utility/src/lib.rs index 22e0066..d9bd573 100644 --- a/utility/src/lib.rs +++ b/utility/src/lib.rs @@ -1,7 +1,9 @@ pub mod graph; pub mod gui; +mod lambda_iterator; mod update_pending; mod weighted_random; +pub use lambda_iterator::{LambdaIterator, make_lambda}; pub use update_pending::{UpdatePending, UpdateTarget}; pub use weighted_random::{weighted_random, weighted_random_index};