#![cfg(feature = "encoder")]
use draco_core::{
DataType, EncoderBuffer, EncoderOptions, GeometryAttributeType, PointAttribute, PointCloud,
PointCloudEncoder,
};
const SEQUENTIAL: i32 = 0;
const POINTS: usize = 360_000;
struct Xorshift(u64);
impl Xorshift {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn unit(&mut self) -> f32 {
(self.next() >> 40) as f32 / (1u64 << 24) as f32
}
}
fn float_attribute(kind: GeometryAttributeType, components: u8, values: &[f32]) -> PointAttribute {
let mut attribute = PointAttribute::new();
attribute.init(kind, components, DataType::Float32, false, POINTS);
for (i, value) in values.iter().enumerate() {
attribute.buffer_mut().write(i * 4, &value.to_le_bytes());
}
attribute
}
fn cloud() -> PointCloud {
let mut rng = Xorshift(0x9e37_79b9_7f4a_7c15);
let positions: Vec<f32> = (0..POINTS * 3)
.map(|i| (i / 3) as f32 * 0.001 + rng.unit())
.collect();
let normals: Vec<f32> = (0..POINTS)
.flat_map(|_| {
let (x, y) = (rng.unit() - 0.5, rng.unit() - 0.5);
let z = (1.0 - x * x - y * y).max(0.0).sqrt();
[x, y, z]
})
.collect();
let generic: Vec<f32> = (0..POINTS).map(|_| rng.unit() * 4.0 - 2.0).collect();
let mut cloud = PointCloud::new();
cloud.set_num_points(POINTS);
cloud.add_attribute(float_attribute(
GeometryAttributeType::Position,
3,
&positions,
));
cloud.add_attribute(float_attribute(GeometryAttributeType::Normal, 3, &normals));
cloud.add_attribute(float_attribute(GeometryAttributeType::Generic, 1, &generic));
let mut colour = PointAttribute::new();
colour.init(
GeometryAttributeType::Color,
3,
DataType::Uint8,
true,
POINTS,
);
for i in 0..POINTS * 3 {
colour.buffer_mut().write(i, &[(rng.next() >> 56) as u8]);
}
cloud.add_attribute(colour);
cloud
}
fn encode(cloud: &PointCloud, options: &EncoderOptions) -> Vec<u8> {
let mut encoder = PointCloudEncoder::new();
encoder.set_point_cloud(cloud.clone());
let mut buffer = EncoderBuffer::new();
encoder.encode(options, &mut buffer).expect("encodes");
buffer.data().to_vec()
}
#[test]
fn the_stream_does_not_depend_on_the_number_of_threads() {
let cloud = cloud();
for (prediction_search, spatial) in [(false, false), (true, true)] {
let mut options = EncoderOptions::new();
options.set_encoding_method(SEQUENTIAL);
options.set_attribute_int(0, "quantization_bits", 16);
options.set_attribute_int(1, "quantization_bits", 10);
options.set_attribute_int(2, "quantization_bits", 12);
options.set_prediction_search(prediction_search);
options.set_spatial_point_order(spatial);
options.set_threads(1);
let single = encode(&cloud, &options);
for threads in [2, 7, 16, 0] {
options.set_threads(threads);
assert!(
encode(&cloud, &options) == single,
"{threads} threads (prediction search {prediction_search}, spatial {spatial})"
);
}
}
}
#[test]
fn every_attribute_shape_cut_into_pieces_writes_the_same_stream() {
const POINTS: usize = 16 * (1 << 16) + 1;
let mut rng = Xorshift(0x2545_f491_4f6c_dd1d);
let attribute = |kind, components: u8, data_type, bytes: Vec<u8>| {
let mut attribute = PointAttribute::new();
attribute.init(kind, components, data_type, false, POINTS);
attribute.buffer_mut().write(0, &bytes);
attribute
};
let floats =
|values: Vec<f32>| -> Vec<u8> { values.iter().flat_map(|v| v.to_le_bytes()).collect() };
let zeros_apart: Vec<f32> = (0..POINTS)
.map(|i| match i {
100 => 0.0,
327_687 => -0.0,
_ => 1.0 + (i % 977) as f32 * 0.01,
})
.collect();
let normals: Vec<f32> = (0..POINTS * 3).map(|_| rng.unit() * 2.0 - 1.0).collect();
let pairs: Vec<u8> = (0..POINTS * 2)
.flat_map(|i| (((i / 2) as i16).wrapping_mul(31) ^ (rng.next() >> 60) as i16).to_le_bytes())
.collect();
let quads: Vec<u8> = (0..POINTS * 4).map(|_| (rng.next() >> 59) as u8).collect();
let mut cloud = PointCloud::new();
cloud.set_num_points(POINTS);
cloud.add_attribute(attribute(
GeometryAttributeType::Generic,
1,
DataType::Float32,
floats(zeros_apart),
));
cloud.add_attribute(attribute(
GeometryAttributeType::Normal,
3,
DataType::Float32,
floats(normals),
));
cloud.add_attribute(attribute(
GeometryAttributeType::Generic,
2,
DataType::Int16,
pairs,
));
cloud.add_attribute(attribute(
GeometryAttributeType::Color,
4,
DataType::Uint8,
quads,
));
let mut options = EncoderOptions::new();
options.set_encoding_method(SEQUENTIAL);
options.set_attribute_int(0, "quantization_bits", 14);
options.set_attribute_int(1, "quantization_bits", 10);
options.set_threads(1);
let single = encode(&cloud, &options);
options.set_threads(16);
assert!(encode(&cloud, &options) == single);
}