You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lib.rs 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. // Copyright (C) 2019 Inderjit Gill
  2. // This program is free software: you can redistribute it and/or modify
  3. // it under the terms of the GNU General Public License as published by
  4. // the Free Software Foundation, either version 3 of the License, or
  5. // (at your option) any later version.
  6. // This program is distributed in the hope that it will be useful,
  7. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. // GNU General Public License for more details.
  10. // You should have received a copy of the GNU General Public License
  11. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  12. #![allow(dead_code)]
  13. #![cfg_attr(
  14. feature = "cargo-clippy",
  15. allow(clippy::many_single_char_names, clippy::too_many_arguments)
  16. )]
  17. use cfg_if::cfg_if;
  18. use wasm_bindgen::prelude::*;
  19. use core::{
  20. build_traits, next_generation, program_from_source, program_from_source_and_genotype,
  21. run_program_with_preamble, simplified_unparse, unparse, bitmaps_to_transfer
  22. };
  23. use core::{BitmapInfo, Context, Genotype, Packable, Program, TraitList, Vm};
  24. use log::{error, info};
  25. cfg_if! {
  26. // When the `console_error_panic_hook` feature is enabled, we can call the
  27. // `set_panic_hook` function at least once during initialization, and then
  28. // we will get better error messages if our code ever panics.
  29. //
  30. // For more details see
  31. // https://github.com/rustwasm/console_error_panic_hook#readme
  32. if #[cfg(feature = "console_error_panic_hook")] {
  33. extern crate console_error_panic_hook;
  34. pub use self::console_error_panic_hook::set_once as set_panic_hook;
  35. } else {
  36. #[inline]
  37. pub fn set_panic_hook() {}
  38. }
  39. }
  40. cfg_if! {
  41. if #[cfg(feature = "console_log")] {
  42. fn init_log() {
  43. use log::Level;
  44. console_log::init_with_level(Level::Error).expect("error initializing log");
  45. }
  46. } else {
  47. fn init_log() {}
  48. }
  49. }
  50. cfg_if! {
  51. // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
  52. // allocator.
  53. if #[cfg(feature = "wee_alloc")] {
  54. extern crate wee_alloc;
  55. #[global_allocator]
  56. static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
  57. }
  58. }
  59. #[wasm_bindgen]
  60. #[derive(Default)]
  61. pub struct Bridge {
  62. vm: Vm,
  63. context: Context,
  64. genotype_list: Vec<Genotype>,
  65. // only used during sequence of calls for rendering
  66. program: Option<Program>,
  67. }
  68. #[wasm_bindgen]
  69. pub fn init_client_system() {
  70. init_log();
  71. // info!("init_client_system");
  72. }
  73. #[wasm_bindgen]
  74. impl Bridge {
  75. #[wasm_bindgen(constructor)]
  76. pub fn new() -> Bridge {
  77. Bridge {
  78. vm: Default::default(),
  79. context: Default::default(),
  80. genotype_list: vec![],
  81. program: None,
  82. }
  83. }
  84. // --------------------------------------------------------------------------------
  85. // new rendering api
  86. pub fn compile_program_from_source(&mut self, source: &str) -> bool {
  87. let res = program_from_source(&source);
  88. match res {
  89. Ok(program) => {
  90. self.program = Some(program);
  91. true
  92. }
  93. Err(e) => {
  94. error!("{}", e);
  95. false
  96. }
  97. }
  98. }
  99. pub fn compile_program_from_source_and_genotype(
  100. &mut self,
  101. source: &str,
  102. packed_genotype: &str,
  103. ) -> bool {
  104. if let Ok((mut genotype, _)) = Genotype::unpack(packed_genotype) {
  105. let res = program_from_source_and_genotype(source, &mut genotype);
  106. match res {
  107. Ok(program) => {
  108. self.program = Some(program);
  109. true
  110. }
  111. Err(e) => {
  112. error!("{}", e);
  113. false
  114. }
  115. }
  116. } else {
  117. error!("program_from_source_and_genotype: Genotype failed to unpack");
  118. return false;
  119. }
  120. }
  121. // return the list of required bitmaps to the host system
  122. // it will send the bitmap data back here via the add_rgba_bitmap function
  123. pub fn get_bitmap_transfers_as_json(&mut self) -> String {
  124. // todo: check that we're in the RENDER state
  125. if let Some(program) = &self.program {
  126. let bitmaps_to_transfer = bitmaps_to_transfer(program, &self.context);
  127. let mut res: String = "[".to_string();
  128. for (i, b) in bitmaps_to_transfer.iter().enumerate() {
  129. res.push_str(&format!("\"{}\"", b));
  130. if i < bitmaps_to_transfer.len() - 1 {
  131. res.push_str(", ");
  132. }
  133. }
  134. res.push_str("]");
  135. res
  136. } else {
  137. "[]".to_string()
  138. }
  139. }
  140. // the program has been compiled,
  141. // all required bitmaps have been loaded
  142. pub fn run_program(&mut self) -> usize {
  143. // todo: check that we're in the RENDER state
  144. if let Some(program) = &self.program {
  145. match run_program_with_preamble(&mut self.vm, &mut self.context, &program) {
  146. Ok(_) => {
  147. self.vm.debug_str_clear();
  148. self.context.geometry.get_num_render_packets()
  149. }
  150. Err(e) => {
  151. error!("{}", e);
  152. 0
  153. }
  154. }
  155. } else {
  156. 0
  157. }
  158. }
  159. pub fn output_linear_colour_space(&self) -> bool {
  160. self.context.output_linear_colour_space
  161. }
  162. // --------------------------------------------------------------------------------
  163. pub fn get_render_packet_geo_len(&self, packet_number: usize) -> usize {
  164. // info!("get_render_packet_geo_len");
  165. self.context.get_render_packet_geo_len(packet_number)
  166. }
  167. pub fn get_render_packet_geo_ptr(&self, packet_number: usize) -> *const f32 {
  168. // info!("get_render_packet_geo_ptr");
  169. self.context.get_render_packet_geo_ptr(packet_number)
  170. }
  171. pub fn add_rgba_bitmap(&mut self, name: &str, width: usize, height: usize, data: Vec<u8>) {
  172. let bitmap_info = BitmapInfo::new(width, height, data);
  173. self.context.bitmap_cache.insert(name, bitmap_info).unwrap();
  174. }
  175. // todo: is bool the best return type?
  176. pub fn build_traits(&mut self, source: &str) -> String {
  177. info!("build_traits");
  178. match build_traits(source) {
  179. Ok(trait_list) => {
  180. let mut traits_buffer = "".to_string();
  181. let packed_trait_list_res = trait_list.pack(&mut traits_buffer);
  182. if packed_trait_list_res.is_ok() {
  183. return traits_buffer;
  184. }
  185. }
  186. Err(e) => error!("{:?}", e),
  187. }
  188. return "".to_string();
  189. }
  190. pub fn single_genotype_from_seed(&mut self, packed_trait_list: &str, seed: i32) -> bool {
  191. info!("single_genotype_from_seed");
  192. if let Ok((trait_list, _)) = TraitList::unpack(packed_trait_list) {
  193. if let Ok(genotype) = Genotype::build_from_seed(&trait_list, seed) {
  194. self.genotype_list = vec![genotype];
  195. return true;
  196. } else {
  197. error!("single_genotype_from_seed: can't build genotype from unpacked TraitList");
  198. return false;
  199. }
  200. } else {
  201. error!("single_genotype_from_seed: TraitList failed to unpack");
  202. return false;
  203. }
  204. }
  205. // todo: is bool the best return type?
  206. pub fn create_initial_generation(
  207. &mut self,
  208. packed_trait_list: &str,
  209. population_size: i32,
  210. seed: i32,
  211. ) -> bool {
  212. info!("create_initial_generation");
  213. if let Ok((trait_list, _)) = TraitList::unpack(packed_trait_list) {
  214. if let Ok(genotype_list) = Genotype::build_genotypes(&trait_list, population_size, seed)
  215. {
  216. self.genotype_list = genotype_list;
  217. return true;
  218. } else {
  219. error!("create_initial_generation: can't build genotypes from unpacked TraitList");
  220. return false;
  221. }
  222. } else {
  223. error!("create_initial_generation: TraitList failed to unpack");
  224. return false;
  225. }
  226. }
  227. pub fn get_genotype(&mut self, index: usize) -> String {
  228. info!("genotype_move_to_buffer");
  229. if let Some(ref genotype) = self.genotype_list.get(index) {
  230. let mut packed_genotype = "".to_string();
  231. let res = genotype.pack(&mut packed_genotype);
  232. if res.is_ok() {
  233. return packed_genotype;
  234. }
  235. }
  236. "".to_string()
  237. }
  238. pub fn script_cleanup(&self) {
  239. info!("script_cleanup");
  240. }
  241. pub fn next_generation_prepare(&mut self) {
  242. info!("next_generation_prepare");
  243. self.genotype_list = vec![];
  244. }
  245. pub fn next_generation_add_genotype(&mut self, packed_genotype: &str) {
  246. info!("next_generation_add_genotype");
  247. if let Ok((genotype, _)) = Genotype::unpack(packed_genotype) {
  248. self.genotype_list.push(genotype);
  249. } else {
  250. error!("next_generation_add_genotype: Genotype failed to unpack");
  251. }
  252. }
  253. // todo: population_size should be usize
  254. pub fn next_generation_build(
  255. &mut self,
  256. packed_trait_list: &str,
  257. parent_size: usize,
  258. population_size: i32,
  259. mutation_rate: f32,
  260. rng: i32,
  261. ) -> bool {
  262. info!("next_generation_build");
  263. // confirm that we have parent_size genotypes in self.genotype_list
  264. if self.genotype_list.len() != parent_size {
  265. error!(
  266. "next_generation_build: parent_size ({}) mismatch with genotypes given ({})",
  267. parent_size,
  268. self.genotype_list.len()
  269. );
  270. return false;
  271. }
  272. if let Ok((trait_list, _)) = TraitList::unpack(packed_trait_list) {
  273. match next_generation(
  274. &self.genotype_list,
  275. population_size as usize,
  276. mutation_rate,
  277. rng,
  278. &trait_list,
  279. ) {
  280. Ok(new_generation) => {
  281. self.genotype_list = new_generation;
  282. return true;
  283. }
  284. Err(e) => {
  285. error!("{:?}", e);
  286. return false;
  287. }
  288. }
  289. } else {
  290. error!("next_generation_build: TraitList failed to unpack");
  291. return false;
  292. }
  293. }
  294. pub fn unparse_with_genotype(&mut self, source: &str, packed_genotype: &str) -> String {
  295. info!("unparse_with_genotype");
  296. if let Ok((mut genotype, _)) = Genotype::unpack(packed_genotype) {
  297. if let Ok(out_source) = unparse(source, &mut genotype) {
  298. return out_source;
  299. } else {
  300. error!("unparse_with_genotype: unparse failed");
  301. }
  302. } else {
  303. error!("unparse_with_genotype: Genotype failed to unpack");
  304. }
  305. "".to_string()
  306. }
  307. pub fn simplify_script(&mut self, source: &str) -> String {
  308. info!("simplify_script");
  309. if let Ok(out_source) = simplified_unparse(source) {
  310. return out_source;
  311. } else {
  312. error!("simplify_script: failed");
  313. }
  314. "".to_string()
  315. }
  316. }