diff --git a/Cargo.toml b/Cargo.toml index c1a80c2..cdee0d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,3 +78,9 @@ lto = "fat" strip = true codegen-units = 1 panic = "abort" + +[profile.dev] +opt-level = 1 + +[profile.dev.package."*"] +opt-level = 3 diff --git a/crates/bone-app/src/chrome.rs b/crates/bone-app/src/chrome.rs index 6dcc061..a99275f 100644 --- a/crates/bone-app/src/chrome.rs +++ b/crates/bone-app/src/chrome.rs @@ -1,11 +1,11 @@ use std::borrow::Cow; use bone_render::{ChromeInstance, ConvexInstance, IconInstance, SdfGlyphInstance, StrokeInstance}; -use bone_text::{FontFace, FontWeight, ShapeRequest, ShapedLine, ShapedText, Shaper}; +use bone_text::{FontFace, FontWeight, MaxWidth, ShapeRequest, ShapedLine, ShapedText, Shaper}; use bone_types::IconTile; use bone_ui::layout::LayoutRect; use bone_ui::strings::StringTable; -use bone_ui::text::{MaskAtlas, MaskAtlasKey}; +use bone_ui::text::{MaskAtlas, MaskAtlasKey, line_start_x}; use bone_ui::theme::{Border, Color, StrokeWidth, Theme}; use bone_ui::widgets::{ ConvexPoly, HorizontalAlign, IconTint, PaintPrim, PolyPath, WidgetPaint, lower_paint, @@ -33,6 +33,7 @@ pub struct ChromeTextSpan<'a> { pub weight: FontWeight, pub vertical: SpanVertical, pub horizontal: HorizontalAlign, + pub wrap: bool, } #[must_use] @@ -44,6 +45,7 @@ pub fn paint_to_instances(theme: &Theme, paints: &[WidgetPaint]) -> Vec( weight: role.weight, vertical: SpanVertical::Baseline, horizontal: HorizontalAlign::Center, + wrap: false, }), WidgetPaint::AlignedLabel { rect, @@ -188,6 +191,24 @@ pub fn paint_to_text_spans<'a>( weight: role.weight, vertical: SpanVertical::Baseline, horizontal: *align, + wrap: false, + }), + WidgetPaint::Paragraph { + rect, + text, + color, + role, + align, + } => Some(ChromeTextSpan { + rect_px: rect_to_xywh(*rect), + text: text.resolve(strings), + color_premul_rgba: color.linear_rgba_premul(), + font_size_px: role.size.as_px_f32(), + face: role.face, + weight: role.weight, + vertical: SpanVertical::Baseline, + horizontal: *align, + wrap: true, }), WidgetPaint::Mark { rect, kind, color } => Some(ChromeTextSpan { rect_px: rect_to_xywh(*rect), @@ -198,6 +219,7 @@ pub fn paint_to_text_spans<'a>( weight: FontWeight::Regular, vertical: SpanVertical::RectCenter, horizontal: HorizontalAlign::Center, + wrap: false, }), _ => None, }) @@ -245,6 +267,11 @@ fn push_span_instances( shaper: &mut Shaper, fonts: &ChromeFonts<'_>, ) { + let [rx, ry, rw, rh] = item.rect_px; + if item.wrap { + push_wrapped_span_instances(out, item, atlas, shaper, [rx, ry, rw]); + return; + } let request = ShapeRequest { face: item.face, size_px: item.font_size_px, @@ -253,7 +280,6 @@ fn push_span_instances( letter_spacing_px: 0.0, max_width: None, }; - let [rx, ry, rw, rh] = item.rect_px; let (_text_for_paint, layout) = fit_with_ellipsis(item.text, rw, shaper, request); let metrics = fonts .for_face(item.face) @@ -263,11 +289,7 @@ fn push_span_instances( .lines .first() .map_or(0.0, ShapedLine::visible_advance_px); - let start_x = match item.horizontal { - HorizontalAlign::Center => rx + ((rw - visible_advance) * 0.5).max(0.0), - HorizontalAlign::Start => rx, - HorizontalAlign::End => rx + (rw - visible_advance).max(0.0), - }; + let start_x = line_start_x(item.horizontal, rx, rw, visible_advance); let line = LineLayout { face: item.face, size_px: item.font_size_px, @@ -291,6 +313,45 @@ fn push_span_instances( }); } +fn push_wrapped_span_instances( + out: &mut Vec, + item: &ChromeTextSpan<'_>, + atlas: &mut MaskAtlas, + shaper: &mut Shaper, + [rx, ry, rw]: [f32; 3], +) { + let request = ShapeRequest { + face: item.face, + size_px: item.font_size_px, + weight: item.weight, + line_height_px: 0.0, + letter_spacing_px: 0.0, + max_width: MaxWidth::new(rw), + }; + let layout = shaper.shape(item.text, request); + layout.lines.iter().for_each(|shaped| { + let visible_advance = shaped.visible_advance_px(); + let start_x = line_start_x(item.horizontal, rx, rw, visible_advance); + let line = LineLayout { + face: item.face, + size_px: item.font_size_px, + color: item.color_premul_rgba, + start_x, + baseline_y: ry + shaped.baseline_px, + vertical: SpanVertical::Baseline, + rect_center_y: ry + shaped.baseline_px, + }; + shaped.runs.iter().for_each(|run| { + let run_origin = run.origin_x_px; + run.glyphs.iter().for_each(|g| { + if let Some(instance) = build_instance(atlas, &line, g.id, run_origin + g.x_px) { + out.push(instance); + } + }); + }); + }); +} + fn shape_advance(shaper: &mut Shaper, text: &str, request: ShapeRequest) -> f32 { shaper .shape(text, request) @@ -513,6 +574,41 @@ mod tests { assert_eq!(instances.len(), 2, "Hi has two glyphs"); } + #[test] + fn paragraph_paints_wrap_across_multiple_baselines() { + let theme = Theme::light(); + let paints = vec![WidgetPaint::Paragraph { + rect: rect(0.0, 0.0, 90.0, 200.0), + text: LabelText::Owned( + "the quick brown fox jumps over the lazy dog many times over".to_owned(), + ), + color: theme.colors.text_secondary(), + role: theme.typography.body, + align: HorizontalAlign::Start, + }]; + let spans = paint_to_text_spans(&paints, StringTable::empty()); + assert_eq!(spans.len(), 1); + assert!(spans[0].wrap, "a Paragraph span requests wrapping"); + let mut atlas = MaskAtlas::new(bone_ui::MaskAtlasParams::STANDARD); + let mut shaper = Shaper::new(); + let sans_font = bone_text::load_font(FontFace::Sans); + let mono_font = bone_text::load_font(FontFace::Mono); + let instances = + build_glyph_instances(&spans, &mut atlas, &mut shaper, &sans_font, &mono_font); + let min_y = instances + .iter() + .map(|glyph| glyph.rect_xywh_px[1]) + .fold(f32::INFINITY, f32::min); + let max_y = instances + .iter() + .map(|glyph| glyph.rect_xywh_px[1]) + .fold(f32::NEG_INFINITY, f32::max); + assert!( + max_y - min_y > theme.typography.body.size.as_px_f32(), + "wrapped text must span more than one line vertically" + ); + } + fn mark(kind: GlyphMark, theme: &Theme) -> WidgetPaint { WidgetPaint::Mark { rect: rect(40.0, 60.0, 16.0, 16.0), diff --git a/crates/bone-app/src/heads_up.rs b/crates/bone-app/src/heads_up.rs index d41c8c8..957bbeb 100644 --- a/crates/bone-app/src/heads_up.rs +++ b/crates/bone-app/src/heads_up.rs @@ -70,7 +70,7 @@ const GROUPS: [&[HeadsUpTool]; 3] = [ key: "hide_show", icon: IconId::HideShowItems, label: strings::HEADS_UP_HIDE_SHOW, - action: None, + action: Some(MenuAction::TogglePlanes), }, ], &[ diff --git a/crates/bone-app/src/sketch3_mode.rs b/crates/bone-app/src/sketch3_mode.rs index c2a2a4d..72e4f9a 100644 --- a/crates/bone-app/src/sketch3_mode.rs +++ b/crates/bone-app/src/sketch3_mode.rs @@ -32,6 +32,7 @@ type Ray = ((f64, f64, f64), (f64, f64, f64)); #[derive(Copy, Clone, Debug, PartialEq)] pub enum Snap3 { + Origin { at: Point3 }, Endpoint { point: SketchEntityId, at: Point3 }, Midpoint { line: SketchEntityId, at: Point3 }, OnEdge { entity: SketchEntityId, at: Point3 }, @@ -41,7 +42,10 @@ impl Snap3 { #[must_use] pub fn at(self) -> Point3 { match self { - Self::Endpoint { at, .. } | Self::Midpoint { at, .. } | Self::OnEdge { at, .. } => at, + Self::Origin { at } + | Self::Endpoint { at, .. } + | Self::Midpoint { at, .. } + | Self::OnEdge { at, .. } => at, } } } @@ -96,6 +100,7 @@ pub fn nearest_snap3( pixel: ViewportPoint, tolerance: Length, exclude: Option, + reference: Option, ) -> Option { let Ok((origin, direction)) = world_ray(camera, extent, pixel) else { return None; @@ -104,6 +109,12 @@ pub fn nearest_snap3( let tol = tolerance.get::(); let tol2 = tol * tol; + if let Some(anchor) = reference + && ray_perp_dist2(ray, anchor) <= tol2 + { + return Some(Snap3::Origin { at: anchor }); + } + let points = sketch .entity_order() .iter() diff --git a/crates/bone-app/src/sketch3_project.rs b/crates/bone-app/src/sketch3_project.rs index 33771ad..31133f4 100644 --- a/crates/bone-app/src/sketch3_project.rs +++ b/crates/bone-app/src/sketch3_project.rs @@ -287,7 +287,7 @@ fn unproject_relation( kind: dimension_kind(reference), })); } - Sketch3Relation::OnPlane { .. } => return None, + Sketch3Relation::OnPlane { .. } | Sketch3Relation::Pierce { .. } => return None, }; Some(SketchEdit::AddRelation(mapped)) } @@ -468,6 +468,7 @@ fn embed_entity( } } +#[cfg(test)] #[must_use] pub fn embed_sketch(sketch: &Sketch) -> Sketch3 { let basis = sketch.plane(); diff --git a/crates/bone-app/src/sketch_mode.rs b/crates/bone-app/src/sketch_mode.rs index df84812..8dba71e 100644 --- a/crates/bone-app/src/sketch_mode.rs +++ b/crates/bone-app/src/sketch_mode.rs @@ -2,12 +2,13 @@ use core::num::NonZeroU32; use bone_document::{ AxisRecipe, CsysAxisRef, CsysRecipe, DatumFeature, ExtrudeEndCondition, ExtrudeFeature, - ExtrudeOperation, PlaneRecipe, PointRecipe, RevolveAngle, RevolveAxis, RevolveFeature, Sketch3, - Sketch3Entity, Sketch3Relation, SketchDimension, SweepFeature, SweepPath, + ExtrudeOperation, PlaneRecipe, PointRecipe, PrincipalPlane, RevolveAngle, RevolveAxis, + RevolveFeature, Sketch3, Sketch3Entity, Sketch3Relation, SketchDimension, SweepFeature, + SweepPath, }; use bone_types::{ DatumId, DatumTarget, ExtrudeId, FeatureId, Length, Point2, Point3, PositiveLength, RevolveId, - SketchEntityId, SketchId, SketchPlaneBasis, SweepId, Tolerance, UnitVec3, + SketchEntityId, SketchId, SketchPlaneBasis, SweepId, }; use bone_ui::hotkey::ActionId; use uom::si::length::millimeter; @@ -431,15 +432,27 @@ impl Plane { #[must_use] pub fn basis(self) -> SketchPlaneBasis { - let (x, y) = match self { - Self::Xy => (UnitVec3::x_axis(), UnitVec3::y_axis()), - Self::Yz => (UnitVec3::y_axis(), UnitVec3::z_axis()), - Self::Zx => (UnitVec3::z_axis(), UnitVec3::x_axis()), - }; - let Ok(basis) = SketchPlaneBasis::new(Point3::origin(), x, y, Tolerance::new(1e-9)) else { - unreachable!("canonical principal-plane axes are orthonormal"); - }; - basis + PrincipalPlane::from(self).basis() + } +} + +impl From for PrincipalPlane { + fn from(plane: Plane) -> Self { + match plane { + Plane::Xy => Self::Xy, + Plane::Yz => Self::Yz, + Plane::Zx => Self::Zx, + } + } +} + +impl From for Plane { + fn from(plane: PrincipalPlane) -> Self { + match plane { + PrincipalPlane::Xy => Self::Xy, + PrincipalPlane::Yz => Self::Yz, + PrincipalPlane::Zx => Self::Zx, + } } } @@ -583,9 +596,34 @@ pub struct SketchSession { pub aid_anchor: Option, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SketchPlaneSource { + Principal(Plane), + Datum(FeatureId), +} + #[derive(Copy, Clone, Debug, PartialEq)] pub struct Spatial { - pub plane: Plane, + pub basis: SketchPlaneBasis, + pub source: SketchPlaneSource, +} + +impl Spatial { + #[must_use] + pub fn principal(plane: Plane) -> Self { + Self { + basis: plane.basis(), + source: SketchPlaneSource::Principal(plane), + } + } + + #[must_use] + pub fn datum(feature: FeatureId, basis: SketchPlaneBasis) -> Self { + Self { + basis, + source: SketchPlaneSource::Datum(feature), + } + } } #[derive(Clone, Debug, Default, PartialEq)] @@ -677,11 +715,11 @@ impl Mode { } #[must_use] - pub fn enter_sketch3(sketch_id: SketchId, plane: Plane) -> Self { + pub fn enter_sketch3(sketch_id: SketchId, spatial: Spatial) -> Self { Self::Sketch { sketch_id, session: Box::new(SketchSession { - spatial: Some(Spatial { plane }), + spatial: Some(spatial), ..SketchSession::default() }), } @@ -689,11 +727,15 @@ impl Mode { #[must_use] pub fn cycle_sketch3_plane(self) -> Self { - self.map_session_spatial(|s, sp| SketchSession { - spatial: Some(Spatial { - plane: sp.plane.next(), - }), - ..s + self.map_session_spatial(|s, sp| match sp.source { + SketchPlaneSource::Principal(plane) => SketchSession { + spatial: Some(Spatial::principal(plane.next())), + ..s + }, + SketchPlaneSource::Datum(_) => SketchSession { + spatial: Some(sp), + ..s + }, }) } @@ -709,7 +751,9 @@ impl Mode { sketch_id, session: Box::new(f(*session)), }, - Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => self, + Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => { + self + } } } @@ -760,7 +804,9 @@ impl Mode { pub fn spline_draft(&self) -> &[SplineDraftPoint] { match self { Self::Sketch { session, .. } => &session.spline_draft, - Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => &[], + Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => { + &[] + } } } @@ -871,7 +917,9 @@ impl Mode { pub fn sketch_id(&self) -> Option { match self { Self::Sketch { sketch_id, .. } => Some(*sketch_id), - Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => None, + Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => { + None + } } } @@ -895,7 +943,9 @@ impl Mode { pub fn dim_flow(&self) -> Option { match self { Self::Sketch { session, .. } => session.dim_flow, - Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => None, + Self::Idle | Self::Extrude(_) | Self::Revolve(_) | Self::Sweep(_) | Self::Datum(_) => { + None + } } } @@ -908,12 +958,28 @@ impl Mode { } #[must_use] - pub fn sketch3_plane(&self) -> Option { + pub fn sketch3_source(&self) -> Option { match self { - Self::Sketch { session, .. } => session.spatial.map(|sp| sp.plane), + Self::Sketch { session, .. } => session.spatial.map(|sp| sp.source), _ => None, } } + + #[must_use] + pub fn sketch3_basis(&self) -> Option { + match self { + Self::Sketch { session, .. } => session.spatial.map(|sp| sp.basis), + _ => None, + } + } + + #[must_use] + pub fn sketch3_plane(&self) -> Option { + match self.sketch3_source()? { + SketchPlaneSource::Principal(plane) => Some(plane), + SketchPlaneSource::Datum(_) => None, + } + } } #[cfg(test)] @@ -921,8 +987,8 @@ mod tests { use super::{ ClickAnchor, DEFAULT_EXTRUDE_DEPTH_MM, DimensionFlow, DragPins, DragSession, EndConditionKind, ExtrudeArming, ExtrudeEndCondition, ExtrudeOperation, FeatureTool, Mode, - ModeExit, ModeLifecycle, Pending, Plane, SketchSession, SketchTool, SplineDraftPoint, - default_extrude_feature, end_condition_depth, + ModeExit, ModeLifecycle, Pending, Plane, SketchPlaneSource, SketchSession, SketchTool, + Spatial, SplineDraftPoint, default_extrude_feature, end_condition_depth, }; use bone_document::{ExtrudeDirection, ExtrudeSense, MergeResult}; use bone_types::{Length, Point2, Point3, PositiveLength, SketchEntityId, SketchId}; @@ -947,7 +1013,7 @@ mod tests { }, ); assert_eq!( - Mode::enter_sketch3(sketch3, Plane::Xy).lifecycle(), + Mode::enter_sketch3(sketch3, Spatial::principal(Plane::Xy)).lifecycle(), ModeLifecycle { button: Some(FeatureTool::Sketch3D), exit: ModeExit::Toggle, @@ -966,7 +1032,7 @@ mod tests { #[test] fn a_toggle_mode_button_round_trips_to_its_owner() { - let mode = Mode::enter_sketch3(SketchId::default(), Plane::Xy); + let mode = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)); let lifecycle = mode.lifecycle(); assert_eq!(lifecycle.exit, ModeExit::Toggle); assert_eq!(lifecycle.button, Some(FeatureTool::Sketch3D)); @@ -1113,7 +1179,7 @@ mod tests { #[test] fn spline_draft_accumulates_then_clears() { let id = SketchEntityId::default(); - let mode = Mode::enter_sketch3(SketchId::default(), Plane::Xy) + let mode = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)) .with_spline_point(SplineDraftPoint::fresh(id)) .with_spline_point(SplineDraftPoint::fresh(id)) .with_spline_point(SplineDraftPoint::fresh(id)); @@ -1131,7 +1197,7 @@ mod tests { #[test] fn escape_path_clears_spline_draft_first() { let id = SketchEntityId::default(); - let mode = Mode::enter_sketch3(SketchId::default(), Plane::Xy) + let mode = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)) .arm_tool(SketchTool::Spline) .with_spline_point(SplineDraftPoint::fresh(id)); let cleared = mode.clear_pending(); @@ -1341,28 +1407,30 @@ mod tests { #[test] fn cycle_sketch3_plane_advances_only_in_sketch3() { - let sketch3 = Mode::enter_sketch3(SketchId::default(), Plane::Xy); + let sketch3 = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)); let Mode::Sketch { session, .. } = sketch3.cycle_sketch3_plane() else { panic!("cycling a 3D sketch stays in sketch mode"); }; let Some(spatial) = session.spatial else { panic!("cycling a 3D sketch stays spatial"); }; - assert_eq!(spatial.plane, Plane::Yz); + assert_eq!(spatial.source, SketchPlaneSource::Principal(Plane::Yz)); assert_eq!(Mode::Idle.cycle_sketch3_plane(), Mode::Idle); } #[test] fn every_entity_tool_arms_in_the_3d_sketch() { SketchTool::ENTITIES.iter().copied().for_each(|tool| { - let armed = Mode::enter_sketch3(SketchId::default(), Plane::Xy).arm_tool(tool); + let armed = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)) + .arm_tool(tool); assert_eq!(armed.sketch3_tool(), Some(tool)); }); } #[test] fn sketch3_tool_reads_the_armed_3d_tool_only_in_sketch3() { - let armed = Mode::enter_sketch3(SketchId::default(), Plane::Xy).arm_tool(SketchTool::Line); + let armed = Mode::enter_sketch3(SketchId::default(), Spatial::principal(Plane::Xy)) + .arm_tool(SketchTool::Line); assert_eq!(armed.sketch3_tool(), Some(SketchTool::Line)); assert_eq!(armed.active_tool(), None); assert_eq!(Mode::Idle.sketch3_tool(), None); diff --git a/crates/bone-app/src/status_badge.rs b/crates/bone-app/src/status_badge.rs index 0a0080b..219dc43 100644 --- a/crates/bone-app/src/status_badge.rs +++ b/crates/bone-app/src/status_badge.rs @@ -86,7 +86,8 @@ fn extrude_error_is_dangling(error: &ExtrudeError) -> bool { } ExtrudeError::UnsolvedSketch(_) | ExtrudeError::CutWithoutBody - | ExtrudeError::FlipSideUnsupported => false, + | ExtrudeError::FlipSideUnsupported + | ExtrudeError::UnresolvedReference => false, } } @@ -100,6 +101,9 @@ pub fn extrude_panel_line(error: &ExtrudeError, strings_table: &StringTable) -> ExtrudeError::FlipSideUnsupported => { LabelText::Key(strings::EXTRUDE_PANEL_UNSUPPORTED_FLIP) } + ExtrudeError::UnresolvedReference => { + LabelText::Key(strings::EXTRUDE_PANEL_UNRESOLVED_REFERENCE) + } } } @@ -115,6 +119,11 @@ fn rebuild_error_key(error: RebuildError) -> StringKey { fn kernel_panel_line(error: &BrepError, strings_table: &StringTable) -> LabelText { match error { BrepError::InvalidProfile { reason } => LabelText::Key(profile_defect_key(*reason)), + BrepError::InvalidSweep { reason } => LabelText::Owned( + strings_table + .resolve(strings::SWEEP_PANEL_INVALID) + .replace("{detail}", &reason.to_string()), + ), BrepError::EmptyExtrudeDepth | BrepError::EndConditionBehindProfile => { LabelText::Key(strings::EXTRUDE_PANEL_EMPTY_DEPTH) } @@ -139,6 +148,8 @@ fn kernel_panel_line(error: &BrepError, strings_table: &StringTable) -> LabelTex LabelText::Key(strings::EXTRUDE_PANEL_DANGLING_REFERENCE) } BrepError::RevolveAxisUnresolved => LabelText::Key(strings::EXTRUDE_PANEL_REVOLVE_AXIS), + BrepError::SweepAlignmentUnresolved => LabelText::Key(strings::SWEEP_PANEL_ALIGNMENT_AXIS), + BrepError::SweepToolUnresolved => LabelText::Key(strings::SWEEP_PANEL_TOOL_BODY), BrepError::RevolveAxisNotInPlane => { LabelText::Key(strings::EXTRUDE_PANEL_REVOLVE_AXIS_PLANE) } @@ -202,11 +213,13 @@ fn truck_gap_key(gap: TruckGap) -> StringKey { TruckGap::RevolvePolePartial | TruckGap::RevolvePoleThin => { strings::REVOLVE_PANEL_UNSUPPORTED_POLE } - TruckGap::SweepGeneralPath => strings::SWEEP_PANEL_GENERAL_PATH, - TruckGap::SweepProfileKind => strings::SWEEP_PANEL_PROFILE_KIND, - TruckGap::SweepOrientation => strings::SWEEP_PANEL_ORIENTATION, - TruckGap::SweepProfileOffPath => strings::SWEEP_PANEL_OFF_PATH, - TruckGap::SweepThinWall => strings::SWEEP_PANEL_THIN, + TruckGap::SweepPathCorner => strings::SWEEP_PANEL_PATH_CORNER, + TruckGap::SweepClosedPath => strings::SWEEP_PANEL_CLOSED_PATH, + TruckGap::SweepMultiLoopProfile => strings::SWEEP_PANEL_MULTI_LOOP, + TruckGap::SweepThinCap => strings::SWEEP_PANEL_THIN, + TruckGap::SweepGuideCount => strings::SWEEP_PANEL_GUIDE_COUNT, + TruckGap::SweepSolidTool => strings::SWEEP_PANEL_SOLID_TOOL, + TruckGap::SweepSolidEnvelope => strings::SWEEP_PANEL_SOLID_ENVELOPE, } } diff --git a/crates/bone-app/src/strings.rs b/crates/bone-app/src/strings.rs index 1389dfd..bbd4fd1 100644 --- a/crates/bone-app/src/strings.rs +++ b/crates/bone-app/src/strings.rs @@ -115,6 +115,8 @@ pub const FEATURE_CTX_SUPPRESS: StringKey = StringKey::new("feature.ctx.suppress pub const FEATURE_CTX_UNSUPPRESS: StringKey = StringKey::new("feature.ctx.unsuppress"); pub const FEATURE_CTX_ROLLBACK: StringKey = StringKey::new("feature.ctx.rollback"); pub const FEATURE_CTX_DELETE: StringKey = StringKey::new("feature.ctx.delete"); +pub const FEATURE_CTX_HIDE: StringKey = StringKey::new("feature.ctx.hide"); +pub const FEATURE_CTX_SHOW: StringKey = StringKey::new("feature.ctx.show"); pub const FEATURE_CTX_RELATIONSHIPS: StringKey = StringKey::new("feature.ctx.relationships"); pub const FEATURE_REL_PARENTS: StringKey = StringKey::new("feature.rel.parents"); pub const FEATURE_REL_CHILDREN: StringKey = StringKey::new("feature.rel.children"); @@ -285,8 +287,13 @@ pub const SHORTCUT_BAR_TITLE: StringKey = StringKey::new("shortcut_bar.title"); pub const PROPERTY_PANE_NO_SELECTION: StringKey = StringKey::new("property.no_selection"); pub const EXTRUDE_PROMPT_SELECT_SKETCH: StringKey = StringKey::new("extrude.prompt.select_sketch"); +pub const SWEEP_PROMPT_SELECT_PROFILE: StringKey = StringKey::new("sweep.prompt.select_profile"); pub const EXTRUDE_PROFILE_SELECTED: StringKey = StringKey::new("extrude.profile.selected"); pub const FEATURE_HINT_EXIT_SKETCH: StringKey = StringKey::new("feature.hint.exit_sketch"); +pub const FEATURE_HINT_PLANE_AT_ENDPOINT: StringKey = + StringKey::new("feature.hint.plane_at_endpoint"); +pub const FEATURE_HINT_PLANE_NEEDS_ENDPOINT: StringKey = + StringKey::new("feature.hint.plane_needs_endpoint"); pub const NOTIFY_EXTRUDE_NO_SKETCH: StringKey = StringKey::new("notify.extrude.no_sketch"); pub const NOTIFY_EXTRUDE_FAILED: StringKey = StringKey::new("notify.extrude.failed"); pub const PROPERTY_ROW_EXTRUDE_END: StringKey = @@ -326,6 +333,7 @@ pub const PROPERTY_PANE_SWEEP_TITLE: StringKey = StringKey::new("property.pane.s pub const PROPERTY_GROUP_SWEEP_PROFILE_PATH: StringKey = StringKey::new("property.group.sweep.profile_path"); pub const PROPERTY_GROUP_SWEEP_OPTIONS: StringKey = StringKey::new("property.group.sweep.options"); +pub const PROPERTY_GROUP_SWEEP_GUIDES: StringKey = StringKey::new("property.group.sweep.guides"); pub const PROPERTY_ROW_SWEEP_PROFILE: StringKey = StringKey::new("property.row.sweep.profile"); pub const PROPERTY_ROW_SWEEP_PATH: StringKey = StringKey::new("property.row.sweep.path"); pub const PROPERTY_ROW_SWEEP_ORIENTATION: StringKey = @@ -333,15 +341,59 @@ pub const PROPERTY_ROW_SWEEP_ORIENTATION: StringKey = pub const PROPERTY_ROW_SWEEP_PROFILE_KIND: StringKey = StringKey::new("property.row.sweep.profile_kind"); pub const PROPERTY_ROW_SWEEP_DIAMETER: StringKey = StringKey::new("property.row.sweep.diameter"); +pub const PROPERTY_ROW_SWEEP_TWIST: StringKey = StringKey::new("property.row.sweep.twist"); +pub const PROPERTY_ROW_SWEEP_TWIST_UNIT: StringKey = + StringKey::new("property.row.sweep.twist_unit"); +pub const PROPERTY_ROW_SWEEP_DIRECTION: StringKey = StringKey::new("property.row.sweep.direction"); pub const SWEEP_PROFILE_SKETCH: StringKey = StringKey::new("sweep.profile.sketch"); pub const SWEEP_PATH_SELECT: StringKey = StringKey::new("sweep.path.select"); +pub const SWEEP_PATH_PICK: StringKey = StringKey::new("sweep.path.pick"); pub const SWEEP_PATH_SKETCH: StringKey = StringKey::new("sweep.path.sketch"); pub const SWEEP_PATH_EDGE: StringKey = StringKey::new("sweep.path.edge"); pub const SWEEP_ORIENTATION_FOLLOW: StringKey = StringKey::new("sweep.orientation.follow"); pub const SWEEP_ORIENTATION_KEEP_NORMAL: StringKey = StringKey::new("sweep.orientation.keep_normal"); +pub const SWEEP_ORIENTATION_TWIST: StringKey = StringKey::new("sweep.orientation.twist"); +pub const SWEEP_ORIENTATION_TWIST_NORMAL: StringKey = + StringKey::new("sweep.orientation.twist_normal"); +pub const SWEEP_TWIST_UNIT_DEGREES: StringKey = StringKey::new("sweep.twist_unit.degrees"); +pub const SWEEP_TWIST_UNIT_RADIANS: StringKey = StringKey::new("sweep.twist_unit.radians"); +pub const SWEEP_TWIST_UNIT_REVOLUTIONS: StringKey = StringKey::new("sweep.twist_unit.revolutions"); pub const SWEEP_PROFILE_KIND_SKETCH: StringKey = StringKey::new("sweep.profile_kind.sketch"); pub const SWEEP_PROFILE_KIND_CIRCULAR: StringKey = StringKey::new("sweep.profile_kind.circular"); +pub const SWEEP_PROFILE_KIND_SOLID: StringKey = StringKey::new("sweep.profile_kind.solid"); +pub const PROPERTY_ROW_SWEEP_TOOL: StringKey = StringKey::new("property.row.sweep.tool"); +pub const SWEEP_TOOL_BODY: StringKey = StringKey::new("sweep.tool.body"); +pub const SWEEP_TOOL_SELECT: StringKey = StringKey::new("sweep.tool.select"); +pub const SWEEP_DIRECTION_ONE: StringKey = StringKey::new("sweep.direction.one"); +pub const SWEEP_DIRECTION_TWO: StringKey = StringKey::new("sweep.direction.two"); +pub const SWEEP_DIRECTION_BIDIRECTIONAL: StringKey = + StringKey::new("sweep.direction.bidirectional"); +pub const PROPERTY_ROW_SWEEP_GUIDE_ONE: StringKey = StringKey::new("property.row.sweep.guide_one"); +pub const PROPERTY_ROW_SWEEP_GUIDE_TWO: StringKey = StringKey::new("property.row.sweep.guide_two"); +pub const SWEEP_GUIDE_NONE: StringKey = StringKey::new("sweep.guide.none"); +pub const SWEEP_GUIDE_SELECT: StringKey = StringKey::new("sweep.guide.select"); +pub const SWEEP_GUIDE_SKETCH: StringKey = StringKey::new("sweep.guide.sketch"); +pub const SWEEP_GUIDE_EDGE: StringKey = StringKey::new("sweep.guide.edge"); +pub const SWEEP_GUIDE_CLEAR: StringKey = StringKey::new("sweep.guide.clear"); +pub const SWEEP_ORIENTATION_FIRST_GUIDE: StringKey = + StringKey::new("sweep.orientation.first_guide"); +pub const SWEEP_ORIENTATION_BOTH_GUIDES: StringKey = + StringKey::new("sweep.orientation.both_guides"); +pub const PROPERTY_ROW_SWEEP_START_TANGENCY: StringKey = + StringKey::new("property.row.sweep.start_tangency"); +pub const PROPERTY_ROW_SWEEP_END_TANGENCY: StringKey = + StringKey::new("property.row.sweep.end_tangency"); +pub const SWEEP_TANGENCY_NONE: StringKey = StringKey::new("sweep.tangency.none"); +pub const SWEEP_TANGENCY_PATH: StringKey = StringKey::new("sweep.tangency.path"); +pub const PROPERTY_ROW_SWEEP_ALIGNMENT: StringKey = StringKey::new("property.row.sweep.alignment"); +pub const PROPERTY_ROW_SWEEP_ALIGNMENT_AXIS: StringKey = + StringKey::new("property.row.sweep.alignment_axis"); +pub const SWEEP_ALIGNMENT_MINIMUM_TWIST: StringKey = + StringKey::new("sweep.alignment.minimum_twist"); +pub const SWEEP_ALIGNMENT_NONE: StringKey = StringKey::new("sweep.alignment.none"); +pub const SWEEP_ALIGNMENT_DIRECTION: StringKey = StringKey::new("sweep.alignment.direction"); +pub const SWEEP_ALIGNMENT_ALL_FACES: StringKey = StringKey::new("sweep.alignment.all_faces"); pub const RIBBON_GROUP_DATUM: StringKey = StringKey::new("shell.ribbon.group.datum"); pub const RIBBON_GROUP_SKETCH3: StringKey = StringKey::new("shell.ribbon.group.sketch3"); pub const TOOL_SKETCH3: StringKey = StringKey::new("tool.sketch3"); @@ -352,7 +404,6 @@ pub const TOOL_DATUM_COORDINATE_SYSTEM: StringKey = StringKey::new("tool.datum_c pub const STATUS_DATUM_ACTIVE: StringKey = StringKey::new("status.datum_active"); pub const STATUS_SKETCH3_ACTIVE: StringKey = StringKey::new("status.sketch3_active"); pub const NOTIFY_SKETCH3_PLANE: StringKey = StringKey::new("notify.sketch3_plane"); -pub const NOTIFY_SKETCH3_ACTIVE: StringKey = StringKey::new("notify.sketch3_active"); pub const PROPERTY_PANE_DATUM_PLANE_TITLE: StringKey = StringKey::new("property.pane.datum.plane.title"); pub const PROPERTY_PANE_DATUM_AXIS_TITLE: StringKey = @@ -449,6 +500,8 @@ pub const EXTRUDE_PANEL_UNSUPPORTED_THIN: StringKey = StringKey::new("extrude.panel.unsupported.thin"); pub const EXTRUDE_PANEL_UNSUPPORTED_FLIP: StringKey = StringKey::new("extrude.panel.unsupported.flip"); +pub const EXTRUDE_PANEL_UNRESOLVED_REFERENCE: StringKey = + StringKey::new("extrude.panel.unresolved_reference"); pub const EXTRUDE_PANEL_INTERNAL: StringKey = StringKey::new("extrude.panel.internal"); pub const EXTRUDE_PANEL_REVOLVE_AXIS: StringKey = StringKey::new("extrude.panel.revolve_axis"); pub const EXTRUDE_PANEL_REVOLVE_AXIS_PLANE: StringKey = @@ -461,11 +514,16 @@ pub const EXTRUDE_PANEL_REVOLVE_MIDPLANE: StringKey = StringKey::new("extrude.panel.revolve_midplane"); pub const REVOLVE_PANEL_UNSUPPORTED_POLE: StringKey = StringKey::new("revolve.panel.unsupported_pole"); -pub const SWEEP_PANEL_GENERAL_PATH: StringKey = StringKey::new("sweep.panel.general_path"); -pub const SWEEP_PANEL_PROFILE_KIND: StringKey = StringKey::new("sweep.panel.profile_kind"); -pub const SWEEP_PANEL_ORIENTATION: StringKey = StringKey::new("sweep.panel.orientation"); -pub const SWEEP_PANEL_OFF_PATH: StringKey = StringKey::new("sweep.panel.off_path"); +pub const SWEEP_PANEL_PATH_CORNER: StringKey = StringKey::new("sweep.panel.path_corner"); +pub const SWEEP_PANEL_CLOSED_PATH: StringKey = StringKey::new("sweep.panel.closed_path"); +pub const SWEEP_PANEL_INVALID: StringKey = StringKey::new("sweep.panel.invalid"); +pub const SWEEP_PANEL_MULTI_LOOP: StringKey = StringKey::new("sweep.panel.multi_loop"); pub const SWEEP_PANEL_THIN: StringKey = StringKey::new("sweep.panel.thin"); +pub const SWEEP_PANEL_GUIDE_COUNT: StringKey = StringKey::new("sweep.panel.guide_count"); +pub const SWEEP_PANEL_ALIGNMENT_AXIS: StringKey = StringKey::new("sweep.panel.alignment_axis"); +pub const SWEEP_PANEL_SOLID_TOOL: StringKey = StringKey::new("sweep.panel.solid_tool"); +pub const SWEEP_PANEL_SOLID_ENVELOPE: StringKey = StringKey::new("sweep.panel.solid_envelope"); +pub const SWEEP_PANEL_TOOL_BODY: StringKey = StringKey::new("sweep.panel.tool_body"); pub const EXTRUDE_PANEL_DANGLING_REFERENCE: StringKey = StringKey::new("extrude.panel.dangling_reference"); pub const EXTRUDE_PANEL_NON_PLANAR_TARGET: StringKey = @@ -641,6 +699,8 @@ const EN_US: &[(StringKey, &str)] = &[ (FEATURE_CTX_UNSUPPRESS, "Unsuppress"), (FEATURE_CTX_ROLLBACK, "Rollback to Here"), (FEATURE_CTX_DELETE, "Delete"), + (FEATURE_CTX_HIDE, "Hide"), + (FEATURE_CTX_SHOW, "Show"), (FEATURE_CTX_RELATIONSHIPS, "Parent/Child Relationships"), (FEATURE_REL_PARENTS, "Parent Features"), (FEATURE_REL_CHILDREN, "Child Features"), @@ -825,8 +885,20 @@ const EN_US: &[(StringKey, &str)] = &[ (SHORTCUT_BAR_TITLE, "Shortcut Bar"), (PROPERTY_PANE_NO_SELECTION, "Nothing selected"), (EXTRUDE_PROMPT_SELECT_SKETCH, "Select a sketch to extrude"), + ( + SWEEP_PROMPT_SELECT_PROFILE, + "Double-click the profile sketch in the feature tree, then Esc to cancel", + ), (EXTRUDE_PROFILE_SELECTED, "Sketch selected"), (FEATURE_HINT_EXIT_SKETCH, "Exit the sketch to add a feature"), + ( + FEATURE_HINT_PLANE_AT_ENDPOINT, + "Plane normal to the selected line at its endpoint", + ), + ( + FEATURE_HINT_PLANE_NEEDS_ENDPOINT, + "Select a line endpoint to place a plane here", + ), (NOTIFY_EXTRUDE_NO_SKETCH, "Create a sketch first"), (NOTIFY_EXTRUDE_FAILED, "Can't preview this extrude"), (PROPERTY_ROW_EXTRUDE_END, "End Condition"), @@ -861,19 +933,64 @@ const EN_US: &[(StringKey, &str)] = &[ (PROPERTY_PANE_SWEEP_TITLE, "Sweep"), (PROPERTY_GROUP_SWEEP_PROFILE_PATH, "Profile and Path"), (PROPERTY_GROUP_SWEEP_OPTIONS, "Options"), + (PROPERTY_GROUP_SWEEP_GUIDES, "Guide Curves"), (PROPERTY_ROW_SWEEP_PROFILE, "Profile"), (PROPERTY_ROW_SWEEP_PATH, "Path"), (PROPERTY_ROW_SWEEP_ORIENTATION, "Profile Orientation"), (PROPERTY_ROW_SWEEP_PROFILE_KIND, "Profile Type"), (PROPERTY_ROW_SWEEP_DIAMETER, "Diameter"), + (PROPERTY_ROW_SWEEP_TOOL, "Tool"), + (PROPERTY_ROW_SWEEP_TWIST, "Twist Angle"), + (PROPERTY_ROW_SWEEP_TWIST_UNIT, "Twist Unit"), + (PROPERTY_ROW_SWEEP_DIRECTION, "Direction"), (SWEEP_PROFILE_SKETCH, "Sketch profile"), (SWEEP_PATH_SELECT, "Select a path"), + (SWEEP_PATH_PICK, "Select sketch or edge"), (SWEEP_PATH_SKETCH, "Sketch"), (SWEEP_PATH_EDGE, "Model edge"), (SWEEP_ORIENTATION_FOLLOW, "Follow path"), (SWEEP_ORIENTATION_KEEP_NORMAL, "Keep normal constant"), + (SWEEP_ORIENTATION_TWIST, "Twist along path"), + ( + SWEEP_ORIENTATION_TWIST_NORMAL, + "Twist along path with normal constant", + ), + (SWEEP_TWIST_UNIT_DEGREES, "Degrees"), + (SWEEP_TWIST_UNIT_RADIANS, "Radians"), + (SWEEP_TWIST_UNIT_REVOLUTIONS, "Revolutions"), (SWEEP_PROFILE_KIND_SKETCH, "Sketch profile"), (SWEEP_PROFILE_KIND_CIRCULAR, "Circular profile"), + (SWEEP_PROFILE_KIND_SOLID, "Solid tool"), + (SWEEP_TOOL_BODY, "Tool body"), + (SWEEP_TOOL_SELECT, "Select a body"), + (SWEEP_DIRECTION_ONE, "Direction 1"), + (SWEEP_DIRECTION_TWO, "Direction 2"), + (SWEEP_DIRECTION_BIDIRECTIONAL, "Bidirectional"), + (PROPERTY_ROW_SWEEP_GUIDE_ONE, "Guide 1"), + (PROPERTY_ROW_SWEEP_GUIDE_TWO, "Guide 2"), + (SWEEP_GUIDE_NONE, "No guide"), + (SWEEP_GUIDE_SELECT, "Select a guide"), + (SWEEP_GUIDE_SKETCH, "Sketch"), + (SWEEP_GUIDE_EDGE, "Model edge"), + (SWEEP_GUIDE_CLEAR, "Remove"), + ( + SWEEP_ORIENTATION_FIRST_GUIDE, + "Follow path and first guide curve", + ), + ( + SWEEP_ORIENTATION_BOTH_GUIDES, + "Follow first and second guide curves", + ), + (PROPERTY_ROW_SWEEP_START_TANGENCY, "Start Tangency"), + (PROPERTY_ROW_SWEEP_END_TANGENCY, "End Tangency"), + (SWEEP_TANGENCY_NONE, "None"), + (SWEEP_TANGENCY_PATH, "Path tangent"), + (PROPERTY_ROW_SWEEP_ALIGNMENT, "Path Alignment"), + (PROPERTY_ROW_SWEEP_ALIGNMENT_AXIS, "Direction Vector"), + (SWEEP_ALIGNMENT_MINIMUM_TWIST, "Minimum twist"), + (SWEEP_ALIGNMENT_NONE, "None"), + (SWEEP_ALIGNMENT_DIRECTION, "Direction vector"), + (SWEEP_ALIGNMENT_ALL_FACES, "All faces"), (RIBBON_GROUP_DATUM, "Reference Geometry"), (RIBBON_GROUP_SKETCH3, "3D Sketch"), (TOOL_SKETCH3, "3D Sketch"), @@ -884,7 +1001,6 @@ const EN_US: &[(StringKey, &str)] = &[ (STATUS_DATUM_ACTIVE, "Reference Geometry"), (STATUS_SKETCH3_ACTIVE, "3D Sketch"), (NOTIFY_SKETCH3_PLANE, "3D sketch plane"), - (NOTIFY_SKETCH3_ACTIVE, "3D sketch started"), (PROPERTY_PANE_DATUM_PLANE_TITLE, "Plane"), (PROPERTY_PANE_DATUM_AXIS_TITLE, "Axis"), (PROPERTY_PANE_DATUM_POINT_TITLE, "Point"), @@ -990,6 +1106,10 @@ const EN_US: &[(StringKey, &str)] = &[ EXTRUDE_PANEL_UNSUPPORTED_FLIP, "Flip side to cut is not supported yet", ), + ( + EXTRUDE_PANEL_UNRESOLVED_REFERENCE, + "A referenced profile or path could not be resolved", + ), (EXTRUDE_PANEL_INTERNAL, "Internal kernel error"), ( EXTRUDE_PANEL_REVOLVE_AXIS, @@ -1016,25 +1136,39 @@ const EN_US: &[(StringKey, &str)] = &[ "An axis-touching profile revolves only as a full solid turn", ), ( - SWEEP_PANEL_GENERAL_PATH, - "Sweep path must be a single straight or circular segment", + SWEEP_PANEL_PATH_CORNER, + "Sweep path must be tangent-continuous; a corner is not yet supported", ), ( - SWEEP_PANEL_PROFILE_KIND, - "Circular sweep profile is only supported along a straight path", + SWEEP_PANEL_CLOSED_PATH, + "A closed multi-segment sweep path is not yet supported", ), + (SWEEP_PANEL_INVALID, "Sweep is invalid: {detail}"), ( - SWEEP_PANEL_ORIENTATION, - "Only the follow path profile orientation is supported", + SWEEP_PANEL_MULTI_LOOP, + "Multi-loop sweep profiles are not yet supported", ), ( - SWEEP_PANEL_OFF_PATH, - "Sweep profile must be perpendicular to the path", + SWEEP_PANEL_THIN, + "A capped thin-wall sweep on a curved path is not yet supported", ), ( - SWEEP_PANEL_THIN, - "Thin-wall sweep is only supported along a perpendicular path", + SWEEP_PANEL_GUIDE_COUNT, + "More than two guide curves are not yet supported", + ), + ( + SWEEP_PANEL_ALIGNMENT_AXIS, + "Select a direction vector reference", + ), + ( + SWEEP_PANEL_SOLID_TOOL, + "A non-cylindrical sweep tool body is not yet supported", ), + ( + SWEEP_PANEL_SOLID_ENVELOPE, + "A solid tool path beyond a coaxial straight line is not yet supported", + ), + (SWEEP_PANEL_TOOL_BODY, "Select a sweep tool body"), ( EXTRUDE_PANEL_DANGLING_REFERENCE, "Sketch plane references a face that no longer exists", @@ -1214,6 +1348,8 @@ const AR_XB: &[(StringKey, &str)] = &[ (FEATURE_CTX_UNSUPPRESS, "[!! Unsuppréss !!]"), (FEATURE_CTX_ROLLBACK, "[!! Rôllback to Hére !!]"), (FEATURE_CTX_DELETE, "[!! Delête !!]"), + (FEATURE_CTX_HIDE, "[!! Hîde !!]"), + (FEATURE_CTX_SHOW, "[!! Shôw !!]"), (FEATURE_CTX_RELATIONSHIPS, "[!! Parént/Chîld Relâtions !!]"), (FEATURE_REL_PARENTS, "[!! Parént Featûres !!]"), (FEATURE_REL_CHILDREN, "[!! Chîld Featûres !!]"), @@ -1413,11 +1549,23 @@ const AR_XB: &[(StringKey, &str)] = &[ EXTRUDE_PROMPT_SELECT_SKETCH, "[!! Sêlect a skêtch to êxtrude !!]", ), + ( + SWEEP_PROMPT_SELECT_PROFILE, + "[!! Doûble-click the prôfile skêtch in the trêe, then Êsc to câncel !!]", + ), (EXTRUDE_PROFILE_SELECTED, "[!! Skêtch sêlected !!]"), ( FEATURE_HINT_EXIT_SKETCH, "[!! Êxit the skêtch to add a fêature !!]", ), + ( + FEATURE_HINT_PLANE_AT_ENDPOINT, + "[!! Plâne nôrmal to the sêlected line at its êndpoint !!]", + ), + ( + FEATURE_HINT_PLANE_NEEDS_ENDPOINT, + "[!! Sêlect a line êndpoint to plâce a plâne hêre !!]", + ), (NOTIFY_EXTRUDE_NO_SKETCH, "[!! Crêate a skêtch fîrst !!]"), (NOTIFY_EXTRUDE_FAILED, "[!! Cân't prêview this êxtrude !!]"), (PROPERTY_ROW_EXTRUDE_END, "[!! Énd Condîtion !!]"), @@ -1450,21 +1598,78 @@ const AR_XB: &[(StringKey, &str)] = &[ (TOOL_SWEPT_CUT, "[!! Swêpt Cût !!]"), (STATUS_SWEEP_ACTIVE, "[!! Swêep !!]"), (PROPERTY_PANE_SWEEP_TITLE, "[!! Swêep !!]"), - (PROPERTY_GROUP_SWEEP_PROFILE_PATH, "[!! Prôfile and Pâth !!]"), + ( + PROPERTY_GROUP_SWEEP_PROFILE_PATH, + "[!! Prôfile and Pâth !!]", + ), (PROPERTY_GROUP_SWEEP_OPTIONS, "[!! Ôptions !!]"), + (PROPERTY_GROUP_SWEEP_GUIDES, "[!! Gûide Cûrves !!]"), (PROPERTY_ROW_SWEEP_PROFILE, "[!! Prôfile !!]"), (PROPERTY_ROW_SWEEP_PATH, "[!! Pâth !!]"), - (PROPERTY_ROW_SWEEP_ORIENTATION, "[!! Prôfile Ôrientation !!]"), + ( + PROPERTY_ROW_SWEEP_ORIENTATION, + "[!! Prôfile Ôrientation !!]", + ), (PROPERTY_ROW_SWEEP_PROFILE_KIND, "[!! Prôfile Týpe !!]"), (PROPERTY_ROW_SWEEP_DIAMETER, "[!! Dîameter !!]"), + (PROPERTY_ROW_SWEEP_TOOL, "[!! Tôol !!]"), + (PROPERTY_ROW_SWEEP_TWIST, "[!! Twîst Ângle !!]"), + (PROPERTY_ROW_SWEEP_TWIST_UNIT, "[!! Twîst Ûnit !!]"), (SWEEP_PROFILE_SKETCH, "[!! Skêtch prôfile !!]"), (SWEEP_PATH_SELECT, "[!! Sêlect a pâth !!]"), + (SWEEP_PATH_PICK, "[!! Sêlect skêtch or êdge !!]"), (SWEEP_PATH_SKETCH, "[!! Skêtch !!]"), (SWEEP_PATH_EDGE, "[!! Môdel êdge !!]"), (SWEEP_ORIENTATION_FOLLOW, "[!! Fôllow pâth !!]"), - (SWEEP_ORIENTATION_KEEP_NORMAL, "[!! Kêep nôrmal cônstant !!]"), + ( + SWEEP_ORIENTATION_KEEP_NORMAL, + "[!! Kêep nôrmal cônstant !!]", + ), + (SWEEP_ORIENTATION_TWIST, "[!! Twîst alông pâth !!]"), + ( + SWEEP_ORIENTATION_TWIST_NORMAL, + "[!! Twîst alông pâth wîth nôrmal cônstant !!]", + ), + (SWEEP_TWIST_UNIT_DEGREES, "[!! Dêgrees !!]"), + (SWEEP_TWIST_UNIT_RADIANS, "[!! ʁâdians !!]"), + (SWEEP_TWIST_UNIT_REVOLUTIONS, "[!! ʁevôlutions !!]"), (SWEEP_PROFILE_KIND_SKETCH, "[!! Skêtch prôfile !!]"), (SWEEP_PROFILE_KIND_CIRCULAR, "[!! Cîrcular prôfile !!]"), + (SWEEP_PROFILE_KIND_SOLID, "[!! Sôlid tôol !!]"), + (SWEEP_TOOL_BODY, "[!! Tôol bôdy !!]"), + (SWEEP_TOOL_SELECT, "[!! Sêlect a bôdy !!]"), + (PROPERTY_ROW_SWEEP_DIRECTION, "[!! Dirêction !!]"), + (SWEEP_DIRECTION_ONE, "[!! Dirêction 1 !!]"), + (SWEEP_DIRECTION_TWO, "[!! Dirêction 2 !!]"), + (SWEEP_DIRECTION_BIDIRECTIONAL, "[!! Bidirêctional !!]"), + (PROPERTY_ROW_SWEEP_GUIDE_ONE, "[!! Gûide 1 !!]"), + (PROPERTY_ROW_SWEEP_GUIDE_TWO, "[!! Gûide 2 !!]"), + (SWEEP_GUIDE_NONE, "[!! Nô gûide !!]"), + (SWEEP_GUIDE_SELECT, "[!! Sêlect a gûide !!]"), + (SWEEP_GUIDE_SKETCH, "[!! Skêtch !!]"), + (SWEEP_GUIDE_EDGE, "[!! Môdel êdge !!]"), + (SWEEP_GUIDE_CLEAR, "[!! ʁemôve !!]"), + ( + SWEEP_ORIENTATION_FIRST_GUIDE, + "[!! Fôllow pâth and fîrst gûide cûrve !!]", + ), + ( + SWEEP_ORIENTATION_BOTH_GUIDES, + "[!! Fôllow fîrst and sêcond gûide cûrves !!]", + ), + (PROPERTY_ROW_SWEEP_START_TANGENCY, "[!! Stârt Tângency !!]"), + (PROPERTY_ROW_SWEEP_END_TANGENCY, "[!! Ênd Tângency !!]"), + (SWEEP_TANGENCY_NONE, "[!! Nône !!]"), + (SWEEP_TANGENCY_PATH, "[!! Pâth tângent !!]"), + (PROPERTY_ROW_SWEEP_ALIGNMENT, "[!! Pâth Alîgnment !!]"), + ( + PROPERTY_ROW_SWEEP_ALIGNMENT_AXIS, + "[!! Dirêction Vêctor !!]", + ), + (SWEEP_ALIGNMENT_MINIMUM_TWIST, "[!! Mînimum twîst !!]"), + (SWEEP_ALIGNMENT_NONE, "[!! Nône !!]"), + (SWEEP_ALIGNMENT_DIRECTION, "[!! Dirêction vêctor !!]"), + (SWEEP_ALIGNMENT_ALL_FACES, "[!! Âll fâces !!]"), (RIBBON_GROUP_DATUM, "[!! ʁeference Gêometry !!]"), (RIBBON_GROUP_SKETCH3, "[!! 3D Skêtch !!]"), (TOOL_SKETCH3, "[!! 3D Skêtch !!]"), @@ -1475,7 +1680,6 @@ const AR_XB: &[(StringKey, &str)] = &[ (STATUS_DATUM_ACTIVE, "[!! ʁeference Gêometry !!]"), (STATUS_SKETCH3_ACTIVE, "[!! 3D Skêtch !!]"), (NOTIFY_SKETCH3_PLANE, "[!! 3D skêtch plâne !!]"), - (NOTIFY_SKETCH3_ACTIVE, "[!! 3D skêtch stârted !!]"), (PROPERTY_PANE_DATUM_PLANE_TITLE, "[!! Plâne !!]"), (PROPERTY_PANE_DATUM_AXIS_TITLE, "[!! Áxis !!]"), (PROPERTY_PANE_DATUM_POINT_TITLE, "[!! Pôint !!]"), @@ -1602,6 +1806,10 @@ const AR_XB: &[(StringKey, &str)] = &[ EXTRUDE_PANEL_UNSUPPORTED_FLIP, "[!! Flîp sîde to cût is nôt suppôrted yêt !!]", ), + ( + EXTRUDE_PANEL_UNRESOLVED_REFERENCE, + "[!! Á refêrenced prôfile or pâth côuld not be resôlved !!]", + ), (EXTRUDE_PANEL_INTERNAL, "[!! Intêrnal kêrnel êrror !!]"), ( EXTRUDE_PANEL_REVOLVE_AXIS, @@ -1628,25 +1836,39 @@ const AR_XB: &[(StringKey, &str)] = &[ "[!! An âxis-touching prôfile revôlves ônly as a fûll sôlid tûrn !!]", ), ( - SWEEP_PANEL_GENERAL_PATH, - "[!! Swêep pâth mûst be a sîngle strâight or cîrcular sêgment !!]", + SWEEP_PANEL_PATH_CORNER, + "[!! Swêep pâth mûst be tângent-contînuous; a côrner is nôt yet suppôrted !!]", ), ( - SWEEP_PANEL_PROFILE_KIND, - "[!! Cîrcular swêep prôfile is ônly suppôrted along a strâight pâth !!]", + SWEEP_PANEL_CLOSED_PATH, + "[!! A clôsed mûlti-sêgment swêep pâth is nôt yet suppôrted !!]", ), + (SWEEP_PANEL_INVALID, "[!! Swêep is invâlid: {detail} !!]"), ( - SWEEP_PANEL_ORIENTATION, - "[!! Ônly the fôllow pâth prôfile oriêntation is suppôrted !!]", + SWEEP_PANEL_MULTI_LOOP, + "[!! Mûlti-lôop swêep prôfiles are nôt yet suppôrted !!]", ), ( - SWEEP_PANEL_OFF_PATH, - "[!! Swêep prôfile mûst be perpendîcular to the pâth !!]", + SWEEP_PANEL_THIN, + "[!! Â câpped thîn-wâll swêep ôn a cûrved pâth is nôt yet suppôrted !!]", ), ( - SWEEP_PANEL_THIN, - "[!! Thîn-wâll swêep is ônly suppôrted along a perpendîcular pâth !!]", + SWEEP_PANEL_GUIDE_COUNT, + "[!! Môre than twô gûide cûrves are nôt yet suppôrted !!]", + ), + ( + SWEEP_PANEL_ALIGNMENT_AXIS, + "[!! Sêlect a dirêction vêctor refêrence !!]", + ), + ( + SWEEP_PANEL_SOLID_TOOL, + "[!! A nôn-cylîndrical swêep tôol bôdy is nôt yet suppôrted !!]", + ), + ( + SWEEP_PANEL_SOLID_ENVELOPE, + "[!! A sôlid tôol pâth beyônd a côaxial strâight lîne is nôt yet suppôrted !!]", ), + (SWEEP_PANEL_TOOL_BODY, "[!! Sêlect a swêep tôol bôdy !!]"), ( EXTRUDE_PANEL_DANGLING_REFERENCE, "[!! Skêtch plâne refêrences a fâce that nô longer exîsts !!]",