diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 58d54f9a0..a30573a1c 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -6485,6 +6485,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = إضافة إلى مجموعة العمل .remove-from-working-set = إزالة من مجموعة العمل .toolbar-objects = كائنات diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index acb1ac860..4d36dc851 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Добави към работния набор .remove-from-working-set = Премахни от работния набор .toolbar-objects = обекта diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index c6f6151c8..e36c18500 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -6470,6 +6470,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Přidat do pracovní sady .remove-from-working-set = Odebrat z pracovní sady .toolbar-objects = objektů diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 9686e0aaf..2e19397ff 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -6471,6 +6471,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Zum Arbeitssatz hinzufügen .remove-from-working-set = Aus dem Arbeitssatz entfernen .toolbar-objects = Objekte diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 26eca339a..1947fcb4a 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Προσθήκη στο σύνολο εργασίας .remove-from-working-set = Αφαίρεση από το σύνολο εργασίας .toolbar-objects = αντικείμενα diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 0f4734280..7afea647c 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -6472,6 +6472,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Add to Working Set .remove-from-working-set = Remove from Working Set .toolbar-objects = objects diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 4ae2ed045..9369dbb40 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Añadir al conjunto de trabajo .remove-from-working-set = Eliminar del conjunto de trabajo .toolbar-objects = objetos diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 950164de4..f3201449c 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -6470,6 +6470,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Lisää työjoukkoon .remove-from-working-set = Poista työjoukosta .toolbar-objects = objektia diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 8b24070bd..c2645e706 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Ajouter au jeu de travail .remove-from-working-set = Supprimer du jeu de travail .toolbar-objects = objets diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 0aab05d14..8fcf188f5 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -6461,6 +6461,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = कार्य सेट में जोड़ें .remove-from-working-set = कार्य सेट से हटाएँ .toolbar-objects = ऑब्जेक्ट diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 4565836cd..6af9ce6cf 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -6457,6 +6457,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Hozzáadás a munkakészlethez .remove-from-working-set = Eltávolítás a munkakészletből .toolbar-objects = objektum diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 9f8ace95c..24d2da5ad 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -6472,6 +6472,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Aggiungi al gruppo di lavoro .remove-from-working-set = Rimuovi dal gruppo di lavoro .toolbar-objects = oggetti diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 6703c8b44..af3c245a5 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -6461,6 +6461,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = 作業セットに追加 .remove-from-working-set = 作業セットから除去 .toolbar-objects = 個のオブジェクト diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index b9bf8d88f..cb12d1b10 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -6472,6 +6472,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = 작업 세트에 추가 .remove-from-working-set = 작업 세트에서 제거 .toolbar-objects = 개 객체 diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 3fc078836..76691d11e 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = ເພີ່ມໃສ່ຊຸດເຮັດວຽກ .remove-from-working-set = ລຶບອອກຈາກຊຸດເຮັດວຽກ .toolbar-objects = ວັດຖຸ diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 1aba8d7c3..e17fa57ec 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -6471,6 +6471,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Toevoegen aan werkset .remove-from-working-set = Verwijderen uit werkset .toolbar-objects = objecten diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 0aa504dcc..e7a5a49ac 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -6472,6 +6472,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Dodaj do zbioru roboczego .remove-from-working-set = Usuń ze zbioru roboczego .toolbar-objects = obiekty diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index d68f4385d..15be3e15c 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -6473,6 +6473,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Adicionar ao conjunto de trabalho .remove-from-working-set = Remover do conjunto de trabalho .toolbar-objects = objetos diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 25f2d76cc..de93cb86d 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -6474,6 +6474,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = Добавить в рабочий набор .remove-from-working-set = Удалить из рабочего набора .toolbar-objects = объектов diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index da6152f3a..e493d5098 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -6394,6 +6394,9 @@ view-background = .select-image = Arka Plan Görüntüsü Seç ref-edit = + .entities-added = __ocs_fmt_0__ nesne eklendi + .use-refclose = Referans düzenleme oturumunu bitirmek için REFCLOSE komutunu veya referans düzenleme araç çubuğunu kullanın. + .opposite-corner = Karşı köşeyi belirtin: .add-to-working-set = Çalışma Kümesine Ekle .remove-from-working-set = Çalışma Kümesinden Çıkar .toolbar-objects = nesne diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index ea8690e95..fdefaae90 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -6456,6 +6456,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = 添加到工作集 .remove-from-working-set = 从工作集删除 .toolbar-objects = 个对象 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index aa43eb0bf..3f7e91475 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -6468,6 +6468,9 @@ view-background = .select-image = Select Background Image ref-edit = + .entities-added = __ocs_fmt_0__ entities added + .use-refclose = Use REFCLOSE or the Refedit toolbar to end reference editing session. + .opposite-corner = Specify opposite corner: .add-to-working-set = 加入工作集 .remove-from-working-set = 從工作集移除 .toolbar-objects = 個物件 diff --git a/src/app/commands/refedit.rs b/src/app/commands/refedit.rs index 3c924187b..351a825fe 100644 --- a/src/app/commands/refedit.rs +++ b/src/app/commands/refedit.rs @@ -256,7 +256,7 @@ impl OpenCADStudio { .map(|l| l.insert.value().to_string()) .collect(); return self.dispatch_command(&format!( - "REFEDIT_BEGIN:{}:{}:{}::{}:{}", + "REFEDIT_BEGIN:{}:{}:{}::{}:{}:1", state.top.value(), path.join(","), attributes as u8, @@ -293,6 +293,8 @@ impl OpenCADStudio { let unique = parts .next() .map_or(self.refedit_options.unique_names, |v| v == "1"); + // Opened from the Reference Edit dialog (REFEDIT, not -REFEDIT). + let from_dialog = parts.next() == Some("1"); if self.tabs[i].refedit_session.is_some() || self.reject_locked_edit(i, top) { return; } @@ -396,6 +398,19 @@ impl OpenCADStudio { self.tabs[i].scene.refedit_lock = lock; self.tabs[i].scene.deselect_all(); self.tabs[i].dirty = true; + if from_dialog { + match nested.len() { + 0 => {} + 1 => self.command_line.push_output(crate::t!("1 item selected").as_ref()), + n => self + .command_line + .push_output(crate::tf!("{} items selected", n).as_ref()), + } + self.command_line.push_output( + crate::t!("Use REFCLOSE or the Refedit toolbar to end reference editing session.") + .as_ref(), + ); + } // No active command — the definition's objects are edited freely // (move, grips, draw, erase…) until REFCLOSE. self.tabs[i].active_cmd = None; diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 3d578321d..7c2808177 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -3,6 +3,9 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static str)> { match source { + "{} entities added" => Some(("ref-edit", "entities-added")), + "Use REFCLOSE or the Refedit toolbar to end reference editing session." => Some(("ref-edit", "use-refclose")), + "Specify opposite corner:" => Some(("ref-edit", "opposite-corner")), "Add to Working Set" => Some(("ref-edit", "add-to-working-set")), "Remove from Working Set" => Some(("ref-edit", "remove-from-working-set")), "Select reference:" => Some(("ref-edit", "select-reference")), diff --git a/src/modules/draw/modify/refedit.rs b/src/modules/draw/modify/refedit.rs index b1d25e969..918451570 100644 --- a/src/modules/draw/modify/refedit.rs +++ b/src/modules/draw/modify/refedit.rs @@ -414,6 +414,8 @@ pub struct RefEditOptionsCommand { /// Set when the Edit Reference dialog already chose the attribute /// option: the nested pick then opens the session directly. attributes: Option, + /// First corner of a window started by a pick on empty space. + corner: Option, } impl RefEditOptionsCommand { @@ -435,6 +437,7 @@ impl RefEditOptionsCommand { lock, unique, attributes: None, + corner: None, } } @@ -464,34 +467,81 @@ impl RefEditOptionsCommand { let nested: Vec = self.nested.iter().map(|h| h.value().to_string()).collect(); CmdResult::Relaunch( format!( - "REFEDIT_BEGIN:{}:{}:{}:{}:{}:{}", + "REFEDIT_BEGIN:{}:{}:{}:{}:{}:{}:{}", self.top.value(), path.join(","), attributes as u8, nested.join(","), self.lock as u8, - self.unique as u8 + self.unique as u8, + self.attributes.is_some() as u8 ), vec![], ) } - fn pick_nested(&mut self, point: DVec3) -> CmdResult { + /// A pick at `point`: the nearest object of the level within `tolerance` + /// (any distance when None), else the first corner of a window. + fn pick_nested(&mut self, point: DVec3, tolerance: Option) -> CmdResult { + if let Some(first) = self.corner.take() { + return self.window_nested(first, point); + } let nearest = self .hits .get(self.level) .into_iter() .flatten() .filter_map(|(h, paths)| paths_distance(paths, point).map(|d| (*h, d))) + .filter(|(_, d)| tolerance.is_none_or(|t| *d <= t)) .min_by(|a, b| a.1.total_cmp(&b.1)); match nearest { - Some((h, _)) if !self.nested.contains(&h) => { - self.nested.push(h); - CmdResult::ReportMeasurement(t!("1 entity added").into_owned()) + Some((h, _)) => { + let added = !self.nested.contains(&h); + if added { + self.nested.push(h); + } + CmdResult::ReportMeasurement(entities_added(added as usize)) + } + None => { + self.corner = Some(point); + CmdResult::NeedPoint } - _ => CmdResult::NeedPoint, } } + + /// The level's objects in the window from `first` to `second`: wholly + /// inside when it was dragged to the right, touching it when to the left. + fn window_nested(&mut self, first: DVec3, second: DVec3) -> CmdResult { + let (lo, hi) = (first.min(second), first.max(second)); + let crossing = second.x < first.x; + let inside = |p: &[f64; 3]| p[0] >= lo.x && p[0] <= hi.x && p[1] >= lo.y && p[1] <= hi.y; + let picked: Vec = self + .hits + .get(self.level) + .into_iter() + .flatten() + .filter(|(_, paths)| { + let points = || paths.iter().flatten(); + if crossing { + points().any(inside) + || paths.iter().any(|path| { + path.windows(2).any(|s| segment_meets_box(s[0], s[1], lo, hi)) + }) + } else { + points().next().is_some() && points().all(inside) + } + }) + .map(|(h, _)| *h) + .collect(); + let mut added = 0; + for h in picked { + if !self.nested.contains(&h) { + self.nested.push(h); + added += 1; + } + } + CmdResult::ReportMeasurement(entities_added(added)) + } } impl CadCommand for RefEditOptionsCommand { @@ -504,6 +554,9 @@ impl CadCommand for RefEditOptionsCommand { OptionStep::Method => { t!("Enter object selection method [All/Nested] :").into_owned() } + OptionStep::Nested if self.corner.is_some() => { + t!("Specify opposite corner:").into_owned() + } OptionStep::Nested => t!("Select nested objects:").into_owned(), OptionStep::Attributes => { t!("Display attribute definitions [Yes/No] :").into_owned() @@ -526,8 +579,13 @@ impl CadCommand for RefEditOptionsCommand { fn needs_entity_pick(&self) -> bool { matches!(self.step, OptionStep::Nested) } - fn on_entity_pick(&mut self, _handle: Handle, pt: DVec3) -> CmdResult { - self.pick_nested(pt) + fn on_entity_pick(&mut self, handle: Handle, pt: DVec3) -> CmdResult { + // A click on empty space starts a window, as does one beside the + // reference's objects. + if handle.is_null() { + return self.pick_nested(pt, Some(0.0)); + } + self.pick_nested(pt, None) } fn on_text_input(&mut self, text: &str) -> Option { let word = text.trim().to_ascii_uppercase(); @@ -554,7 +612,8 @@ impl CadCommand for RefEditOptionsCommand { } fn on_point(&mut self, pt: DVec3) -> CmdResult { match self.step { - OptionStep::Nested => self.pick_nested(pt), + // A typed point picks the object it lies on. + OptionStep::Nested => self.pick_nested(pt, Some(1e-6 * pt.length().max(1.0))), _ => CmdResult::NeedPoint, } } @@ -573,12 +632,14 @@ impl CadCommand for RefEditOptionsCommand { self.step = OptionStep::Attributes; CmdResult::NeedPoint } + OptionStep::Nested if self.corner.take().is_some() => CmdResult::NeedPoint, OptionStep::Nested => { let n = self.nested.len(); if n == 0 { return CmdResult::CancelWithMessage(t!("0 items selected").into_owned()); } if let Some(attributes) = self.attributes { + // The count and the session's note follow as it opens. return self.begin(attributes); } self.step = OptionStep::Attributes; @@ -593,6 +654,45 @@ impl CadCommand for RefEditOptionsCommand { } } +/// `N entity added` / `N entities added`. +fn entities_added(n: usize) -> String { + if n == 1 { + t!("1 entity added").into_owned() + } else { + crate::tf!("{} entities added", n).into_owned() + } +} + +/// Whether the segment from `a` to `b` crosses the box `lo`..`hi` (XY). +fn segment_meets_box(a: [f64; 3], b: [f64; 3], lo: DVec3, hi: DVec3) -> bool { + // Liang–Barsky clip of the segment against the box. + let (mut t0, mut t1) = (0.0f64, 1.0f64); + let d = [b[0] - a[0], b[1] - a[1]]; + for (p, q) in [ + (-d[0], a[0] - lo.x), + (d[0], hi.x - a[0]), + (-d[1], a[1] - lo.y), + (d[1], hi.y - a[1]), + ] { + if p == 0.0 { + if q < 0.0 { + return false; + } + } else { + let r = q / p; + if p < 0.0 { + t0 = t0.max(r); + } else { + t1 = t1.min(r); + } + if t0 > t1 { + return false; + } + } + } + true +} + // ── REFSET ──────────────────────────────────────────────────────────────── /// `Enter an option [Add/Remove] :` then `Select objects:`.