Skip to main content

draco_oxide/encode/connectivity/
edgebreaker.rs

1use std::{cmp, fmt};
2
3use crate::encode::entropy::rans::{self, RabsCoder};
4use crate::encode::entropy::symbol_coding::encode_symbols;
5use draco_oxide_core::attribute::AttributeType;
6use draco_oxide_core::bit_coder::{BitWriter, ByteWriter};
7use draco_oxide_core::buffer::LsbFirst;
8use draco_oxide_core::codec::connectivity::edgebreaker::symbol_encoder::{
9    CrLight, Symbol, SymbolEncoder,
10};
11use draco_oxide_core::debug_write;
12use draco_oxide_core::mesh::ds::CornerTable;
13use draco_oxide_core::mesh::ds::GenericCornerTable;
14use draco_oxide_core::mesh::ds::{AttributeDS, DS};
15
16use draco_oxide_core::types::{
17    ConfigType, CornerIdx, FaceIdx, VecCornerIdx, VecFaceIdx, VecVertexIdx, VertexIdx,
18};
19
20use draco_oxide_core::codec::connectivity::edgebreaker::{
21    self, EdgebreakerKind, Orientation, TopologySplit, MAX_VALENCE, MIN_VALENCE,
22};
23use draco_oxide_core::codec::entropy::SymbolEncodingMethod;
24use draco_oxide_core::utils::bit_coder::leb128_write;
25use std::vec;
26
27use crate::encode::connectivity::ConnectivityEncoder;
28
29pub(crate) struct Edgebreaker<'ads, 'faces, T>
30where
31    T: Traversal,
32{
33    /// The 'i'th entry of 'visited_vertices' is true if the Edgebreaker has
34    /// already visited the 'i' th vertex.
35    visited_vertices: VecVertexIdx<bool>,
36
37    /// The 'i'th entry of 'visited_edges' is true if the Edgebreaker has
38    /// already visited the 'i' th face.
39    visited_faces: VecFaceIdx<bool>,
40
41    /// The visited holes. i th entry of this array records whether the i th hole is visited or not.
42    visited_holes: Vec<bool>,
43
44    // A map from vertices to the hole id if the vertex is on a hole or void if the vertex is not on a hole.
45    vertex_hole_id: VecVertexIdx<Option<usize>>,
46
47    corner_traversal_stack: Vec<CornerIdx>,
48
49    last_encoded_symbol_idx: usize,
50
51    processed_connectivity_corners: Vec<CornerIdx>,
52
53    /// Per-face index of the S symbol that split it, or `u32::MAX` if the face
54    /// carries no split. Symbol indices fit `u32` because every symbol consumes
55    /// a face and face indices are `u32`-backed.
56    face_to_split_symbol_map: VecFaceIdx<u32>,
57
58    num_split_symbols: usize,
59
60    vertex_traversal_length: Vec<usize>,
61
62    init_face_connectivity_corners: Vec<CornerIdx>,
63
64    traversal: T,
65
66    /// Records the topology splits detected during the edgebreaker encoding.
67    topology_splits: Vec<TopologySplit>,
68
69    adss: &'ads [AttributeDS<'faces>],
70
71    pos_corner_table: &'ads CornerTable,
72
73    posds: &'ads AttributeDS<'faces>,
74
75    gds: &'ads DS,
76
77    /// configurations for the encoder
78    config: Config,
79}
80
81/// Configuration for edgebreaker connectivity encoding. Exported as
82/// `EdgebreakerConfig`.
83#[derive(Clone, fmt::Debug, cmp::PartialEq)]
84pub struct Config {
85    /// The edgebreaker variant used to traverse the mesh and code the
86    /// topology symbols.
87    pub traversal: EdgebreakerKind,
88}
89
90impl ConfigType for Config {
91    fn default() -> Self {
92        Self {
93            traversal: EdgebreakerKind::Valence,
94        }
95    }
96}
97
98#[derive(Debug, PartialEq)]
99#[remain::sorted]
100#[derive(thiserror::Error)]
101pub enum Err {
102    #[error("Edgebreaker error: {0}")]
103    EdgebreakerError(#[from] edgebreaker::Err),
104    #[error("The input mesh has an empty AttributeDS array.")]
105    EmptyAttributeDSArray,
106    #[error("Entropy encoding error: {0}")]
107    EntropyEncodingError(#[from] crate::encode::entropy::symbol_coding::Err),
108    #[error("Too many handles.")]
109    HandleSizeTooLarge,
110    #[error("Too many holes.")]
111    HoleSizeTooLarge,
112    #[error("The input mesh is non-orientable.")]
113    NonOrientable,
114    #[error("Rabs coder error: {0}")]
115    RabsCoderError(#[from] rans::Err),
116    #[error("The input mesh has too many connected components: {0}")]
117    TooManyConnectedComponents(usize),
118}
119
120impl<'ads, 'faces, T> Edgebreaker<'ads, 'faces, T>
121where
122    T: Traversal,
123{
124    // Build the object with empty arrays.
125    pub fn new(
126        config: Config,
127        adss: &'ads [AttributeDS<'faces>],
128        make_traversal: impl FnOnce(&'ads AttributeDS<'faces>) -> T,
129    ) -> Result<Self, Err> {
130        let pos_ads = adss
131            .iter()
132            .find(|ads| ads.att_data().get_attribute_type() == AttributeType::Position)
133            .ok_or(Err::EmptyAttributeDSArray)?;
134        let gds = pos_ads.global_ds();
135        let traversal = make_traversal(pos_ads);
136
137        let out = Self {
138            visited_vertices: VecVertexIdx::from(vec![false; pos_ads.num_vertices()]),
139            visited_faces: VecFaceIdx::from(vec![false; gds.num_faces()]),
140            visited_holes: Vec::new(),
141            pos_corner_table: pos_ads.corner_table().pos_corner_table(),
142            posds: pos_ads,
143            vertex_hole_id: VecVertexIdx::new(),
144            corner_traversal_stack: Vec::new(),
145            last_encoded_symbol_idx: usize::MAX,
146            processed_connectivity_corners: Vec::new(),
147            face_to_split_symbol_map: VecFaceIdx::from(vec![u32::MAX; gds.num_faces()]),
148            num_split_symbols: 0,
149            vertex_traversal_length: Vec::new(),
150            init_face_connectivity_corners: Vec::new(),
151            traversal,
152            topology_splits: Vec::new(),
153            gds,
154            adss,
155            config,
156        };
157        Ok(out)
158    }
159
160    fn compute_boundaries(&mut self) -> Result<(), Err> {
161        self.vertex_hole_id = VecVertexIdx::from(vec![None; self.posds.num_vertices()]);
162        for c in 0..self.gds.num_corners() {
163            let c = CornerIdx::from(c);
164            if self.pos_corner_table.opposite(c).is_none() {
165                // 'c' is on a boundary.
166                let mut v = self.posds.vertex_idx(c.next());
167                if self.vertex_hole_id[v].is_some() {
168                    // The hole is already processed.
169                    continue;
170                }
171                // Now we have found a new boundary containing the vertex 'v'.
172                let boundary_idx = self.visited_holes.len();
173                self.visited_holes.push(false);
174
175                let mut c = c;
176                while self.vertex_hole_id[v].is_none() {
177                    self.vertex_hole_id[v] = Some(boundary_idx);
178                    c = c.next();
179
180                    while self.pos_corner_table.opposite(c).is_some() {
181                        c = c.next();
182                    }
183                    // Id of the next vertex in the vertex on the hole.
184                    v = self.posds.vertex_idx(c.next());
185                }
186            }
187        }
188        Ok(())
189    }
190
191    fn process_boundary(&mut self, start_corner: CornerIdx, encode_first_vertex: bool) -> usize {
192        let mut corner = start_corner.previous();
193        while let Some(opp) = self.pos_corner_table.opposite(corner) {
194            corner = opp.next();
195        } // 'corner' now faces the hole
196
197        let start_v = self.posds.vertex_idx(start_corner);
198
199        let mut num_encoded_hole_verts = 0;
200        if encode_first_vertex {
201            self.visited_vertices[start_v] = true;
202            num_encoded_hole_verts += 1;
203        }
204
205        self.visited_holes[self.vertex_hole_id[start_v].unwrap()] = true; // it is safe to unwrap here as start_v is on a hole.
206        let mut curr_v = self.posds.vertex_idx(corner.previous());
207        while curr_v != start_v {
208            self.visited_vertices[curr_v] = true;
209            num_encoded_hole_verts += 1;
210            corner = corner.next();
211            while let Some(opp) = self.pos_corner_table.opposite(corner) {
212                corner = opp.next();
213            }
214            curr_v = self.posds.vertex_idx(corner.previous());
215        }
216        num_encoded_hole_verts
217    }
218
219    /// A function implementing the Edgebreaker algorithm for a connected component that contains `c`.
220    fn edgebreaker_from(&mut self, mut c: CornerIdx) -> Result<(), Err> {
221        self.corner_traversal_stack.clear();
222        self.corner_traversal_stack.push(c);
223        let num_faces = self.gds.num_faces();
224        while let Some(&start) = self.corner_traversal_stack.last() {
225            c = start;
226            // Make sure the face hasn't been visited yet.
227            if self.visited_faces[c.face_idx()] {
228                self.corner_traversal_stack.pop();
229                continue;
230            }
231
232            let mut num_visited_faces = 0;
233            while num_visited_faces < num_faces {
234                num_visited_faces += 1;
235                self.last_encoded_symbol_idx = self.last_encoded_symbol_idx.wrapping_add(1); // since the initial value of 'last_encoded_symbol_idx' is usize::MAX, we do wrapping-add.
236
237                let face_idx = c.face_idx();
238                self.visited_faces[face_idx] = true;
239                self.processed_connectivity_corners.push(c);
240                self.traversal.new_corner_reached(c);
241                let v = self.posds.vertex_idx(c);
242                if !self.visited_vertices[v] {
243                    self.visited_vertices[v] = true;
244                    if self.vertex_hole_id[v].is_none() {
245                        self.traversal.record_symbol(
246                            Symbol::C,
247                            &self.visited_faces,
248                            self.pos_corner_table,
249                        );
250                        c = self.posds.corner_table().get_right_corner(c).unwrap(); // unwrap is safe here; we checked that the right edge is not on a boundary, and this implies that the right face exists.
251                        continue;
252                    }
253                }
254                let maybe_right_c = self.posds.corner_table().get_right_corner(c);
255                let maybe_left_c = self.posds.corner_table().get_left_corner(c);
256                let maybe_right_face = maybe_right_c.map(|c| c.face_idx());
257                let maybe_left_face = maybe_left_c.map(|c| c.face_idx());
258                if self.is_right_face_visited(c) {
259                    if let Some(right_face) = maybe_right_face {
260                        self.check_and_store_topology_split_event(
261                            self.last_encoded_symbol_idx,
262                            Orientation::Right,
263                            right_face,
264                        );
265                    }
266                    if self.is_left_face_visited(c) {
267                        // 'E' symbol
268                        if let Some(left_face) = maybe_left_face {
269                            self.check_and_store_topology_split_event(
270                                self.last_encoded_symbol_idx,
271                                Orientation::Left,
272                                left_face,
273                            );
274                        }
275                        self.traversal.record_symbol(
276                            Symbol::E,
277                            &self.visited_faces,
278                            self.pos_corner_table,
279                        );
280                        self.corner_traversal_stack.pop();
281                        // End of a branch of the traversal.
282                        break;
283                    } else {
284                        // 'R' symbol
285                        self.traversal.record_symbol(
286                            Symbol::R,
287                            &self.visited_faces,
288                            self.pos_corner_table,
289                        );
290                        c = maybe_left_c.unwrap(); // unwrap is safe here; we checked that the left face is not visited, which implies that the left face exist.
291                    }
292                } else if self.is_left_face_visited(c) {
293                    // 'L' symbol
294                    if let Some(left_face) = maybe_left_face {
295                        self.check_and_store_topology_split_event(
296                            self.last_encoded_symbol_idx,
297                            Orientation::Left,
298                            left_face,
299                        );
300                    }
301                    self.traversal.record_symbol(
302                        Symbol::L,
303                        &self.visited_faces,
304                        self.pos_corner_table,
305                    );
306                    c = maybe_right_c.unwrap(); // unwrap is safe here; we checked that the right face is not visited, which implies that the right face exist.
307                } else {
308                    self.traversal.record_symbol(
309                        Symbol::S,
310                        &self.visited_faces,
311                        self.pos_corner_table,
312                    );
313                    self.num_split_symbols += 1;
314                    if let Some(hole_idx) = self.vertex_hole_id[v] {
315                        if !self.visited_holes[hole_idx] {
316                            self.process_boundary(c, false);
317                        }
318                    }
319                    self.face_to_split_symbol_map[face_idx] = self.last_encoded_symbol_idx as u32;
320                    *self.corner_traversal_stack.last_mut().unwrap() = maybe_left_c.unwrap();
321                    self.corner_traversal_stack.push(maybe_right_c.unwrap());
322                    break;
323                }
324            }
325        }
326        Ok(())
327    }
328
329    /// Checks whether the right face of the corner 'c' is visited.
330    /// If the corner is on a boundary and if the right face does not exist,
331    /// then it returns true by convention.
332    fn is_right_face_visited(&self, c: CornerIdx) -> bool {
333        if let Some(c_r) = self.posds.corner_table().get_right_corner(c) {
334            self.visited_faces[c_r.face_idx()]
335        } else {
336            true
337        }
338    }
339
340    /// Checks whether the left face of the corner 'c' is visited.
341    /// If the corner is on a boundary and if the left face does not exist,
342    /// then it returns true by convention.
343    fn is_left_face_visited(&self, c: CornerIdx) -> bool {
344        if let Some(c_l) = self.pos_corner_table.get_left_corner(c) {
345            self.visited_faces[c_l.face_idx()]
346        } else {
347            true
348        }
349    }
350
351    fn encode_topology_splits<W>(&mut self, writer: &mut W) -> Result<(), Err>
352    where
353        W: ByteWriter,
354    {
355        let mut last_idx = 0;
356        // write the number of topology splits.
357        leb128_write(self.topology_splits.len() as u64, writer);
358        for split in self.topology_splits.iter() {
359            leb128_write((split.merging_symbol_idx - last_idx) as u64, writer);
360            leb128_write(
361                (split.merging_symbol_idx - split.split_symbol_idx) as u64,
362                writer,
363            );
364            last_idx = split.merging_symbol_idx;
365        }
366        let mut bit_coder: BitWriter<'_, W, LsbFirst> = BitWriter::spown_from(writer);
367        for split in self.topology_splits.iter() {
368            let orientation = match split.merging_edge_orientation {
369                Orientation::Left => (1, 0),
370                Orientation::Right => (1, 1),
371            };
372            bit_coder.write_bits(orientation);
373        }
374        Ok(())
375    }
376
377    /// Begins the Edgebreaker iteration from the given face.
378    /// The first boolean indicates whether the face is interior (i.e. the face does not touch a boundary) or not.
379    /// The second 'usize' element is a corner chosen as follows:
380    /// It chooses the first corner of the face as the starting point is such a way that corner faces the the boundary
381    /// if the face is on the boundary.
382    /// If the face is not on the boundary, then it returns the input corner.
383    fn begin_from(&mut self, face_idx: FaceIdx) -> (bool, CornerIdx) {
384        let mut corner_index = CornerIdx::from(3 * usize::from(face_idx));
385        for _ in 0..3 {
386            if self.pos_corner_table.opposite(corner_index).is_none() {
387                // corner faces a boundary
388                return (false, corner_index);
389            }
390            if self.vertex_hole_id[self.posds.vertex_idx(corner_index)].is_some() {
391                // The corner is on a boundary.
392                while let Some(right_corner) = self.posds.corner_table().swing_right(corner_index) {
393                    corner_index = right_corner;
394                }
395                let start_corner = corner_index.previous();
396                return (false, start_corner);
397            }
398            corner_index = corner_index.next();
399        }
400        (true, corner_index)
401    }
402
403    fn check_and_store_topology_split_event(
404        &mut self,
405        merging_symbol_idx: usize,
406        merging_edge_orientation: Orientation,
407        split_face_idx: FaceIdx,
408    ) {
409        let split_symbol_idx = self.face_to_split_symbol_map[split_face_idx];
410        if split_symbol_idx == u32::MAX {
411            return;
412        }
413        let split = TopologySplit {
414            merging_symbol_idx,
415            split_symbol_idx: split_symbol_idx as usize,
416            merging_edge_orientation,
417        };
418
419        self.topology_splits.push(split);
420    }
421}
422
423impl<'ads, 'faces, T> ConnectivityEncoder for Edgebreaker<'ads, 'faces, T>
424where
425    T: Traversal,
426{
427    type Config = Config;
428    type Err = Err;
429    /// The main encoding paradigm for Edgebreaker.
430    ///
431    /// Returns the corners of the edgebreaker traversal (`corners_of_edgebreaker`), i.e. the
432    /// last-encoded corner of each connected component in encoded order. This ordering seeds the
433    /// per-attribute sequencing (`Traverser`) during attribute encoding, so it must be surfaced
434    /// back to the caller.
435    fn encode_connectivity<W>(mut self, writer: &mut W) -> Result<Vec<CornerIdx>, Self::Err>
436    where
437        W: ByteWriter,
438    {
439        debug_write!("Init Decoder", writer);
440        // encode the traversal decoder type
441        self.config.traversal.write_to(writer);
442        debug_write!("Init Decoder Done", writer);
443
444        self.compute_boundaries()?;
445
446        leb128_write(self.posds.num_vertices() as u64, writer);
447        leb128_write(self.gds.num_faces() as u64, writer);
448
449        writer.write_u8((self.adss.len() - 1) as u8);
450
451        // Run Edgebreaker once for each connected component.
452        for c in 0..self.gds.num_corners() {
453            let c = CornerIdx::from(c);
454            let face_idx = c.face_idx();
455            if self.visited_faces[face_idx] {
456                // if the face is already visited, then skip it.
457                continue;
458            }
459
460            let (is_start_face_interior, start_corner) = self.begin_from(face_idx);
461
462            self.traversal
463                .record_start_face_config(is_start_face_interior);
464
465            if is_start_face_interior {
466                let corner_index = start_corner;
467                let v = self.posds.vertex_idx(corner_index);
468                let n = self.posds.vertex_idx(corner_index.next());
469                let p = self.posds.vertex_idx(corner_index.previous());
470                self.visited_vertices[v] = true;
471                self.visited_vertices[n] = true;
472                self.visited_vertices[p] = true;
473
474                self.vertex_traversal_length.push(1);
475
476                self.visited_faces[face_idx] = true;
477
478                self.init_face_connectivity_corners
479                    .push(corner_index.next());
480                let corner_opp = self.pos_corner_table.opposite(corner_index.next()).unwrap(); // the face is interior, so every edge has an opposite corner
481                self.edgebreaker_from(corner_opp)?;
482            } else {
483                // if the face is on the boundary, then we start from the boundary.
484                self.process_boundary(start_corner.next(), true);
485                self.edgebreaker_from(start_corner)?;
486            }
487        }
488
489        // write the number of symbols.
490        leb128_write(self.traversal.num_symbols() as u64, writer);
491
492        // write the number of encoded split symbols.
493        leb128_write(self.num_split_symbols as u64, writer);
494
495        self.encode_topology_splits(writer)?;
496        // encode the edgebreaker symbols.
497        self.traversal
498            .encode(writer, self.adss, self.pos_corner_table, self.gds)?;
499
500        self.init_face_connectivity_corners.reverse();
501        self.init_face_connectivity_corners
502            .append(&mut self.processed_connectivity_corners);
503
504        Ok(self.init_face_connectivity_corners)
505    }
506}
507
508pub(crate) trait Traversal {
509    fn record_symbol(
510        &mut self,
511        symbol: Symbol,
512        visited_faces: &VecFaceIdx<bool>,
513        corner_table: &CornerTable,
514    );
515    fn record_start_face_config(&mut self, interior_cfg: bool);
516    fn new_corner_reached(&mut self, corner: CornerIdx);
517    fn num_symbols(&self) -> usize;
518    fn encode<W>(
519        self,
520        writer: &mut W,
521        att_data: &[AttributeDS<'_>],
522        corner_table: &CornerTable,
523        gds: &DS,
524    ) -> Result<(), Err>
525    where
526        W: ByteWriter;
527}
528
529pub(crate) struct DefaultTraversal {
530    symbols: Vec<Symbol>,
531    interior_cfg: Vec<bool>,
532    processed_connectivity_corners: Vec<CornerIdx>,
533}
534
535impl DefaultTraversal {
536    pub(crate) fn new() -> Self {
537        Self {
538            symbols: Vec::new(),
539            interior_cfg: Vec::new(),
540            processed_connectivity_corners: Vec::new(),
541        }
542    }
543}
544
545impl Traversal for DefaultTraversal {
546    fn record_symbol(
547        &mut self,
548        symbol: Symbol,
549        _visited_faces: &VecFaceIdx<bool>,
550        _corner_table: &CornerTable,
551    ) {
552        self.symbols.push(symbol);
553    }
554
555    fn new_corner_reached(&mut self, corner: CornerIdx) {
556        self.processed_connectivity_corners.push(corner);
557    }
558
559    fn record_start_face_config(&mut self, interior_cfg: bool) {
560        self.interior_cfg.push(interior_cfg);
561    }
562
563    fn num_symbols(&self) -> usize {
564        self.symbols.len()
565    }
566
567    fn encode<W>(
568        self,
569        final_writer: &mut W,
570        att_data: &[AttributeDS<'_>],
571        pos_corner_table: &CornerTable,
572        gds: &DS,
573    ) -> Result<(), Err>
574    where
575        W: ByteWriter,
576    {
577        let mut writer = Vec::new();
578        {
579            let mut writer: BitWriter<'_, Vec<u8>, LsbFirst> = BitWriter::spown_from(&mut writer);
580            for s in self.symbols.into_iter().rev() {
581                writer.write_bits(CrLight::encode_symbol(s));
582            }
583        }
584
585        // encode the size
586        leb128_write(writer.len() as u64, final_writer);
587        // write the encoded symbols.
588        for byte in writer {
589            final_writer.write_u8(byte);
590        }
591
592        encode_start_faces(&self.interior_cfg, final_writer)?;
593        encode_attribute_seams(
594            self.processed_connectivity_corners,
595            att_data,
596            pos_corner_table,
597            gds,
598            final_writer,
599        )
600    }
601}
602
603/// Encodes the start-face interior flags as a rabs sub-stream
604/// (`[prob_zero | leb128 len | bytes]`), bits written in reverse.
605fn encode_start_faces<W>(interior_cfg: &[bool], final_writer: &mut W) -> Result<(), Err>
606where
607    W: ByteWriter,
608{
609    let freq_count_0 = interior_cfg.iter().filter(|&&cfg| !cfg).count();
610    // the probability of zero in [0,1] is scaled to [0,256], and clamped to [1,255] as the rans does not accept the zero probability.
611    let zero_prob = (((freq_count_0 as f32 / interior_cfg.len() as f32) * 256.0 + 0.5) as u16)
612        .clamp(1, 255) as u8;
613    final_writer.write_u8(zero_prob);
614    let mut writer: RabsCoder = RabsCoder::new(zero_prob as usize, None);
615    for &cfg in interior_cfg.iter().rev() {
616        writer.write(if cfg { 1 } else { 0 })?;
617    }
618    let buffer = writer.flush()?;
619    leb128_write(buffer.len() as u64, final_writer);
620    for byte in buffer {
621        final_writer.write_u8(byte);
622    }
623    Ok(())
624}
625
626/// Encodes the per-attribute seam bits, one rabs sub-stream per non-position
627/// attribute, in the face order the decoder reconstructs (the processed corners
628/// reversed).
629///
630/// A single walk packs every stream's flag for an edge into one byte and counts
631/// each stream's zeros, so the sub-stream probabilities need no second pass; the
632/// coders then all run over one reverse pass of those bytes. This mirrors the
633/// decoder, which unpacks the same byte layout with all its rabs decoders live
634/// at once.
635///
636/// Seams are encoded per non-position attribute only: the position attribute
637/// defines the base connectivity and carries no seams. This must match the
638/// `adss.len() - 1` attribute count written in `encode_connectivity`; including
639/// the position attribute here would emit one extra seam stream and desync the
640/// decoder.
641fn encode_attribute_seams<W>(
642    processed_connectivity_corners: Vec<CornerIdx>,
643    att_data: &[AttributeDS<'_>],
644    pos_corner_table: &CornerTable,
645    gds: &DS,
646    final_writer: &mut W,
647) -> Result<(), Err>
648where
649    W: ByteWriter,
650{
651    let seam_atts = att_data
652        .iter()
653        .filter(|ads| ads.att_data().get_attribute_type() != AttributeType::Position)
654        .collect::<Vec<_>>();
655    // The flags of up to eight streams pack into one byte, matching how the
656    // decoder unpacks them; a mesh carrying more seam attributes than that takes
657    // one walk per group of eight.
658    for group in seam_atts.chunks(8) {
659        let mut visited_faces = vec![false; gds.num_faces()];
660        let mut packed: Vec<u8> = Vec::with_capacity(gds.num_corners() >> 1);
661        let mut zeros = vec![0usize; group.len()];
662        for c in processed_connectivity_corners.iter().rev().copied() {
663            let corners = [c, c.next(), c.previous()];
664            let f_idx = c.face_idx();
665            visited_faces[usize::from(f_idx)] = true;
666            for corner in &corners {
667                if let Some(opp_corner) = pos_corner_table.opposite(*corner) {
668                    let opp_face = opp_corner.face_idx();
669                    if visited_faces[usize::from(opp_face)] {
670                        // if the opposite face is already visited, then we do not need to record the attribute seam.
671                        continue;
672                    }
673                } else {
674                    // if the edge opposite to the corner is on a boundary, then we do not need to record the attribute seam.
675                    continue;
676                }
677
678                let mut bits = 0u8;
679                for (j, ads) in group.iter().enumerate() {
680                    if ads.corner_table().opposite(*corner).is_none() {
681                        bits |= 1 << j;
682                    } else {
683                        zeros[j] += 1;
684                    }
685                }
686                packed.push(bits);
687            }
688        }
689        write_seam_streams(&packed, &zeros, final_writer)?;
690    }
691
692    Ok(())
693}
694
695/// The zero probability of a seam sub-stream, scaled from `[0,1]` to `[0,256]`
696/// and clamped to `[1,255]` as rans rejects a zero probability.
697fn seam_prob_zero(zeros: usize, total: usize) -> u8 {
698    (((zeros as f32 / total as f32) * 256.0 + 0.5) as u16).clamp(1, 255) as u8
699}
700
701/// Emits one rabs sub-stream per packed stream, in stream order, each as
702/// `[prob_zero | leb128 len | bytes]`. Every stream's coder runs concurrently
703/// over a single reverse pass of `packed`, mirroring the decoder's concurrent
704/// seam decode; bits go out reversed because rabs decodes in the order opposite
705/// to encoding.
706fn write_seam_streams<W>(packed: &[u8], zeros: &[usize], final_writer: &mut W) -> Result<(), Err>
707where
708    W: ByteWriter,
709{
710    let probs: Vec<u8> = zeros
711        .iter()
712        .map(|&z| seam_prob_zero(z, packed.len()))
713        .collect();
714    let buffers = match probs.len() {
715        1 => encode_seams_fixed::<1>(packed, &probs),
716        2 => encode_seams_fixed::<2>(packed, &probs),
717        _ => encode_seams_general(packed, &probs),
718    }?;
719    for (prob, buffer) in probs.iter().zip(buffers) {
720        final_writer.write_u8(*prob);
721        leb128_write(buffer.len() as u64, final_writer);
722        for byte in buffer {
723            final_writer.write_u8(byte);
724        }
725    }
726    Ok(())
727}
728
729/// The concurrent seam encode monomorphized on the stream count, so the coder
730/// states stay in locals.
731fn encode_seams_fixed<const N: usize>(packed: &[u8], probs: &[u8]) -> Result<Vec<Vec<u8>>, Err> {
732    let mut coders: [RabsCoder; N] =
733        std::array::from_fn(|j| RabsCoder::new(probs[j] as usize, None));
734    for &bits in packed.iter().rev() {
735        for (j, coder) in coders.iter_mut().enumerate() {
736            coder.write((bits >> j) & 1)?;
737        }
738    }
739    let mut buffers = Vec::with_capacity(coders.len());
740    for coder in coders {
741        buffers.push(coder.flush()?);
742    }
743    Ok(buffers)
744}
745
746/// Fallback encode for stream counts without a monomorphization.
747fn encode_seams_general(packed: &[u8], probs: &[u8]) -> Result<Vec<Vec<u8>>, Err> {
748    let mut coders: Vec<RabsCoder> = probs
749        .iter()
750        .map(|&prob| RabsCoder::new(prob as usize, None))
751        .collect();
752    for &bits in packed.iter().rev() {
753        for (j, coder) in coders.iter_mut().enumerate() {
754            coder.write((bits >> j) & 1)?;
755        }
756    }
757    let mut buffers = Vec::with_capacity(coders.len());
758    for coder in coders {
759        buffers.push(coder.flush()?);
760    }
761    Ok(buffers)
762}
763
764pub(crate) struct ValenceTraversal {
765    /// Valence of the not-yet-encoded part of the mesh per vertex. Signed to
766    /// tolerate transient negative values on malformed inputs, as in Google's
767    /// reference implementation.
768    vertex_valences: VecVertexIdx<isize>,
769    /// Per-corner vertex, diverging from the position DS as S symbols split
770    /// vertices.
771    corner_to_vertex_map: VecCornerIdx<VertexIdx>,
772    context_symbols: Vec<Vec<Symbol>>,
773    last_corner: CornerIdx,
774    prev_symbol: Option<Symbol>,
775    interior_cfg: Vec<bool>,
776    num_symbols: usize,
777    processed_connectivity_corners: Vec<CornerIdx>,
778}
779impl ValenceTraversal {
780    #[inline]
781    fn vertex_idx(&self, corner: CornerIdx) -> VertexIdx {
782        self.corner_to_vertex_map[corner]
783    }
784
785    pub(crate) fn new(pos_ds: &AttributeDS) -> Self {
786        let mut vertex_valences: VecVertexIdx<isize> =
787            Vec::with_capacity(pos_ds.num_vertices()).into();
788        for i in 0..pos_ds.num_vertices() {
789            let v = VertexIdx::from(i);
790            vertex_valences.push(pos_ds.vertex_valence(v) as isize);
791        }
792
793        let num_corners = pos_ds.global_ds().num_corners();
794        let mut corner_to_vertex_map: VecCornerIdx<VertexIdx> =
795            Vec::with_capacity(num_corners).into();
796        for c in 0..num_corners {
797            corner_to_vertex_map.push(pos_ds.vertex_idx(CornerIdx::from(c)));
798        }
799
800        let num_unique_valences = MAX_VALENCE - MIN_VALENCE + 1;
801
802        let context_symbols = vec![Vec::new(); num_unique_valences];
803        Self {
804            vertex_valences,
805            corner_to_vertex_map,
806            context_symbols,
807            last_corner: CornerIdx::INVALID, // This will be set to a valid corner index in `new_corner_reached` before the first call to record symbol.
808            prev_symbol: None,
809            interior_cfg: Vec::new(),
810            num_symbols: 0,
811            processed_connectivity_corners: Vec::new(),
812        }
813    }
814}
815
816impl Traversal for ValenceTraversal {
817    fn record_symbol(
818        &mut self,
819        symbol: Symbol,
820        visited_faces: &VecFaceIdx<bool>,
821        corner_table: &CornerTable,
822    ) {
823        self.num_symbols += 1;
824
825        let next = self.last_corner.next();
826        let prev = self.last_corner.previous();
827
828        let v_last = self.vertex_idx(self.last_corner);
829        let v_next = self.vertex_idx(next);
830        let v_prev = self.vertex_idx(prev);
831
832        let active_valence = self.vertex_valences[v_next];
833        match symbol {
834            Symbol::C | Symbol::S => {
835                self.vertex_valences[v_next] -= 1;
836                self.vertex_valences[v_prev] -= 1;
837            }
838            Symbol::R => {
839                // Update valences.
840                self.vertex_valences[v_last] -= 1;
841                self.vertex_valences[v_next] -= 1;
842                self.vertex_valences[v_prev] -= 2;
843            }
844            Symbol::L => {
845                self.vertex_valences[v_last] -= 1;
846                self.vertex_valences[v_next] -= 2;
847                self.vertex_valences[v_prev] -= 1;
848            }
849            Symbol::E => {
850                self.vertex_valences[v_last] -= 2;
851                self.vertex_valences[v_next] -= 2;
852                self.vertex_valences[v_prev] -= 2;
853            }
854        }
855        if symbol == Symbol::S {
856            // The decoder merges the split vertex only when it processes the S
857            // symbol (it decodes in reverse), so the vertex is split here: the
858            // left side keeps the vertex with the valence of the still
859            // unencoded left fan, and the corners of the right fan are
860            // remapped to a fresh vertex carrying the right-fan valence.
861            let mut num_left_faces = 0;
862            let mut maybe_act_c = corner_table.opposite(prev);
863            while let Some(act_c) = maybe_act_c {
864                if visited_faces[act_c.face_idx()] {
865                    break;
866                }
867                num_left_faces += 1;
868                maybe_act_c = corner_table.opposite(act_c.next());
869            }
870            self.vertex_valences[v_last] = num_left_faces + 1;
871
872            let new_vertex = self.vertex_valences.len();
873            let mut num_right_faces = 0;
874
875            maybe_act_c = corner_table.opposite(next);
876            while let Some(act_c) = maybe_act_c {
877                if visited_faces[act_c.face_idx()] {
878                    break;
879                }
880                num_right_faces += 1;
881                self.corner_to_vertex_map[act_c.next()] = new_vertex.into();
882                maybe_act_c = corner_table.opposite(act_c.previous());
883            }
884            self.vertex_valences.push(num_right_faces + 1);
885        }
886
887        if let Some(prev_symbol) = self.prev_symbol {
888            let clamped_valence = active_valence.clamp(MIN_VALENCE as isize, MAX_VALENCE as isize);
889
890            let context = (clamped_valence - MIN_VALENCE as isize) as usize;
891            self.context_symbols[context].push(prev_symbol);
892        }
893
894        self.prev_symbol = Some(symbol);
895    }
896
897    fn record_start_face_config(&mut self, interior_cfg: bool) {
898        self.interior_cfg.push(interior_cfg);
899    }
900
901    fn new_corner_reached(&mut self, c: CornerIdx) {
902        self.last_corner = c;
903        self.processed_connectivity_corners.push(c);
904    }
905
906    fn num_symbols(&self) -> usize {
907        self.num_symbols
908    }
909
910    fn encode<W>(
911        self,
912        writer: &mut W,
913        att_data: &[AttributeDS<'_>],
914        pos_corner_table: &CornerTable,
915        gds: &DS,
916    ) -> Result<(), Err>
917    where
918        W: ByteWriter,
919    {
920        encode_start_faces(&self.interior_cfg, writer)?;
921        encode_attribute_seams(
922            self.processed_connectivity_corners,
923            att_data,
924            pos_corner_table,
925            gds,
926            writer,
927        )?;
928
929        // Store the contexts.
930        for context in self.context_symbols {
931            leb128_write(context.len() as u64, writer);
932            if context.is_empty() {
933                continue;
934            }
935            let context = context
936                .iter()
937                .map(|&s| s.get_id() as u64)
938                .collect::<Vec<_>>();
939
940            encode_symbols(context, 1, SymbolEncodingMethod::DirectCoded, writer)?;
941        }
942
943        Ok(())
944    }
945}