1 /** 2 Multi-dimensional vector. 3 4 Copyright: 5 Copyright © 2020-2026, Inochi2D Project 6 7 License: 8 $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License) 9 10 Authors: 11 Luna Nielsen 12 Mireille Arseneault 13 */ 14 module inochi2d.core.vector2d; 15 import numem; 16 17 public import inochi2d.core.slice2d; 18 19 /** 20 A contiguous 2D vector. 21 22 The data is stored row-major. 23 */ 24 struct vector2d(T) { 25 private: 26 @nogc: 27 T[] _data = null; 28 size_t _rows = 0; 29 size_t _columns = 0; 30 31 public: 32 33 /** 34 Type of the data stored within. 35 */ 36 alias DT = T; 37 38 /** 39 Pointer to the start of the data. 40 */ 41 @property inout(T)* ptr() inout => _data.ptr; 42 43 /** 44 Total length of this vector's memory block. 45 */ 46 @property size_t length() const => _data.length; 47 48 /** 49 Slice to the underlying memory block. 50 */ 51 @property inout(T)[] data() inout => _data[]; 52 53 /** 54 The number of rows in this vector. 55 */ 56 @property size_t rows() const => _rows; 57 58 /** 59 The number of columns in this vector. 60 */ 61 @property size_t columns() const => _columns; 62 63 /** 64 Construct a new vector of the given size. 65 66 Params: 67 rows = The number of rows in the vector. 68 columns = The number of columns in the vector. 69 */ 70 this(size_t rows, size_t columns) { 71 resize(rows, columns); 72 } 73 74 /** 75 Copy-construct a new vector. 76 77 Params: 78 other = The vector to copy during construction. 79 */ 80 this(ref return scope inout vector2d other) { 81 _data = cast(T[])other._data.nu_dup; 82 _rows = other._rows; 83 _columns = other._columns; 84 } 85 86 /** 87 Move-construct a new vector. 88 89 Params: 90 other = The vector to move during construction. 91 */ 92 this(return scope vector2d other) { 93 _data = other._data; 94 _rows = other._rows; 95 _columns = other._columns; 96 97 other._data = null; 98 other._rows = 0; 99 other._columns = 0; 100 } 101 102 ~this() { 103 nu_freea(_data); 104 } 105 106 /** 107 Resize this 2D vector. 108 109 Params: 110 rows = The new row size of this vector. 111 cols = The new column size of this vector. 112 */ 113 void resize(size_t rows, size_t cols) { 114 115 // NOTE: When resizing from empty to non-empty, we can skip the 116 // other steps. 117 if ((_rows == 0 || _columns == 0) && (rows > 0 && cols > 0)) { 118 this._rows = rows; 119 this._columns = cols; 120 this._data = nu_malloca!T(_rows * _columns); 121 return; 122 } 123 124 // Get which row/column pair is the smallest, then use that 125 // for copying data over to the new array. 126 size_t mRows = nu_min(cast(size_t)_rows, cast(size_t)rows); 127 size_t mCols = nu_min(cast(size_t)_columns, cast(size_t)cols); 128 129 auto oldData = _data; 130 auto newData = nu_malloca!T(rows * cols); 131 foreach (x; 0 .. mRows) { 132 foreach (y; 0 .. mCols) { 133 newData[(rows * y) + x] = oldData[(_rows * y) + x]; 134 } 135 } 136 137 this._rows = rows; 138 this._columns = cols; 139 this._data = newData; 140 nu_freea(oldData); 141 } 142 143 /** 144 Clear the contents of the vector. 145 */ 146 void clear() { 147 _rows = 0; 148 _columns = 0; 149 nu_freea(_data); 150 _data = null; 151 } 152 153 /** 154 Index this vector. 155 156 Params: 157 row = The row to index. 158 column = The column to index. 159 160 Returns: 161 The item at the given row and column index. 162 */ 163 ref inout(T) opIndex(size_t row, size_t column) inout { 164 assert(row <= _rows, "Row index outside bounds of vector."); 165 assert(column <= _columns, "Column index outside bounds of vector."); 166 return _data[(_columns * row) + column]; 167 } 168 169 /** 170 Index this vector. 171 172 Params: 173 row = The row to index. 174 columns = The columns to index. 175 176 Returns: 177 The slice of items at the given row/column span. 178 */ 179 slice2d!T opIndex(size_t row, size_t[2] columns) { 180 const a = (_columns * row) + columns[0]; 181 const b = (_columns * (row + 1)) + columns[1]; 182 return slice2d!T(_data[a .. b], _columns, 1, columns[1] - columns[0]); 183 } 184 185 /** 186 Index this vector. 187 188 Params: 189 rows = The rows to index. 190 column = The column to index. 191 192 Returns: 193 The slice of items at the given row/column span. 194 */ 195 slice2d!T opIndex(size_t[2] rows, size_t column) { 196 const a = (_columns * rows[0]) + column; 197 const b = (_columns * rows[1]) + column + 1; 198 return slice2d!T(_data[a .. b], _columns, rows[1] - rows[0], 1); 199 } 200 201 /** 202 Index this vector. 203 204 Params: 205 rows = The rows to index. 206 columns = The columns to index. 207 208 Returns: 209 The slice of items at the given row/column span. 210 */ 211 slice2d!T opIndex(size_t[2] rows, size_t[2] columns) { 212 const a = (_columns * rows[0]) + columns[0]; 213 const b = (_columns * rows[1]) + columns[1]; 214 return slice2d!T(_data[a .. b], _columns, rows[1] - rows[0], columns[1] - columns[0]); 215 } 216 217 /** 218 Slices this vector. 219 220 Returns: 221 The slice of the entire contents of this vector. 222 */ 223 slice2d!T opIndex() { 224 return slice2d!T(_data, _columns, _rows, _columns); 225 } 226 227 /** 228 Assign to the value at the given index. 229 */ 230 auto opIndexAssign(T value, size_t row, size_t column) { 231 assert(row <= _rows, "Row index outside bounds of array."); 232 assert(column <= _columns, "Column index outside bounds of array."); 233 return _data[(_columns * row) + column] = value; 234 } 235 236 /** 237 Mass-assign to the values at the given span. 238 */ 239 auto opIndexAssign(T value, size_t row, size_t[2] columns) { 240 auto slice = this[row, columns]; 241 return slice[] = value; 242 } 243 244 /** 245 Mass-assign to the values at the given span. 246 */ 247 auto opIndexAssign(T value, size_t[2] rows, size_t column) { 248 auto slice = this[rows, column]; 249 return slice[] = value; 250 } 251 252 /** 253 Mass-assign to the values at the given span. 254 */ 255 auto opIndexAssign(T value, size_t[2] rows, size_t[2] columns) { 256 auto slice = this[rows, columns]; 257 return slice[] = value; 258 } 259 260 /** 261 Mass-assign to all values in this vector. 262 */ 263 auto opIndexAssign(T value) { 264 _data[] = value; 265 return value; 266 } 267 268 /** 269 Mass-assign a slice to all values of this vector 270 */ 271 auto opIndexAssign(slice2d!T value) { 272 auto slice = this[]; 273 return slice[] = value; 274 } 275 276 /** 277 Mass-assign a slice to a region of this vector. 278 */ 279 auto opIndexAssign(slice2d!T value, size_t row, size_t[2] columns) { 280 auto slice = this[row, columns]; 281 return slice[] = value; 282 } 283 284 /** 285 Mass-assign a slice to a region of this vector. 286 */ 287 auto opIndexAssign(slice2d!T value, size_t[2] rows, size_t column) { 288 auto slice = this[rows, column]; 289 return slice[] = value; 290 } 291 292 /** 293 Mass-assign a slice to a region of this vector. 294 */ 295 auto opIndexAssign(slice2d!T value, size_t[2] rows, size_t[2] columns) { 296 auto slice = this[rows, columns]; 297 return slice[] = value; 298 } 299 300 /** 301 Obtain a pair of indices describing a span of rows. 302 303 Params: 304 a = The lower bound. 305 b = The upper bound. 306 307 Returns: 308 A "tuple" of our bounds. 309 */ 310 size_t[2] opSlice(size_t dim : 0)(size_t a, size_t b) const { 311 assert(a <= b, "Slice bounds given in the wrong order"); 312 return [a, b]; 313 } 314 315 /** 316 Obtain a pair of indices describing a span of columns. 317 318 Params: 319 a = The lower bound. 320 b = The upper bound. 321 322 Returns: 323 A "tuple" of our bounds. 324 */ 325 size_t[2] opSlice(size_t dim : 1)(size_t a, size_t b) const { 326 assert(a <= b, "Slice bounds given in the wrong order"); 327 return [a, b]; 328 } 329 330 /** 331 Obtain the number of rows in this vector using the dollar operator. 332 333 Returns: 334 The number of rows in this vector. 335 */ 336 size_t opDollar(size_t dim : 0)() const { 337 return _rows; 338 } 339 340 /** 341 Obtain the number of columns in this vector using the dollar operator. 342 343 Returns: 344 The number of columns in this vector. 345 */ 346 size_t opDollar(size_t dim : 1)() const { 347 return _columns; 348 } 349 } 350 351 @("vector2d") 352 unittest { 353 vector2d!int ints; 354 ints.resize(4, 4); 355 ints[2, 2] = 24; 356 357 ints.resize(8, 8); 358 assert(ints[2, 2] == 24); 359 }