use super::scenes::SceneType;
use super::ViewerSettings;
use bevy::prelude::*;
use eulumdat::Eulumdat;
const DIMENSION_STEP: f32 = 0.5;
const FINE_STEP: f32 = 0.1;
const MIN_DIMENSION: f32 = 1.0;
const MAX_DIMENSION: f32 = 50.0;
const MIN_HEIGHT: f32 = 2.0;
const MAX_HEIGHT: f32 = 20.0;
const MAX_PENDULUM: f32 = 20.0;
pub fn viewer_controls_system(
mut settings: ResMut<ViewerSettings>,
keyboard: Res<ButtonInput<KeyCode>>,
) {
if keyboard.just_pressed(KeyCode::KeyP) {
settings.show_photometric_solid = !settings.show_photometric_solid;
}
if keyboard.just_pressed(KeyCode::KeyL) {
settings.show_luminaire = !settings.show_luminaire;
}
if keyboard.just_pressed(KeyCode::KeyH) {
settings.show_shadows = !settings.show_shadows;
}
if keyboard.just_pressed(KeyCode::Digit1) {
settings.scene_type = SceneType::Room;
}
if keyboard.just_pressed(KeyCode::Digit2) {
settings.scene_type = SceneType::Road;
}
if keyboard.just_pressed(KeyCode::Digit3) {
settings.scene_type = SceneType::Parking;
}
if keyboard.just_pressed(KeyCode::Digit4) {
settings.scene_type = SceneType::Outdoor;
}
if keyboard.just_pressed(KeyCode::BracketLeft) {
settings.room_width = (settings.room_width - DIMENSION_STEP).max(MIN_DIMENSION);
}
if keyboard.just_pressed(KeyCode::BracketRight) {
settings.room_width = (settings.room_width + DIMENSION_STEP).min(MAX_DIMENSION);
}
if keyboard.just_pressed(KeyCode::Minus) {
settings.room_length = (settings.room_length - DIMENSION_STEP).max(MIN_DIMENSION);
}
if keyboard.just_pressed(KeyCode::Equal) {
settings.room_length = (settings.room_length + DIMENSION_STEP).min(MAX_DIMENSION);
}
if keyboard.just_pressed(KeyCode::Digit9) {
settings.room_height = (settings.room_height - DIMENSION_STEP).max(MIN_HEIGHT);
if settings.mounting_height > settings.room_height - 0.1 {
settings.mounting_height = settings.room_height - 0.1;
}
}
if keyboard.just_pressed(KeyCode::Digit0) {
settings.room_height = (settings.room_height + DIMENSION_STEP).min(MAX_HEIGHT);
}
if keyboard.just_pressed(KeyCode::Semicolon) {
settings.pendulum_length = (settings.pendulum_length - FINE_STEP).max(0.0);
}
if keyboard.just_pressed(KeyCode::Quote) {
let max_pendulum = (settings.attachment_height() - 1.0).clamp(0.0, MAX_PENDULUM);
settings.pendulum_length = (settings.pendulum_length + FINE_STEP).min(max_pendulum);
}
if keyboard.just_pressed(KeyCode::Comma) {
settings.mounting_height = (settings.mounting_height - FINE_STEP).max(2.0);
}
if keyboard.just_pressed(KeyCode::Period) {
settings.mounting_height = (settings.mounting_height + FINE_STEP).min(MAX_HEIGHT);
}
const TILT_STEP: f32 = 5.0;
if keyboard.just_pressed(KeyCode::KeyT) {
settings.luminaire_tilt = (settings.luminaire_tilt - TILT_STEP).max(0.0);
}
if keyboard.just_pressed(KeyCode::KeyY) {
settings.luminaire_tilt = (settings.luminaire_tilt + TILT_STEP).min(90.0);
}
}
pub fn sync_viewer_to_lights(
mut commands: Commands,
settings: Res<ViewerSettings>,
lights: Query<(
Entity,
&crate::photometric::PhotometricLight<Eulumdat>,
&Transform,
)>,
) {
if !settings.is_changed() {
return;
}
let ldt_data = lights.iter().next().map(|(_, l, _)| l.data.clone());
let Some(ldt) = ldt_data else {
return;
};
let transforms = calculate_all_luminaire_transforms(&settings, &ldt);
let current_count = lights.iter().count();
let required_count = transforms.len();
if current_count != required_count {
for (entity, _, _) in lights.iter() {
commands.entity(entity).despawn();
}
for transform in transforms {
commands.spawn(
crate::eulumdat_impl::EulumdatLightBundle::new(ldt.clone())
.with_transform(
Transform::from_translation(transform.position)
.with_rotation(transform.rotation),
)
.with_solid(settings.show_photometric_solid)
.with_model(settings.show_luminaire)
.with_shadows(settings.show_shadows),
);
}
} else {
for (idx, (entity, light, _)) in lights.iter().enumerate() {
if let Some(lt) = transforms.get(idx) {
let mut updated_light =
crate::photometric::PhotometricLight::new(light.data.clone());
updated_light.show_solid = settings.show_photometric_solid;
updated_light.show_model = settings.show_luminaire;
updated_light.shadows_enabled = settings.show_shadows;
updated_light.intensity_scale = light.intensity_scale;
commands.entity(entity).insert((
Transform::from_translation(lt.position).with_rotation(lt.rotation),
updated_light,
));
}
}
}
}
#[derive(Clone, Copy)]
pub struct LuminaireTransform {
pub position: Vec3,
pub rotation: Quat,
}
pub fn calculate_all_luminaire_transforms(
settings: &ViewerSettings,
ldt: &Eulumdat,
) -> Vec<LuminaireTransform> {
let y = settings.luminaire_height(ldt);
match settings.scene_type {
SceneType::Room => {
vec![LuminaireTransform {
position: Vec3::new(settings.room_width / 2.0, y, settings.room_length / 2.0),
rotation: Quat::IDENTITY,
}]
}
SceneType::Road => calculate_road_luminaires(settings, y),
SceneType::Parking | SceneType::Outdoor => {
vec![LuminaireTransform {
position: Vec3::new(
settings.room_width / 2.0 - 0.2,
y,
settings.room_length / 2.0,
),
rotation: Quat::IDENTITY,
}]
}
}
}
fn calculate_road_luminaires(settings: &ViewerSettings, y: f32) -> Vec<LuminaireTransform> {
let lane_w = settings.lane_width;
let num_lanes = settings.num_lanes;
let sidewalk_w = settings.sidewalk_width;
let road_width = num_lanes as f32 * lane_w;
let total_width = road_width + 2.0 * sidewalk_w;
let road_length = settings.room_length;
let pole_spacing = settings.effective_pole_spacing();
let num_poles = ((road_length / pole_spacing).floor() as i32).max(1);
let actual_spacing = road_length / (num_poles as f32 + 1.0);
let ratio = road_width / settings.mounting_height;
let tilt = settings.luminaire_tilt.to_radians();
let arm_length = 1.5;
let mut transforms = Vec::new();
let middle_pole_spacing = 50.0;
let center_x = sidewalk_w + road_width / 2.0;
if ratio < 1.0 {
let rotation = Quat::from_rotation_z(tilt);
for i in 1..=num_poles {
let z = i as f32 * actual_spacing;
transforms.push(LuminaireTransform {
position: Vec3::new(total_width - sidewalk_w / 2.0 - arm_length, y, z),
rotation,
});
}
} else if ratio < 1.5 {
for i in 1..=num_poles {
let z = i as f32 * actual_spacing;
if i % 2 == 0 {
transforms.push(LuminaireTransform {
position: Vec3::new(sidewalk_w / 2.0 + arm_length, y, z),
rotation: Quat::from_rotation_z(-tilt),
});
} else {
transforms.push(LuminaireTransform {
position: Vec3::new(total_width - sidewalk_w / 2.0 - arm_length, y, z),
rotation: Quat::from_rotation_z(tilt),
});
}
}
} else {
for i in 1..=num_poles {
let z = i as f32 * actual_spacing;
transforms.push(LuminaireTransform {
position: Vec3::new(sidewalk_w / 2.0 + arm_length, y, z),
rotation: Quat::from_rotation_z(-tilt),
});
transforms.push(LuminaireTransform {
position: Vec3::new(total_width - sidewalk_w / 2.0 - arm_length, y, z),
rotation: Quat::from_rotation_z(tilt),
});
}
if road_width > 6.0 {
let num_middle_poles = ((road_length / middle_pole_spacing).floor() as i32).max(0);
for i in 1..=num_middle_poles {
let z = i as f32 * middle_pole_spacing;
transforms.push(LuminaireTransform {
position: Vec3::new(center_x - 1.0, y, z),
rotation: Quat::from_rotation_z(-tilt * 0.5), });
transforms.push(LuminaireTransform {
position: Vec3::new(center_x + 1.0, y, z),
rotation: Quat::from_rotation_z(tilt * 0.5),
});
}
}
}
transforms
}
pub fn calculate_light_position(settings: &ViewerSettings, ldt: &Eulumdat) -> Vec3 {
let transforms = calculate_all_luminaire_transforms(settings, ldt);
transforms.first().map(|t| t.position).unwrap_or(Vec3::ZERO)
}
pub fn calculate_light_rotation(settings: &ViewerSettings) -> Quat {
match settings.scene_type {
SceneType::Room => Quat::IDENTITY, SceneType::Road => {
let tilt_angle = -settings.luminaire_tilt.to_radians();
Quat::from_rotation_z(tilt_angle)
}
SceneType::Parking => Quat::IDENTITY, SceneType::Outdoor => Quat::IDENTITY, }
}