@@ -35,7 +35,7 @@ constexpr R_xlen_t operator""_rz(unsigned long long n) {
3535 return n;
3636}
3737
38- // helpers ---------------------------------------------------------------------------
38+ // container helpers ---------------------------------------------------------------------------
3939
4040// / Signed size of a container (from C++20)
4141template <class C >
@@ -89,7 +89,50 @@ Rcpp::IntegerVector wilkinson_bin_to_right_(const Rcpp::NumericVector& x, const
8989 return bins;
9090}
9191
92- // grid_swarm ------------------------------------------------------------
92+ // reversible sequence helpers ------------------------------------------------------
93+ // These helpers allow us to write the core grid swarm placement methods in a way that
94+ // is agnostic to whether we are iterating forward or backward through the candidate dots.
95+
96+ // / const begin iterator for forward or reverse iteration
97+ // / @tparam reverse iterate in reverse?
98+ // / @tparam C container type
99+ // / @param container object to iterate over
100+ template <bool reverse, typename C>
101+ inline auto cbegin (const C& container) {
102+ if constexpr (reverse) {
103+ return container.crbegin ();
104+ } else {
105+ return container.cbegin ();
106+ }
107+ }
108+
109+ // / const end iterator for forward or reverse iteration
110+ // / @tparam reverse iterate in reverse?
111+ // / @tparam C container type
112+ // / @param container object to iterate over
113+ template <bool reverse, typename C>
114+ inline auto cend (const C& container) {
115+ if constexpr (reverse) {
116+ return container.crend ();
117+ } else {
118+ return container.cend ();
119+ }
120+ }
121+
122+ // / Erase an element from a container via a (possibly reversed) iterator
123+ // / @tparam reverse is the iterator reversed?
124+ // / @tparam C container type
125+ // / @tparam It iterator type
126+ // / @param container object to erase from
127+ // / @param it iterator pointing at element to erase
128+ template <bool reverse, typename C, typename It>
129+ inline auto erase (C& container, const It& it) -> It {
130+ if constexpr (reverse) {
131+ return std::reverse_iterator (container.erase (std::next (it).base ()));
132+ } else {
133+ return container.erase (it);
134+ }
135+ }
93136
94137// / Add distance to a value, possibly in reverse direction
95138// / @tparam reverse add in reverse direction?
@@ -119,6 +162,27 @@ inline auto min_candidate(const double a, const double b) -> double {
119162 }
120163}
121164
165+ // / Advance an iterator on a set of candidates to at least `min_next_candidate`
166+ // / @tparam reverse is the iterator reversed?
167+ // / @tparam Candidates container type
168+ // / @param candidates set of candidates
169+ // / @param it current iterator position
170+ // / @param min_next_candidate minimum candidate value to advance to
171+ template <bool reverse, typename Iterator, typename Candidates>
172+ inline auto advance_to_at_least (
173+ const Candidates& candidates, const Iterator it, const double min_next_candidate
174+ ) {
175+ if constexpr (reverse) {
176+ auto next_it = std::reverse_iterator (upper_bound (candidates, min_next_candidate));
177+ return std::max (it, next_it);
178+ } else {
179+ auto next_it = lower_bound (candidates, min_next_candidate);
180+ return std::max (it, next_it);
181+ }
182+ }
183+
184+ // core grid swarm placement methods ------------------------------------------------------
185+
122186// / Attempt to place a candidate dot in a target row
123187// / @tparam Row container type for rows of already-placed dots
124188// / @param candidate candidate x position
@@ -185,79 +249,6 @@ inline auto place_candidate(
185249 return true ;
186250}
187251
188- // / const begin iterator for forward or reverse s
189- // / @tparam reverse iterate in reverse?
190- // / @tparam C container type
191- // / @param container object to iterate over
192- template <bool reverse, typename C>
193- inline auto cbegin (const C& container) {
194- if constexpr (reverse) {
195- return container.crbegin ();
196- } else {
197- return container.cbegin ();
198- }
199- }
200-
201- // / const end iterator for forward or reverse iteration
202- // / @tparam reverse iterate in reverse?
203- // / @tparam C container type
204- // / @param container object to iterate over
205- template <bool reverse, typename C>
206- inline auto cend (const C& container) {
207- if constexpr (reverse) {
208- return container.crend ();
209- } else {
210- return container.cend ();
211- }
212- }
213-
214- // / push onto front or back of a container
215- // / @tparam front push onto front?
216- // / @tparam C container type
217- // / @param container object to push onto
218- template <bool front, typename C, typename V>
219- inline void push (C& container, V&& value) {
220- if constexpr (front) {
221- container.push_front (std::forward<V>(value));
222- } else {
223- container.push_back (std::forward<V>(value));
224- }
225- }
226-
227- // / Erase an element from a container via a (possibly reversed) iterator
228- // / @tparam reverse is the iterator reversed?
229- // / @tparam C container type
230- // / @tparam It iterator type
231- // / @param container object to erase from
232- // / @param it iterator pointing at element to erase
233- template <bool reverse, typename C, typename It>
234- inline auto erase (C& container, const It& it) -> It {
235- if constexpr (reverse) {
236- return std::reverse_iterator (container.erase (std::next (it).base ()));
237- } else {
238- return container.erase (it);
239- }
240- }
241-
242- // / Advance an iterator on a set of candidates to at least `min_next_candidate`
243- // / @tparam reverse is the iterator reversed?
244- // / @tparam Candidates container type
245- // / @param candidates set of candidates
246- // / @param it current iterator position
247- // / @param min_next_candidate minimum candidate value to advance to
248- template <bool reverse, typename Iterator, typename Candidates>
249- inline auto advance_to_at_least (
250- const Candidates& candidates, const Iterator it, const double min_next_candidate
251- ) {
252- if constexpr (reverse) {
253- auto next_it = std::reverse_iterator (upper_bound (candidates, min_next_candidate));
254- return std::max (it, next_it);
255- } else {
256- auto next_it = lower_bound (candidates, min_next_candidate);
257- return std::max (it, next_it);
258- }
259- }
260-
261252// / Attempt to place dots in a specific row in the grid_swarm algorithm
262253// / @tparam reverse are we placing dots in reverse order?
263254// / @tparam Row container type for rows of already-placed dots
@@ -296,14 +287,19 @@ inline auto place_row(
296287 for (auto it = cbegin<reverse>(candidates); it != cend<reverse>(candidates); ) {
297288 auto candidate = *it;
298289
299- if (place_candidate<reverse>(candidate, xsize, ygrid, rows, row_i, min_next_candidate_top)) {
300- it = erase<reverse>(candidates, it);
301- } else if (both && place_candidate<reverse>(candidate, xsize, ygrid, rows_bottom, row_i, min_next_candidate_bottom)) {
290+ // attempt to place candidate, updating min_next_candidate_{top,bottom} so we can
291+ // skip candidates that are definitely not placeable (this is very important for performance,
292+ // especially when binwidth is large and many candidates are rejected)
293+ if (
294+ place_candidate<reverse>(candidate, xsize, ygrid, rows, row_i, min_next_candidate_top) ||
295+ (both && place_candidate<reverse>(candidate, xsize, ygrid, rows_bottom, row_i, min_next_candidate_bottom))
296+ ) {
302297 it = erase<reverse>(candidates, it);
303298 } else {
304299 ++it;
305300 }
306301
302+ // skip candidates that are definitely not placeable
307303 if (both) {
308304 min_next_candidate = min_candidate<reverse>(min_next_candidate_top, min_next_candidate_bottom);
309305 } else {
@@ -410,6 +406,8 @@ SEXP grid_swarm_(
410406 );
411407}
412408
409+ // swarm cluster recentering ------------------------------------------------------------
410+
413411// ' Re-center contiguous clusters around their mean y position so that
414412// ' small clusters are visually centered (rather than e.g. a cluster of
415413// ' two points having one point on the origin line and one above it)
0 commit comments