/// Generate an expanded Bevel & Emboss PSD set for ground-truth MAE tuning. /// /// Includes more shapes and OuterBevel in addition to InnerBevel/Emboss. /// Technique is kept as Smooth for the main set; ChiselHard/ChiselSoft can be /// generated separately if needed. /// /// Shapes: rect, circle, triangle, star, L, T /// Styles: InnerBevel, Emboss, OuterBevel /// Output: `_tmp/bevel_emboss_psd_set/` use hcie_protocol::{Layer, LayerType, LayerData, BlendMode, effects::{LayerEffect, BevelStyle, Technique, Direction}}; use std::sync::atomic::{AtomicBool}; use std::sync::Mutex; fn make_shape(shape: &str, canvas: u32) -> Vec { let mut pixels = vec![0u8; (canvas * canvas * 4) as usize]; let cx = canvas / 2; let cy = canvas / 2; for y in 0..canvas { for x in 0..canvas { let i = (y * canvas + x) as usize * 4; let inside = match shape { "rect" => { x >= 64 && x < canvas - 64 && y >= 64 && y < canvas - 64 } "tall" => { x >= 96 && x < canvas - 96 && y >= 32 && y < canvas - 32 } "wide" => { x >= 32 && x < canvas - 32 && y >= 96 && y < canvas - 96 } "circle" => { let dx = x as f32 - cx as f32; let dy = y as f32 - cy as f32; let r = (canvas / 2 - 64) as f32; (dx * dx + dy * dy).sqrt() <= r } "triangle" => { let dx = (x as i32 - cx as i32).abs(); let top = 64i32; let bottom = (canvas - 64) as i32; if (y as i32) < top || (y as i32) > bottom { false } else { let y_span = ((y as i32) - top) as f32 / (bottom - top) as f32; let half_width_at_y = (canvas - 128) as f32 / 2.0 * (1.0 - y_span); (dx as f32) <= half_width_at_y } } "star" => { let dx = x as f32 - cx as f32; let dy = y as f32 - cy as f32; let r = (dx * dx + dy * dy).sqrt(); if r > 96.0 || r < 20.0 { false } else { let angle = dy.atan2(dx); let points = 5.0f32; let sector = (angle * points).cos(); let limit = 64.0 + sector * 32.0; r <= limit } } "L" => { (x >= 64 && x < 96 && y >= 64 && y < canvas - 64) || (x >= 64 && x < canvas - 64 && y >= canvas - 96 && y < canvas - 64) } "T" => { (y >= 64 && y < 96 && x >= 64 && x < canvas - 64) || (x >= 112 && x < 144 && y >= 64 && y < canvas - 64) } "cross" => { (x >= 112 && x < 144 && y >= 48 && y < canvas - 48) || (y >= 112 && y < 144 && x >= 48 && x < canvas - 48) } _ => false, }; if inside { pixels[i] = 0; pixels[i + 1] = 0; pixels[i + 2] = 0; pixels[i + 3] = 255; } } } pixels } fn main() { let out_dir = std::path::Path::new("_tmp/bevel_emboss_psd_set"); std::fs::create_dir_all(out_dir).expect("create output dir"); let canvas: u32 = 256; let shapes: &[(&str, &str)] = &[ ("rect", "rect"), ("circle", "circle"), ("triangle", "triangle"), ("star", "star"), ("L", "L"), ("T", "T"), ]; let styles: &[(BevelStyle, &str)] = &[ (BevelStyle::InnerBevel, "inner"), (BevelStyle::Emboss, "emboss"), (BevelStyle::OuterBevel, "outer"), ]; let techniques: &[(Technique, &str)] = &[ (Technique::Smooth, "smooth"), ]; let depths: &[f32] = &[50.0, 100.0, 200.0]; let sizes: &[f32] = &[10.0, 25.0]; let softens: &[f32] = &[0.0, 5.0]; let directions: &[(Direction, &str)] = &[ (Direction::Up, "up"), (Direction::Down, "down"), ]; let altitudes: &[f32] = &[30.0, 60.0]; let total = shapes.len() * styles.len() * techniques.len() * depths.len() * sizes.len() * softens.len() * directions.len() * altitudes.len(); println!("Generating {} bevel & emboss PSDs...", total); let mut count = 0usize; let mut errors = 0usize; for &(shape_name, shape_tag) in shapes { for &(style, style_tag) in styles { for &(technique, tech_tag) in techniques { for &depth in depths { for &size in sizes { for &soften in softens { for &(direction, dir_tag) in directions { for &altitude in altitudes { count += 1; let filename = format!( "be_{}_{}_d{}_sz{}_sf{}_{}_alt{}_{}.psd", shape_tag, style_tag, depth as u32, size as u32, soften as u32, dir_tag, altitude as u32, tech_tag ); let path = out_dir.join(&filename); if path.exists() { continue; } let pixels = make_shape(shape_name, canvas); let effect = LayerEffect::BevelEmboss { enabled: true, style, technique, depth, direction, size, soften, angle: 120.0, altitude, highlight_blend: "screen".to_string(), highlight_color: [255, 255, 255, 255], highlight_opacity: 1.0, shadow_blend: "multiply".to_string(), shadow_color: [0, 0, 0, 255], shadow_opacity: 1.0, contour: None, }; let layer = Layer { name: "shape".to_string(), layer_type: LayerType::Raster, data: LayerData::Raster, pixels: pixels.clone(), width: canvas, height: canvas, visible: true, opacity: 1.0, blend_mode: BlendMode::Normal, locked: false, dirty: false, id: 1, parent_id: None, styles: vec![], clipping_mask: false, collapsed: false, adjustment: None, mask_pixels: None, mask_bounds: None, mask_default_color: 0, curve_points: vec![], fill_opacity: 1.0, effects: vec![effect], effects_dirty: AtomicBool::new(false), effects_cache: Mutex::new(None), adjustment_raw: None, is_section_divider: false, image_resources_raw: None, }; match hcie_psd::save_psd(&[layer], canvas, canvas, &pixels, &path) { Ok(()) => {} Err(e) => { eprintln!(" ERROR {}: {}", filename, e); errors += 1; } } if count % 20 == 0 { println!(" [{}/{}] errors={}", count, total, errors); } } } } } } } } } println!("Done. {} PSDs generated, {} errors.", count - errors, errors); }