1 /** 2 * This module defines the notion of a $(NEIGHBOURHOOD). A $(NEIGHBOURHOOD) 3 * defines what cells in the lattice that are "neighbours". Most ca $(RULE)s 4 * change the state of a $(I cell) depending on the $(I cells) $(I neighbours). 5 * 6 * It provides templates for testing whether a given object is a $(NEIGHBOURHOOD), 7 * and what kind of $(NEIGHBOURHOOD) it is. 8 * 9 * $(CALIB_ABSTRACT_DESC) 10 * 11 * Macros: 12 * IS_ANY = 13 * Tests if something is a $(B $0). 14 * $(DDOC_BLANKLINE) 15 * returns $(DDOC_BACKQUOTED true) if $(DDOC_BACKQUOTED T) is a $(B $0). of 16 * any dimension. 17 * $(DDOC_BLANKLINE) 18 * This template is the same as $(B is$0) but the dimension need not be 19 * specified. Refer to $(B is$0) for more details about what a $(B $0) is. 20 * $(DDOC_BLANKLINE) 21 * $(DDOC_PARAMS T type to be tested) 22 * $(DDOC_RETURNS $(B true) if $(B T) is any $(B $0), $(B false) if not) 23 */ 24 25 module caLib_abstract.neighbourhood; 26 27 import std.meta : Repeat; 28 import caLib_abstract.util : hasDimension, isDimensionsCompatible; 29 30 31 32 /** 33 * Tests if something is a $(B Neighbourhood). 34 * 35 * returns `true` if `T` is a $(B Neighbourhood) of dimension `N`. 36 * 37 * A $(B Neighbourhood) is the most basic form of a $(NEIGHBOURHOOD). 38 * It's `getNeighboursCoordinates` function takes a $(I cells) position 39 * and returns a list of coordinate pairs for it's neighbours positions. 40 * 41 * It must define the primitive: $(BR)`int[N][] getNeighboursCoordinates(Coord)`. $(BR) 42 * Where N is the dimension of the $(B Neighbourhood) and `Coord` is an 43 * alias for a typetuple containing `int`'s, one `int` for each 44 * dimension (in a 3 dimensional $(B Neighbourhood) `Coord` would be: `(int, int, int)`). 45 * 46 * Params: 47 * T = type to be tested 48 * N = number of dimensions 49 * 50 * Returns: true if T is a $(B Neighbourhood), false if not 51 */ 52 53 template isNeighbourhood(T, uint N) 54 { 55 alias Coord = Repeat!(N, int); 56 57 static if(hasDimension!T) 58 { 59 enum isNeighbourhood = 60 isDimensionsCompatible!(T.Dimension, N) && 61 is(typeof(T.init.getNeighboursCoordinates(Coord.init)) : int[N][]); 62 } 63 else 64 { 65 enum isNeighbourhood = false; 66 } 67 } 68 69 /// 70 unittest 71 { 72 struct A { 73 enum uint Dimension = 2; 74 int[2][] getNeighboursCoordinates(int x, int y) { 75 return [[x+1, y+1], [x+1, y], [x+1, y-1]]; 76 } 77 } 78 79 static assert( isNeighbourhood!(A, 2)); 80 static assert(!isNeighbourhood!(string, 1)); 81 } 82 83 unittest 84 { 85 static assert( isNeighbourhood!(Neighbourhood!(1), 1)); 86 static assert( isNeighbourhood!(Neighbourhood!(2), 2)); 87 static assert( isNeighbourhood!(Neighbourhood!(3), 3)); 88 89 static assert(!isNeighbourhood!(Neighbourhood!(3), 2)); 90 } 91 92 93 94 ///$(IS_ANY Neighbourhood) 95 template isAnyNeighbourhood(T) 96 { 97 static if(hasDimension!T) 98 enum isAnyNeighbourhood = isNeighbourhood!(T, T.Dimension); 99 else 100 enum isAnyNeighbourhood = false; 101 } 102 103 /// 104 unittest 105 { 106 struct Foo { 107 enum uint Dimension = 2; 108 109 int[Dimension][] getNeighboursCoordinates(int x, int y) { 110 return [[x+1, y+1], [x+1, y], [x+1, y-1]]; 111 } 112 } 113 114 static assert(! isNeighbourhood!(Foo, 3)); 115 static assert( isAnyNeighbourhood!(Foo )); 116 } 117 118 unittest 119 { 120 static assert( isAnyNeighbourhood!(Neighbourhood!(1))); 121 static assert( isAnyNeighbourhood!(StaticNeighbourhood!(2))); 122 static assert(!isAnyNeighbourhood!string); 123 } 124 125 126 127 /** 128 * Tests if something is a $(B StaticNeighbourhood). 129 * 130 * returns `true` if `T` is a $(B StaticNeighbourhood) of dimension `N`. 131 * 132 * A $(B StaticNeighbourhood) is a $(NEIGHBOURHOOD) that never changes. 133 * If a particular $(I cell) has a particular $(I neighbour) it will always have 134 * that $(I neighbour). Also, if a $(I cell) where to have a neighbour "to the right" 135 * all other $(I cells) will also have a neighbour "to the right". 136 * A $(B StaticNeighbourhood) dosen't really add any additional functionality. 137 * All it does is having the a uint enum NeighboursAmount. 138 * 139 * Params: 140 * T = type to be tested 141 * N = number of dimensions 142 * 143 * Returns: true if T is a $(B StaticNeighbourhood), false if not 144 */ 145 template isStaticNeighbourhood(T, uint N) 146 { 147 alias Coord = Repeat!(N, int); 148 149 enum isStaticNeighbourhood = 150 isNeighbourhood!(T, N) && 151 is(typeof(T.NeighboursAmount) : uint) && 152 is(typeof(T.init.NeighboursAmount) : uint); 153 } 154 155 /// 156 unittest 157 { 158 struct A { 159 enum uint Dimension = 2; 160 enum uint NeighboursAmount = 1; 161 enum isStatic; 162 int[2][] getNeighboursCoordinates(int x, int y) { 163 return [[x+1, y+1]]; 164 } 165 } 166 167 static assert( isNeighbourhood!(A, 2)); 168 static assert( isStaticNeighbourhood!(A, 2)); 169 static assert(!isStaticNeighbourhood!(string, 1)); 170 } 171 172 unittest 173 { 174 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(1), 1)); 175 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(2), 2)); 176 static assert( isStaticNeighbourhood!(StaticNeighbourhood!(3), 3)); 177 178 static assert(!isStaticNeighbourhood!(StaticNeighbourhood!(3), 2)); 179 static assert(!isStaticNeighbourhood!(Neighbourhood!(1), 1)); 180 } 181 182 183 184 ///$(IS_ANY StaticNeighbourhood) 185 template isAnyStaticNeighbourhood(T) 186 { 187 static if(hasDimension!T) 188 enum isAnyStaticNeighbourhood = isStaticNeighbourhood!(T, T.Dimension); 189 else 190 enum isAnyStaticNeighbourhood = false; 191 } 192 193 /// 194 unittest 195 { 196 struct Foo { 197 enum uint Dimension = 2; 198 enum uint NeighboursAmount = 1; 199 int[2][] getNeighboursCoordinates(int x, int y) { 200 return [[x+1, y+1]]; 201 } 202 } 203 static assert(! isStaticNeighbourhood!(Foo, 3)); 204 static assert( isAnyStaticNeighbourhood!(Foo )); 205 } 206 207 unittest 208 { 209 static assert(!isAnyStaticNeighbourhood!(Neighbourhood!(1))); 210 static assert( isAnyStaticNeighbourhood!(StaticNeighbourhood!(2))); 211 static assert(!isAnyStaticNeighbourhood!string); 212 } 213 214 215 216 version(unittest) 217 { 218 struct Neighbourhood(uint N) 219 { 220 enum uint Dimension = N; 221 222 alias Coord = Repeat!(N, int); 223 224 int[N][] getNeighboursCoordinates(Coord) { int[N][]a; return a.init; } 225 } 226 227 struct StaticNeighbourhood(uint N) 228 { 229 Neighbourhood!N neighbourhood; 230 alias neighbourhood this; 231 232 enum uint NeighboursAmount = 0; 233 } 234 235 struct BlockNeighbourhood(uint N) 236 { 237 alias Coord = Repeat!(N, int); 238 239 Neighbourhood!N neighbourhood; 240 alias neighbourhood this; 241 242 int[N][] getBlockCoordinates(Coord) { int[N][]a; return a.init; } 243 } 244 }