diff --git a/scripts/props/commonFeatures.js b/scripts/props/commonFeatures.js new file mode 100644 index 000000000..f6b9163eb --- /dev/null +++ b/scripts/props/commonFeatures.js @@ -0,0 +1,71 @@ +const fs = require('fs'); +const path = require('path'); + +const addFrame = (start) => { + const previousControl = Object.values(data.control) + .filter(d => d.start <= start) + .reduce((prev, curr) => (curr.start > prev.start ? curr : prev), { start: -Infinity }); + const status = JSON.parse(JSON.stringify(previousControl.status)); + const led_status = JSON.parse(JSON.stringify(previousControl.led_status)); + + // TODO: Add status, led_status + + const controlData = { + start, + fade: true, // TODO + status, + led_status, + }; + + const entry = Object.entries(data.control).find(([_, value]) => value.start == start); + if (entry) { + const [key, _] = entry; + data.control[key] = controlData; + return; + } + + const maxKey = Math.max(...Object.keys(data.control).map(Number)); + const nextKey = maxKey + 1; + data.control[nextKey.toString()] = controlData; +} + +const updateFrame = (key, frame, partLength, defaultColorData, secondaryColorData, direction, double, index, LEDindex) => { + const start = frame.start; + const status = JSON.parse(JSON.stringify(frame.status)); + const led_status = JSON.parse(JSON.stringify(frame.led_status)); + + // TODO: Add status, led_status + + const controlData = { + ...frame, + fade: true, // TODO + status, + led_status, + }; + + data.control[key] = controlData; +} + +// TODO: create specific props + +let period = 500; +let LEDlength = 15; +// left: 1. right: -1 +let direction = "mid"; +let double = false; +let partLength = 266; + +let index = data.dancer.findIndex(d => d.name === PropName); +let LEDindex = data.dancer[index].parts.findIndex(d => d.name === LEDPart); + +let startTime = 390007; +let endTime = 391207; + +let defaultColorData = []; +let secondaryColorData = []; + + +// fs.writeFileSync(path.join(__dirname, "./../../LightTableBackup/2025.03.17.json"), JSON.stringify(data, null, 0)); +fs.writeFileSync(path.join(__dirname, "./props.json"), JSON.stringify(data, null, 0)); +console.log(Object.keys(data.control).length) +console.log("Updated data has been saved to ./props.json"); \ No newline at end of file diff --git a/scripts/props/common_features/Cargo.lock b/scripts/props/common_features/Cargo.lock new file mode 100644 index 000000000..8942026ba --- /dev/null +++ b/scripts/props/common_features/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "common_features" +version = "0.1.0" diff --git a/scripts/props/common_features/Cargo.toml b/scripts/props/common_features/Cargo.toml new file mode 100644 index 000000000..83f70fa37 --- /dev/null +++ b/scripts/props/common_features/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "common_features" +version = "0.1.0" +edition = "2021" + +[lib] +name = "common_features" +path = "src/common_features.rs" + +[dependencies] +# Uncomment when needed for JSON serialization: +# serde = { version = "1.0", features = ["derive"] } +# serde_json = "1.0" diff --git a/scripts/props/common_features/src/common_features.rs b/scripts/props/common_features/src/common_features.rs new file mode 100644 index 000000000..5de27e02a --- /dev/null +++ b/scripts/props/common_features/src/common_features.rs @@ -0,0 +1,259 @@ +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; +// Note: Add serde and serde_json to Cargo.toml dependencies: +// serde = { version = "1.0", features = ["derive"] } +// serde_json = "1.0" + +// Type aliases matching the editor-server types +pub type PartControlString = (String, i32); // (color/effect name, alpha) +pub type PartControlBulbs = Vec<(String, i32)>; // Vec<(color/effect name, alpha)> + +/// Control frame data structure +#[derive(Debug, Clone)] +// #[derive(Serialize, Deserialize)] // Uncomment when serde is added +pub struct ControlData { + pub start: i32, + pub fade: bool, + pub status: Vec>, + pub led_status: Vec>, +} + +/// Main data structure containing control frames +#[derive(Debug)] +// #[derive(Serialize, Deserialize)] // Uncomment when serde is added +pub struct PropData { + pub control: BTreeMap, +} + +/// Trait for customizing frame creation and updates +/// Each prop implementation should implement this trait with their specific logic +pub trait PropCustomizer { + + fn customize_add_frame( + &self, + status: &mut Vec>, + led_status: &mut Vec>, + previous_control: &ControlData, + start: i32, + ); + + fn customize_update_frame( + &self, + status: &mut Vec>, + led_status: &mut Vec>, + frame: &ControlData, + ); + + fn get_fade_value(&self, previous_control: &ControlData, _start: i32) -> bool { + previous_control.fade + } + +} + +impl PropData { + + /// Creates a new PropData instance + pub fn new() -> Self { + Self { + control: BTreeMap::new(), + } + } + + /// =============== add frame================ /// + + fn find_previous_control(&self, start: i32) -> Option<&ControlData> { + self.control + .values() + .filter(|d| d.start <= start) + .max_by_key(|d| d.start) + } + + pub fn add_frame(&mut self, start: i32, customizer: &C) { + // Find previous control frame + let previous_control = self + .find_previous_control(start) + .cloned() + .unwrap_or_else(|| ControlData { + start: i32::MIN, + fade: true, + status: Vec::new(), + led_status: Vec::new(), + }); + + // Deep clone status and led_status + let mut status = previous_control.status.clone(); + let mut led_status = previous_control.led_status.clone(); + + // Allow customization of status and led_status + customizer.customize_add_frame(&mut status, &mut led_status, &previous_control, start); + + // Determine fade value + let fade_value = customizer.get_fade_value(&previous_control, start); + + // Create control data + let control_data = ControlData { + start, + fade: fade_value, + status, + led_status, + }; + + // Find existing frame or create new key + let key = self.control + .iter() + .find(|(_, value)| value.start == start) + .map(|(key, _)| key.clone()) + .unwrap_or_else(|| { + let max_key = self + .control + .keys() + .filter_map(|k| k.parse::().ok()) + .max() + .unwrap_or(0); + (max_key + 1).to_string() + }); + self.control.insert(key, control_data); + } + + pub fn update_frame(&mut self, key: &str, customizer: &C) { + let frame = match self.control.get(key) { + Some(f) => f.clone(), + None => return, + }; + + let mut status = frame.status.clone(); + let mut led_status = frame.led_status.clone(); + + // Allow customization of status and led_status + customizer.customize_update_frame(&mut status, &mut led_status, &frame); + + let control_data = ControlData { + start: frame.start, + fade: frame.fade, + status, + led_status, + }; + + self.control.insert(key.to_string(), control_data); + } + + /// Gets the count of control frames + pub fn control_count(&self) -> usize { + self.control.len() + } + +} + +impl Default for PropData { + fn default() -> Self { + Self::new() + } +} + +/// Common configuration structure for prop animations +/// Each prop implementation can use this as a base and extend with additional fields +#[derive(Debug, Clone)] +pub struct PropConfig { + pub period: f64, + pub led_length: usize, + pub direction: i32, + pub double: bool, + pub part_length: usize, + pub index: usize, + pub led_index: usize, + pub start_time: i32, + pub end_time: i32, + pub default_color_data: PartControlString, + pub secondary_color_data: PartControlString, +} + +impl PropConfig { + /// Creates a new PropConfig with default values + pub fn new() -> Self { + Self { + period: 500.0, + led_length: 15, + direction: 0, // "mid" + double: false, + part_length: 266, + index: 0, + led_index: 0, + start_time: 0, + end_time: 0, + default_color_data: ("".to_string(), 255), + secondary_color_data: ("".to_string(), 255), + } + } + + /// Sets the direction from a string ("left", "right", "mid") or number + pub fn set_direction(&mut self, direction: &str) { + self.direction = match direction { + "right" => -1, + "left" => 1, + "mid" => 0, + _ => direction.parse().unwrap_or(0), + }; + } + + /// Normalizes direction string to number + pub fn normalize_direction(direction: &str) -> i32 { + match direction { + "right" => -1, + "left" => 1, + "mid" => 0, + _ => direction.parse().unwrap_or(0), + } + } +} + +impl Default for PropConfig { + fn default() -> Self { + Self::new() + } +} + +impl PropData { + /// Saves the data to a JSON file + /// + /// # Arguments + /// * `file_path` - Path to save the file (relative to current directory or absolute) + /// * `pretty` - Whether to format JSON with indentation (default: false) + /// + /// # Note + /// Requires serde and serde_json dependencies. Uncomment the Serialize derives above. + pub fn save_to_file(&self, file_path: &str, pretty: bool) -> std::io::Result<()> { + // When serde is enabled, use: + // let json = if pretty { + // serde_json::to_string_pretty(self)? + // } else { + // serde_json::to_string(self)? + // }; + // fs::write(file_path, json)?; + + // Placeholder implementation + println!("Saving to {} (pretty: {})", file_path, pretty); + Ok(()) + } + + /// Logs the number of control frames + pub fn log_control_count(&self) { + println!("{}", self.control_count()); + } + + /// Saves the data and logs the control count (equivalent to JS lines 69-71) + /// + /// Equivalent to: + /// ```javascript + /// fs.writeFileSync(path.join(__dirname, "./props.json"), JSON.stringify(data, null, 0)); + /// console.log(Object.keys(data.control).length) + /// console.log("Updated data has been saved to ./props.json"); + /// ``` + pub fn save_and_log(&self, file_path: &str) -> std::io::Result<()> { + self.save_to_file(file_path, false)?; + self.log_control_count(); + println!("Updated data has been saved to {}", file_path); + Ok(()) + } +} + diff --git a/scripts/props/common_features/src/main.rs b/scripts/props/common_features/src/main.rs new file mode 100644 index 000000000..e7a11a969 --- /dev/null +++ b/scripts/props/common_features/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/scripts/props/props.sh b/scripts/props/props.sh old mode 100644 new mode 100755 index dff36379e..ee5999d0b --- a/scripts/props/props.sh +++ b/scripts/props/props.sh @@ -1,3 +1,4 @@ node createBigCannon.js node createSpinningSmallOrb.js -node createSpinningBigOrb.js \ No newline at end of file +node createSpinningBigOrb.js +node commonFeatures.js \ No newline at end of file