62 const int*
begin() const noexcept {
65 const int*
end() const noexcept {
68 int size() const noexcept {
76template<
typename Scalar>
92 nx_ =
static_cast<int>(std::ceil((xmax - xmin) / cs_)) + 2;
93 ny_ =
static_cast<int>(std::ceil((ymax - ymin) / cs_)) + 2;
94 const int total = nx_ * ny_;
95 start_.assign(total + 1, 0);
96 count_.assign(total, 0);
104 template<
typename PosAccessor>
105 void build(PosAccessor&& get_pos,
int n) {
107 const int total = nx_ * ny_;
110 std::fill(count_.begin(), count_.end(), 0);
111 for (
int i = 0; i < n; ++i)
112 ++count_[cell_id_of(get_pos(i))];
116 for (
int c = 0; c < total; ++c)
117 start_[c + 1] = start_[c] + count_[c];
120 std::fill(count_.begin(), count_.end(), 0);
121 for (
int i = 0; i < n; ++i) {
122 const int cid = cell_id_of(get_pos(i));
123 sorted_[start_[cid] + count_[cid]] = i;
133 void query(Scalar px, Scalar py, F&& f)
const {
134 const int cx = cell_x(px);
135 const int cy = cell_y(py);
136 for (
int dy = -1; dy <= 1; ++dy) {
137 const int qy = cy + dy;
138 if (qy < 0 || qy >= ny_)
140 for (
int dx = -1; dx <= 1; ++dx) {
141 const int qx = cx + dx;
142 if (qx < 0 || qx >= nx_)
144 const int cid = qy * nx_ + qx;
145 for (
int k = start_[cid]; k < start_[cid + 1]; ++k)
162 static constexpr int FDX[4] = {+1, 0, +1, -1};
163 static constexpr int FDY[4] = {0, +1, +1, +1};
165 for (
int cy = 0; cy < ny_; ++cy) {
166 for (
int cx = 0; cx < nx_; ++cx) {
167 const int cid = cy * nx_ + cx;
168 const int beg = start_[cid];
169 const int end = start_[cid + 1];
174 for (
int a = beg; a < end; ++a) {
175 for (
int b = a + 1; b < end; ++b) {
176 f(sorted_[a], sorted_[b]);
181 for (
int d = 0; d < 4; ++d) {
182 const int ncx = cx + FDX[d];
183 const int ncy = cy + FDY[d];
184 if (ncx < 0 || ncx >= nx_ || ncy < 0 || ncy >= ny_)
186 const int ncid = ncy * nx_ + ncx;
187 const int nbeg = start_[ncid];
188 const int nend = start_[ncid + 1];
191 for (
int a = beg; a < end; ++a) {
192 for (
int b = nbeg; b < nend; ++b) {
193 f(sorted_[a], sorted_[b]);
203 const int cid = cy * nx_ + cx;
204 return {sorted_.data() + start_[cid], sorted_.data() + start_[cid + 1]};
207 int nx() const noexcept {
210 int ny() const noexcept {
214 return static_cast<int>(sorted_.size());
218 Scalar cs_ = 0, xmin_ = 0, ymin_ = 0;
219 int nx_ = 0, ny_ = 0;
221 std::vector<int> sorted_;
224 std::vector<int> count_;
226 int cell_x(Scalar x)
const noexcept {
228 const int cx =
static_cast<int>(std::floor((x - xmin_) / cs_)) + 1;
229 return cx < 0 ? 0 : (cx >= nx_ ? nx_ - 1 : cx);
231 int cell_y(Scalar y)
const noexcept {
232 const int cy =
static_cast<int>(std::floor((y - ymin_) / cs_)) + 1;
233 return cy < 0 ? 0 : (cy >= ny_ ? ny_ - 1 : cy);
235 int cell_id_of(std::pair<Scalar, Scalar> p)
const noexcept {
236 return cell_y(p.second) * nx_ + cell_x(p.first);
void query(Scalar px, Scalar py, F &&f) const
Point query: calls f(int j) for every particle in the 3x3 cell neighbourhood of (px,...
void iterate_pairs(F &&f) const
Newton's 3rd law pair traversal.
CellList2D(Scalar cell_size, Scalar xmin, Scalar xmax, Scalar ymin, Scalar ymax)
int n_particles() const noexcept
IntRange cell_particles(int cx, int cy) const noexcept
Direct access to sorted particle indices for cell (cx, cy).
void build(PosAccessor &&get_pos, int n)
Rebuild the cell list from n particles.
Lightweight read-only range over a contiguous int array (C++17-safe span).
const int * begin() const noexcept
bool empty() const noexcept
const int * end() const noexcept
int size() const noexcept