use std::{ops, vec};
use crate::encode::entropy::symbol_coding::encode_symbols;
use draco_oxide_core::attribute::Attribute;
use draco_oxide_core::attribute::AttributeType;
use draco_oxide_core::attribute::{AttributeDomain, ComponentDataType};
use draco_oxide_core::bit_coder::ByteWriter;
use draco_oxide_core::codec::attribute::sequence::Traverser;
use draco_oxide_core::codec::attribute::Portable;
use draco_oxide_core::codec::entropy::SymbolEncodingMethod;
use draco_oxide_core::mesh::ds::AttributeDS;
use draco_oxide_core::types::ConfigType;
use draco_oxide_core::types::{CornerIdx, DataValue, NdVector};
use thiserror::Error;
#[cfg(feature = "evaluation")]
#[allow(unused_imports)]
use crate::eval;
#[derive(Error, Debug)]
pub enum Err {
#[error("Entropy Symbol Encoding Error: {0}")]
EntropyEncodingError(#[from] crate::encode::entropy::symbol_coding::Err),
#[error("Invalid attribute id: {0}")]
InvalidAttributeId(usize),
#[error("Invalid prediction scheme id: {0}")]
InvalidPredictionSchemeId(usize),
#[error("Attribute Encoder has too many encoding groups: {0}")]
TooManyEncodingGroups(usize),
#[error("An attribute has too many parents: {0}")]
TooManyParents(usize),
#[error("Unsupported data type.")]
UnsupportedDataType,
#[error("Attribute data has too many components; it must be less than {}, but it is {}.", 5, .0)]
UnsupportedNumComponents(usize),
#[error("Prediction Error: {0}")]
PredictionError(#[from] draco_oxide_core::codec::attribute::prediction_scheme::Err),
}
#[derive(Clone, Debug)]
pub struct GroupConfig {
#[allow(unused)]
range: Vec<ops::Range<usize>>,
pub prediction_scheme: prediction_scheme::Config,
pub prediction_transform: prediction_transform::Config,
}
impl GroupConfig {
#[allow(clippy::single_range_in_vec_init)]
fn default_with_size(size: usize) -> Self {
Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config::default(),
prediction_transform: prediction_transform::Config::default(),
}
}
#[allow(clippy::single_range_in_vec_init, clippy::needless_update)]
fn default_for(att_ty: AttributeType, size: usize) -> Self {
match att_ty {
AttributeType::Position => Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config {
ty: prediction_scheme::PredictionSchemeType::MeshParallelogramPrediction,
..prediction_scheme::Config::default()
},
prediction_transform: prediction_transform::Config {
ty: prediction_transform::PredictionTransformType::WrappedDifference,
portabilization: portabilization::Config::default_for(att_ty),
},
},
AttributeType::Normal => Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config {
ty: prediction_scheme::PredictionSchemeType::MeshNormalPrediction,
..prediction_scheme::Config::default()
},
prediction_transform: prediction_transform::Config {
ty: prediction_transform::PredictionTransformType::OctahedralOrthogonal,
portabilization: portabilization::Config::default_for(att_ty),
},
},
AttributeType::TextureCoordinate => Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config {
ty:
prediction_scheme::PredictionSchemeType::MeshPredictionForTextureCoordinates,
..prediction_scheme::Config::default()
},
prediction_transform: prediction_transform::Config {
ty: prediction_transform::PredictionTransformType::WrappedDifference,
portabilization: portabilization::Config::default_for(att_ty),
},
},
AttributeType::Custom => Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config {
ty: prediction_scheme::PredictionSchemeType::DeltaPrediction,
..prediction_scheme::Config::default()
},
prediction_transform: prediction_transform::Config {
ty: prediction_transform::PredictionTransformType::WrappedDifference,
portabilization: portabilization::Config::default_for(AttributeType::Custom),
},
},
AttributeType::Color => Self {
range: vec![0..size],
prediction_scheme: prediction_scheme::Config {
ty: prediction_scheme::PredictionSchemeType::DeltaPrediction,
..prediction_scheme::Config::default()
},
prediction_transform: prediction_transform::Config {
ty: prediction_transform::PredictionTransformType::WrappedDifference,
portabilization: portabilization::Config::default_for(AttributeType::Color),
},
},
_ => Self::default_with_size(size),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EncodingMode {
Full,
ZeroCorrection,
}
#[derive(Clone, Debug)]
pub struct Config {
group_cfgs: Vec<GroupConfig>,
rans_encoding: bool,
mode: EncodingMode,
}
impl ConfigType for Config {
fn default() -> Self {
Self {
group_cfgs: Vec::new(),
rans_encoding: true,
mode: EncodingMode::Full,
}
}
}
impl Config {
pub fn default_for(att_ty: AttributeType, size: usize) -> Self {
Self {
group_cfgs: vec![GroupConfig::default_for(att_ty, size)],
rans_encoding: true,
mode: EncodingMode::Full,
}
}
pub fn predicted_normals(size: usize) -> Self {
Self {
group_cfgs: vec![GroupConfig::default_for(AttributeType::Normal, size)],
rans_encoding: true,
mode: EncodingMode::ZeroCorrection,
}
}
pub fn set_prediction_scheme(&mut self, ty: prediction_scheme::PredictionSchemeType) {
self.group_cfgs[0].prediction_scheme.ty = ty;
}
pub fn set_prediction_transform(&mut self, ty: prediction_transform::PredictionTransformType) {
self.group_cfgs[0].prediction_transform.ty = ty;
}
pub fn set_quantization(&mut self, quantization: portabilization::Quantization) {
self.group_cfgs[0]
.prediction_transform
.portabilization
.quantization = quantization;
}
}
pub(super) struct AttributeEncoder<'parents, 'encoder, 'writer, 'ds, W> {
cfg: Config,
writer: &'writer mut W,
parents: &'encoder [&'parents Attribute],
ads: AttributeDS<'ds>,
corners_of_edgebreaker: &'encoder [CornerIdx],
}
impl<'parents, 'encoder, 'writer, 'ds, W> AttributeEncoder<'parents, 'encoder, 'writer, 'ds, W>
where
W: ByteWriter,
'parents: 'encoder,
{
pub(super) fn new(
ads: AttributeDS<'ds>,
parents: &'encoder [&'parents Attribute],
corners_of_edgebreaker: &'encoder [CornerIdx],
writer: &'writer mut W,
cfg: Config,
) -> Self {
AttributeEncoder {
cfg,
writer,
parents,
ads,
corners_of_edgebreaker,
}
}
pub(super) fn encode<const WRITE_NOW: bool, const BOOST: bool>(self) -> Result<Attribute, Err> {
self.cfg.group_cfgs[0]
.prediction_scheme
.ty
.write_to(self.writer);
self.cfg.group_cfgs[0]
.prediction_transform
.ty
.write_to(self.writer);
if self.cfg.mode == EncodingMode::ZeroCorrection {
return self.encode_zero_correction_normal();
}
let component_type = self.ads.att_data().get_component_type();
match component_type {
ComponentDataType::F32 => self.unpack_num_components::<WRITE_NOW, BOOST, f32>(),
ComponentDataType::F64 => self.unpack_num_components::<WRITE_NOW, BOOST, f64>(),
ComponentDataType::U8 => self.unpack_num_components::<WRITE_NOW, BOOST, u8>(),
ComponentDataType::U16 => self.unpack_num_components::<WRITE_NOW, BOOST, u16>(),
ComponentDataType::U32 => self.unpack_num_components::<WRITE_NOW, BOOST, u32>(),
ComponentDataType::U64 => self.unpack_num_components::<WRITE_NOW, BOOST, u64>(),
ComponentDataType::I8 => self.unpack_num_components::<WRITE_NOW, BOOST, i8>(),
ComponentDataType::I16 => self.unpack_num_components::<WRITE_NOW, BOOST, i16>(),
ComponentDataType::I32 => self.unpack_num_components::<WRITE_NOW, BOOST, i32>(),
ComponentDataType::I64 => self.unpack_num_components::<WRITE_NOW, BOOST, i64>(),
ComponentDataType::Invalid => Err(Err::UnsupportedDataType),
}
}
fn encode_zero_correction_normal(self) -> Result<Attribute, Err> {
let sequence =
Traverser::new(&self.ads, self.corners_of_edgebreaker.to_vec()).compute_seqeunce();
let num_values = sequence.len();
const N: usize = 2;
let por_cfg = portabilization::Config::default_for(AttributeType::Normal);
let mut port_info_buffer: Vec<u8> = Vec::new();
port_info_buffer.write_u8(por_cfg.quantization.resolve(0.0));
let mut transform_info_buffer: Vec<u8> = Vec::new();
let transform = PredictionTransform::<N>::new(self.cfg.group_cfgs[0].prediction_transform);
let _ = transform.squeeze(&mut transform_info_buffer);
self.writer.write_u8(self.cfg.rans_encoding as u8);
if self.cfg.rans_encoding {
let symbols = vec![0u64; num_values * N];
encode_symbols(symbols, N, SymbolEncodingMethod::DirectCoded, self.writer)?;
} else {
let zero = NdVector::<N, i32>::zero();
for _ in 0..num_values {
zero.write_to(self.writer);
}
}
for byte in transform_info_buffer {
self.writer.write_u8(byte);
}
prediction_scheme::mesh_normal_prediction::encode_flip_metadata(
&vec![false; num_values],
self.writer,
)?;
for byte in port_info_buffer {
self.writer.write_u8(byte);
}
Ok(Attribute::from_without_removing_duplicates::<
NdVector<N, i32>,
N,
>(
self.ads.att_data().get_id(),
Vec::new(),
AttributeType::Normal,
self.ads.att_data().get_domain(),
self.ads.att_data().get_parents().clone(),
))
}
fn unpack_num_components<const WRITE_NOW: bool, const BOOST: bool, T>(
self,
) -> Result<Attribute, Err>
where
T: DataValue + Copy,
NdVector<1, T>: Vector<1>,
NdVector<2, T>: Vector<2>,
NdVector<3, T>: Vector<3>,
NdVector<4, T>: Vector<4>,
{
let num_components = self.ads.att_data().get_num_components();
match num_components {
0 => unreachable!("Vector of dimension 0 is not allowed"),
1 => self.encode_typed::<WRITE_NOW, BOOST, 1, _>(),
2 => self.encode_typed::<WRITE_NOW, BOOST, 2, _>(),
3 => self.encode_typed::<WRITE_NOW, BOOST, 3, _>(),
4 => self.encode_typed::<WRITE_NOW, BOOST, 4, _>(),
_ => Err(Err::UnsupportedNumComponents(num_components)),
}
}
fn encode_typed<const WRITE_NOW: bool, const BOOST: bool, const N: usize, T>(
self,
) -> Result<Attribute, Err>
where
T: DataValue + Copy,
NdVector<N, T>: Vector<N> + Portable,
NdVector<N, i32>: Vector<N, Component = i32>,
NdVector<N, f32>: Vector<N, Component = f32> + Portable,
{
if !BOOST {
if !self.corners_of_edgebreaker.is_empty() {
let sequence = Traverser::new(&self.ads, self.corners_of_edgebreaker.to_vec())
.compute_seqeunce();
self.encode_impl_edgebreaker::<WRITE_NOW, _, NdVector<N, T>, N>(
sequence.into_iter(),
)
} else {
unimplemented!("Sequential connectivity encoding is not implemented yet");
}
} else {
unimplemented!("BOOST is not implemented yet");
}
}
fn encode_impl_edgebreaker<const WRITE_NOW: bool, S, Data, const N: usize>(
mut self,
sequence: S,
) -> Result<Attribute, Err>
where
S: Iterator<Item = CornerIdx> + Clone,
Data: Vector<N> + Portable,
NdVector<N, i32>: Vector<N, Component = i32>,
NdVector<N, f32>: Vector<N, Component = f32> + Portable,
{
let por_cfg = self.cfg.group_cfgs[0].prediction_transform.portabilization;
let mut att = Attribute::new(
Vec::<Data>::new(),
AttributeType::Position,
AttributeDomain::Position,
Vec::new(),
);
std::mem::swap(&mut att, self.ads.att_data_mut());
let mut port_info_buffer = Vec::new();
let portabilization: portabilization::Portabilization<Data, N> =
portabilization::Portabilization::new(att, por_cfg, &mut port_info_buffer);
let port_att = portabilization.portabilize();
match port_att.get_num_components() {
1 => self.encode_portabilized::<S, 1>(sequence, port_att, port_info_buffer),
2 => self.encode_portabilized::<S, 2>(sequence, port_att, port_info_buffer),
3 => self.encode_portabilized::<S, 3>(sequence, port_att, port_info_buffer),
4 => self.encode_portabilized::<S, 4>(sequence, port_att, port_info_buffer),
_ => Err(Err::UnsupportedNumComponents(port_att.get_num_components())),
}
}
fn encode_portabilized<S, const N: usize>(
&mut self,
sequence: S,
port_att: Attribute,
port_info_buffer: Vec<u8>,
) -> Result<Attribute, Err>
where
S: Iterator<Item = CornerIdx>,
NdVector<N, i32>: Vector<N, Component = i32> + Portable,
{
let mut prediction_scheme = prediction_scheme::PredictionScheme::new(
self.cfg.group_cfgs[0].prediction_scheme.ty.clone(),
self.parents,
&self.ads,
);
let mut transform = PredictionTransform::new(self.cfg.group_cfgs[0].prediction_transform);
let mut sequence_record = Vec::new();
for c in sequence {
let val = prediction_scheme.predict(c, &sequence_record, &port_att);
let v = self.ads.vertex_idx(c);
sequence_record.push(v);
let p = self.ads.global_ds().point_idx(c);
transform.map_with_tentative_metadata(port_att.get(p), val);
}
let mut transform_info_buffer = Vec::new();
let output = transform.squeeze(&mut transform_info_buffer);
self.writer.write_u8(self.cfg.rans_encoding as u8);
if self.cfg.rans_encoding {
let symbols = output
.iter()
.flat_map(|v| (0..N).map(|i| *v.get(i) as u64))
.collect::<Vec<_>>();
encode_symbols(symbols, N, SymbolEncodingMethod::DirectCoded, self.writer)?;
} else {
for value in output {
value.write_to(self.writer);
}
}
if prediction_scheme.get_type()
== prediction_scheme::PredictionSchemeType::MeshNormalPrediction
{
for byte in transform_info_buffer {
self.writer.write_u8(byte);
}
prediction_scheme.encode_prediction_metadtata(self.writer)?;
} else if prediction_scheme.get_type()
== prediction_scheme::PredictionSchemeType::MeshPredictionForTextureCoordinates
{
prediction_scheme.encode_prediction_metadtata(self.writer)?;
for byte in transform_info_buffer {
self.writer.write_u8(byte);
}
} else {
assert!({
let mut buffer = Vec::new();
prediction_scheme.encode_prediction_metadtata(&mut buffer)?;
buffer.is_empty()
});
for byte in transform_info_buffer {
self.writer.write_u8(byte);
}
}
for byte in port_info_buffer {
self.writer.write_u8(byte);
}
Ok(port_att)
}
}
use super::prediction_transform::{self, PredictionTransform};
use crate::encode::attribute::portabilization;
use crate::encode::attribute::prediction_transform::PredictionTransformImpl;
use draco_oxide_core::codec::attribute::prediction_scheme;
use draco_oxide_core::types::Vector;