1 /**
2     Multi-dimensional slices.
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.slice2d;
15 import numem;
16 
17 /**
18     A contiguous 2D slice.
19 
20     The data is stored row-major.
21 */
22 struct slice2d(T) {
23 private:
24 @nogc:
25     T[] data = null;
26     size_t stride = 0;
27     size_t _rows = 0;
28     size_t _columns = 0;
29 
30 public:
31     /**
32         The number of rows in this slice.
33     */
34     @property size_t rows() const => _rows;
35 
36     /**
37         The number of columns in this slice.
38     */
39     @property size_t columns() const => _columns;
40 
41     /**
42         Construct a new slice of the given data with the given size.
43 
44         Params:
45             data    = The data of this slice.
46             stride  = The data stride of this slice.
47             rows    = The number of rows in the slice.
48             columns = The number of columns in the slice.
49     */
50     this(T[] data, size_t stride, size_t rows, size_t columns) {
51         this.data = data;
52         this.stride = stride;
53         _rows = rows;
54         _columns = columns;
55     }
56 
57     /**
58         Index this slice.
59 
60         Params:
61             row =       The row to index.
62             column =    The column to index.
63 
64         Returns:
65             The item at the given row and column index.
66     */
67     ref T opIndex(size_t row, size_t column) {
68         assert(row <= _rows, "Row index outside bounds of array.");
69         assert(column <= _columns, "Column index outside bounds of array.");
70         return data[(stride * row) + column];
71     }
72 
73     slice2d!T opIndex(size_t row, size_t[2] columns) {
74         const a = (stride * row) + columns[0];
75         const b = (stride * (row + 1)) + columns[1];
76         return slice2d!T(data[a .. b], stride, 1, columns[1] - columns[0]);
77     }
78 
79     slice2d!T opIndex(size_t[2] rows, size_t column) {
80         const a = (stride * rows[0]) + column;
81         const b = (stride * rows[1]) + column + 1;
82         return slice2d!T(data[a .. b], stride, rows[1] - rows[0], 1);
83     }
84 
85     slice2d!T opIndex(size_t[2] rows, size_t[2] columns) {
86         const a = (stride * rows[0]) + columns[0];
87         const b = (stride * rows[1]) + columns[1];
88         return slice2d!T(data[a .. b], stride, rows[1] - rows[0], columns[1] - columns[0]);
89     }
90 
91     slice2d!T opIndex() {
92         return slice2d!T(data, stride, _rows, _columns);
93     }
94 
95     auto opIndexAssign(T value) {
96         foreach (row; 0 .. _rows) {
97             auto dest = &data.ptr[row * stride];
98             dest[0 .. _columns] = value;
99         }
100         return value;
101     }
102 
103     auto opIndexAssign(T value, size_t row, size_t column) {
104         assert(row <= _rows, "Row index outside bounds of array.");
105         assert(column <= _columns, "Column index outside bounds of array.");
106         return data[(stride * row) + column] = value;
107     }
108 
109     auto opIndexAssign(slice2d!T value) {
110         assert(_rows == value._rows);
111         assert(_columns == value._columns);
112         foreach (row; 0 .. _rows) {
113             auto dest = &data.ptr[row * stride];
114             auto src = &value.data.ptr[row * value.stride];
115             dest[0 .. _columns] = src[0 .. _columns];
116         }
117         return value;
118     }
119 
120     auto opIndexAssign(slice2d!T value, size_t[2] rows, size_t[2] columns) {
121         auto slice = this[rows, columns];
122         return slice = value;
123     }
124 
125     /**
126         Obtain a pair of indices describing a span of rows.
127 
128         Params:
129             a = The lower bound.
130             b = The upper bound.
131 
132         Returns:
133             A "tuple" of our bounds.
134     */
135     size_t[2] opSlice(size_t dim : 0)(size_t a, size_t b) const {
136         return [a, b];
137     }
138 
139     /**
140         Obtain a pair of indices describing a span of columns.
141 
142         Params:
143             a = The lower bound.
144             b = The upper bound.
145 
146         Returns:
147             A "tuple" of our bounds.
148     */
149     size_t[2] opSlice(size_t dim : 1)(size_t a, size_t b) const {
150         return [a, b];
151     }
152 
153     /**
154         Obtain the number of rows in this vector using the dollar operator.
155 
156         Returns:
157             The number of rows in this vector.
158     */
159     size_t opDollar(size_t dim : 0)() const {
160         return _rows;
161     }
162 
163     /**
164         Obtain the number of columns in this vector using the dollar operator.
165 
166         Returns:
167             The number of columns in this vector.
168     */
169     size_t opDollar(size_t dim : 1)() const {
170         return _columns;
171     }
172 }