initial commit
This commit is contained in:
commit
8153da2091
39 changed files with 4339 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Cargo.lock
|
||||
/target
|
||||
*~
|
||||
\#*\#
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
authors = ["Michael Sippel <micha@fragmental.art>"]
|
||||
edition = "2018"
|
||||
name = "r3vi"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cgmath = { version = "0.18.0", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1.3.3"
|
||||
serde_json = "*"
|
||||
|
||||
[dependencies.async-std]
|
||||
version = "1.9.0"
|
||||
features = ["unstable", "attributes"]
|
43
README.md
Normal file
43
README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# r3vi
|
||||
|
||||
Rust Runtime for Reactive View-Projections
|
||||
|
||||
Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are
|
||||
updated *reactively* on changes in the source-view.
|
||||
These updates are performed incrementally using fine-granuar diffs.
|
||||
|
||||
*Views* are abstract accessor-interfaces that also define the update protocol (the diff).
|
||||
*Observers* can register to observe a *View* and are notified with the according diff-message
|
||||
whenever the view changes.
|
||||
*Projections* are transformations from one view into antoher.
|
||||
They are made of the target view and an observer that observes the source view.
|
||||
|
||||
R3vi provides basic data-structures and projections to build projectional pipelines
|
||||
with an interface similar to native rust iterators.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```rust
|
||||
use r3vi::buffer::vec::*;
|
||||
|
||||
let mut buffer = VecBuffer::<i32>::new();
|
||||
buffer.push(3);
|
||||
|
||||
let projected_port = buffer.get_port()
|
||||
.to_sequence() // make SequenceView from Vec
|
||||
.map(|x| x + 10)
|
||||
.filter(|x| x > 10);
|
||||
|
||||
let projected_view = projected_port.get_view();
|
||||
|
||||
assert_eq!(projected_view.get(&0), Some(13));
|
||||
|
||||
buffer.push(5); // maps to 15
|
||||
buffer.push(-9); // maps to 1, is eliminated by filter
|
||||
buffer.push(1); // maps to 11
|
||||
|
||||
assert_eq!(projected_view.get(&1), Some(15));
|
||||
assert_eq!(projected_view.get(&2), Some(11));
|
||||
|
||||
```
|
90
src/buffer/grid_hashmap.rs
Normal file
90
src/buffer/grid_hashmap.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
|
||||
use {
|
||||
std::{
|
||||
sync::Arc,
|
||||
collections::HashMap,
|
||||
hash::Hash
|
||||
},
|
||||
std::sync::RwLock,
|
||||
crate::{
|
||||
core::{
|
||||
Observer,
|
||||
ObserverBroadcast,
|
||||
View,
|
||||
InnerViewPort
|
||||
},
|
||||
index::{IndexArea, IndexView}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct GridBuffer<Item> {
|
||||
data: HashMap<Point2<i16>, Item>,
|
||||
limit: Point2<i16>
|
||||
}
|
||||
|
||||
impl<Item> View for GridBuffer<Item>
|
||||
where Item: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl<Item> IndexView<Point2<i16>> for GridBufferView<Item>
|
||||
where Item: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn get(&self, key: &Point2<i16>) -> Option<Self::Item> {
|
||||
self.data.get(key).cloned()
|
||||
}
|
||||
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Range(
|
||||
Point2::new(0, 0)
|
||||
..= self.limit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GridBufferController<Item>
|
||||
where Item: Clone + Send + Sync + 'static
|
||||
{
|
||||
data: Arc<RwLock<HashMap<Point2<i16>, Item>>>,
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Point2<i16>, Item = Item>>>>
|
||||
}
|
||||
|
||||
impl<Key, Item> GridBuffer<Key, Item>
|
||||
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static
|
||||
{
|
||||
pub fn new(port: InnerViewPort<dyn IndexView<Point2<i16>, Item = Item>>) -> Self {
|
||||
let data = Arc::new(RwLock::new(HashMap::<Point2<i16>, Item>::new()));
|
||||
port.set_view(Some(Arc::new(GridBufferView(data.clone()))));
|
||||
|
||||
GridBuffer {
|
||||
data,
|
||||
cast: port.get_broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: Point2<i16>, item: Item) {
|
||||
self.data.write().unwrap().insert(key.clone(), item);
|
||||
|
||||
if
|
||||
|
||||
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||
}
|
||||
|
||||
pub fn insert_iter<T>(&mut self, iter: T)
|
||||
where T: IntoIterator<Item = (Point2<i16>, Item)> {
|
||||
for (key, item) in iter {
|
||||
self.insert(key, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: Point2<i16>) {
|
||||
self.data.write().unwrap().remove(&key);
|
||||
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||
}
|
||||
}
|
||||
|
158
src/buffer/index_hashmap.rs
Normal file
158
src/buffer/index_hashmap.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
InnerViewPort, OuterViewPort, ViewPort, View,
|
||||
index::{IndexArea, IndexView},
|
||||
},
|
||||
},
|
||||
std::sync::RwLock,
|
||||
std::{collections::HashMap, hash::Hash, sync::Arc, ops::{Deref, DerefMut}},
|
||||
};
|
||||
|
||||
pub struct IndexBufferView<Key, Item>(Arc<RwLock<HashMap<Key, Item>>>)
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static;
|
||||
|
||||
impl<Key, Item> View for IndexBufferView<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Msg = IndexArea<Key>;
|
||||
}
|
||||
|
||||
impl<Key, Item> IndexView<Key> for IndexBufferView<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||
self.0.read().unwrap().get(key).cloned()
|
||||
}
|
||||
|
||||
fn area(&self) -> IndexArea<Key> {
|
||||
IndexArea::Set(self.0.read().unwrap().keys().cloned().collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IndexBuffer<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
data: Arc<RwLock<HashMap<Key, Item>>>,
|
||||
port: InnerViewPort<dyn IndexView<Key, Item = Item>>,
|
||||
}
|
||||
|
||||
impl<Key, Item> IndexBuffer<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
pub fn with_port(port: InnerViewPort<dyn IndexView<Key, Item = Item>>) -> Self {
|
||||
let data = Arc::new(RwLock::new(HashMap::<Key, Item>::new()));
|
||||
port.set_view(Some(Arc::new(IndexBufferView(data.clone()))));
|
||||
|
||||
IndexBuffer {
|
||||
data,
|
||||
port
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
IndexBuffer::with_port(ViewPort::new().into_inner())
|
||||
}
|
||||
|
||||
pub fn get_port(&self) -> OuterViewPort<dyn IndexView<Key, Item = Item>> {
|
||||
self.port.0.outer()
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &Key) -> Option<Item> {
|
||||
self.data.read().unwrap().get(key).cloned()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, key: &Key) -> MutableIndexAccess<Key, Item> {
|
||||
MutableIndexAccess {
|
||||
buf: self.clone(),
|
||||
key: key.clone(),
|
||||
val: self.get(key)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, key: Key, item: Option<Item>) {
|
||||
if let Some(item) = item {
|
||||
self.data.write().unwrap().insert(key.clone(), item);
|
||||
} else {
|
||||
self.data.write().unwrap().remove(&key);
|
||||
}
|
||||
self.port.notify(&IndexArea::Set(vec![key]));
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: Key, item: Item) {
|
||||
self.data.write().unwrap().insert(key.clone(), item);
|
||||
self.port.notify(&IndexArea::Set(vec![key]));
|
||||
}
|
||||
|
||||
pub fn insert_iter<T>(&mut self, iter: T)
|
||||
where
|
||||
T: IntoIterator<Item = (Key, Item)>,
|
||||
{
|
||||
for (key, item) in iter {
|
||||
self.insert(key, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: Key) {
|
||||
self.data.write().unwrap().remove(&key);
|
||||
self.port.notify(&IndexArea::Set(vec![key]));
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct MutableIndexAccess<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
buf: IndexBuffer<Key, Item>,
|
||||
key: Key,
|
||||
val: Option<Item>,
|
||||
}
|
||||
|
||||
impl<Key, Item> Deref for MutableIndexAccess<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Target = Option<Item>;
|
||||
|
||||
fn deref(&self) -> &Option<Item> {
|
||||
&self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<Key, Item> DerefMut for MutableIndexAccess<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<Key, Item> Drop for MutableIndexAccess<Key, Item>
|
||||
where
|
||||
Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||
Item: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.buf.update(self.key.clone(), self.val.clone());
|
||||
}
|
||||
}
|
||||
|
5
src/buffer/mod.rs
Normal file
5
src/buffer/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
pub mod singleton;
|
||||
pub mod vec;
|
||||
pub mod index_hashmap;
|
||||
|
147
src/buffer/singleton.rs
Normal file
147
src/buffer/singleton.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
InnerViewPort, OuterViewPort, View, ViewPort,
|
||||
singleton::SingletonView
|
||||
},
|
||||
},
|
||||
std::sync::RwLock,
|
||||
std::{
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
},
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct SingletonBufferView<T: Clone + Send + Sync + 'static>(Arc<RwLock<T>>);
|
||||
|
||||
impl<T> View for SingletonBufferView<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Msg = ();
|
||||
}
|
||||
|
||||
impl<T> SingletonView for SingletonBufferView<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn get(&self) -> Self::Item {
|
||||
self.0.read().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SingletonBuffer<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
value: Arc<RwLock<T>>,
|
||||
port: InnerViewPort<dyn SingletonView<Item = T>>
|
||||
}
|
||||
|
||||
impl<T> SingletonBuffer<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
pub fn with_port(value: T, port: InnerViewPort<dyn SingletonView<Item = T>>) -> Self {
|
||||
let value = Arc::new(RwLock::new(value));
|
||||
port.set_view(Some(Arc::new(SingletonBufferView(value.clone()))));
|
||||
|
||||
SingletonBuffer {
|
||||
value,
|
||||
port
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(value: T) -> Self {
|
||||
SingletonBuffer::with_port(value, ViewPort::new().into_inner())
|
||||
}
|
||||
|
||||
pub fn get_port(&self) -> OuterViewPort<dyn SingletonView<Item = T>> {
|
||||
self.port.0.outer()
|
||||
}
|
||||
|
||||
pub fn get(&self) -> T {
|
||||
self.value.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> MutableSingletonAccess<T> {
|
||||
MutableSingletonAccess {
|
||||
buf: self.clone(),
|
||||
val: self.get(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, new_value: T) {
|
||||
let mut v = self.value.write().unwrap();
|
||||
*v = new_value;
|
||||
drop(v);
|
||||
self.port.notify(&());
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct MutableSingletonAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
buf: SingletonBuffer<T>,
|
||||
val: T,
|
||||
}
|
||||
|
||||
impl<T> Deref for MutableSingletonAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for MutableSingletonAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for MutableSingletonAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.buf.set(self.val.clone());
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::buffer::singleton::*;
|
||||
|
||||
#[test]
|
||||
fn singleton_buffer1() {
|
||||
let buffer = SingletonBuffer::<char>::new('a');
|
||||
let port = buffer.get_port();
|
||||
|
||||
assert_eq!(buffer.get(), 'a');
|
||||
assert_eq!(port.get_view().get(), 'a');
|
||||
|
||||
*buffer.get_mut() = 'b';
|
||||
assert_eq!(buffer.get(), 'b');
|
||||
assert_eq!(port.get_view().get(), 'b');
|
||||
}
|
||||
}
|
||||
|
193
src/buffer/vec.rs
Normal file
193
src/buffer/vec.rs
Normal file
|
@ -0,0 +1,193 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{InnerViewPort, OuterViewPort, View, ViewPort},
|
||||
},
|
||||
std::sync::RwLock,
|
||||
std::{
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
},
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum VecDiff<T> {
|
||||
Clear,
|
||||
Push(T),
|
||||
Remove(usize),
|
||||
Insert { idx: usize, val: T },
|
||||
Update { idx: usize, val: T },
|
||||
}
|
||||
|
||||
impl<T> View for Vec<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Msg = VecDiff<T>;
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VecBuffer<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
data: Arc<RwLock<Vec<T>>>,
|
||||
port: InnerViewPort<RwLock<Vec<T>>>
|
||||
}
|
||||
|
||||
impl<T> VecBuffer<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
pub fn with_data_port(data: Vec<T>, port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
|
||||
let data = Arc::new(RwLock::new(data));
|
||||
port.set_view(Some(data.clone()));
|
||||
|
||||
for x in data.read().unwrap().iter().cloned() {
|
||||
port.notify(&VecDiff::Push(x));
|
||||
}
|
||||
|
||||
VecBuffer {
|
||||
data,
|
||||
port
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_data(data: Vec<T>) -> Self {
|
||||
VecBuffer::with_data_port(data, ViewPort::new().into_inner())
|
||||
}
|
||||
|
||||
pub fn with_port(port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
|
||||
VecBuffer::with_data_port(vec![], port)
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
VecBuffer::with_port(ViewPort::new().into_inner())
|
||||
}
|
||||
|
||||
pub fn get_port(&self) -> OuterViewPort<RwLock<Vec<T>>> {
|
||||
self.port.0.outer()
|
||||
}
|
||||
|
||||
pub fn apply_diff(&mut self, diff: VecDiff<T>) {
|
||||
let mut data = self.data.write().unwrap();
|
||||
match &diff {
|
||||
VecDiff::Clear => {
|
||||
data.clear();
|
||||
}
|
||||
VecDiff::Push(val) => {
|
||||
data.push(val.clone());
|
||||
}
|
||||
VecDiff::Remove(idx) => {
|
||||
data.remove(*idx);
|
||||
}
|
||||
VecDiff::Insert { idx, val } => {
|
||||
data.insert(*idx, val.clone());
|
||||
}
|
||||
VecDiff::Update { idx, val } => {
|
||||
data[*idx] = val.clone();
|
||||
}
|
||||
}
|
||||
drop(data);
|
||||
|
||||
self.port.notify(&diff);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.read().unwrap().len()
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: usize) -> T {
|
||||
self.data.read().unwrap()[idx].clone()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.apply_diff(VecDiff::Clear);
|
||||
}
|
||||
|
||||
pub fn push(&mut self, val: T) {
|
||||
self.apply_diff(VecDiff::Push(val));
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, idx: usize) {
|
||||
self.apply_diff(VecDiff::Remove(idx));
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, idx: usize, val: T) {
|
||||
self.apply_diff(VecDiff::Insert { idx, val });
|
||||
}
|
||||
|
||||
pub fn update(&mut self, idx: usize, val: T) {
|
||||
self.apply_diff(VecDiff::Update { idx, val });
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, idx: usize) -> MutableVecAccess<T> {
|
||||
MutableVecAccess {
|
||||
buf: self.clone(),
|
||||
idx,
|
||||
val: self.get(idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct MutableVecAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
buf: VecBuffer<T>,
|
||||
idx: usize,
|
||||
val: T,
|
||||
}
|
||||
|
||||
impl<T> Deref for MutableVecAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for MutableVecAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for MutableVecAccess<T>
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.buf.update(self.idx, self.val.clone());
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::buffer::vec::*;
|
||||
|
||||
#[test]
|
||||
fn vec_buffer1() {
|
||||
let mut buffer = VecBuffer::new();
|
||||
|
||||
buffer.push('a');
|
||||
buffer.push('b');
|
||||
buffer.push('c');
|
||||
}
|
||||
}
|
||||
|
48
src/lib.rs
Normal file
48
src/lib.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#![feature(trait_alias)]
|
||||
|
||||
//! Rust Runtime for Reactive View-Projections
|
||||
//!
|
||||
//! Using **r3vi** you can define *Projections*, i.e. transformations on *Views* that are
|
||||
//! updated *reactively* on changes in the source-view.
|
||||
//! These updates are performed incrementally using fine-granuar diffs.
|
||||
//!
|
||||
//! *Views* are abstract accessor-interfaces that also define the update protocol (the diff).
|
||||
//! *Observers* can register to observe a *View* and are notified with the according diff-message
|
||||
//! whenever the view changes.
|
||||
//! *Projections* are transformations from one view into antoher.
|
||||
//! They are made of the target view and an observer that observes the source view.
|
||||
//!
|
||||
//! R3vi provides basic data-structures and projections to build projectional pipelines
|
||||
//! with an interface similar to native rust iterators.
|
||||
//!
|
||||
//!
|
||||
//!# Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use r3vi::buffer::vec::*;
|
||||
//!
|
||||
//! let mut buffer = VecBuffer::<i32>::new();
|
||||
//! buffer.push(3);
|
||||
//!
|
||||
//! let projected_port = buffer.get_port()
|
||||
//! .to_sequence() // make SequenceView from Vec
|
||||
//! .map(|x| x + 10)
|
||||
//! .filter(|x| x > 10);
|
||||
//!
|
||||
//! let projected_view = projected_port.get_view();
|
||||
//!
|
||||
//! assert_eq!(projected_view.get(&0), Some(13));
|
||||
//!
|
||||
//! buffer.push(5); // maps to 15
|
||||
//! buffer.push(-9); // maps to 1, is eliminated by filter
|
||||
//! buffer.push(1); // maps to 11
|
||||
//!
|
||||
//! assert_eq!(projected_view.get(&1), Some(15));
|
||||
//! assert_eq!(projected_view.get(&2), Some(11));
|
||||
//!
|
||||
//! ```
|
||||
|
||||
pub mod view;
|
||||
pub mod buffer;
|
||||
pub mod projection;
|
||||
|
171
src/projection/decorate_sequence.rs
Normal file
171
src/projection/decorate_sequence.rs
Normal file
|
@ -0,0 +1,171 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
View, OuterViewPort, Observer, ViewPort, ObserverBroadcast,
|
||||
sequence::*
|
||||
},
|
||||
projection::projection_helper::ProjectionHelper,
|
||||
},
|
||||
std::sync::Arc,
|
||||
std::sync::RwLock,
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
// Wrap
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct Wrapped<T>
|
||||
where T: Send + Sync + 'static
|
||||
{
|
||||
pub(super) opening: T,
|
||||
pub(super) closing: T,
|
||||
pub(super) items: Arc<dyn SequenceView<Item = T>>,
|
||||
|
||||
pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
|
||||
pub(super) proj_helper: ProjectionHelper<(), Self>,
|
||||
}
|
||||
|
||||
impl<T> View for Wrapped<T>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl<T> SequenceView for Wrapped<T>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
Some(self.items.len()? + 2)
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||
let l = self.items.len().unwrap_or((-2 as i32) as usize);
|
||||
if *idx < l+2 {
|
||||
Some(
|
||||
if *idx == 0 {
|
||||
self.opening.clone()
|
||||
} else if *idx < l+1 {
|
||||
self.items.get(&(*idx - 1))?
|
||||
} else {
|
||||
self.closing.clone()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Wrap<T> {
|
||||
fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
|
||||
}
|
||||
|
||||
impl<T> Wrap<T> for OuterViewPort<dyn SequenceView<Item = T>>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
fn wrap(&self, opening: T, closing: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||
let port = ViewPort::new();
|
||||
|
||||
let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone());
|
||||
let w = Arc::new(RwLock::new(Wrapped {
|
||||
opening,
|
||||
closing,
|
||||
items: proj_helper.new_sequence_arg((), self.clone(), |s: &mut Wrapped<T>, item_idx| {
|
||||
s.cast.notify(&(*item_idx + 1));
|
||||
s.cast.notify(&(*item_idx + 2));
|
||||
}),
|
||||
cast: port.get_cast(),
|
||||
proj_helper,
|
||||
}));
|
||||
|
||||
w.write().unwrap().proj_helper.set_proj(&w);
|
||||
port.set_view(Some(w.clone()));
|
||||
port.into_outer()
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
// Separate
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct Separated<T>
|
||||
where T: Send + Sync + 'static
|
||||
{
|
||||
pub(super) delimiter: T,
|
||||
pub(super) items: Arc<dyn SequenceView<Item = T>>,
|
||||
|
||||
pub(super) cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>,
|
||||
pub(super) proj_helper: ProjectionHelper<(), Self>,
|
||||
}
|
||||
|
||||
impl<T> View for Separated<T>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl<T> SequenceView for Separated<T>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
let l = self.items.len()?;
|
||||
if l == 0 {
|
||||
Some(0)
|
||||
} else if l == 1 {
|
||||
Some(1)
|
||||
} else {
|
||||
Some(l*2 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<T> {
|
||||
let l = self.items.len().unwrap_or(usize::MAX);
|
||||
if *idx+1 < l*2 {
|
||||
if *idx % 2 == 0 {
|
||||
self.items.get(&(*idx / 2))
|
||||
} else {
|
||||
Some(self.delimiter.clone())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Separate<T> {
|
||||
fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>>;
|
||||
}
|
||||
|
||||
impl<T> Separate<T> for OuterViewPort<dyn SequenceView<Item = T>>
|
||||
where T: Clone + Send + Sync + 'static
|
||||
{
|
||||
fn separate(&self, delimiter: T) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||
let port = ViewPort::new();
|
||||
|
||||
let mut proj_helper = ProjectionHelper::new(port.update_hooks.clone());
|
||||
let w = Arc::new(RwLock::new(Separated {
|
||||
delimiter,
|
||||
items: proj_helper.new_sequence_arg(
|
||||
(),
|
||||
self.clone(),
|
||||
|s: &mut Separated<T>, item_idx| {
|
||||
s.cast.notify(&(*item_idx * 2));
|
||||
if *item_idx > 0 {
|
||||
s.cast.notify(&(*item_idx * 2 - 1));
|
||||
}
|
||||
}),
|
||||
|
||||
cast: port.get_cast(),
|
||||
proj_helper,
|
||||
}));
|
||||
|
||||
w.write().unwrap().proj_helper.set_proj(&w);
|
||||
port.set_view(Some(w.clone()));
|
||||
port.into_outer()
|
||||
}
|
||||
}
|
||||
|
119
src/projection/enumerate_sequence.rs
Normal file
119
src/projection/enumerate_sequence.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
||||
sequence::SequenceView,
|
||||
}
|
||||
},
|
||||
std::sync::Arc,
|
||||
std::sync::RwLock,
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||
pub fn enumerate(&self) -> OuterViewPort<dyn SequenceView<Item = (usize, Item)>> {
|
||||
let port = ViewPort::new();
|
||||
port.add_update_hook(Arc::new(self.0.clone()));
|
||||
|
||||
let view = Arc::new(RwLock::new(EnumerateSequence {
|
||||
src_view: None,
|
||||
cast: port.inner().get_broadcast(),
|
||||
}));
|
||||
|
||||
self.add_observer(view.clone());
|
||||
port.inner().set_view(Some(view));
|
||||
port.into_outer()
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
pub struct EnumerateSequence<SrcView>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized,
|
||||
{
|
||||
src_view: Option<Arc<SrcView>>,
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = (usize, SrcView::Item)>>>>,
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<SrcView> View for EnumerateSequence<SrcView>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized,
|
||||
{
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl<SrcView> SequenceView for EnumerateSequence<SrcView>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized
|
||||
{
|
||||
type Item = (usize, SrcView::Item);
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
self.src_view.len()
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<(usize, SrcView::Item)> {
|
||||
self.src_view.get(idx).map(|item| (*idx, item))
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<SrcView> Observer<SrcView> for EnumerateSequence<SrcView>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized
|
||||
{
|
||||
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||
let old_len = self.len();
|
||||
self.src_view = view;
|
||||
let new_len = self.len();
|
||||
|
||||
if let Some(len) = old_len {
|
||||
self.cast.notify_each(0..len);
|
||||
}
|
||||
if let Some(len) = new_len {
|
||||
self.cast.notify_each(0..len);
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&mut self, msg: &usize) {
|
||||
self.cast.notify(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::buffer::vec::*;
|
||||
use crate::projection::enumerate_sequence::*;
|
||||
|
||||
use crate::view::port::UpdateTask;
|
||||
|
||||
#[test]
|
||||
fn map_seq1() {
|
||||
let mut buffer = VecBuffer::new();
|
||||
|
||||
let target_port = buffer.get_port().to_sequence().enumerate();
|
||||
let target_view = target_port.get_view();
|
||||
|
||||
buffer.push(0);
|
||||
buffer.push(7);
|
||||
buffer.push(9);
|
||||
|
||||
target_port.0.update();
|
||||
|
||||
assert_eq!(target_view.len(), Some(3));
|
||||
|
||||
assert_eq!(target_view.get(&0), Some((0, 0)));
|
||||
assert_eq!(target_view.get(&1), Some((1, 7)));
|
||||
assert_eq!(target_view.get(&2), Some((2, 9)));
|
||||
assert_eq!(target_view.get(&3), None);
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/projection/filter_map_sequence.rs
Normal file
25
src/projection/filter_map_sequence.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
OuterViewPort,
|
||||
sequence::SequenceView,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||
pub fn filter_map<
|
||||
DstItem: Clone + 'static,
|
||||
F: Fn(&Item) -> Option<DstItem> + Send + Sync + 'static,
|
||||
>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
|
||||
self.map(f)
|
||||
.filter(|x| x.is_some())
|
||||
.map(|x| x.clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
213
src/projection/filter_sequence.rs
Normal file
213
src/projection/filter_sequence.rs
Normal file
|
@ -0,0 +1,213 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
||||
sequence::SequenceView,
|
||||
},
|
||||
},
|
||||
std::sync::Arc,
|
||||
std::sync::RwLock,
|
||||
};
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<V: SequenceView + ?Sized + 'static> OuterViewPort<V> {
|
||||
pub fn filter<P: Fn(&V::Item) -> bool + Send + Sync + 'static>(
|
||||
&self,
|
||||
pred: P,
|
||||
) -> OuterViewPort<dyn SequenceView<Item = V::Item>> {
|
||||
let port = ViewPort::new();
|
||||
port.add_update_hook(Arc::new(self.0.clone()));
|
||||
|
||||
let filter = Arc::new(RwLock::new(Filter {
|
||||
src_view: None,
|
||||
pred,
|
||||
old_preds: RwLock::new(Vec::new()),
|
||||
cast: port.inner().get_broadcast(),
|
||||
}));
|
||||
|
||||
self.add_observer(filter.clone());
|
||||
port.inner().set_view(Some(filter));
|
||||
port.into_outer()
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
struct Filter<SrcView, P>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized + 'static,
|
||||
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
src_view: Option<Arc<SrcView>>,
|
||||
pred: P,
|
||||
old_preds: RwLock<Vec<bool>>,
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>,
|
||||
}
|
||||
|
||||
impl<SrcView, P> Filter<SrcView, P>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized + 'static,
|
||||
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
fn get_offset(&self, idx: usize) -> usize {
|
||||
if let Some(v) = self.src_view.clone() {
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
let mut offset = 0;
|
||||
|
||||
while let (Some(x), true) = (v.get(&i), j <= idx) {
|
||||
if (self.pred)(&x) {
|
||||
j += 1;
|
||||
} else {
|
||||
offset += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
offset
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<SrcView, P> View for Filter<SrcView, P>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized + 'static,
|
||||
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
type Msg = usize;
|
||||
}
|
||||
|
||||
impl<SrcView, P> SequenceView for Filter<SrcView, P>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized + 'static,
|
||||
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
type Item = SrcView::Item;
|
||||
|
||||
fn len(&self) -> Option<usize> {
|
||||
if let Some(src_len) = self.src_view.len() {
|
||||
Some(src_len - self.get_offset(src_len))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||
self.src_view.get(&(idx + self.get_offset(*idx)))
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
impl<SrcView, P> Observer<SrcView> for Filter<SrcView, P>
|
||||
where
|
||||
SrcView: SequenceView + ?Sized + 'static,
|
||||
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
fn reset(&mut self, new_src: Option<Arc<SrcView>>) {
|
||||
let old_len = self.len();
|
||||
self.src_view = new_src;
|
||||
self.old_preds = RwLock::new(Vec::new());
|
||||
let new_len = self.len();
|
||||
|
||||
if let Some(len) = old_len {
|
||||
self.cast.notify_each(0..len);
|
||||
}
|
||||
if let Some(len) = new_len {
|
||||
self.cast.notify_each(0..len);
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(&mut self, idx: &usize) {
|
||||
let l = self.len().unwrap_or(0) + 1;
|
||||
let np = if let Some(x) = self.src_view.get(idx) {
|
||||
(self.pred)(&x)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut opds = self.old_preds.write().unwrap();
|
||||
|
||||
opds.resize_with(1 + *idx, || false);
|
||||
let op = opds.get(*idx).cloned().unwrap_or(false);
|
||||
*opds.get_mut(*idx).unwrap() = np;
|
||||
|
||||
drop(opds);
|
||||
|
||||
let i = (0..*idx)
|
||||
.map(|j| {
|
||||
if let Some(x) = self.src_view.get(&j) {
|
||||
if (self.pred)(&x) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum();
|
||||
|
||||
if np != op {
|
||||
self.cast.notify_each(i..l);
|
||||
} else {
|
||||
self.cast.notify(&i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::buffer::vec::*;
|
||||
use crate::projection::filter_sequence::*;
|
||||
|
||||
use crate::view::port::UpdateTask;
|
||||
|
||||
#[test]
|
||||
fn filter_seq1() {
|
||||
let mut buffer = VecBuffer::new();
|
||||
|
||||
let target_port = buffer.get_port()
|
||||
.to_sequence()
|
||||
.filter(
|
||||
|x| *x > 10
|
||||
);
|
||||
|
||||
let target_view = target_port.get_view();
|
||||
|
||||
buffer.push(0);
|
||||
buffer.push(7);
|
||||
buffer.push(9);
|
||||
|
||||
target_port.0.update();
|
||||
assert_eq!(target_view.len(), Some(0));
|
||||
assert_eq!(target_view.get(&0), None);
|
||||
|
||||
buffer.push(11);
|
||||
|
||||
target_port.0.update();
|
||||
assert_eq!(target_view.len(), Some(1));
|
||||
assert_eq!(target_view.get(&0), Some(11));
|
||||
assert_eq!(target_view.get(&1), None);
|
||||
|
||||
buffer.push(13);
|
||||
buffer.push(1);
|
||||
buffer.push(5);
|
||||
buffer.push(19);
|
||||
|
||||
target_port.0.update();
|
||||
assert_eq!(target_view.len(), Some(3));
|
||||
assert_eq!(target_view.get(&0), Some(11));
|
||||
assert_eq!(target_view.get(&1), Some(13));
|
||||
assert_eq!(target_view.get(&2), Some(19));
|
||||
}
|
||||
}
|
||||
|
||||
|
246
src/projection/flatten_grid.rs
Normal file
246
src/projection/flatten_grid.rs
Normal file
|
@ -0,0 +1,246 @@
|
|||
use {
|
||||
crate::{
|
||||
view::{
|
||||
InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
|
||||
grid::*,
|
||||
index::*,
|
||||
},
|
||||
projection::projection_helper::ProjectionHelper,
|
||||
},
|
||||
cgmath::{Point2, Vector2},
|
||||
std::sync::RwLock,
|
||||
std::{cmp::max, collections::HashMap, sync::Arc},
|
||||
};
|
||||
|
||||
impl<Item> OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
pub fn flatten(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||
let port = ViewPort::new();
|
||||
port.add_update_hook(Arc::new(self.0.clone()));
|
||||
Flatten::new(self.clone(), port.inner());
|
||||
port.into_outer()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Chunk<Item>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
offset: Vector2<i16>,
|
||||
limit: Point2<i16>,
|
||||
view: Arc<dyn GridView<Item = Item>>,
|
||||
}
|
||||
|
||||
pub struct Flatten<Item>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
limit: Point2<i16>,
|
||||
top: Arc<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||
chunks: HashMap<Point2<i16>, Chunk<Item>>,
|
||||
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = Item>>>>,
|
||||
proj_helper: ProjectionHelper<Point2<i16>, Self>,
|
||||
}
|
||||
|
||||
impl<Item> View for Flatten<Item>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
type Msg = IndexArea<Point2<i16>>;
|
||||
}
|
||||
|
||||
impl<Item> IndexView<Point2<i16>> for Flatten<Item>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn get(&self, idx: &Point2<i16>) -> Option<Self::Item> {
|
||||
let chunk_idx = self.get_chunk_idx(*idx)?;
|
||||
let chunk = self.chunks.get(&chunk_idx)?;
|
||||
chunk.view.get(&(*idx - chunk.offset))
|
||||
}
|
||||
|
||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||
IndexArea::Range(Point2::new(0, 0)..=self.limit)
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport */
|
||||
impl<Item> Flatten<Item>
|
||||
where
|
||||
Item: 'static,
|
||||
{
|
||||
pub fn new(
|
||||
top_port: OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||
out_port: InnerViewPort<dyn GridView<Item = Item>>,
|
||||
) -> Arc<RwLock<Self>> {
|
||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||
|
||||
let flat = Arc::new(RwLock::new(Flatten {
|
||||
limit: Point2::new(0, 0),
|
||||
top: proj_helper.new_index_arg(
|
||||
Point2::new(-1, -1),
|
||||
top_port,
|
||||
|s: &mut Self, chunk_area| {
|
||||
for chunk_idx in chunk_area.iter() {
|
||||
s.update_chunk(chunk_idx);
|
||||
}
|
||||
},
|
||||
),
|
||||
chunks: HashMap::new(),
|
||||
cast: out_port.get_broadcast(),
|
||||
proj_helper,
|
||||
}));
|
||||
|
||||
flat.write().unwrap().proj_helper.set_proj(&flat);
|
||||
out_port.set_view(Some(flat.clone()));
|
||||
flat
|
||||
}
|
||||
|
||||
/// the top-sequence has changed the item at chunk_idx,
|
||||
/// create a new observer for the contained sub sequence
|
||||
fn update_chunk(&mut self, chunk_idx: Point2<i16>) {
|
||||
if let Some(chunk_port) = self.top.get(&chunk_idx) {
|
||||
let view = self.proj_helper.new_index_arg(
|
||||
chunk_idx,
|
||||
chunk_port.clone(),
|
||||
move |s: &mut Self, area| {
|
||||
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||
if chunk.limit != *chunk.view.area().range().end() {
|
||||
s.update_all_offsets();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||
s.cast.notify(&area.map(|pt| pt + chunk.offset));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||
chunk.view = view;
|
||||
|
||||
let old_limit = chunk.limit;
|
||||
let new_limit = *chunk.view.area().range().end();
|
||||
|
||||
self.cast.notify(
|
||||
&IndexArea::Range(
|
||||
Point2::new(chunk.offset.x, chunk.offset.y) ..= Point2::new(chunk.offset.x + max(old_limit.x, new_limit.x), chunk.offset.y + max(old_limit.y, new_limit.y) )));
|
||||
|
||||
} else {
|
||||
self.chunks.insert(
|
||||
chunk_idx,
|
||||
Chunk {
|
||||
offset: Vector2::new(-1, -1),
|
||||
limit: Point2::new(-1, -1),
|
||||
view,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
self.update_all_offsets();
|
||||
} else |