Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"app.click_a_filled_face_first": "Click a filled face first",
"app.click_a_reference_point_away_from_the_pivot": "Click a reference point away from the pivot first",
"app.click_a_reference_point_away_from_the_pivot_2": "Click a reference point away from the pivot · Esc to cancel",
"app.click_the_opposite_corner_type_width_height_5m": "Click the opposite corner · Type width,height (5m,50mm) + Enter",
"app.click_the_opposite_corner_type_width_height_5m": "Click the opposite corner · Type width, Tab for height (or 5m,50mm) + Enter",
"app.click_to_confirm_esc_to_cancel": "{} · Click to confirm · Esc to cancel",
"app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · Click to confirm · Esc to cancel",
"app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · Click to confirm · Esc to cancel",
Expand Down Expand Up @@ -261,6 +261,9 @@
"app.wavefront_obj_obj": "Wavefront OBJ (.obj)…",
"app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj; metres)…",
"app.width_height": "Width, height",
"app.push_pull_total": "Total: {total}",
"app.width": "Width",
"app.height": "Height",
"app.wood_joinery": "Wood Joinery",
"app.your_model_stays_on_this_device": "Your model stays on this device",
"app.your_work_is_saved_automatically_on_this_device": "Your work is saved automatically on this device. Save a .bup project to keep a portable copy. Legacy .bup files are also supported.",
Expand Down Expand Up @@ -2182,5 +2185,6 @@
"app.calculating_measurements": "Calculating measurements…",
"sidebar.move_wireframe": "Wireframe other objects while moving",
"sidebar.selected_edge_lengths": "Selected Edge Lengths",
"sidebar.floating_dimension_input": "Floating Dimension Input",
"cli.agent_edit": "Agent edit"
}
6 changes: 5 additions & 1 deletion assets/i18n/es_419.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"app.click_a_filled_face_first": "Haz clic primero en una cara rellena",
"app.click_a_reference_point_away_from_the_pivot": "Haz clic primero en un punto de referencia alejado del pivote",
"app.click_a_reference_point_away_from_the_pivot_2": "Haz clic en un punto de referencia alejado del pivote · Esc para cancelar",
"app.click_the_opposite_corner_type_width_height_5m": "Haz clic en la esquina opuesta · Escribe ancho,alto (5m,50mm) + Enter",
"app.click_the_opposite_corner_type_width_height_5m": "Haz clic en la esquina opuesta · Escribe el ancho, Tab para el alto (o 5m,50mm) + Enter",
"app.click_to_confirm_esc_to_cancel": "{} · Haz clic para confirmar · Esc para cancelar",
"app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · Haz clic para confirmar · Esc para cancelar",
"app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · Haz clic para confirmar · Esc para cancelar",
Expand Down Expand Up @@ -261,6 +261,9 @@
"app.wavefront_obj_obj": "Wavefront OBJ (.obj)…",
"app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj; metros)…",
"app.width_height": "Ancho, alto",
"app.push_pull_total": "Total: {total}",
"app.width": "Ancho",
"app.height": "Alto",
"app.wood_joinery": "Ensambles de madera",
"app.your_model_stays_on_this_device": "Tu modelo permanece en este dispositivo",
"app.your_work_is_saved_automatically_on_this_device": "Tu trabajo se guarda automáticamente en este dispositivo. Guarda un proyecto .bup para conservar una copia portátil. También se admiten archivos .bup antiguos.",
Expand Down Expand Up @@ -2182,5 +2185,6 @@
"app.calculating_measurements": "Calculando mediciones…",
"sidebar.move_wireframe": "Estructura alámbrica de otros objetos al mover",
"sidebar.selected_edge_lengths": "Longitudes de aristas seleccionadas",
"sidebar.floating_dimension_input": "Cuadro de medidas flotante",
"cli.agent_edit": "Edición del agente"
}
6 changes: 5 additions & 1 deletion assets/i18n/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"app.click_a_filled_face_first": "Clique primeiro em uma face preenchida",
"app.click_a_reference_point_away_from_the_pivot": "Clique primeiro em um ponto de referência afastado do pivô",
"app.click_a_reference_point_away_from_the_pivot_2": "Clique em um ponto de referência afastado do pivô · Esc para cancelar",
"app.click_the_opposite_corner_type_width_height_5m": "Clique no canto oposto · Digite largura,altura (5m,50mm) + Enter",
"app.click_the_opposite_corner_type_width_height_5m": "Clique no canto oposto · Digite a largura, Tab para a altura (ou 5m,50mm) + Enter",
"app.click_to_confirm_esc_to_cancel": "{} · Clique para confirmar · Esc para cancelar",
"app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · Clique para confirmar · Esc para cancelar",
"app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · Clique para confirmar · Esc para cancelar",
Expand Down Expand Up @@ -261,6 +261,9 @@
"app.wavefront_obj_obj": "Wavefront OBJ (.obj)…",
"app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj; metros)…",
"app.width_height": "Largura, altura",
"app.push_pull_total": "Total: {total}",
"app.width": "Largura",
"app.height": "Altura",
"app.wood_joinery": "Encaixes de madeira",
"app.your_model_stays_on_this_device": "Seu modelo fica neste dispositivo",
"app.your_work_is_saved_automatically_on_this_device": "Seu trabalho é salvo automaticamente neste dispositivo. Salve um projeto .bup para ter uma cópia portátil. Arquivos .bup antigos também são aceitos.",
Expand Down Expand Up @@ -2182,5 +2185,6 @@
"app.calculating_measurements": "Calculando medições…",
"sidebar.move_wireframe": "Exibir outros objetos em aramado ao mover",
"sidebar.selected_edge_lengths": "Comprimentos das arestas selecionadas",
"sidebar.floating_dimension_input": "Caixa de medidas flutuante",
"cli.agent_edit": "Edição do agente"
}
6 changes: 5 additions & 1 deletion assets/i18n/vi_VN.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"app.click_a_filled_face_first": "Trước tiên, hãy nhấp vào một mặt đã tô.",
"app.click_a_reference_point_away_from_the_pivot": "Nhấp vào điểm tham chiếu cách xa trục trước",
"app.click_a_reference_point_away_from_the_pivot_2": "Nhấp vào điểm tham chiếu cách xa trục quay · Esc để hủy",
"app.click_the_opposite_corner_type_width_height_5m": "Nhấp vào góc đối diện · Nhập chiều rộng,chiều cao (5m,50mm) rồi nhấn Enter",
"app.click_the_opposite_corner_type_width_height_5m": "Nhấp vào góc đối diện · Nhập chiều rộng, Tab để nhập chiều cao (hoặc 5m,50mm) + Enter",
"app.click_to_confirm_esc_to_cancel": "{} · Nhấp để xác nhận · Esc để hủy",
"app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · Nhấp để xác nhận · Esc để hủy",
"app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · Nhấp để xác nhận · Esc để hủy",
Expand Down Expand Up @@ -261,6 +261,9 @@
"app.wavefront_obj_obj": "Wavefront OBJ (.obj)…",
"app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj; mét)…",
"app.width_height": "Chiều rộng, chiều cao",
"app.push_pull_total": "Tổng: {total}",
"app.width": "Chiều rộng",
"app.height": "Chiều cao",
"app.wood_joinery": "Mối nối gỗ",
"app.your_model_stays_on_this_device": "Mô hình của bạn vẫn ở trên thiết bị này",
"app.your_work_is_saved_automatically_on_this_device": "Công việc của bạn được lưu tự động trên thiết bị này. Lưu dự án .bup để giữ một bản sao di động. Các tệp .bup cũ cũng được hỗ trợ.",
Expand Down Expand Up @@ -2182,5 +2185,6 @@
"app.calculating_measurements": "Tính số đo…",
"sidebar.move_wireframe": "Hiển thị khung dây cho đối tượng khác khi di chuyển",
"sidebar.selected_edge_lengths": "Chiều dài các cạnh đã chọn",
"sidebar.floating_dimension_input": "Ô nhập kích thước nổi",
"cli.agent_edit": "Chỉnh sửa của tác nhân"
}
6 changes: 5 additions & 1 deletion assets/i18n/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"app.click_a_filled_face_first": "请先单击已填充的面",
"app.click_a_reference_point_away_from_the_pivot": "请先单击远离旋转中心的参考点",
"app.click_a_reference_point_away_from_the_pivot_2": "单击远离旋转中心的参考点 · 按 Esc 取消",
"app.click_the_opposite_corner_type_width_height_5m": "单击对角点 · 输入 宽度,高度(5m,50mm)+ Enter",
"app.click_the_opposite_corner_type_width_height_5m": "点击对角 · 输入宽度,按 Tab 输入高度(或 5m,50mm)+ Enter",
"app.click_to_confirm_esc_to_cancel": "{} · 单击确认 · 按 Esc 取消",
"app.click_to_confirm_esc_to_cancel_2": "{value:.3} × · 单击确认 · 按 Esc 取消",
"app.click_to_confirm_esc_to_cancel_3": "{}{}{alignment} · 单击确认 · 按 Esc 取消",
Expand Down Expand Up @@ -261,6 +261,9 @@
"app.wavefront_obj_obj": "Wavefront OBJ (.obj)…",
"app.wavefront_obj_obj_metres": "Wavefront OBJ (.obj;米)…",
"app.width_height": "宽度、高度",
"app.push_pull_total": "总计:{total}",
"app.width": "宽度",
"app.height": "高度",
"app.wood_joinery": "木工接合",
"app.your_model_stays_on_this_device": "您的模型保存在本设备上",
"app.your_work_is_saved_automatically_on_this_device": "您的工作会自动保存在本设备上。保存 .bup 项目可保留一份便携副本。也支持旧版 .bup 文件。",
Expand Down Expand Up @@ -2182,5 +2185,6 @@
"app.calculating_measurements": "正在计算测量数据…",
"sidebar.move_wireframe": "移动时以线框形式显示其他对象",
"sidebar.selected_edge_lengths": "所选边长",
"sidebar.floating_dimension_input": "浮动尺寸输入框",
"cli.agent_edit": "智能体编辑"
}
104 changes: 84 additions & 20 deletions crates/benchup-app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ mod currency;
pub(crate) mod display_scene;
#[path = "construction_guides.rs"]
mod construction_guides;
#[path = "dimension_overlay.rs"]
mod dimension_overlay;
#[path = "drawing_inference.rs"]
mod drawing_inference;
#[path = "entity_gizmo.rs"]
Expand Down Expand Up @@ -403,6 +405,10 @@ pub struct BenchUpApp {
lock: Option<Axis>,
second_lock: Option<Axis>,
length_input: String,
/// Rectangle typing goes to height instead of width (toggled with Tab).
rect_height_active: bool,
/// After Tab, the active rectangle field is selected: typing replaces it.
rect_field_selected: bool,
modeling: Option<Modeling>,
push_pull_new_faces: bool,
push_pull_modifier_down: bool,
Expand Down Expand Up @@ -596,6 +602,8 @@ impl BenchUpApp {
lock: None,
second_lock: None,
length_input: String::new(),
rect_height_active: false,
rect_field_selected: false,
modeling: None,
push_pull_new_faces: false,
push_pull_modifier_down: false,
Expand Down Expand Up @@ -1505,10 +1513,20 @@ impl BenchUpApp {
.fold(0., f64::max)
}

/// Resulting depth when pushing an already extruded face. `None` for a
/// fresh face, where it always equals the distance and would repeat it.
fn push_pull_total(&self, edit: &Modeling, value: f64) -> Option<f64> {
let total = (edit.pull_depth + value * edit.direction.dot(edit.unlocked_direction)).abs();
(edit.pull_depth.abs() > 1e-9).then_some(total)
}

fn push_pull_readout(&self, edit: &Modeling, value: f64) -> String {
let units = self.doc.display.units;
let total = (edit.pull_depth + value * edit.direction.dot(edit.unlocked_direction)).abs();
format!("Difference: {} · Total: {}", units.format_length(value), units.format_length(total))
let distance = units.format_length(value);
match self.push_pull_total(edit, value) {
Some(total) => format!("{distance} · {}", tr!("app.push_pull_total", total = units.format_length(total))),
None => distance,
}
}

fn selected_modeling_face(&self) -> Option<u64> {
Expand Down Expand Up @@ -2345,6 +2363,29 @@ impl BenchUpApp {

fn sidebar_shortcut(&mut self, ctx: &egui::Context) {
let walking = self.tool == Tool::Walk;
// While sizing a rectangle, Tab switches between its width and height.
if self.rect_fields_active() {
let tab = ctx.input_mut(|i| {
let mut tab = false;
i.events.retain(|event| {
if let egui::Event::Key { key: Key::Tab, pressed: true, modifiers, .. } = event
&& (!modifiers.any() || *modifiers == egui::Modifiers::SHIFT)
{
tab = true;
false
} else { true }
});
tab
});
if tab {
self.toggle_rect_field();
ctx.memory_mut(|m| m.move_focus(egui::FocusDirection::None));
ctx.request_repaint();
}
return;
}
self.rect_height_active = false;
self.rect_field_selected = false;
let consumed = ctx.input_mut(|i| {
let mut consumed = false;
if i.focused {
Expand Down Expand Up @@ -2516,6 +2557,23 @@ impl BenchUpApp {
});
self.prompt_drawn_group_name(ctx, completed_group);
}
// An empty focused precision field still allows Space to cancel the tool.
if self.precision_available()
&& !self.dimension_has_typed_value()
&& ctx.memory(|m| m.has_focus(egui::Id::unique("precision_input")))
&& ctx.input(|i| {
i.modifiers.is_none()
&& (i.key_pressed(Key::Space)
|| i.events.iter().any(|event| matches!(event,
egui::Event::Text(text) if text == " ")))
&& !i.events.iter().any(|event| matches!(event,
egui::Event::Text(text) if text.chars().any(|c| !c.is_whitespace())))
})
{
ctx.memory_mut(|m| m.surrender_focus(egui::Id::unique("precision_input")));
self.set_tool(Tool::Select);
return;
}
if ctx.egui_wants_keyboard_input()
|| self.help
|| self.object_creation.is_some()
Expand Down Expand Up @@ -2630,8 +2688,8 @@ impl BenchUpApp {
// Letter keys belong to an in-progress dimension, including
// unit suffixes. Letter shortcuts resume after the dimension is cleared.
if self.precision_available()
&& !self.length_input.is_empty()
&& (Key::A..=Key::Z).contains(&key)
&& self.dimension_has_typed_value()
&& ((Key::A..=Key::Z).contains(&key) || key == Key::Space)
{
continue;
}
Expand All @@ -2642,7 +2700,9 @@ impl BenchUpApp {
match key {
Key::Delete => self.erase_selected(ctx),
Key::Backspace => {
if self.precision_available() {
if self.rect_fields_active() {
self.rect_input_backspace();
} else if self.precision_available() {
self.length_input.pop();
} else {
self.erase_selected(ctx);
Expand Down Expand Up @@ -2684,6 +2744,12 @@ impl BenchUpApp {
}
}
egui::Event::Text(text) if self.precision_available() => {
// Some backends deliver Space as text before its key event,
// or as text alone. Do not seed an empty dimension with it.
if text == " " && !self.dimension_has_typed_value() {
self.set_tool(Tool::Select);
continue;
}
if text.chars().all(|c| {
c.is_ascii_alphanumeric()
|| matches!(
Expand All @@ -2692,7 +2758,11 @@ impl BenchUpApp {
)
}) && self.length_input.len() + text.len() <= precision::MAX_INPUT_LEN
{
self.length_input.push_str(&text);
if self.rect_fields_active() {
self.rect_input_text(&text);
} else {
self.length_input.push_str(&text);
}
}
}
_ => {}
Expand Down Expand Up @@ -3286,6 +3356,9 @@ impl BenchUpApp {
// Reserve the precision controls before fitting the status message.
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if self.tool == Tool::Text { return; }
// The floating boxes on the geometry replace this field;
// it returns when they are turned off in View options.
if self.doc.display.dimension_input_overlay && !self.tool.is_measuring() { return; }
if self.tool.is_measuring() && !self.measurement_precision_available() {
ui.text_label(
RichText::new(
Expand Down Expand Up @@ -3333,6 +3406,7 @@ impl BenchUpApp {
let field = ui.add_enabled(
available,
egui::TextEdit::singleline(&mut self.length_input)
.id(egui::Id::unique("precision_input"))
.desired_width(if matches!(self.tool, Tool::Rectangle | Tool::Image) { 300. } else { 212. })
.font(egui::TextStyle::Monospace)
.hint_text(hint)
Expand Down Expand Up @@ -3360,19 +3434,7 @@ impl BenchUpApp {
field.surrender_focus();
}
ui.text_label(
RichText::new(match self.tool {
Tool::Scale => tr!("app.factor"),
Tool::Rotate | Tool::Protractor => tr!("app.angle"),
Tool::PushPull => tr!("app.distance"),
Tool::Offset => tr!("app.offset"),
Tool::Move if self.last_move_copy.is_some() => tr!("app.copies"),
Tool::Move => tr!("app.distance"),
Tool::Rectangle | Tool::Image => tr!("app.width_height"),
Tool::Circle | Tool::Polygon => tr!("app.radius"),
Tool::Arc if self.drawing.as_ref().is_some_and(|drawing| drawing.arc_end.is_some()) => tr!("app.arc_height"),
Tool::Arc => tr!("app.arc_chord"),
_ => tr!("app.length"),
})
RichText::new(self.precision_field_label())
.size(12.)
.color(colors.muted),
);
Expand Down Expand Up @@ -6459,7 +6521,8 @@ impl BenchUpApp {
colors.paper.gamma_multiply(0.95),
);
p.galley(at, galley, color);
if let Some(start) = self.line_start {
// The floating dimension box replaces this inline length.
if let Some(start) = self.line_start.filter(|_| !display.dimension_input_overlay) {
if let Some(a) = screen(start) {
// Keep the length clear of the endpoint's inference
// tooltip, including short vertical previews.
Expand Down Expand Up @@ -6618,6 +6681,7 @@ impl BenchUpApp {
self.paint_construction_guides(&p, rect);
self.paint_measurements(&p, rect);
self.paint_scale_cage(&p, rect);
self.paint_dimension_overlay(&p, rect);
if let Some((notice, until)) = &self.notice {
if ui.input(|i| i.time) < *until {
let galley = p.layout(
Expand Down
Loading
Loading