diff --git a/assets/locales/pending/continuity-words.json b/assets/locales/pending/continuity-words.json new file mode 100644 index 0000000000..a388142cca --- /dev/null +++ b/assets/locales/pending/continuity-words.json @@ -0,0 +1,219 @@ +{ + "en": { + "ui.continuity_position": "Position", + "ui.continuity_tangent": "Tangent", + "ui.continuity_curvature": "Curvature", + "ui.continuity_curvature_change": "Curvature change", + "ui.continuity_crease": "Crease", + "ui.continuity_curvature_change_tip": "Curvature change (G3): the curvature also changes smoothly across the seam.", + "ui.continuity_curvature_tip": "Curvature-continuous (G2): reflections run through without a kink.", + "ui.continuity_position_tip": "Position (G0): the surfaces meet, with a crease where they do.", + "ui.continuity_tangent_tip": "Tangent (G1): no crease, though the curvature jumps across the seam." + }, + "ar": { + "ui.continuity_position": "الموضع", + "ui.continuity_tangent": "مماس", + "ui.continuity_curvature": "الانحناء", + "ui.continuity_curvature_change": "تغيّر الانحناء", + "ui.continuity_crease": "تجعّد", + "ui.continuity_curvature_change_tip": "تغيّر الانحناء (G3): يتغير الانحناء أيضًا بسلاسة عبر خط الالتقاء.", + "ui.continuity_curvature_tip": "متصل الانحناء (G2): تمر الانعكاسات دون انكسار.", + "ui.continuity_position_tip": "الموضع (G0): تلتقي الأسطح، مع تجعّد عند التقائها.", + "ui.continuity_tangent_tip": "مماس (G1): لا تجعّد، لكن الانحناء يقفز عبر خط الالتقاء." + }, + "cs": { + "ui.continuity_position": "Poloha", + "ui.continuity_tangent": "Tečnost", + "ui.continuity_curvature": "Křivost", + "ui.continuity_curvature_change": "Změna křivosti", + "ui.continuity_crease": "Hrana" + }, + "da": { + "ui.continuity_position": "Positionskontinuitet", + "ui.continuity_tangent": "Tangentkontinuitet", + "ui.continuity_curvature": "Krumning", + "ui.continuity_curvature_change": "Ændring i krumning", + "ui.continuity_crease": "Knæk" + }, + "de": { + "ui.continuity_position": "Position", + "ui.continuity_tangent": "Tangential", + "ui.continuity_curvature": "Krümmung", + "ui.continuity_curvature_change": "Krümmungsänderung", + "ui.continuity_crease": "Knick", + "ui.continuity_curvature_change_tip": "Krümmungsänderung (G3): Auch die Krümmung ändert sich an der Naht glatt.", + "ui.continuity_curvature_tip": "Krümmungsstetig (G2): Spiegelungen laufen ohne Knick hindurch.", + "ui.continuity_position_tip": "Position (G0): Die Flächen treffen sich, mit einem Knick an der Naht.", + "ui.continuity_tangent_tip": "Tangential (G1): kein Knick, aber die Krümmung springt an der Naht." + }, + "es": { + "ui.continuity_position": "Posición", + "ui.continuity_tangent": "Tangente", + "ui.continuity_curvature": "Curvatura", + "ui.continuity_curvature_change": "Cambio de curvatura", + "ui.continuity_crease": "Arista" + }, + "fr": { + "ui.continuity_position": "Position", + "ui.continuity_tangent": "Tangence", + "ui.continuity_curvature": "Courbure", + "ui.continuity_curvature_change": "Variation de courbure", + "ui.continuity_crease": "Angle vif" + }, + "gl": { + "ui.continuity_position": "Posición", + "ui.continuity_tangent": "Tanxente", + "ui.continuity_curvature": "Curvatura", + "ui.continuity_curvature_change": "Cambio de curvatura", + "ui.continuity_crease": "Aresta" + }, + "he": { + "ui.continuity_position": "מיקום", + "ui.continuity_tangent": "משיק", + "ui.continuity_curvature": "עקמומיות", + "ui.continuity_curvature_change": "שינוי עקמומיות", + "ui.continuity_crease": "שבר", + "ui.continuity_curvature_change_tip": "שינוי עקמומיות (G3): גם העקמומיות משתנה בצורה חלקה לאורך התפר.", + "ui.continuity_curvature_tip": "רציף בעקמומיות (G2): ההשתקפויות עוברות בלי שבר.", + "ui.continuity_position_tip": "מיקום (G0): המשטחים נפגשים, עם שבר במקום המפגש.", + "ui.continuity_tangent_tip": "משיק (G1): אין שבר, אך העקמומיות קופצת לאורך התפר." + }, + "hu": { + "ui.continuity_position": "Pozíció", + "ui.continuity_tangent": "Érintő", + "ui.continuity_curvature": "Görbület", + "ui.continuity_curvature_change": "Görbületváltozás", + "ui.continuity_crease": "Törés" + }, + "id": { + "ui.continuity_position": "Posisi", + "ui.continuity_tangent": "Tangen", + "ui.continuity_curvature": "Kelengkungan", + "ui.continuity_curvature_change": "Perubahan kelengkungan", + "ui.continuity_crease": "Lipatan" + }, + "it": { + "ui.continuity_position": "Posizione", + "ui.continuity_tangent": "Tangente", + "ui.continuity_curvature": "Curvatura", + "ui.continuity_curvature_change": "Variazione di curvatura", + "ui.continuity_crease": "Spigolo vivo" + }, + "ja": { + "ui.continuity_position": "位置", + "ui.continuity_tangent": "接線", + "ui.continuity_curvature": "曲率", + "ui.continuity_curvature_change": "曲率変化", + "ui.continuity_crease": "折れ", + "ui.continuity_curvature_change_tip": "曲率変化 (G3): 境目で曲率の変化もなめらかです。", + "ui.continuity_curvature_tip": "曲率連続 (G2): 映り込みが折れずに通ります。", + "ui.continuity_position_tip": "位置 (G0): 面はつながりますが、境目に折れがあります。", + "ui.continuity_tangent_tip": "接線 (G1): 折れはありませんが、境目で曲率が飛びます。" + }, + "ko": { + "ui.continuity_position": "위치", + "ui.continuity_tangent": "접선", + "ui.continuity_curvature": "곡률", + "ui.continuity_curvature_change": "곡률 변화", + "ui.continuity_crease": "꺾임", + "ui.continuity_curvature_change_tip": "곡률 변화(G3): 이음매에서 곡률의 변화도 부드럽습니다.", + "ui.continuity_curvature_tip": "곡률 연속(G2): 반사가 꺾임 없이 이어집니다.", + "ui.continuity_position_tip": "위치(G0): 면이 만나지만, 만나는 곳에 꺾임이 있습니다.", + "ui.continuity_tangent_tip": "접선(G1): 꺾임은 없지만, 이음매에서 곡률이 급변합니다." + }, + "ms": { + "ui.continuity_position": "Kedudukan", + "ui.continuity_tangent": "Tangen", + "ui.continuity_curvature": "Kelengkungan", + "ui.continuity_curvature_change": "Perubahan kelengkungan", + "ui.continuity_crease": "Lipatan" + }, + "nb": { + "ui.continuity_position": "Posisjon", + "ui.continuity_tangent": "Tangent", + "ui.continuity_curvature": "Krumning", + "ui.continuity_curvature_change": "Krumningsendring", + "ui.continuity_crease": "Knekk" + }, + "nl": { + "ui.continuity_position": "Positie", + "ui.continuity_tangent": "Tangentieel", + "ui.continuity_curvature": "Kromming", + "ui.continuity_curvature_change": "Krommingsverandering", + "ui.continuity_crease": "Knik" + }, + "pl": { + "ui.continuity_position": "Położenie", + "ui.continuity_tangent": "Styczność", + "ui.continuity_curvature": "Krzywizna", + "ui.continuity_curvature_change": "Zmiana krzywizny", + "ui.continuity_crease": "Załamanie" + }, + "pt-BR": { + "ui.continuity_position": "Posição", + "ui.continuity_tangent": "Tangente", + "ui.continuity_curvature": "Curvatura", + "ui.continuity_curvature_change": "Variação de curvatura", + "ui.continuity_crease": "Vinco" + }, + "ro": { + "ui.continuity_position": "Poziție", + "ui.continuity_tangent": "Tangent", + "ui.continuity_curvature": "Curbură", + "ui.continuity_curvature_change": "Variația curburii", + "ui.continuity_crease": "Muchie" + }, + "ru": { + "ui.continuity_position": "Положение", + "ui.continuity_tangent": "Касание", + "ui.continuity_curvature": "Кривизна", + "ui.continuity_curvature_change": "Изменение кривизны", + "ui.continuity_crease": "Излом" + }, + "sv": { + "ui.continuity_position": "Läge", + "ui.continuity_tangent": "Tangent", + "ui.continuity_curvature": "Krökning", + "ui.continuity_curvature_change": "Krökningsändring", + "ui.continuity_crease": "Veck" + }, + "tr": { + "ui.continuity_position": "Konum", + "ui.continuity_tangent": "Teğet", + "ui.continuity_curvature": "Eğrilik", + "ui.continuity_curvature_change": "Eğrilik değişimi", + "ui.continuity_crease": "Kırık" + }, + "uk": { + "ui.continuity_position": "Положення", + "ui.continuity_tangent": "Дотична", + "ui.continuity_curvature": "Кривизна", + "ui.continuity_curvature_change": "Зміна кривини", + "ui.continuity_crease": "Злам" + }, + "vi": { + "ui.continuity_position": "Vị trí liên tục", + "ui.continuity_tangent": "Tiếp tuyến", + "ui.continuity_curvature": "Độ cong", + "ui.continuity_curvature_change": "Thay đổi độ cong", + "ui.continuity_crease": "Nếp gập" + }, + "zh": { + "ui.continuity_position": "位置", + "ui.continuity_tangent": "相切", + "ui.continuity_curvature": "曲率", + "ui.continuity_curvature_change": "曲率变化", + "ui.continuity_crease": "折痕", + "ui.continuity_curvature_change_tip": "曲率变化 (G3):曲率在接缝处的变化也是平滑的。", + "ui.continuity_curvature_tip": "曲率连续 (G2):反射平滑穿过,没有折痕。", + "ui.continuity_position_tip": "位置 (G0):曲面相接,但接缝处有折痕。", + "ui.continuity_tangent_tip": "相切 (G1):没有折痕,但曲率在接缝处突变。" + }, + "zh-TW": { + "ui.continuity_position": "位置", + "ui.continuity_tangent": "相切", + "ui.continuity_curvature": "曲率", + "ui.continuity_curvature_change": "曲率變化", + "ui.continuity_crease": "摺痕" + } +} diff --git a/crates/ui-egui/src/dialogs_surface.rs b/crates/ui-egui/src/dialogs_surface.rs index 82f09f3ddb..3886152a06 100644 --- a/crates/ui-egui/src/dialogs_surface.rs +++ b/crates/ui-egui/src/dialogs_surface.rs @@ -138,12 +138,29 @@ impl Surf { } /// How a patch on edges meets the faces beside its loop (labels, and the `continuity` parameter). -const PATCH_CONTINUITY: [(&str, &str); 3] = [("Position (G0)", "g0"), ("Tangent (G1)", "g1"), ("Curvature (G2)", "g2")]; +/// Labels are plain words; the order's code (G0, G1, G2) is in each one's hover text. +const PATCH_CONTINUITY: [(&str, &str); 3] = [("ui.continuity_position", "g0"), ("ui.continuity_tangent", "g1"), ("ui.continuity_curvature", "g2")]; /// How much of the surface at its edges each end of a blend repeats (labels, and the /// `continuity` words). const BLEND_CONTINUITY: [(&str, &str); 4] = - [("Position (G0)", "g0"), ("Tangent (G1)", "g1"), ("Curvature (G2)", "g2"), ("Curvature change (G3)", "g3")]; + [("ui.continuity_position", "g0"), ("ui.continuity_tangent", "g1"), ("ui.continuity_curvature", "g2"), ("ui.continuity_curvature_change", "g3")]; + +/// Each continuity's hover text, in the order of [`BLEND_CONTINUITY`]: where its code (G0 … G3) +/// is shown. +const CONTINUITY_TIPS: [&str; 4] = + ["ui.continuity_position_tip", "ui.continuity_tangent_tip", "ui.continuity_curvature_tip", "ui.continuity_curvature_change_tip"]; + +/// A Continuity row's segments over `orders` (the segments draw their text as given). +fn continuity_segments(ui: &mut egui::Ui, selected: &mut usize, orders: &[(&str, &str)]) -> egui::Response { + let labels: Vec<&str> = orders.iter().map(|(label, _)| crate::i18n::tr(label)).collect(); + ui.scope(|ui| ui_kit::segmented(ui, selected, &labels, Default::default())).response +} + +/// The hover text of a list of `orders` continuities: a line for each. +fn continuity_tip(orders: usize) -> String { + CONTINUITY_TIPS.iter().take(orders).map(|tip| crate::i18n::tr(tip)).collect::>().join("\n") +} /// What a boundary surface becomes: a body of its own, or part of the body of its edges. const BOUNDARY_OPS: [&str; 2] = ["New Body", "Join"]; @@ -275,31 +292,27 @@ pub fn rows(ui: &mut egui::Ui, k: &mut Surf) -> bool { ui_kit::options(ui, "untrim_options", &mut [("Restore outer boundary", outer), ("In place (surfaces)", in_place)]); } Surf::Patch { continuity } => { - form_row(ui, "Continuity", |ui| { - ui_kit::segmented(ui, continuity, &PATCH_CONTINUITY.map(|(label, _)| label), Default::default()); - }); + form_row(ui, "Continuity", |ui| continuity_segments(ui, continuity, &PATCH_CONTINUITY)) + .on_hover_text(continuity_tip(PATCH_CONTINUITY.len())); } Surf::Blend { continuity, bulge, stitch } => { let [from, to] = continuity; let [from_bulge, to_bulge] = bulge; - form_row(ui, "Start continuity", |ui| { - combo(ui, "bl_cont0", &BLEND_CONTINUITY.map(|(label, _)| label), from); - }); + form_row(ui, "Start continuity", |ui| ui_kit::dropdown(ui, "bl_cont0", from, &BLEND_CONTINUITY.map(|(label, _)| label))) + .on_hover_text(continuity_tip(BLEND_CONTINUITY.len())); form_row(ui, "Start bulge", |ui| { ui_kit::scalar_field(ui, from_bulge, -0.9..=3.0); }); - form_row(ui, "End continuity", |ui| { - combo(ui, "bl_cont1", &BLEND_CONTINUITY.map(|(label, _)| label), to); - }); + form_row(ui, "End continuity", |ui| ui_kit::dropdown(ui, "bl_cont1", to, &BLEND_CONTINUITY.map(|(label, _)| label))) + .on_hover_text(continuity_tip(BLEND_CONTINUITY.len())); form_row(ui, "End bulge", |ui| { ui_kit::scalar_field(ui, to_bulge, -0.9..=3.0); }); ui_kit::options(ui, "blend_options", &mut [("Stitch to both bodies", stitch)]); } Surf::Boundary { continuity, join } => { - form_row(ui, "Continuity", |ui| { - ui_kit::segmented(ui, continuity, &PATCH_CONTINUITY.map(|(label, _)| label), Default::default()); - }); + form_row(ui, "Continuity", |ui| continuity_segments(ui, continuity, &PATCH_CONTINUITY)) + .on_hover_text(continuity_tip(PATCH_CONTINUITY.len())); form_row(ui, "Operation", |ui| { let mut op = usize::from(*join); combo(ui, "bo_op", &BOUNDARY_OPS, &mut op); diff --git a/crates/ui-egui/src/preselect_tests.rs b/crates/ui-egui/src/preselect_tests.rs index ffcd756930..ccf5ce9dbc 100644 --- a/crates/ui-egui/src/preselect_tests.rs +++ b/crates/ui-egui/src/preselect_tests.rs @@ -2232,7 +2232,8 @@ fn patch_dialog_takes_continuity_and_points_and_brings_them_back() { #[test] fn patch_dialog_rows_are_translated() { - for (lang, continuity, through) in [("en", "Continuity", "Pass through"), ("de", "Stetigkeit", "Durch Punkte"), ("ja", "連続性", "通過点")] + for (lang, continuity, through, tangent) in + [("en", "Continuity", "Pass through", "Tangent"), ("de", "Stetigkeit", "Durch Punkte", "Tangential"), ("ja", "連続性", "通過点", "接線")] { let mut h = crate::scenario::Harness::new(); h.app.preferences.language = lang.into(); @@ -2241,6 +2242,12 @@ fn patch_dialog_rows_are_translated() { h.frames(3); assert!(h.widget(continuity).is_some(), "{lang}: the Continuity row must show {continuity:?}"); assert!(h.widget(through).is_some(), "{lang}: the points input must show {through:?}"); + // The continuity in the language too, in words (the code is in the hover text). + if let Some(crate::dialogs::Dialog { kind: Kind::Surface(crate::dialogs_surface::Surf::Patch { continuity }), .. }) = h.app.dialog.as_mut() { + *continuity = 1; + } + h.frames(2); + assert!(h.widget(tangent).is_some(), "{lang}: the Tangent segment must show {tangent:?}"); } } diff --git a/crates/ui-egui/src/sketch_continuity_tests.rs b/crates/ui-egui/src/sketch_continuity_tests.rs index b624c8ab6e..f62e0e4b99 100644 --- a/crates/ui-egui/src/sketch_continuity_tests.rs +++ b/crates/ui-egui/src/sketch_continuity_tests.rs @@ -21,7 +21,7 @@ fn the_button_shades_every_seam_pins_an_edge_and_turns_off() { assert_eq!(h.app.session.continuity.len(), 12, "every edge of the box"); assert_eq!(h.app.tool.as_ref().map(|t| t.cmd), Some("inspect.continuity")); h.step(&json!({"click": {"world": [10, 0, 20]}})).unwrap(); - assert_eq!(marks(), ["G0 90.000\u{b0}"], "{:?}", h.app.status); + assert_eq!(marks(), ["Crease 90.000\u{b0}"], "{:?}", h.app.status); assert_eq!(h.app.session.continuity.len(), 12, "the clicked edge is measured again, not added twice"); h.step(&json!({"start": "inspect.continuity"})).unwrap(); assert!(h.app.session.continuity.is_empty() && h.app.tool.is_none()); @@ -29,7 +29,7 @@ fn the_button_shades_every_seam_pins_an_edge_and_turns_off() { assert!(marks().is_empty(), "the markers go with the tool"); } -/// A round's tangent seam reads G1 with its curvature jump (1 / 4 mm against the flat side). +/// A round's tangent seam reads Tangent with its curvature jump (1 / 4 mm against the flat side). #[test] fn a_tangent_seam_is_labelled_with_its_curvature_jump() { let mut h = crate::scenario::Harness::new(); @@ -41,7 +41,7 @@ fn a_tangent_seam_is_labelled_with_its_curvature_jump() { h.step(&json!({"click": {"world": [16, 0, 10]}})).unwrap(); let labels = marks(); assert_eq!(labels.len(), 1, "{labels:?} {:?}", h.app.status); - assert!(labels[0].starts_with("G1 0.000\u{b0} \u{394}k 0.2500/mm"), "{labels:?}"); + assert!(labels[0].starts_with("Tangent 0.000\u{b0} \u{394}k 0.2500/mm"), "{labels:?}"); } /// The legend shows while seams are shaded and goes with the analysis. diff --git a/crates/ui-egui/src/sketch_tools.rs b/crates/ui-egui/src/sketch_tools.rs index 1415be77b3..6825eeec91 100644 --- a/crates/ui-egui/src/sketch_tools.rs +++ b/crates/ui-egui/src/sketch_tools.rs @@ -181,9 +181,9 @@ fn continuity_label(app: &SolveApp, seam: &Value) -> String { let jump = seam["curvature_jump_per_mm"]["value"].as_f64().map(|k| format!("\u{394}k {k:.4}/mm")).unwrap_or_default(); match seam["verdict"].as_str().unwrap_or_default() { "gap" => format!("Gap {}", seam["gap_mm"]["value"].as_f64().map(|g| crate::prefs::show_mm(app, g, 1)).unwrap_or_default()), - "sharp" => format!("G0 {}", deg("normal_angle_deg")), - "curvature_jump" => format!("G1 {} {jump}", deg("normal_angle_deg")), - "smooth" => format!("G2 {} {jump}", deg("normal_angle_deg")), + "sharp" => format!("{} {}", crate::i18n::tr("ui.continuity_crease"), deg("normal_angle_deg")), + "curvature_jump" => format!("{} {} {jump}", crate::i18n::tr("ui.continuity_tangent"), deg("normal_angle_deg")), + "smooth" => format!("{} {} {jump}", crate::i18n::tr("ui.continuity_curvature"), deg("normal_angle_deg")), other => other.to_string(), } } @@ -198,7 +198,12 @@ fn continuity_legend(app: &SolveApp, painter: &egui::Painter, proj: &Proj) { let [smooth, jump, sharp, gap] = tk.continuity; // Stable ids: this Gap is a hole between faces, not the sheet-metal gap (`ui.sheet_gap`). let tr = crate::i18n::tr; - let rows = [(tr("ui.continuity_g2"), smooth), (tr("ui.continuity_g1"), jump), (tr("ui.continuity_g0"), sharp), (tr("ui.continuity_gap"), gap)]; + let rows = [ + (tr("ui.continuity_curvature"), smooth), + (tr("ui.continuity_tangent"), jump), + (tr("ui.continuity_crease"), sharp), + (tr("ui.continuity_gap"), gap), + ]; let font = egui::FontId::proportional(tk.font_body); let (row, pad, swatch) = (18.0, 8.0, 22.0); let text_width = rows.iter().map(|(label, _)| painter.layout_no_wrap(label.to_string(), font.clone(), tk.text).size().x).fold(0.0, f32::max); @@ -303,10 +308,16 @@ pub fn start_hook(app: &mut SolveApp, id: &str) -> bool { } let Ok(v) = app.run(id, json!({})) else { return false }; let summary = &v["summary"]; - let counts: Vec = [("smooth", "G2"), ("curvature_jump", "G1"), ("sharp", "G0"), ("gap", "gap")] - .iter() - .filter_map(|(key, class)| summary[*key].as_u64().filter(|n| *n > 0).map(|n| format!("{n} {class}"))) - .collect(); + // Each class in the legend's words. + let counts: Vec = [ + ("smooth", "ui.continuity_curvature"), + ("curvature_jump", "ui.continuity_tangent"), + ("sharp", "ui.continuity_crease"), + ("gap", "ui.continuity_gap"), + ] + .iter() + .filter_map(|(key, class)| summary[*key].as_u64().filter(|n| *n > 0).map(|n| format!("{n} {}", crate::i18n::tr(class)))) + .collect(); app.set_status(format!("Continuity: {} (click the button again to turn it off)", counts.join(", ")), false); return true; }