Skip to content

Commit d26d6c3

Browse files
flatheadmillfulmicoton-dd
authored andcommitted
Fix select_nth_unstable_by_key midpoint duplicates.
Existing code behaved as if the result of `select_nth_unstable_by_key` was either a sorted array or the product of an algorithm that gathered partition values as in the Dutch national flag problem. The existing code was written knowing that the former isn't true and the latter isn't advertised. Knowing, but not remembering. Quite the oversight.
1 parent 6da54fa commit d26d6c3

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

src/spatial/bkd.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ fn write_leaf_pages(
139139
triangles: &mut [Triangle],
140140
write: &mut CountingWriter<WritePtr>,
141141
) -> io::Result<BuildNode> {
142+
// If less than 512 triangles we are at a leaf, otherwise we still in the inner nodes.
142143
if triangles.len() <= 512 {
143144
let pos = write.written_bytes();
144145
let mut spreads = [SpreadSurvey::default(); 4];
@@ -197,6 +198,7 @@ fn write_leaf_pages(
197198
max_spread = current_spread;
198199
}
199200
}
201+
// Partition the triangles.
200202
let mid = triangles.len() / 2;
201203
triangles.select_nth_unstable_by_key(mid, |t| t.words[dimension]);
202204
let partition = triangles[mid].words[dimension];
@@ -205,12 +207,37 @@ fn write_leaf_pages(
205207
{
206208
split_point += 1;
207209
}
210+
// If we reached the end of triangles then all of the triangles share the partition value
211+
// for the dimension. We handle this degeneracy by splitting at the midpoint so that we
212+
// won't have a leaf with zero triangles.
208213
if split_point == triangles.len() {
209214
split_point = mid; // Force split at midpoint index
215+
} else {
216+
// Our partition does not sort the triangles, it only partitions. We have scan our right
217+
// partition to find all the midpoint values and move them to the left partition.
218+
let mut reverse = triangles.len() - 1;
219+
loop {
220+
// Scan backwards looking for the partition value.
221+
while triangles[reverse].words[dimension] != partition {
222+
reverse -= 1;
223+
}
224+
// If we have reached the split point then we are done.
225+
if reverse <= split_point {
226+
break;
227+
}
228+
// Swap the midpoint value with our current split point.
229+
triangles.swap(split_point, reverse);
230+
// Move the split point up one.
231+
split_point += 1;
232+
// We know that what was at the split point was not the midpoint value.
233+
reverse -= 1;
234+
}
210235
}
236+
// Split into left and write partitions and create child nodes.
211237
let (left, right) = triangles.split_at_mut(split_point);
212238
let left_node = write_leaf_pages(left, write)?;
213239
let right_node = write_leaf_pages(right, write)?;
240+
// Return an inner node.
214241
Ok(BuildNode::Branch {
215242
bbox: bounding_box.bbox(),
216243
left: Box::new(left_node),

0 commit comments

Comments
 (0)