draco_core/encoder_options.rs
1use std::collections::HashMap;
2
3/// Integer option bag used to configure Draco encoding.
4///
5/// Options mirror the C++ Draco encoder style: global options apply to the
6/// whole geometry, while attribute options override a value for one attribute
7/// id and fall back to the global value when unset.
8///
9/// Common keys include `quantization_bits` (per-attribute precision),
10/// `encoding_speed`/`decoding_speed`, `encoding_method`, and
11/// `prediction_scheme`. Keys without an explicit setter are read and written
12/// with [`get_global_int`](EncoderOptions::get_global_int) /
13/// [`set_global_int`](EncoderOptions::set_global_int).
14///
15/// # Examples
16///
17/// ```
18/// use draco_core::EncoderOptions;
19///
20/// let mut options = EncoderOptions::new();
21/// options.set_global_int("quantization_bits", 14); // default for all attributes
22/// options.set_attribute_int(0, "quantization_bits", 10); // override attribute 0
23///
24/// assert_eq!(options.get_attribute_int(0, "quantization_bits", 0), 10);
25/// // Attribute 1 has no override, so it falls back to the global value.
26/// assert_eq!(options.get_attribute_int(1, "quantization_bits", 0), 14);
27/// ```
28#[derive(Debug, Clone, Default)]
29pub struct EncoderOptions {
30 global_options: HashMap<String, i32>,
31 attribute_options: HashMap<i32, HashMap<String, i32>>,
32}
33
34impl EncoderOptions {
35 /// Creates options with Draco-compatible defaults.
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 /// Returns the configured encoding speed, defaulting to 5.
41 pub fn get_encoding_speed(&self) -> i32 {
42 self.get_global_int("encoding_speed", 5)
43 }
44
45 /// Returns the configured decoding speed target, defaulting to 5.
46 pub fn get_decoding_speed(&self) -> i32 {
47 self.get_global_int("decoding_speed", 5)
48 }
49
50 /// Returns the maximum speed for both encoding/decoding.
51 /// Matches C++ ExpertEncoder::GetSpeed() behavior.
52 pub fn get_speed(&self) -> i32 {
53 let encoding_speed = self
54 .global_options
55 .get("encoding_speed")
56 .copied()
57 .unwrap_or(-1);
58 let decoding_speed = self
59 .global_options
60 .get("decoding_speed")
61 .copied()
62 .unwrap_or(-1);
63 let max_speed = encoding_speed.max(decoding_speed);
64 if max_speed == -1 {
65 5 // Default value
66 } else {
67 max_speed
68 }
69 }
70
71 /// Returns the forced prediction scheme, or -1 for the encoder default.
72 pub fn get_prediction_scheme(&self) -> i32 {
73 self.get_global_int("prediction_scheme", -1)
74 }
75
76 /// Returns the prediction scheme forced for one attribute, falling back to
77 /// the global setting and then to -1 for the encoder default.
78 ///
79 /// Upstream reads this per attribute (`GetPredictionMethodFromOptions`), and
80 /// `get_attribute_int` itself falls back to the global option, so a value
81 /// set either way is honoured. Crate-internal: callers outside can already
82 /// express both halves with `set_attribute_int` and `set_prediction_scheme`,
83 /// and a getter with no matching per-attribute setter would be a half API.
84 pub(crate) fn get_attribute_prediction_scheme(&self, att_id: i32) -> i32 {
85 self.get_attribute_int(att_id, "prediction_scheme", -1)
86 }
87
88 /// Forces a prediction scheme by numeric Draco method id.
89 pub fn set_prediction_scheme(&mut self, value: i32) {
90 self.set_global_int("prediction_scheme", value);
91 }
92
93 /// Returns the forced encoding method, if one was set.
94 pub fn get_encoding_method(&self) -> Option<i32> {
95 self.global_options.get("encoding_method").cloned()
96 }
97
98 /// Forces an encoding method by numeric Draco method id.
99 pub fn set_encoding_method(&mut self, value: i32) {
100 self.set_global_int("encoding_method", value);
101 }
102
103 /// Sets the target Draco bitstream version.
104 pub fn set_version(&mut self, major: u8, minor: u8) {
105 self.set_global_int("version_major", major as i32);
106 self.set_global_int("version_minor", minor as i32);
107 }
108
109 /// Returns the target Draco bitstream version, or `(0, 0)` for default.
110 pub fn get_version(&self) -> (u8, u8) {
111 let major = self.get_global_int("version_major", -1);
112 let minor = self.get_global_int("version_minor", -1);
113 if major == -1 || minor == -1 {
114 // Default version depends on the encoder type and method,
115 // but we'll return (0, 0) to indicate "use default".
116 (0, 0)
117 } else {
118 (major as u8, minor as u8)
119 }
120 }
121
122 /// Sets a global integer option.
123 pub fn set_global_int(&mut self, key: &str, value: i32) {
124 self.global_options.insert(key.to_string(), value);
125 }
126
127 /// Returns a global integer option or the supplied default.
128 pub fn get_global_int(&self, key: &str, default_val: i32) -> i32 {
129 *self.global_options.get(key).unwrap_or(&default_val)
130 }
131
132 /// Sets an integer option for one attribute id.
133 pub fn set_attribute_int(&mut self, att_id: i32, key: &str, value: i32) {
134 self.attribute_options
135 .entry(att_id)
136 .or_default()
137 .insert(key.to_string(), value);
138 }
139
140 /// Returns an attribute integer option, falling back to the global value.
141 pub fn get_attribute_int(&self, att_id: i32, key: &str, default_val: i32) -> i32 {
142 if let Some(opts) = self.attribute_options.get(&att_id) {
143 if let Some(val) = opts.get(key) {
144 return *val;
145 }
146 }
147 // Fallback to global options if not found for attribute?
148 // Draco C++ implementation does fallback to global options.
149 self.get_global_int(key, default_val)
150 }
151}