Tighten web surfaces and clean handoff
This commit is contained in:
@@ -22,7 +22,7 @@ impl Default for TickSchedule {
|
||||
logic_hz: 120,
|
||||
frame_synthesis_hz: 60,
|
||||
network_send_hz: 60,
|
||||
preview_hz: 15,
|
||||
preview_hz: 60,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,12 +54,12 @@ impl RealtimeEngine {
|
||||
mode: ValidationMode,
|
||||
) -> ValidationReport {
|
||||
let mut report = project.validate(mode);
|
||||
if self.schedule.preview_hz >= self.schedule.frame_synthesis_hz {
|
||||
if self.schedule.preview_hz > self.schedule.frame_synthesis_hz {
|
||||
report.issues.push(ValidationIssue {
|
||||
severity: ValidationSeverity::Warning,
|
||||
code: "preview_rate_too_high",
|
||||
path: "runtime.schedule.preview_hz".to_string(),
|
||||
message: "preview rate should stay below frame synthesis rate".to_string(),
|
||||
message: "preview rate should not exceed frame synthesis rate".to_string(),
|
||||
});
|
||||
}
|
||||
report
|
||||
|
||||
@@ -317,7 +317,7 @@ fn render_pattern_leds(
|
||||
let speed = scene_scalar(scene, "speed", 0.45).max(0.05);
|
||||
let tempo_multiplier = scene_scalar(scene, "tempo_multiplier", 1.0).clamp(0.25, 8.0);
|
||||
let intensity = scene_scalar(scene, "intensity", 1.0).clamp(0.0, 1.0);
|
||||
let fade = scene_scalar(scene, "fade", 0.35).clamp(0.0, 1.0);
|
||||
let fade = scene_scalar(scene, "fade", 0.0).clamp(0.0, 1.0);
|
||||
let pattern_brightness = scene_scalar(scene, "brightness", 1.0).clamp(0.0, 2.0);
|
||||
let block_size = scene_scalar(scene, "block_size", 1.0).max(0.1);
|
||||
let on_width = scene_scalar(scene, "on_width", 1.0).clamp(0.1, 2.0);
|
||||
@@ -543,16 +543,31 @@ fn render_pattern_leds(
|
||||
_ => vec![primary; led_count],
|
||||
};
|
||||
|
||||
if fade > 0.0 && colors.len() > 2 {
|
||||
if fade > 0.0 && colors.len() > 2 && smoothing_allowed_for_pattern(pattern_id) {
|
||||
colors = smooth_led_sequence(colors, fade);
|
||||
}
|
||||
|
||||
colors
|
||||
.into_iter()
|
||||
.map(|color| color.scale((intensity * pattern_brightness).clamp(0.0, 1.0)))
|
||||
.map(|color| {
|
||||
color
|
||||
.scale(quantize_intensity(
|
||||
(intensity * pattern_brightness).clamp(0.0, 1.0),
|
||||
))
|
||||
.quantize_10_percent()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn smoothing_allowed_for_pattern(pattern_id: &str) -> bool {
|
||||
matches!(pattern_id, "breathing")
|
||||
}
|
||||
|
||||
fn quantize_intensity(value: f32) -> f32 {
|
||||
let clamped = value.clamp(0.0, 1.0);
|
||||
(clamped * 10.0).round() / 10.0
|
||||
}
|
||||
|
||||
fn canonical_pattern_id(pattern_id: &str) -> &str {
|
||||
match pattern_id {
|
||||
"solid_color" => "solid",
|
||||
@@ -715,7 +730,7 @@ fn choose_pair(
|
||||
) -> (RgbColor, RgbColor) {
|
||||
let primary = scene_text(scene, "primary_color", "#4D7CFF");
|
||||
let secondary = scene_text(scene, "secondary_color", "#0E1630");
|
||||
let palette_name = scene_text(scene, "palette", "Laser Club");
|
||||
let palette_name = scene_text(scene, "palette", "Deep Blue");
|
||||
let seed_amount = amount + panel_row as f32 * 0.13 + panel_col as f32 * 0.07;
|
||||
match color_mode {
|
||||
"palette" => (
|
||||
@@ -770,159 +785,129 @@ fn sample_palette_list(colors: &[RgbColor], amount: f32) -> RgbColor {
|
||||
}
|
||||
|
||||
fn named_palette(name: &str) -> Option<&'static [RgbColor]> {
|
||||
const LASER_CLUB: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 0,
|
||||
g: 240,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 0,
|
||||
g: 140,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 106,
|
||||
g: 0,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor { r: 6, g: 8, b: 20 },
|
||||
];
|
||||
const AFTERHOURS: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 247,
|
||||
g: 37,
|
||||
b: 133,
|
||||
},
|
||||
RgbColor {
|
||||
r: 181,
|
||||
g: 23,
|
||||
b: 158,
|
||||
},
|
||||
RgbColor {
|
||||
r: 114,
|
||||
g: 9,
|
||||
b: 183,
|
||||
},
|
||||
RgbColor { r: 20, g: 3, b: 26 },
|
||||
];
|
||||
const VOLTAGE: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 0,
|
||||
g: 229,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 0,
|
||||
g: 179,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 58,
|
||||
g: 134,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor { r: 5, g: 10, b: 20 },
|
||||
];
|
||||
const MAGENTA_DRIVE: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 110,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 77,
|
||||
b: 166,
|
||||
},
|
||||
RgbColor {
|
||||
r: 122,
|
||||
g: 0,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor { r: 18, g: 3, b: 24 },
|
||||
];
|
||||
const WAREHOUSE_HEAT: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 90,
|
||||
b: 31,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 158,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 208,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor { r: 20, g: 6, b: 0 },
|
||||
];
|
||||
const UV_RIOT: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 122,
|
||||
g: 0,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 177,
|
||||
g: 0,
|
||||
b: 255,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 168,
|
||||
},
|
||||
RgbColor { r: 16, g: 0, b: 20 },
|
||||
];
|
||||
const REDLINE: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 45,
|
||||
b: 85,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 106,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 176,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor { r: 22, g: 4, b: 6 },
|
||||
];
|
||||
const SODIUM_HAZE: [RgbColor; 4] = [
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 122,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 176,
|
||||
b: 0,
|
||||
},
|
||||
RgbColor {
|
||||
r: 255,
|
||||
g: 216,
|
||||
b: 107,
|
||||
},
|
||||
RgbColor { r: 18, g: 7, b: 0 },
|
||||
];
|
||||
const CANDLE: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 147,
|
||||
b: 41,
|
||||
}];
|
||||
const TUNGSTEN_40W: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 197,
|
||||
b: 143,
|
||||
}];
|
||||
const TUNGSTEN_100W: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 214,
|
||||
b: 170,
|
||||
}];
|
||||
const HALOGEN: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 241,
|
||||
b: 224,
|
||||
}];
|
||||
const CARBON_ARC: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 250,
|
||||
b: 244,
|
||||
}];
|
||||
const HIGH_NOON_SUN: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 251,
|
||||
}];
|
||||
const OVERCAST_SKY: [RgbColor; 1] = [RgbColor {
|
||||
r: 201,
|
||||
g: 226,
|
||||
b: 255,
|
||||
}];
|
||||
const CLEAR_BLUE_SKY: [RgbColor; 1] = [RgbColor {
|
||||
r: 64,
|
||||
g: 156,
|
||||
b: 255,
|
||||
}];
|
||||
|
||||
const DEEP_BLUE: [RgbColor; 1] = [RgbColor {
|
||||
r: 0,
|
||||
g: 71,
|
||||
b: 255,
|
||||
}];
|
||||
const ELECTRIC_CYAN: [RgbColor; 1] = [RgbColor {
|
||||
r: 0,
|
||||
g: 216,
|
||||
b: 255,
|
||||
}];
|
||||
const EMERALD: [RgbColor; 1] = [RgbColor {
|
||||
r: 0,
|
||||
g: 200,
|
||||
b: 83,
|
||||
}];
|
||||
const ACID_LIME: [RgbColor; 1] = [RgbColor {
|
||||
r: 174,
|
||||
g: 234,
|
||||
b: 0,
|
||||
}];
|
||||
const AMBER: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 157,
|
||||
b: 0,
|
||||
}];
|
||||
const SIGNAL_RED: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 45,
|
||||
b: 45,
|
||||
}];
|
||||
const HOT_MAGENTA: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 168,
|
||||
}];
|
||||
const VIOLET: [RgbColor; 1] = [RgbColor {
|
||||
r: 122,
|
||||
g: 60,
|
||||
b: 255,
|
||||
}];
|
||||
const WARM_WHITE: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 214,
|
||||
b: 170,
|
||||
}];
|
||||
const NEUTRAL_WHITE: [RgbColor; 1] = [RgbColor {
|
||||
r: 255,
|
||||
g: 241,
|
||||
b: 224,
|
||||
}];
|
||||
const COOL_WHITE: [RgbColor; 1] = [RgbColor {
|
||||
r: 201,
|
||||
g: 226,
|
||||
b: 255,
|
||||
}];
|
||||
const BLACKLIGHT_VIOLET: [RgbColor; 1] = [RgbColor {
|
||||
r: 167,
|
||||
g: 0,
|
||||
b: 255,
|
||||
}];
|
||||
|
||||
match name {
|
||||
"Magenta Drive" => Some(&MAGENTA_DRIVE),
|
||||
"Warehouse Heat" => Some(&WAREHOUSE_HEAT),
|
||||
"UV Riot" => Some(&UV_RIOT),
|
||||
"Redline" => Some(&REDLINE),
|
||||
"Sodium Haze" => Some(&SODIUM_HAZE),
|
||||
"Afterhours" => Some(&AFTERHOURS),
|
||||
"Voltage" => Some(&VOLTAGE),
|
||||
"Laser Club" => Some(&LASER_CLUB),
|
||||
"Candle" => Some(&CANDLE),
|
||||
"Tungsten 40W" => Some(&TUNGSTEN_40W),
|
||||
"Tungsten 100W" => Some(&TUNGSTEN_100W),
|
||||
"Halogen" => Some(&HALOGEN),
|
||||
"Carbon Arc" => Some(&CARBON_ARC),
|
||||
"High Noon Sun" => Some(&HIGH_NOON_SUN),
|
||||
"Overcast Sky" => Some(&OVERCAST_SKY),
|
||||
"Clear Blue Sky" => Some(&CLEAR_BLUE_SKY),
|
||||
"Deep Blue" => Some(&DEEP_BLUE),
|
||||
"Electric Cyan" => Some(&ELECTRIC_CYAN),
|
||||
"Emerald" => Some(&EMERALD),
|
||||
"Acid Lime" => Some(&ACID_LIME),
|
||||
"Amber" => Some(&AMBER),
|
||||
"Signal Red" => Some(&SIGNAL_RED),
|
||||
"Hot Magenta" => Some(&HOT_MAGENTA),
|
||||
"Violet" => Some(&VIOLET),
|
||||
"Warm White" => Some(&WARM_WHITE),
|
||||
"Neutral White" => Some(&NEUTRAL_WHITE),
|
||||
"Cool White" => Some(&COOL_WHITE),
|
||||
"Blacklight Violet" => Some(&BLACKLIGHT_VIOLET),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1853,12 +1838,12 @@ fn common_motion_parameters(extra_keys: Vec<&str>) -> Vec<SceneParameterSpec> {
|
||||
scalar_spec("speed", "Speed", 0.05, 8.0, 0.05, 0.45),
|
||||
scalar_spec("intensity", "Intensity", 0.0, 1.0, 0.01, 1.0),
|
||||
scalar_spec("brightness", "Brightness", 0.0, 2.0, 0.01, 1.0),
|
||||
scalar_spec("fade", "Smoothing", 0.0, 1.0, 0.01, 0.35),
|
||||
scalar_spec("fade", "Smoothing", 0.0, 1.0, 0.01, 0.0),
|
||||
scalar_spec("tempo_multiplier", "Tempo Multiplier", 0.25, 8.0, 0.05, 1.0),
|
||||
text_spec("color_mode", "Color Mode", "dual"),
|
||||
text_spec("primary_color", "Primary Color", "#4D7CFF"),
|
||||
text_spec("secondary_color", "Secondary Color", "#0E1630"),
|
||||
text_spec("palette", "Palette", "Laser Club"),
|
||||
text_spec("palette", "Palette", "Deep Blue"),
|
||||
];
|
||||
|
||||
for key in extra_keys {
|
||||
@@ -1960,6 +1945,14 @@ impl RgbColor {
|
||||
}
|
||||
}
|
||||
|
||||
fn quantize_10_percent(self) -> Self {
|
||||
Self {
|
||||
r: quantize_channel_10_percent(self.r),
|
||||
g: quantize_channel_10_percent(self.g),
|
||||
b: quantize_channel_10_percent(self.b),
|
||||
}
|
||||
}
|
||||
|
||||
fn complementary(self) -> Self {
|
||||
Self {
|
||||
r: 255u8.saturating_sub(self.r),
|
||||
@@ -1998,6 +1991,12 @@ impl RgbColor {
|
||||
}
|
||||
}
|
||||
|
||||
fn quantize_channel_10_percent(value: u8) -> u8 {
|
||||
(((value as f32 / 255.0) * 10.0).round() * 25.5)
|
||||
.round()
|
||||
.clamp(0.0, 255.0) as u8
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user