mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
delta implementation
the delta has a traversable iterator then we create a deltawriter that gets a reference to delta and data deltawriter also gets a traverable interator which uses the existing traversables
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(const_generics, test)]
|
||||
|
||||
extern crate serde_derive;
|
||||
|
@ -23,6 +23,7 @@ use vek::*;
|
||||
Every Indexstore is a ParentLayer.
|
||||
- DetailStore: Every layer must implement this for their KEY.
|
||||
This is used to store the actual DETAIL of every layer.
|
||||
- DetailStoreMut: allows mut borrow for Vec types (Hash not supported)
|
||||
!!Calculations will be implemented on these 2 Stores, rather than the actual structs to reduce duplciate coding!!
|
||||
- ToOptionUsize: to store INDEX in z16/u32 efficiently and move up to usize on calculation
|
||||
- Traversable: trait is used to get child layer and child Index for a concrete position.
|
||||
@ -66,13 +67,19 @@ pub trait IndexStore: ParentLayer {
|
||||
pub trait DetailStore: Layer {
|
||||
type DETAIL;
|
||||
fn load(&self, key: Self::KEY) -> &Self::DETAIL;
|
||||
fn save(&mut self, key: Self::KEY, detail: Self::DETAIL);
|
||||
}
|
||||
pub trait DetailStoreMut: DetailStore {
|
||||
fn load_mut(&mut self, key: Self::KEY) -> &mut Self::DETAIL;
|
||||
}
|
||||
|
||||
pub trait Traversable<C> {
|
||||
fn get(self) -> C;
|
||||
pub trait Traversable {
|
||||
type TRAV_CHILD;
|
||||
fn get(self) -> Self::TRAV_CHILD;
|
||||
}
|
||||
pub trait Materializeable<T> {
|
||||
fn mat(self) -> T;
|
||||
pub trait Materializeable {
|
||||
type MAT_CHILD;
|
||||
fn mat(self) -> Self::MAT_CHILD;
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
@ -176,6 +183,16 @@ impl<C: DetailStore, I: ToOptionUsize, T, const L: u8> DetailStore
|
||||
fn load(&self, key: Self::KEY) -> &Self::DETAIL {
|
||||
&self.detail[key]
|
||||
}
|
||||
fn save(&mut self, key: Self::KEY, detail: Self::DETAIL) {
|
||||
self.detail.insert(key, detail);
|
||||
}
|
||||
}
|
||||
impl<C: DetailStore, I: ToOptionUsize, T, const L: u8> DetailStoreMut
|
||||
for VecNestLayer<C, T, I, { L }>
|
||||
{
|
||||
fn load_mut(&mut self, key: Self::KEY) -> &mut Self::DETAIL {
|
||||
&mut self.detail[key]
|
||||
}
|
||||
}
|
||||
impl<C: DetailStore, I: ToOptionUsize, T, const L: u8> DetailStore
|
||||
for HashNestLayer<C, T, I, { L }>
|
||||
@ -185,12 +202,24 @@ impl<C: DetailStore, I: ToOptionUsize, T, const L: u8> DetailStore
|
||||
debug_assert_eq!(key, key.align_to_level({ L }));
|
||||
&self.detail_index[&key].0
|
||||
}
|
||||
fn save(&mut self, key: LodPos, detail: Self::DETAIL) {
|
||||
debug_assert_eq!(key, key.align_to_level({ L }));
|
||||
self.detail_index.insert(key, (detail, I::none()));
|
||||
}
|
||||
}
|
||||
impl<T, const L: u8> DetailStore for VecLayer<T, { L }> {
|
||||
type DETAIL = T;
|
||||
fn load(&self, key: usize) -> &Self::DETAIL {
|
||||
&self.detail[key]
|
||||
}
|
||||
fn save(&mut self, key: usize, detail: Self::DETAIL) {
|
||||
self.detail[key] = detail;
|
||||
}
|
||||
}
|
||||
impl<T, const L: u8> DetailStoreMut for VecLayer<T, { L }> {
|
||||
fn load_mut(&mut self, key: usize) -> &mut Self::DETAIL {
|
||||
&mut self.detail[key]
|
||||
}
|
||||
}
|
||||
impl<T, const L: u8> DetailStore for HashLayer<T, { L }> {
|
||||
type DETAIL = T;
|
||||
@ -198,13 +227,18 @@ impl<T, const L: u8> DetailStore for HashLayer<T, { L }> {
|
||||
debug_assert_eq!(key, key.align_to_level({ L }));
|
||||
&self.detail[&key]
|
||||
}
|
||||
fn save(&mut self, key: LodPos, detail: Self::DETAIL) {
|
||||
debug_assert_eq!(key, key.align_to_level({ L }));
|
||||
self.detail.insert(key, detail);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L: DetailStore<KEY = LodPos> + IndexStore> Traversable<VecIter<'a, L::CHILD>>
|
||||
for HashIter<'a, L>
|
||||
impl<'a, L: DetailStore<KEY = LodPos> + IndexStore> Traversable for HashIter<'a, L>
|
||||
where
|
||||
L::CHILD: DetailStore,
|
||||
{
|
||||
type TRAV_CHILD = VecIter<'a, L::CHILD>;
|
||||
|
||||
fn get(self) -> VecIter<'a, L::CHILD> {
|
||||
let child_lod = self.wanted.align_to_level(L::CHILD::LEVEL);
|
||||
let pos_offset = relative_to_1d(
|
||||
@ -226,11 +260,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L: DetailStore<KEY = usize> + IndexStore> Traversable<VecIter<'a, L::CHILD>>
|
||||
for VecIter<'a, L>
|
||||
impl<'a, L: DetailStore<KEY = usize> + IndexStore> Traversable for VecIter<'a, L>
|
||||
where
|
||||
L::CHILD: DetailStore,
|
||||
{
|
||||
type TRAV_CHILD = VecIter<'a, L::CHILD>;
|
||||
|
||||
fn get(self) -> VecIter<'a, L::CHILD> {
|
||||
let child_lod = self.wanted.align_to_level(L::CHILD::LEVEL);
|
||||
let pos_offset = relative_to_1d(
|
||||
@ -252,35 +287,39 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L: DetailStore<KEY = LodPos>> Materializeable<&'a L::DETAIL> for HashIter<'a, L> {
|
||||
impl<'a, L: DetailStore<KEY = LodPos>> Materializeable for HashIter<'a, L> {
|
||||
type MAT_CHILD = &'a L::DETAIL;
|
||||
|
||||
fn mat(self) -> &'a L::DETAIL {
|
||||
DetailStore::load(self.layer, self.layer_lod)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L: DetailStore<KEY = usize>> Materializeable<&'a L::DETAIL> for VecIter<'a, L> {
|
||||
impl<'a, L: DetailStore<KEY = usize>> Materializeable for VecIter<'a, L> {
|
||||
type MAT_CHILD = &'a L::DETAIL;
|
||||
|
||||
fn mat(self) -> &'a L::DETAIL {
|
||||
DetailStore::load(self.layer, self.layer_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub type ExampleData =
|
||||
HashNestLayer<
|
||||
VecNestLayer<
|
||||
VecNestLayer<
|
||||
VecLayer<
|
||||
i8, 0
|
||||
> ,Option<()>, u16, 2
|
||||
> ,() , u32, 3
|
||||
> ,() ,u16, 4
|
||||
>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lodstore::data::*;
|
||||
use test::Bencher;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub type ExampleData =
|
||||
HashNestLayer<
|
||||
VecNestLayer<
|
||||
VecNestLayer<
|
||||
VecLayer<
|
||||
i8, 0
|
||||
> ,Option<()>, u16, 2
|
||||
> ,() , u32, 3
|
||||
> ,() ,u16, 4
|
||||
>;
|
||||
|
||||
fn gen_simple_example() -> ExampleData {
|
||||
let mut detail_index = FxHashMap::default();
|
||||
detail_index.insert(LodPos::xyz(0, 0, 0), ((), 0));
|
||||
|
@ -1,15 +1,7 @@
|
||||
/*
|
||||
use super::{
|
||||
data::{
|
||||
LodData,
|
||||
LodConfig,
|
||||
},
|
||||
lodpos::LodPos,
|
||||
area::LodArea,
|
||||
};
|
||||
|
||||
pub type LodIndex = LodPos;
|
||||
|
||||
use super::data::{DetailStore, Layer, ParentLayer, Traversable};
|
||||
use super::index::ToOptionUsize;
|
||||
use super::lodpos::{multily_with_2_pow_n, relative_to_1d, two_pow_u32, LodPos};
|
||||
use serde::export::PhantomData;
|
||||
/*
|
||||
A LodDelta applies a change to a Lod
|
||||
The rules for LodDeltas are strict in order to make them as simple as possible.
|
||||
@ -21,151 +13,148 @@ pub type LodIndex = LodPos;
|
||||
However i belive that most algorithms only change every Value once.
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LodDelta<X: LodConfig> {
|
||||
pub layer0: Vec<(LodIndex, Option<X::L0>)>, // 1/16
|
||||
pub layer1: Vec<(LodIndex, Option<X::L1>)>, // 1/8
|
||||
pub layer2: Vec<(LodIndex, Option<X::L2>)>, // 1/4
|
||||
pub layer3: Vec<(LodIndex, Option<X::L3>)>, // 1/2
|
||||
pub layer4: Vec<(LodIndex, Option<X::L4>)>, // 1
|
||||
pub layer5: Vec<(LodIndex, Option<X::L5>)>, // 2
|
||||
pub layer6: Vec<(LodIndex, Option<X::L6>)>, // 4
|
||||
pub layer7: Vec<(LodIndex, Option<X::L7>)>, // 8
|
||||
pub layer8: Vec<(LodIndex, Option<X::L8>)>, // 16
|
||||
pub layer9: Vec<(LodIndex, Option<X::L9>)>, // 32
|
||||
pub layer10: Vec<(LodIndex, Option<X::L10>)>, // 64
|
||||
pub layer11: Vec<(LodIndex, Option<X::L11>)>, // 128
|
||||
pub layer12: Vec<(LodIndex, Option<X::L12>)>, // 256
|
||||
pub layer13: Vec<(LodIndex, Option<X::L13>)>, // 512
|
||||
pub layer14: Vec<(LodIndex, Option<X::L14>)>, // 1024
|
||||
pub layer15: Vec<(LodIndex, Option<X::L15>)>, // 2048
|
||||
}
|
||||
|
||||
impl<X: LodConfig> LodDelta<X> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layer0: Vec::new(),
|
||||
layer1: Vec::new(),
|
||||
layer2: Vec::new(),
|
||||
layer3: Vec::new(),
|
||||
layer4: Vec::new(),
|
||||
layer5: Vec::new(),
|
||||
layer6: Vec::new(),
|
||||
layer7: Vec::new(),
|
||||
layer8: Vec::new(),
|
||||
layer9: Vec::new(),
|
||||
layer10: Vec::new(),
|
||||
layer11: Vec::new(),
|
||||
layer12: Vec::new(),
|
||||
layer13: Vec::new(),
|
||||
layer14: Vec::new(),
|
||||
layer15: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: apply that moves out
|
||||
pub fn apply(&self, data: &mut LodData<X>) {
|
||||
for (index, item) in &self.layer15 {
|
||||
if let Some(item) = item {
|
||||
data.set15(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer14 {
|
||||
if let Some(item) = item {
|
||||
data.set14(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer13 {
|
||||
if let Some(item) = item {
|
||||
data.set13(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer12 {
|
||||
if let Some(item) = item {
|
||||
data.set12(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer11 {
|
||||
if let Some(item) = item {
|
||||
data.set11(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer10 {
|
||||
if let Some(item) = item {
|
||||
data.set10(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer9 {
|
||||
if let Some(item) = item {
|
||||
data.set9(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer8 {
|
||||
if let Some(item) = item {
|
||||
data.set8(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer7 {
|
||||
if let Some(item) = item {
|
||||
data.set7(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer6 {
|
||||
if let Some(item) = item {
|
||||
data.set6(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer5 {
|
||||
if let Some(item) = item {
|
||||
data.set5(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer4 {
|
||||
if let Some(item) = item {
|
||||
data.set4(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer3 {
|
||||
if let Some(item) = item {
|
||||
data.set3(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer2 {
|
||||
if let Some(item) = item {
|
||||
data.set2(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer1 {
|
||||
if let Some(item) = item {
|
||||
data.set1(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
for (index, item) in &self.layer0 {
|
||||
if let Some(item) = item {
|
||||
data.set0(*index, item.clone(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter(&self, area: LodArea) -> Self {
|
||||
Self {
|
||||
layer0: self.layer0.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer1: self.layer1.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer2: self.layer2.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer3: self.layer3.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer4: self.layer4.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer5: self.layer5.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer6: self.layer6.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer7: self.layer7.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer8: self.layer8.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer9: self.layer9.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer10: self.layer10.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer11: self.layer11.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer12: self.layer12.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer13: self.layer13.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer14: self.layer14.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
layer15: self.layer15.iter().filter(|(index, _)| area.is_inside(index.clone())).cloned().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
The Idea is to create a DeltaWriter that has a mutable Detla and Data and implements the Data interaces.
|
||||
While it borrows a mutable reference to both it locks both with rusts system
|
||||
When the writing is done, we destroy the Writer but keep the Delta and Data.
|
||||
The DeltaWriter will output its own iterator,
|
||||
We only need a traversable trait
|
||||
*/
|
||||
pub trait Delta: Layer {}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct VecDelta<T, const L: u8> {
|
||||
pub detail: Vec<(LodPos, Option<T>)>,
|
||||
}
|
||||
#[derive(Default, Clone)]
|
||||
pub struct VecNestDelta<D: Delta, T, const L: u8> {
|
||||
pub detail: Vec<(LodPos, Option<T>)>,
|
||||
pub child: D,
|
||||
}
|
||||
|
||||
pub struct DeltaWriter<'a, C: DetailStore, D: Delta> {
|
||||
pub delta: &'a mut D,
|
||||
pub data: &'a mut C,
|
||||
}
|
||||
|
||||
struct VecDataIter<'a, D: Delta> {
|
||||
layer: &'a D,
|
||||
}
|
||||
|
||||
struct DataWriterIter<DT: Traversable, CT: Traversable> {
|
||||
delta_iter: DT,
|
||||
data_iter: CT,
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
|
||||
impl<T, const L: u8> Layer for VecDelta<T, { L }> {
|
||||
type KEY = (usize);
|
||||
const LEVEL: u8 = { L };
|
||||
}
|
||||
impl<D: Delta, T, const L: u8> Layer for VecNestDelta<D, T, { L }> {
|
||||
type KEY = (usize);
|
||||
const LEVEL: u8 = { L };
|
||||
}
|
||||
|
||||
impl<D: Delta, T, const L: u8> ParentLayer for VecNestDelta<D, T, { L }> {
|
||||
type CHILD = D;
|
||||
fn child(&self) -> &Self::CHILD {
|
||||
&self.child
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: DetailStore, D: Delta> DeltaWriter<'a, C, D> {
|
||||
pub fn new(delta: &'a mut D, data: &'a mut C) -> Self {
|
||||
DeltaWriter { delta, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: DetailStore, D: Delta> DeltaWriter<'a, C, D> {
|
||||
#[allow(dead_code)]
|
||||
fn trav(&'a self, pos: LodPos) -> VecDataIter<'a, D> {
|
||||
VecDataIter { layer: &self.delta }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: Delta + ParentLayer> Traversable for VecDataIter<'a, D>
|
||||
where
|
||||
D::CHILD: Delta,
|
||||
{
|
||||
type TRAV_CHILD = VecDataIter<'a, D::CHILD>;
|
||||
|
||||
fn get(self) -> VecDataIter<'a, D::CHILD> {
|
||||
VecDataIter {
|
||||
layer: self.layer.child(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DT: Traversable, CT: Traversable> Traversable for DataWriterIter<DT, CT>
|
||||
where
|
||||
DT::TRAV_CHILD: Traversable,
|
||||
CT::TRAV_CHILD: Traversable,
|
||||
{
|
||||
type TRAV_CHILD = DataWriterIter<DT::TRAV_CHILD, CT::TRAV_CHILD>;
|
||||
|
||||
fn get(self) -> DataWriterIter<DT::TRAV_CHILD, CT::TRAV_CHILD> {
|
||||
DataWriterIter {
|
||||
delta_iter: self.delta_iter.get(),
|
||||
data_iter: self.data_iter.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const L: u8> Delta for VecDelta<T, { L }> {}
|
||||
impl<C: Delta, T, const L: u8> Delta for VecNestDelta<C, T, { L }> {}
|
||||
|
||||
//#######################################################
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lodstore::data::*;
|
||||
use crate::lodstore::delta::*;
|
||||
use test::Bencher;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub type ExampleData =
|
||||
HashNestLayer<
|
||||
VecNestLayer<
|
||||
VecNestLayer<
|
||||
VecLayer<
|
||||
i8, 0
|
||||
> ,Option<()>, u16, 2
|
||||
> ,() , u32, 3
|
||||
> ,() ,u16, 4
|
||||
>;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub type ExampleDelta =
|
||||
VecNestDelta<
|
||||
VecNestDelta<
|
||||
VecNestDelta<
|
||||
VecDelta<
|
||||
i8, 0
|
||||
>, Option<()>, 2
|
||||
>, (), 3
|
||||
>, (), 4
|
||||
>;
|
||||
|
||||
#[test]
|
||||
fn compilation() {
|
||||
let mut x = ExampleData::default();
|
||||
let mut d = ExampleDelta::default();
|
||||
{
|
||||
let w = DeltaWriter::new(&mut d, &mut x);
|
||||
let i = LodPos::xyz(0, 1, 2);
|
||||
|
||||
if false {}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn access_first_element() {
|
||||
let x = ExampleDelta::default();
|
||||
let i = LodPos::xyz(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
23
worldsim/src/lodstore/drill.rs
Normal file
23
worldsim/src/lodstore/drill.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use super::delta::Delta;
|
||||
use super::index::ToOptionUsize;
|
||||
use fxhash::FxHashMap;
|
||||
use std::{u16, u32};
|
||||
use vek::*;
|
||||
|
||||
/*
|
||||
traits:
|
||||
- DrillDown
|
||||
- DrillUp
|
||||
*/
|
||||
|
||||
pub trait DrillDownable {
|
||||
type DELTA: Delta;
|
||||
fn drill_down(detail: Self) -> Self::DELTA;
|
||||
}
|
||||
|
||||
pub trait DrillUpable {
|
||||
type DELTA: Delta;
|
||||
fn drill_up(detail: Self) -> Self::DELTA;
|
||||
}
|
||||
|
||||
//#######################################################
|
@ -1,11 +1,15 @@
|
||||
use std::{u16, u32};
|
||||
|
||||
pub trait ToOptionUsize: Copy {
|
||||
fn none() -> Self;
|
||||
fn is_some(self) -> bool;
|
||||
fn into_usize(self) -> usize;
|
||||
}
|
||||
|
||||
impl ToOptionUsize for u32 {
|
||||
fn none() -> Self {
|
||||
u32::MAX
|
||||
}
|
||||
fn is_some(self) -> bool {
|
||||
self != u32::MAX
|
||||
}
|
||||
@ -15,6 +19,9 @@ impl ToOptionUsize for u32 {
|
||||
}
|
||||
|
||||
impl ToOptionUsize for u16 {
|
||||
fn none() -> Self {
|
||||
u16::MAX
|
||||
}
|
||||
fn is_some(self) -> bool {
|
||||
self != u16::MAX
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub mod area;
|
||||
pub mod data;
|
||||
pub mod delta;
|
||||
pub mod drill;
|
||||
pub mod index;
|
||||
pub mod lodpos;
|
||||
pub use data::{HashLayer, HashNestLayer, VecLayer, VecNestLayer};
|
||||
|
Reference in New Issue
Block a user