array3d/
lib.rs

1#[cfg(test)]
2mod tests {
3    #[test]
4    fn it_works() {
5        assert_eq!(2 + 2, 4);
6    }
7}
8
9//use super::*;
10pub type Idx=i32;
11use std::ops::Range;
12use std::ops::{Add,Sub,Mul,Div,Rem,BitOr,BitAnd,BitXor,Index,IndexMut};
13extern crate lininterp;
14use lininterp::{Lerp,avr};
15
16// local mini maths decouples
17// TODO - make another crate declaring our style of Vec<T>
18#[derive(Copy,Debug,Clone)]
19pub struct Vec2<X,Y=X>{pub x:X,pub y:Y} // array3d::Vec3 should 'into()' into vector::Vec3 , etc.
20#[derive(Copy,Debug,Clone)]
21pub struct Vec3<X,Y=X,Z=Y>{pub x:X,pub y:Y,pub z:Z} // array3d::Vec3 should 'into()' into vector::Vec3 , etc.
22#[derive(Copy,Debug,Clone)]
23pub struct Vec4<X,Y=X,Z=Y,W=Z>{pub x:X,pub y:Y,pub z:Z,pub w:W} // array3d::Vec3 should 'into()' into vector::Vec3 , etc.
24pub type V2i=Vec2<Idx>;
25pub type V3i=Vec3<Idx>;
26pub type V4i=Vec4<Idx>;//TODO
27type Axis_t=i32;
28const XAxis:Axis_t=0; const YAxis:Axis_t=1; const ZAxis:Axis_t=2;
29
30impl<T> Index<i32> for Vec3<T>{
31	type Output=T;
32	fn index(&self,i:Axis_t)->&T{match i{ XAxis=>&self.x,YAxis=>&self.y,ZAxis=>&self.z,_=>panic!("Vec3 index out of range")}}
33}
34impl<T> IndexMut<i32> for Vec3<T>{
35	fn index_mut(&mut self,i:Axis_t)->&mut T{match i{ XAxis=>&mut self.x,YAxis=>&mut self.y,ZAxis=>&mut self.z,_=>panic!("Vec3 index out of range")}}
36}
37
38pub fn v2i(x:i32,y:i32)->V2i{Vec2{x:x,y:y}}
39pub fn v3i(x:i32,y:i32,z:i32)->V3i{Vec3{x:x,y:y,z:z}}
40pub fn v3izero()->V3i{v3i(0,0,0)}
41pub fn v3ione()->V3i{v3i(1,1,1)}
42pub fn v3iadd_axis(a:V3i,axis:i32, value:i32)->V3i{let mut r=a; r[axis]+=value; r}
43pub struct Neighbours<T>{pub prev:T,pub next:T}
44type Neighbours2d<T>=Vec2<Neighbours<T>>;
45type Neighbours3d<T>=Vec3<Neighbours<T>>;
46type Neighbours4d<T>=Vec4<Neighbours<T>>;
47
48macro_rules! v3i_operators{[$(($fname:ident=>$op:ident)),*]=>{
49	$(pub fn $fname(a:V3i,b:V3i)->V3i{v3i(a.x.$op(b.x),a.y.$op(b.y),a.z.$op(b.z))})*
50}}
51macro_rules! v3i_permute_v2i{[$($pname:ident($u:ident,$v:ident)),*]=>{
52	$(pub fn $pname(a:V3i)->V2i{v2i(a.$u,a.$v)})*
53}}
54
55pub trait MyMod :Add+Sub+Div+Mul+Sized{
56	fn mymod(&self,b:Self)->Self;
57}
58impl MyMod for i32{
59	fn mymod(&self,b:Self)->Self{ if *self>=0{*self%b}else{ b-((-*self) %b)} }
60}
61v3i_operators![(v3iadd=>add),(v3isub=>sub),(v3imul=>mul),(v3idiv=>div),(v3irem=>rem),(v3imin=>min),(v3imax=>max),(v3imymod=>mymod)];
62v3i_permute_v2i![v3i_xy(x,y), v3i_yz(y,z), v3i_xz(x,z)];
63
64pub fn v3i_hmul_usize(a:V3i)->usize{ a.x as usize*a.y as usize *a.z as usize}
65pub fn v2i_hmul_usize(a:V2i)->usize{ a.x as usize*a.y as usize}
66
67pub struct Array2d<T>{pub shape:V2i,pub data:Vec<T>}
68pub struct Array3d<T>{pub shape:V3i,pub data:Vec<T>}
69pub struct Array4d<T>{pub shape:V4i,pub data:Vec<T>}
70
71impl<T:Clone> Array2d<T>{
72	pub fn new()->Self{Array2d{shape:v2i(0,0),data:Vec::new()}}
73	pub fn len(&self)->usize{ v2i_hmul_usize(self.shape) }
74	pub fn linear_index(&self, pos:V2i)->usize{
75		// now this *could* exceed 2gb.
76		(pos.x as usize)+
77		(self.shape.x as usize) *(pos.y as usize)
78	}
79	pub fn map_pos<B:Clone,F:Fn(V2i,&T)->B> (&self,f:F) -> Array2d<B>{
80		// todo xyz iterator
81		let mut r=Array2d::new();
82		r.data.reserve(self.data.len());
83		
84		for y in 0..self.shape.y { for x in 0.. self.shape.x{
85			let pos=v2i(x,y);
86			r.data.push( f(pos,self.index(pos)) );
87		}}
88		r
89	}
90	pub fn from_fn<F:Fn(V2i)->T> (s:V2i, f:F)->Array2d<T>{
91		let mut d=Array2d{shape:s,data:Vec::new()};
92		d.data.reserve(s.x as usize * s.y as usize);
93		for y in 0..s.y{ for x in 0..s.x{
94				d.data.push(f(v2i(x,y)))
95			}
96		}
97		d
98	}
99}
100
101impl<T:Clone> Index<V2i> for Array2d<T>{
102	type Output=T;
103	fn index(&self, pos:V2i)->&T{
104		let i=self.linear_index(pos);
105		&self.data[i]
106		
107	}
108}
109
110impl<T:Clone> IndexMut<V2i> for Array2d<T>{
111	fn index_mut(&mut self, pos:V2i)->&mut T{
112		let i=self.linear_index(pos);
113		&mut self.data[i]
114	}
115}
116
117impl<T:Clone> Array3d<T>{	
118	pub fn from_fn<F:Fn(V3i)->T> (s:V3i,f:F) -> Array3d<T> {
119		let mut a=Array3d{shape:s, data:Vec::new()};
120		a.data.reserve(v3i_hmul_usize(s));
121		for z in 0..a.shape.z{ for y in 0..a.shape.y { for x in 0.. a.shape.x{
122			a.data.push( f(v3i(x,y,z)) )
123		}}}		
124		a
125	}
126
127	/// production from a function with expansion,
128	/// todo - make this more general e.g.'production in blocks'
129	/// the motivation is to share state across the production of
130	///  a block of adjacent cells
131	pub fn from_fn_doubled<F:Fn(V3i)->(T,T)> (sz:V3i,axis:i32, f:F)->Array3d<T>{
132		let mut scale=v3i(1,1,1); scale[axis]*=2;
133		let mut d=Array3d{shape:v3imul(sz,scale),data:Vec::new()};
134		d.data.reserve(sz.x as usize * sz.y as usize * sz.z as usize);
135		for z in 0..sz.z {for y in 0..sz.y{ for x in 0..sz.x{
136					let (v0,v1)=f(v3i(x,y,z));
137					d.data.push(v0);
138					d.data.push(v1);
139				}
140			}
141		}
142		d
143	}
144
145	pub fn fill_val(size:V3i,val:T)->Array3d<T>{let pval=&val;Self::from_fn(size,|_|pval.clone())}
146
147
148
149	pub fn len(&self)->usize{ v3i_hmul_usize(self.shape) }
150	pub fn linear_index(&self, pos:V3i)->usize{
151		// now this *could* exceed 2gb.
152		(pos.x as usize)+
153		(self.shape.x as usize)*( 
154			(pos.y as usize)+
155			(pos.z as usize)*(self.shape.y as usize)
156		)
157	}
158	pub fn size(&self)->V3i{self.shape}
159	/// produce a new array3d by applying a function to every element
160	pub fn map_xyz<B:Clone,F:Fn(V3i,&T)->B> (&self,f:F) -> Array3d<B>{
161		Array3d::from_fn(self.shape,
162			|pos:V3i|f(pos,self.index(pos))
163		)
164	}
165	pub fn map_strided_region<B:Clone,F:Fn(V3i,&T)->B> 
166		(&self,range:Range<V3i>,stride:V3i, f:F) -> Array3d<B>
167	{
168		Array3d::from_fn(v3idiv(v3isub(range.end,range.start),stride),
169			|outpos:V3i|{
170				let inpos=v3iadd(v3imul(outpos,stride),range.start);
171				f(inpos,self.index(inpos))
172			}
173		)
174	}
175
176	/// internal iteration with inplace mutation
177	pub fn for_each<F:Fn(V3i,&mut T)> (&mut self,f:F){
178		for z in 0..self.shape.z{ for y in 0..self.shape.y { for x in 0.. self.shape.x{
179			let pos=v3i(x,y,z);
180			f(pos,self.index_mut(pos))
181		}}}
182	}
183
184	// mappers along each pair of axes,
185	// form primitive for reducers along the axes
186	// or slice extraction
187	pub fn map_xy<F,B>(&self, a_func:F)->Array2d<B>
188		where F:Fn(&Self,i32,i32)->B, B:Clone
189	{
190		Array2d::from_fn(v3i_xy(self.shape),
191			|pos:V2i|{a_func(self,pos.x,pos.y)}
192		)		
193	}
194	pub fn map_xz<F,B>(&self, a_func:F)->Array2d<B>
195		where F:Fn(&Self,i32,i32)->B, B:Clone
196	{
197		Array2d::from_fn(v3i_xz(self.shape),
198			|pos:V2i|{a_func(self,pos.x,pos.y)}
199		)		
200	}
201	pub fn map_yz<F,B>(&self, a_func:F)->Array2d<B>
202		where F:Fn(&Self,i32,i32)->B, B:Clone
203	{
204		Array2d::from_fn(v3i_yz(self.shape),
205			|pos:V2i|{a_func(self,pos.x,pos.y)}
206		)		
207	}
208
209	pub fn reshape(&mut self, s:V3i){
210		// todo: do allow truncations etc
211		// todo: reshape to 2d, 3d
212		assert!(v3i_hmul_usize(self.shape)==v3i_hmul_usize(s));
213		self.shape=s;
214	}
215
216	// TODO ability to do the opposite e.g. map to vec's which become extra dim.
217	// fold along axes collapse the array ?
218	// e.g. run length encoding,packing, whatever.
219	pub fn fold_z<B,F> (&self,init_val:B, f:F) -> Array2d<B>
220		where B:Clone,F:Fn(V3i,B,&T)->B
221	{
222		self.map_xy(|s:&Self,x:i32,y:i32|{
223			let mut acc=init_val.clone();
224			for z in 0..s.shape.z{
225				let pos=v3i(x,y,z);
226				acc=f(pos,acc,self.index(pos))
227			}
228			acc
229		})
230	}
231/*
232		let mut out=Array2d::new();
233		out.data.reserve(self.shape.y as usize *self.shape.z as usize);
234		for y in 0..self.shape.y { for x in 0.. self.shape.x{
235			let mut acc=input.clone();
236			for z in 0..self.shape.z{
237				let pos=Vec3(x,y,z);
238				acc=f(pos,acc,self.index(pos));
239			}
240			out.data.push(acc);
241		}}		
242		out.shape=Vec2(self.shape.x,self.shape.y);
243		out
244	}
245*/
246	/// produce a 2d array by folding along the X axis
247	pub fn fold_x<B:Clone,F:Fn(V3i,B,&T)->B> (&self,input:B, f:F) -> Array2d<B>{
248		let mut out=Array2d::new();
249		out.data.reserve(self.shape.y as usize *self.shape.z as usize);
250		for z in 0..self.shape.z {
251			for y in 0.. self.shape.y{
252				let mut acc=input.clone();
253				for x in 0..self.shape.x{
254					let pos=v3i(x,y,z);
255					acc=f(pos,acc, self.index(pos));
256				}
257				out.data.push(acc);
258			}
259		}		
260		out.shape=v2i(self.shape.y,self.shape.z);
261		out		
262	}
263	/// fold values along x,y,z in turn without intermediate storage
264	pub fn fold_xyz<A,B,C,FOLDX,FOLDY,FOLDZ>(
265		&self,
266		(input_x,fx):(A,FOLDX), (input_y,fy):(B,FOLDY), (input_z,fz):(C,FOLDZ)
267	)->A
268	where
269				A:Clone,B:Clone,C:Clone,
270				FOLDZ:Fn(V3i,C,&T)->C,
271				FOLDY:Fn(i32,i32,B,&C)->B,
272				FOLDX:Fn(i32,A,&B)->A,
273	{
274		let mut ax=input_x.clone();
275		for x in 0..self.shape.x{
276			let mut ay=input_y.clone();
277			for y in 0..self.shape.y{
278				let mut az=input_z.clone();//x accumulator
279				for z in 0..self.shape.z{
280					let pos=v3i(x,y,z);
281					az=fz(pos,az,self.index(pos));
282				}
283				ay=fy(x,y,ay,&az);
284			}
285			ax= fx(x, ax,&ay);
286		}
287		ax
288	}
289	/// fold values along z,y,x in turn without intermediate storage
290	pub fn fold_zyx<A,B,C,FOLDX,FOLDY,FOLDZ>(
291		&self,
292		(input_x,fx):(A,FOLDX), (input_y,fy):(B,FOLDY), (input_z,fz):(C,FOLDZ)
293	)->C
294	where
295				A:Clone,B:Clone,C:Clone,
296				FOLDZ:Fn(i32,C,&B)->C,
297				FOLDY:Fn(i32,i32,B,&A)->B,
298				FOLDX:Fn(V3i,A,&T)->A,
299	{
300		let mut az=input_z.clone();
301		for z in 0..self.shape.z{
302			let mut ay=input_y.clone();
303			for y in 0..self.shape.y{
304				let mut ax=input_x.clone();//x accumulator
305				for x in 0..self.shape.x{
306					let pos=v3i(x,y,z);
307					ax=fx(pos,ax,self.index(pos));
308				}
309				ay=fy(y,z,ay,&ax);
310			}
311			az= fz(z, az,&ay);
312		}
313		az
314	}
315
316	/// fold the whole array to produce a single value
317	pub fn fold<B,F> (&self,input:B, f:F) -> B
318	where F:Fn(V3i,B,&T)->B,B:Clone
319	{
320		let mut acc=input;
321		for z in 0..self.shape.z { for y in 0.. self.shape.y{ for x in 0..self.shape.x{
322			let pos=v3i(x,y,z);
323			acc=f(pos, acc,self.index(pos));
324		}}}
325		acc
326	}
327
328	/// produce tiles by applying a function to every subtile
329	/// output size is divided by tilesize
330	/// must be exact multiple.
331	pub fn fold_tiles<B,F>(&self,tilesize:V3i, input:B,f:&F)->Array3d<B>
332		where F:Fn(V3i,B,&T)->B,B:Clone
333	{
334		self.map_strided(tilesize,
335			|pos,_:&T|{self.fold_region(pos..v3iadd(pos,tilesize),input.clone(),f)})
336	}
337
338	/// subroutine for 'fold tiles', see context
339	/// closure is borrowed for multiple invocation by caller
340	pub fn fold_region<B,F>(&self,r:Range<V3i>, input:B,f:&F)->B
341		where F:Fn(V3i,B,&T)->B, B:Clone
342	{
343		let mut acc=input.clone();
344		for z in r.start.z..r.end.z{
345			for y in r.start.y..r.end.y{
346				for x in r.start.x..r.end.x{
347					let pos=v3i(x,y,z);
348					acc=f(pos,acc,self.index(pos));
349				}
350			}
351		}
352		acc
353	}
354	pub fn get_indexed(&self,pos:V3i)->(V3i,&T){(pos,self.index(pos))}
355	pub fn region_all(&self)->Range<V3i>{v3i(0,0,0)..self.shape}
356	pub fn map_region_strided<F,B>(&self,region:Range<V3i>,stride:V3i, f:F)->Array3d<B>
357		where F:Fn(V3i,&T)->B, B:Clone{
358		Array3d::from_fn(v3idiv(v3isub(region.end,region.start),stride),
359			|outpos:V3i|{
360				let inpos=v3iadd(region.start,v3imul(outpos,stride));
361				f(inpos,self.index(inpos))  
362			}
363		)
364	}
365	pub fn map_strided<F,B>(&self,stride:V3i,f:F)->Array3d<B>
366		where F:Fn(V3i,&T)->B, B:Clone{
367		self.map_region_strided(self.region_all(),stride,f)
368	}
369	pub fn map_region<F,B>(&self,region:Range<V3i>,f:F)->Array3d<B>
370		where F:Fn(V3i,&T)->B, B:Clone{
371		self.map_region_strided(region, v3ione(), f)
372	}
373	/// _X_     form of convolution  
374	/// XOX		passing each cell and it's
375	/// _X_		immiediate neighbours on each axis
376	pub fn convolute_neighbours<F,B>(&self,f:F)->Array3d<B>
377		where F:Fn(&T,Vec3<Neighbours<&T>>)->B ,B:Clone
378	{
379		self.map_region(v3ione()..v3isub(self.shape,v3ione()),
380			|pos:V3i,current_cell:&T|{
381				f(	current_cell,
382					self::Vec3{
383						x:Neighbours{
384							prev:self.index(v3i(pos.x-1,pos.y,pos.z)),
385							next:self.index(v3i(pos.x+1,pos.y,pos.z))},
386						y:Neighbours{
387							prev:self.index(v3i(pos.x,pos.y-1,pos.z)),
388							next:self.index(v3i(pos.x,pos.y+1,pos.z))},
389						z:Neighbours{
390							prev:self.index(v3i(pos.x,pos.y,pos.z-1)),
391							next:self.index(v3i(pos.x,pos.y,pos.z+1))}})
392		})
393	}
394	pub fn index_wrap(&self,pos:V3i)->&T{self.get_wrap(pos)}
395	pub fn get_wrap(&self,pos:V3i)->&T{
396		self.index( v3imymod(pos, self.shape) )
397	}
398	pub fn get_ofs_wrap(&self,pos:V3i,dx:i32,dy:i32,dz:i32)->&T{
399		self.get_wrap(v3iadd(pos, v3i(dx,dy,dz)))
400	}
401	pub fn convolute_neighbours_wrap<F,B>(&self,f:F)->Array3d<B>
402		where F:Fn(&T,Vec3<Neighbours<&T>>)->B,B:Clone 
403	{
404		// TODO - efficiently, i.e. share the offset addresses internally
405		// and compute the edges explicitely
406		// niave implementation calls mod for x/y/z individually and forms address
407		self.map_region(v3izero()..self.shape,
408			|pos:V3i,current_cell:&T|{
409				f(	current_cell,
410					Vec3{
411						x:Neighbours{
412							prev:self.get_wrap(v3i(pos.x-1,pos.y,pos.z)),
413							next:self.get_wrap(v3i(pos.x+1,pos.y,pos.z))},
414						y:Neighbours{
415							prev:self.get_wrap(v3i(pos.x,pos.y-1,pos.z)),
416							next:self.get_wrap(v3i(pos.x,pos.y+1,pos.z))},
417						z:Neighbours{
418							prev:self.get_wrap(v3i(pos.x,pos.y,pos.z-1)),
419							next:self.get_wrap(v3i(pos.x,pos.y,pos.z+1))}})
420		})
421	}
422	/// special case of convolution for 2x2 cells, e.g. for marching cubes
423	pub fn convolute_2x2x2_wrap<F,B>(&self,f:F)->Array3d<B>
424		where F:Fn(V3i,[[[&T;2];2];2])->B,B:Clone
425	{
426		self.map_region(v3izero()..self.shape,|pos,_|{
427			f(pos,[
428				[	[self.get_ofs_wrap(pos,0, 0, 0),self.get_ofs_wrap(pos, 1, 0, 0)],
429					[self.get_ofs_wrap(pos,0, 1, 0),self.get_ofs_wrap(pos, 1, 1, 0)]
430				],
431				[	[self.get_ofs_wrap(pos,0, 0, 1),self.get_ofs_wrap(pos, 1, 0, 1)],
432					[self.get_ofs_wrap(pos,0, 1, 1),self.get_ofs_wrap(pos, 1, 1, 1)]
433				]
434			])
435		})
436	}
437
438	/// take 2x2x2 blocks, fold to produce new values
439	pub fn fold_half<F,B>(&self,fold_fn:F)->Array3d<B>
440		where F:Fn(V3i,[[[&T;2];2];2])->B,B:Clone
441	{
442		Array3d::from_fn( v3idiv(self.shape,v3i(2,2,2)), |dpos:V3i|{
443			let spos=v3imul(dpos,v3i(2,2,2));
444			fold_fn(dpos,
445					[	[	[self.index(v3iadd(spos,v3i(0,0,0))),self.index(v3iadd(spos,v3i(1,0,0)))],
446							[self.index(v3iadd(spos,v3i(0,1,0))),self.index(v3iadd(spos,v3i(1,1,0)))]
447						],
448						[	[self.index(v3iadd(spos,v3i(0,0,0))),self.index(v3iadd(spos,v3i(1,0,0)))],
449							[self.index(v3iadd(spos,v3i(0,0,0))),self.index(v3iadd(spos,v3i(1,0,0)))]
450						]
451					]
452			)
453		})
454	}
455}
456
457
458/*
459fn avr<Diff,T>(a:&T,b:&T)->T where
460	for<'u> Diff:Mul<f32,Output=Diff>+'u,
461	for<'u,'v>&'u T:Sub<&'v T,Output=Diff>,
462	for<'u,'v>&'u T:Add<&'v Diff,Output=T>
463{
464	a.add(&a.sub(b).mul(0.5f32))
465}
466*/
467// for types T with arithmetic,
468impl<T:Clone+Lerp> Array3d<T>
469{
470	/// downsample, TODO downsample centred on alternate cells
471	fn downsample_half(&self)->Array3d<T>{
472		self.fold_half(|pos:V3i,cell:[[[&T;2];2];2]|->T{
473			
474			avr(
475				&avr(
476					&avr(&cell[0][0][0],&cell[1][0][0]),
477					&avr(&cell[0][1][0],&cell[1][1][0])),
478				&avr(
479					&avr(&cell[0][0][1],&cell[1][0][1]),
480					&avr(&cell[0][1][1],&cell[1][1][1])
481				)
482			)
483
484		})
485	}
486
487	/// expand 2x with simple interpolation (TODO , decent filters)
488	fn upsample_double_axis(&self,axis:i32)->Array3d<T>{
489		Array3d::from_fn_doubled(self.shape,axis,|pos:V3i|->(T,T){
490			let v0=self.index(pos); let v1=self.get_wrap(v3iadd_axis(pos,axis,1));
491			let vm=avr(v0,v1);
492			(v0.clone(),vm)
493		})
494	}
495	/// expand 2x in all axes (TODO, in one step instead of x,y,z composed)
496	fn upsample_double_xyz(&self)->Array3d<T>{
497		// todo - should be possible in one step, without 3 seperate buffer traversals!
498		self.upsample_double_axis(XAxis).upsample_double_axis(YAxis).upsample_double_axis(ZAxis)
499	}
500
501}
502
503impl<T:Clone> Index<V3i> for Array3d<T>{
504	type Output=T;
505	fn index(&self, pos:V3i)->&T{
506		let i=self.linear_index(pos);
507		&self.data[i]
508		
509	}
510}
511
512impl<T:Clone> IndexMut<V3i> for Array3d<T>{
513	fn index_mut(&mut self, pos:V3i)->&mut T{
514		let i=self.linear_index(pos);
515		&mut self.data[i]
516 	}
517}
518
519
520
521
522
523