use concinnity_core::ecs::World;
use crate::editor::hook::declared_id;
use crate::editor::hook::tests::fixtures::{hook, shape_world_entries};
use crate::editor::inject;
use crate::editor::hook::tests::fixtures::select;
use crate::editor::panels::character_shape;
use crate::editor::panels::character_shape_panel;
use crate::editor::panels::registry::PanelKey;
#[test]
fn shape_reset_and_randomize_commit_once_each() {
let mut world = World::new();
inject::editor_hud(&mut world);
let mut h = hook(shape_world_entries());
h.open[PanelKey::CharacterShape] = true;
select(&mut h, &["body"]);
let data = h.shape_data(&world);
h.apply_shape_action(
character_shape_panel::ShapeAction::Randomize,
&data,
[0.0, 0.0],
&mut world,
);
assert!(h.can_undo());
let sliders = h.entries[1]["args"]["sliders"].as_array().unwrap();
for s in sliders {
let v = s["value"].as_f64().unwrap();
assert!(
v.abs() <= f64::from(character_shape::RANDOM_BAND) + 1e-6,
"{v}"
);
}
let legs = h.entries[1]["args"]["proportions"].as_array().unwrap();
assert!(
legs.iter().all(|p| p["joint"] == "thigh_l"),
"only the skeleton's joints are written: {legs:?}"
);
h.undo(&mut world);
assert!(!h.can_undo(), "randomize was one step");
select(&mut h, &["body"]);
let data = h.shape_data(&world);
h.apply_shape_action(
character_shape_panel::ShapeAction::Reset,
&data,
[0.0, 0.0],
&mut world,
);
assert!(
h.entries[1]["args"]["sliders"]
.as_array()
.unwrap()
.is_empty(),
"reset drops every slider"
);
h.undo(&mut world);
assert!(!h.can_undo(), "reset was one step");
}
#[test]
fn shape_add_row_creates_a_shape_for_the_selected_mesh() {
let mut world = World::new();
inject::editor_hud(&mut world);
let mut entries = shape_world_entries();
entries.pop();
let mut h = hook(entries);
h.open[PanelKey::CharacterShape] = true;
select(&mut h, &["body"]);
let data = h.shape_data(&world);
assert_eq!(data.rows, [character_shape::Row::Add]);
h.apply_shape_action(
character_shape_panel::ShapeAction::Add,
&data,
[0.0, 0.0],
&mut world,
);
assert_eq!(h.entries.len(), 2);
assert_eq!(h.entries[1]["args"]["target"], "body");
let name = declared_id(&h.entries[1]).unwrap().to_string();
select(&mut h, &[&name]);
let data = h.shape_data(&world);
let b = data.binding.expect("a selected shape binds");
assert_eq!((b.mesh.as_str(), b.shape_idx), ("body", Some(1)));
h.selection.clear();
let data = h.shape_data(&world);
assert!(data.binding.is_none());
assert_eq!(data.status.as_deref(), Some("Select a SkinnedMesh"));
}
#[test]
fn shape_panel_reads_a_character_models_schema_and_applies_presets() {
let mut world = World::new();
inject::editor_hud(&mut world);
let entries = vec![
serde_json::json!({"type": "CharacterSchema", "args": {
"$id": "sk",
"joints": [{"name": "root"}, {"name": "tail", "parent": "root"}],
"keys": [{"name": "fluff", "caption": "Fluffiness", "region": "tail"}],
"regions": [{"name": "tail", "joints": ["tail"]}],
"proportion_groups": [{"name": "tail_length", "caption": "tail length",
"region": "tail", "joints": ["tail"], "length": 0.1}],
"panel": [{"caption": "Tail", "regions": ["tail"]}],
"presets": [{"name": "bushy", "sliders": [{"name": "fluff", "value": 0.9}],
"proportions": [{"joint": "tail", "length": 0.05}]}]
}}),
serde_json::json!({"type": "CharacterModel", "args": {
"$id": "body",
"schema": "sk", "sources": [{"source": "fox.glb"}]
}}),
serde_json::json!({"type": "CharacterShape", "args": {
"$id": "body_shape",
"target": "body", "sliders": [{"name": "nose", "value": 0.3}]
}}),
];
let mut h = hook(entries);
h.open[PanelKey::CharacterShape] = true;
select(&mut h, &["body"]);
let schema = h.shape_schema("body");
assert_eq!(schema.panel[0].caption, "Tail");
assert_eq!(schema.presets[0].name, "bushy");
let rows = character_shape::derive_rows(
&schema,
&["fluff".to_string(), "nose".to_string()],
&["root".to_string(), "tail".to_string()],
);
assert_eq!(rows.sections, ["Tail", character_shape::OTHER_SECTION]);
assert_eq!(rows.sliders[0].caption, "Fluffiness");
assert_eq!(rows.sliders[1].name, "tail_length");
assert_eq!(rows.sliders[2].name, "nose");
assert_eq!(
rows.sliders[2].section, 1,
"an unknown key lands under Other"
);
let data = h.shape_data(&world);
let b = data.binding.as_ref().expect("a model binds");
assert_eq!((b.mesh.as_str(), b.shape_idx), ("body", Some(2)));
assert_eq!(data.preset_names, ["bushy"]);
assert_eq!(data.rows[0], character_shape::Row::PresetHeader);
assert_eq!(data.rows[1], character_shape::Row::Preset(0));
h.apply_shape_action(
character_shape_panel::ShapeAction::Preset(0),
&data,
[0.0, 0.0],
&mut world,
);
let args = &h.entries[2]["args"];
assert_eq!(args["sliders"][0]["name"], "fluff");
assert!((args["sliders"][0]["value"].as_f64().unwrap() - 0.9).abs() < 1e-6);
assert_eq!(args["sliders"].as_array().unwrap().len(), 1);
assert_eq!(args["proportions"][0]["joint"], "tail");
assert!(h.can_undo());
h.undo(&mut world);
assert!(!h.can_undo(), "a preset is one step");
assert_eq!(h.entries[2]["args"]["sliders"][0]["name"], "nose");
}