use crate::corner_table::CornerTable;
use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
use crate::geometry_indices::{CornerIndex, INVALID_CORNER_INDEX};
use crate::mesh_prediction_scheme_data::MeshPredictionSchemeData;
use crate::portable_attribute::PredictionParent;
use crate::prediction_scheme::{
PredictionScheme, PredictionSchemeMethod, PredictionSchemeTransformType,
};
use std::marker::PhantomData;
#[cfg(feature = "decoder")]
use crate::prediction_scheme::{PredictionSchemeDecoder, PredictionSchemeDecodingTransform};
#[cfg(feature = "encoder")]
use crate::prediction_scheme::{PredictionSchemeEncoder, PredictionSchemeEncodingTransform};
use crate::status::{DracoError, Status};
pub trait ParallelogramDataType: Copy + Default + 'static {
fn compute_parallelogram_prediction(next: Self, prev: Self, opp: Self) -> Self;
fn add_as_unsigned(a: Self, b: Self) -> Self;
}
impl ParallelogramDataType for i32 {
fn compute_parallelogram_prediction(next: Self, prev: Self, opp: Self) -> Self {
next.wrapping_add(prev).wrapping_sub(opp)
}
fn add_as_unsigned(a: Self, b: Self) -> Self {
(a as u32).wrapping_add(b as u32) as i32
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn compute_parallelogram_prediction<DataType: ParallelogramDataType>(
data_entry_id: i32,
ci: CornerIndex,
table: &CornerTable,
vertex_to_data_map: &[i32],
in_data: &[DataType],
num_components: usize,
out_prediction: &mut [DataType],
) -> bool {
let oci = table.opposite(ci);
if oci == INVALID_CORNER_INDEX {
return false;
}
let vert_opp_idx = table.vertex(oci).0 as usize;
let vert_next_idx = table.vertex_after(oci).0 as usize;
let vert_prev_idx = table.vertex_before(oci).0 as usize;
if vert_opp_idx >= vertex_to_data_map.len()
|| vert_next_idx >= vertex_to_data_map.len()
|| vert_prev_idx >= vertex_to_data_map.len()
{
return false;
}
let vert_opp = vertex_to_data_map[vert_opp_idx];
let vert_next = vertex_to_data_map[vert_next_idx];
let vert_prev = vertex_to_data_map[vert_prev_idx];
if vert_opp >= 0
&& vert_next >= 0
&& vert_prev >= 0
&& vert_opp < data_entry_id
&& vert_next < data_entry_id
&& vert_prev < data_entry_id
{
let v_opp_off = (vert_opp as usize) * num_components;
let v_next_off = (vert_next as usize) * num_components;
let v_prev_off = (vert_prev as usize) * num_components;
let v_opp = &in_data[v_opp_off..v_opp_off + num_components];
let v_next = &in_data[v_next_off..v_next_off + num_components];
let v_prev = &in_data[v_prev_off..v_prev_off + num_components];
for (((o, &n), &pr), &op) in out_prediction.iter_mut().zip(v_next).zip(v_prev).zip(v_opp) {
*o = DataType::compute_parallelogram_prediction(n, pr, op);
}
#[cfg(feature = "debug_logs")]
if crate::debug_env_enabled("DRACO_VERBOSE") {
debug_log!(
"Parallelogram pred p={} c={} (o={}): entries({}, {}, {})",
data_entry_id,
ci.0,
oci.0,
vert_opp,
vert_next,
vert_prev
);
}
return true;
}
false
}
#[cfg(feature = "encoder")]
pub struct PredictionSchemeParallelogramEncodingTransform<DataType, CorrType> {
_marker: PhantomData<(DataType, CorrType)>,
}
#[cfg(feature = "encoder")]
impl<DataType, CorrType> Default
for PredictionSchemeParallelogramEncodingTransform<DataType, CorrType>
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "encoder")]
impl<DataType, CorrType> PredictionSchemeParallelogramEncodingTransform<DataType, CorrType> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
#[cfg(feature = "encoder")]
impl<DataType, CorrType> PredictionSchemeEncodingTransform<DataType, CorrType>
for PredictionSchemeParallelogramEncodingTransform<DataType, CorrType>
where
DataType: ParallelogramDataType + Into<i64>,
CorrType: Copy + Default + From<DataType> + std::ops::Sub<Output = CorrType> + From<i32>,
i64: From<DataType>,
i32: From<CorrType>,
{
fn get_type(&self) -> PredictionSchemeTransformType {
PredictionSchemeTransformType::Parallelogram
}
fn init(&mut self, _orig_data: &[DataType], _size: usize, _num_components: usize) {
}
fn compute_correction(
&self,
original_vals: &[DataType],
predicted_vals: &[DataType],
out_corr_vals: &mut [CorrType],
) {
for i in 0..original_vals.len() {
let o: i64 = original_vals[i].into();
let p: i64 = predicted_vals[i].into();
let diff = (o - p) as i32;
out_corr_vals[i] = diff.into();
}
}
fn encode_transform_data(&mut self, _buffer: &mut Vec<u8>) -> Status {
Ok(())
}
}
#[cfg(feature = "decoder")]
pub struct PredictionSchemeParallelogramDecodingTransform<DataType, CorrType> {
_marker: PhantomData<(DataType, CorrType)>,
}
#[cfg(feature = "decoder")]
impl<DataType, CorrType> Default
for PredictionSchemeParallelogramDecodingTransform<DataType, CorrType>
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "decoder")]
impl<DataType, CorrType> PredictionSchemeParallelogramDecodingTransform<DataType, CorrType> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
#[cfg(feature = "decoder")]
impl<DataType, CorrType> PredictionSchemeDecodingTransform<DataType>
for PredictionSchemeParallelogramDecodingTransform<DataType, CorrType>
where
DataType: ParallelogramDataType + From<i32> + Into<i64>,
{
fn get_type(&self) -> PredictionSchemeTransformType {
PredictionSchemeTransformType::Parallelogram
}
fn init(&mut self, _num_components: usize) -> Status {
Ok(())
}
fn compute_original_value(&self, predicted_vals: &[DataType], data: &mut [DataType]) {
for i in 0..predicted_vals.len() {
let p: i64 = predicted_vals[i].into();
let c: i64 = data[i].into();
data[i] = ((p + c) as i32).into();
}
}
fn decode_transform_data(
&mut self,
_buffer: &mut crate::decoder_buffer::DecoderBuffer,
) -> Status {
Ok(())
}
}
#[cfg(feature = "encoder")]
pub struct MeshPredictionSchemeParallelogramEncoder<'a, DataType, CorrType, Transform> {
#[allow(dead_code)]
attribute: &'a PointAttribute,
transform: Transform,
mesh_data: MeshPredictionSchemeData<'a>,
_marker: PhantomData<(DataType, CorrType)>,
}
#[cfg(feature = "encoder")]
impl<'a, DataType, CorrType, Transform>
MeshPredictionSchemeParallelogramEncoder<'a, DataType, CorrType, Transform>
{
pub fn new(
attribute: &'a PointAttribute,
transform: Transform,
mesh_data: MeshPredictionSchemeData<'a>,
) -> Self {
Self {
attribute,
transform,
mesh_data,
_marker: PhantomData,
}
}
}
#[cfg(feature = "encoder")]
impl<'a, DataType, CorrType, Transform> PredictionScheme<'a>
for MeshPredictionSchemeParallelogramEncoder<'a, DataType, CorrType, Transform>
where
Transform: PredictionSchemeEncodingTransform<DataType, CorrType>,
{
fn get_prediction_method(&self) -> PredictionSchemeMethod {
PredictionSchemeMethod::MeshPredictionParallelogram
}
fn get_transform_type(&self) -> PredictionSchemeTransformType {
self.transform.get_type()
}
fn is_initialized(&self) -> bool {
self.mesh_data.corner_table().is_some()
}
fn get_num_parent_attributes(&self) -> i32 {
0
}
fn get_parent_attribute_type(&self, _i: i32) -> GeometryAttributeType {
GeometryAttributeType::Invalid
}
fn set_parent_attribute(&mut self, _parent: PredictionParent<'a>) -> Status {
Err(DracoError::invalid_parameter(
"The parallelogram prediction scheme takes no parent attribute".to_string(),
))
}
}
#[cfg(feature = "encoder")]
impl<'a, DataType, CorrType, Transform> PredictionSchemeEncoder<'a, DataType, CorrType>
for MeshPredictionSchemeParallelogramEncoder<'a, DataType, CorrType, Transform>
where
DataType: ParallelogramDataType + std::fmt::Debug,
CorrType: Copy + Default + std::fmt::Debug,
Transform: PredictionSchemeEncodingTransform<DataType, CorrType>,
{
fn compute_correction_values(
&mut self,
in_data: &[DataType],
out_corr: &mut [CorrType],
size: usize,
num_components: usize,
_entry_to_point_id_map: Option<crate::prediction_scheme::EntryToPointIdMap<'_>>,
) -> Status {
self.transform.init(in_data, size, num_components);
if num_components == 0 || !size.is_multiple_of(num_components) {
return Err(DracoError::invalid_parameter(format!(
"{size} values do not divide into {num_components} components"
)));
}
if size == 0 {
return Ok(());
}
let table = self.mesh_data.corner_table().unwrap();
let vertex_to_data_map = self.mesh_data.vertex_to_data_map().unwrap();
let data_to_corner_map = self.mesh_data.data_to_corner_map().unwrap();
let num_entries = size / num_components;
let mut pred_vals = vec![DataType::default(); num_components];
for p in (1..num_entries).rev() {
let corner_id = CornerIndex(data_to_corner_map[p]);
let dst_offset = p * num_components;
let is_parallelogram = compute_parallelogram_prediction(
p as i32,
corner_id,
table,
vertex_to_data_map,
in_data,
num_components,
&mut pred_vals,
);
if !is_parallelogram {
let src_offset = (p - 1) * num_components;
let original = &in_data[dst_offset..dst_offset + num_components];
let predicted = &in_data[src_offset..src_offset + num_components];
let corr = &mut out_corr[dst_offset..dst_offset + num_components];
self.transform.compute_correction(original, predicted, corr);
} else {
let original = &in_data[dst_offset..dst_offset + num_components];
let corr = &mut out_corr[dst_offset..dst_offset + num_components];
self.transform
.compute_correction(original, &pred_vals, corr);
}
}
for i in 0..num_components {
pred_vals[i] = DataType::default();
}
let original = &in_data[0..num_components];
let corr = &mut out_corr[0..num_components];
self.transform
.compute_correction(original, &pred_vals, corr);
Ok(())
}
fn encode_prediction_data(&mut self, buffer: &mut Vec<u8>) -> Status {
self.transform.encode_transform_data(buffer)
}
}
#[cfg(feature = "decoder")]
pub struct MeshPredictionSchemeParallelogramDecoder<'a, DataType, Transform> {
#[allow(dead_code)]
attribute: &'a PointAttribute,
transform: Transform,
mesh_data: MeshPredictionSchemeData<'a>,
_marker: PhantomData<DataType>,
}
#[cfg(feature = "decoder")]
impl<'a, DataType, Transform> MeshPredictionSchemeParallelogramDecoder<'a, DataType, Transform> {
pub fn new(
attribute: &'a PointAttribute,
transform: Transform,
mesh_data: MeshPredictionSchemeData<'a>,
) -> Self {
Self {
attribute,
transform,
mesh_data,
_marker: PhantomData,
}
}
}
#[cfg(feature = "decoder")]
impl<'a, DataType, Transform> PredictionScheme<'a>
for MeshPredictionSchemeParallelogramDecoder<'a, DataType, Transform>
where
Transform: PredictionSchemeDecodingTransform<DataType>,
{
fn get_prediction_method(&self) -> PredictionSchemeMethod {
PredictionSchemeMethod::MeshPredictionParallelogram
}
fn get_transform_type(&self) -> PredictionSchemeTransformType {
self.transform.get_type()
}
fn is_initialized(&self) -> bool {
self.mesh_data.corner_table().is_some()
}
fn get_num_parent_attributes(&self) -> i32 {
0
}
fn get_parent_attribute_type(&self, _i: i32) -> GeometryAttributeType {
GeometryAttributeType::Invalid
}
fn set_parent_attribute(&mut self, _parent: PredictionParent<'a>) -> Status {
Err(DracoError::invalid_parameter(
"The parallelogram prediction scheme takes no parent attribute".to_string(),
))
}
}
#[cfg(feature = "decoder")]
impl<'a, DataType, Transform> PredictionSchemeDecoder<'a, DataType>
for MeshPredictionSchemeParallelogramDecoder<'a, DataType, Transform>
where
DataType: ParallelogramDataType + std::fmt::Debug,
Transform: PredictionSchemeDecodingTransform<DataType>,
{
fn compute_original_values(
&mut self,
data: &mut [DataType],
_size: usize,
num_components: usize,
_entry_to_point_id_map: Option<crate::prediction_scheme::EntryToPointIdMap<'_>>,
) -> Status {
self.transform.init(num_components)?;
if num_components == 0 {
return Err(DracoError::invalid_parameter(
"Parallelogram prediction needs at least one component".to_string(),
));
}
let missing =
|what: &str| DracoError::general(format!("Parallelogram prediction has no {what}"));
let Some(table) = self.mesh_data.corner_table() else {
return Err(missing("corner table"));
};
let Some(vertex_to_data_map) = self.mesh_data.vertex_to_data_map() else {
return Err(missing("vertex-to-data map"));
};
let Some(data_to_corner_map) = self.mesh_data.data_to_corner_map() else {
return Err(missing("data-to-corner map"));
};
let Some(required_values) = data_to_corner_map.len().checked_mul(num_components) else {
return Err(DracoError::general(
"Parallelogram prediction value count overflow".to_string(),
));
};
if required_values == 0 || data.len() < required_values {
return Err(DracoError::general(format!(
"Parallelogram prediction needs {required_values} values, has {}",
data.len()
)));
}
let mut pred_vals = vec![DataType::default(); num_components];
let zero_vals = vec![DataType::default(); num_components];
self.transform
.compute_original_value(&zero_vals, &mut data[0..num_components]);
for p in 1..data_to_corner_map.len() {
let corner_id = CornerIndex(data_to_corner_map[p]);
let dst_offset = p * num_components;
let (decoded_data, remaining_data) = data.split_at_mut(dst_offset);
let current = &mut remaining_data[0..num_components];
let is_parallelogram = compute_parallelogram_prediction(
p as i32,
corner_id,
table,
vertex_to_data_map,
decoded_data,
num_components,
&mut pred_vals,
);
if !is_parallelogram {
let src_offset = (p - 1) * num_components;
let predicted = &decoded_data[src_offset..src_offset + num_components];
pred_vals.copy_from_slice(predicted);
}
self.transform.compute_original_value(&pred_vals, current);
}
Ok(())
}
fn decode_prediction_data(
&mut self,
buffer: &mut crate::decoder_buffer::DecoderBuffer,
) -> Status {
self.transform.decode_transform_data(buffer)
}
}