1use bit_vec::BitVec;
2use rand::{RngCore, SeedableRng, rngs::StdRng};
3use visioncortex::{BinaryImage, ColorImage, PointI32};
4use wasm_bindgen::prelude::*;
5
6use symcode::acute32::{Acute32, Acute32SymcodeConfig, AlphabetReader, AlphabetReaderParams, GlyphLabel};
7use symcode::interfaces::{Decoder, Finder, FinderElement, Fitter, Reader, Encoder, SymcodeScanner, SymcodeGenerator};
8use symcode::math::{into_bitvec, num_bits_to_store};
9use crate::{canvas::Canvas, util::console_log_util};
10use crate::debugger::{Debugger, render_binary_image_to_canvas};
11use super::helper::is_black_hsv;
12
13#[wasm_bindgen]
14pub struct Acute32SymcodeMain {
15 config: Acute32SymcodeConfig,
16 rng: StdRng,
17}
18
19impl Default for Acute32SymcodeMain {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl Acute32SymcodeMain {
26 const PAYLOAD_BITS: usize = 20;
27
28 pub fn from_config(config: Acute32SymcodeConfig, seed: u64) -> Self {
29 Self {
30 config,
31 rng: StdRng::seed_from_u64(seed),
32 }
33 }
34}
35
36#[wasm_bindgen]
37impl Acute32SymcodeMain {
38
39 pub fn new() -> Self {
40 let mut config = Acute32SymcodeConfig::default();
41 if let Some(debug_canvas) = Canvas::new_from_id("debug") {
42 config.debugger = Box::new(Debugger{ debug_canvas });
43 }
44 Self::from_config(config, 125)
45 }
46
47 pub fn seed_rng(&mut self, seed: u64) {
48 self.rng = StdRng::seed_from_u64(seed);
49 }
50
51 pub fn load_alphabet_from_canvas_id(&mut self, canvas_id: &str) {
53 let params = AlphabetReaderParams::default();
54 let canvas = &match Canvas::new_from_id(canvas_id) {
55 Some(c) => c,
56 None => panic!("Canvas with id ".to_owned() + canvas_id + " is not found!"),
57 };
58 let image = canvas
59 .get_image_data_as_color_image(0, 0, canvas.width() as u32, canvas.height() as u32)
60 .to_binary_image(|c| is_black_hsv(&c.to_hsv()));
61 match AlphabetReader::read_alphabet_to_library(image, params, &self.config) {
62 Ok(library) => self.config.symbol_library = Box::new(library),
63 Err(e) => console_log_util(e),
64 }
65 }
66
67 pub fn scan_from_canvas_id(&self, canvas_id: &str) -> Result<String, JsValue> {
68 if self.config.symbol_library.is_empty() {
69 return Err("No templates loaded into the SymcodeScanner instance yet!".into());
70 }
71
72 let raw_frame = if let Some(canvas) = &Canvas::new_from_id(canvas_id) {
74 canvas.get_image_data_as_color_image(0, 0, canvas.width() as u32, canvas.height() as u32)
75 } else {
76 return Err("Cannot read input image from canvas.".into());
77 };
78
79 let symcode = self.scan(raw_frame)?;
80
81 if false {
82 let _debug_code_string = format!("{:?}", symcode);
83 }
84
85 let decoded_bit_string = self.decode(symcode)?;
86 Ok(format!("{:?}", decoded_bit_string))
87 }
88
89 pub fn generate_symcode_to_canvas(&self, canvas_id: &str, payload: &str) -> Result<String, JsValue> {
90 if payload.len() > Self::PAYLOAD_BITS {
91 return Err("Payload has too many bits!".into());
92 }
93
94 let result = usize::from_str_radix(payload, 2);
95 if result.is_err() {
96 return Err("Failed to parse payload as binary number".into());
97 }
98 let payload = result.unwrap();
99
100 let canvas = if let Some(canvas) = Canvas::new_from_id(canvas_id) {
101 canvas
102 } else {
103 return Err("Code generation: Canvas does not exist.".into());
104 };
105
106 let (symcode, ground_truth_code) = self.generate_symcode_with_payload(payload)?;
107
108 if render_binary_image_to_canvas(&canvas, &symcode).is_err() {
109 return Err("Cannot render generated symcode to canvas.".into());
110 }
111
112 Ok(ground_truth_code)
113 }
114
115 pub fn generate_random_symcode_to_canvas(&mut self, canvas_id: &str) -> Result<String, JsValue> {
116 let canvas = if let Some(canvas) = Canvas::new_from_id(canvas_id) {
117 canvas
118 } else {
119 return Err("Code generation: Canvas does not exist.".into());
120 };
121 let (symcode, ground_truth_code) = self.generate_symcode_random()?;
122
123 if render_binary_image_to_canvas(&canvas, &symcode).is_err() {
124 return Err("Cannot render generated symcode to canvas.".into());
125 }
126
127 Ok(ground_truth_code)
128 }
129
130 fn generate_symcode_with_payload(&self, payload: usize) -> Result<(BinaryImage, String), &str> {
131 let payload = into_bitvec(payload, Self::PAYLOAD_BITS);
132 let payload_bit_string = format!("{:?}", payload);
133
134 let num_symbols = self.config.num_glyphs_in_code();
135
136 let acute32 = Acute32::new(&self.config);
137 let symcode_representation = acute32.get_encoder().encode(payload, num_symbols)?;
138 let symcode_string = format!("{:?}", symcode_representation);
139
140 let code_image = self.generate(symcode_representation);
141
142 let msg = format!("{}\n{}", symcode_string, payload_bit_string);
143
144 Ok((code_image, msg))
147 }
148
149 fn generate_symcode_random(&mut self) -> Result<(BinaryImage, String), &str> {
152 let symbol_num_bits = num_bits_to_store(GlyphLabel::num_variants());
153 let num_symbols = self.config.num_glyphs_in_code();
154
155 let payload = BitVec::from_fn(
157 symbol_num_bits*num_symbols - 5, |_| { self.rng.next_u32() < (std::u32::MAX >> 1) }
159 );
160 let payload_bit_string = format!("{:?}", payload);
161
162 let acute32 = Acute32::new(&self.config);
163 let symcode_representation = acute32.get_encoder().encode(payload, num_symbols)?;
164 let symcode_string = format!("{:?}", symcode_representation);
165
166 let code_image = self.generate(symcode_representation);
167
168 let msg = format!("{}\n{}", symcode_string, payload_bit_string);
169
170 Ok((code_image, msg))
173 }
174}
175
176impl SymcodeScanner for Acute32SymcodeMain {
177 type SymcodeRepresentation = Vec<GlyphLabel>;
178
179 type Err = JsValue;
180
181 fn scan(&self, image: ColorImage) -> Result<Self::SymcodeRepresentation, Self::Err> {
182 let acute32 = Acute32::new(&self.config);
183
184 let finder_positions = match acute32.get_finder().find(
186 &image
187 ) {
188 Ok(finder_positions) => finder_positions,
189 Err(e) => {
190 return Err(("Failed at Stage 1: ".to_owned() + e).into());
191 }
192 };
193
194 let image_to_object = match acute32.get_fitter().fit(
196 finder_positions,
197 image.width,
198 image.height
199 ) {
200 Ok(image_to_object) => image_to_object,
201 Err(e) => {
202 return Err(("Failed at Stage 2: ".to_owned() + e).into());
203 }
204 };
205
206 let symcode_instance = match acute32.get_reader().read(
208 image,
209 image_to_object
210 ) {
211 Ok(symcode_instance) => symcode_instance,
212 Err(e) => {
213 return Err(("Failed at Stage 3: ".to_owned() + e).into());
214 }
215 };
216
217 Ok(symcode_instance)
218 }
219
220 fn decode(&self, symcode: Self::SymcodeRepresentation) -> Result<bit_vec::BitVec, Self::Err> {
221 let acute32 = Acute32::new(&self.config);
222
223 match acute32.get_decoder().decode(
225 symcode
226 ) {
227 Ok(decoded_symcode) => {
228 Ok(decoded_symcode)
229 },
230 Err(e) => {
231 Err(("Failed at Stage 4: ".to_owned() + e).into())
232 }
233 }
234 }
235}
236
237impl SymcodeGenerator for Acute32SymcodeMain {
238 type SymcodeRepresentation = Vec<GlyphLabel>;
239
240 fn generate(&self, symcode: Self::SymcodeRepresentation) -> BinaryImage {
241 let mut symcode_image = BinaryImage::new_w_h(self.config.code_width, self.config.code_height);
242
243 let finder_image = self.config.finder.to_image(self.config.symbol_width, self.config.symbol_height);
245 self.config.finder_positions.iter().for_each(|finder_center| {
246 let top_left = finder_center.to_point_i32() - PointI32::new((self.config.symbol_width >> 1) as i32, (self.config.symbol_height >> 1) as i32);
247 symcode_image.paste_from(&finder_image, top_left);
248 });
249
250 symcode.iter().enumerate().for_each(|(i, &glyph_label)| {
252 if glyph_label != GlyphLabel::Invalid {
253 let glyph_top_left = self.config.glyph_anchors[i];
254 if let Some(glyph) = self.config.symbol_library.get_glyph_with_label(glyph_label) {
255 symcode_image.paste_from(&glyph.image, glyph_top_left.to_point_i32());
256 }
257 }
258 });
259
260 symcode_image
261 }
262}