forked from 1b-agent/opencadstudio-upstream
Merge pull request #1793 from ramox81/codex/block-definition-command-line
Block Definition: command-line forms, block record data and dialog defaults Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
838e397a6c
23 files changed
+1860
-435
No files matched your search
@@ -1,27 +1,56 @@
|
||||
use crate::t;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use glam::DVec3;
|
||||
|
||||
// ── Command implementation ────────────────────────────────────────────────
|
||||
|
||||
/// Picks one point and delegates to the inline `BASE <x> <y> <z>` handler,
|
||||
/// which writes the active space's insertion base point onto the document.
|
||||
pub struct BaseCommand;
|
||||
/// BASE (`Enter base point <x,y,z>:`) and INSBASE
|
||||
/// (`Enter new value for INSBASE <x,y,z>:`): a point sets the active space's
|
||||
/// insertion base through the inline `BASE <x> <y> <z>` handler; Enter keeps
|
||||
/// the current one.
|
||||
pub struct BaseCommand {
|
||||
current: DVec3,
|
||||
variable: bool,
|
||||
}
|
||||
|
||||
impl BaseCommand {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
pub fn new(current: DVec3) -> Self {
|
||||
Self {
|
||||
current,
|
||||
variable: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The INSBASE system variable's prompt.
|
||||
pub fn insbase(current: DVec3) -> Self {
|
||||
Self {
|
||||
current,
|
||||
variable: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn point_text(p: DVec3) -> String {
|
||||
format!("{:.4},{:.4},{:.4}", p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
impl CadCommand for BaseCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"BASE"
|
||||
if self.variable {
|
||||
"INSBASE"
|
||||
} else {
|
||||
"BASE"
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
t!("BASE Specify base point:").into_owned()
|
||||
if self.variable {
|
||||
format!(
|
||||
"Enter new value for INSBASE <{}>:",
|
||||
point_text(self.current)
|
||||
)
|
||||
} else {
|
||||
format!("Enter base point <{}>:", point_text(self.current))
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
@@ -29,7 +58,7 @@ impl CadCommand for BaseCommand {
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
CmdResult::Measurement(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,10 +1,8 @@
|
||||
use crate::t;
|
||||
use codec::Handle;
|
||||
use glam::DVec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef {
|
||||
@@ -15,84 +13,6 @@ pub fn tool() -> ToolDef {
|
||||
}
|
||||
}
|
||||
|
||||
enum Step {
|
||||
Name,
|
||||
Base { name: String },
|
||||
}
|
||||
|
||||
pub struct CreateBlockCommand {
|
||||
handles: Vec<Handle>,
|
||||
step: Step,
|
||||
}
|
||||
|
||||
impl CreateBlockCommand {
|
||||
pub fn new(handles: Vec<Handle>) -> Self {
|
||||
Self {
|
||||
handles,
|
||||
step: Step::Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for CreateBlockCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"BLOCK"
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
match &self.step {
|
||||
Step::Name => t!(
|
||||
"BLOCK Enter block name [%{count} objects selected]:",
|
||||
count = self.handles.len()
|
||||
)
|
||||
.into_owned(),
|
||||
Step::Base { name } => t!(
|
||||
"BLOCK Specify base point for \"%{name}\" [%{count} objects]:",
|
||||
name = name,
|
||||
count = self.handles.len()
|
||||
)
|
||||
.into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
match &self.step {
|
||||
Step::Name => CmdResult::NeedPoint,
|
||||
Step::Base { name } => CmdResult::CreateBlock {
|
||||
handles: self.handles.clone(),
|
||||
name: name.clone(),
|
||||
base: pt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
|
||||
fn wants_text_input(&self) -> bool {
|
||||
matches!(self.step, Step::Name)
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
if !matches!(self.step, Step::Name) {
|
||||
return None;
|
||||
}
|
||||
let name = text.trim();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
self.step = Step::Base {
|
||||
name: name.to_string(),
|
||||
};
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
|
||||
fn on_preview_wires(&mut self, _pt: DVec3) -> Vec<WireModel> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
/// One-shot point picker for the Block Definition dialog's "Pick point" button.
|
||||
pub struct BlockPickBasePointCommand;
|
||||
|
||||
@@ -102,7 +22,7 @@ impl CadCommand for BlockPickBasePointCommand {
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
crate::t!("BLOCK Specify insertion base point:").into_owned()
|
||||
"Specify insertion base point:".to_string()
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
@@ -150,9 +70,9 @@ impl CadCommand for BlockOnScreenCommand {
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.need_base {
|
||||
crate::t!("BLOCK Specify insertion base point:").into_owned()
|
||||
"Specify insertion base point:".to_string()
|
||||
} else {
|
||||
crate::t!("BLOCK Select objects:").into_owned()
|
||||
"Select objects:".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +370,8 @@ mod tests {
|
||||
hyperlink_desc: "".to_string(),
|
||||
redefine: false,
|
||||
};
|
||||
assert!(scene.create_block_with_options(opt_no_handles).is_err());
|
||||
// No objects define an empty block.
|
||||
assert!(scene.create_block_with_options(opt_no_handles).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4,6 +4,7 @@ mod attdef;
|
||||
pub(crate) mod attedit;
|
||||
mod attman;
|
||||
pub mod base_point;
|
||||
pub(crate) mod block_cmdline;
|
||||
mod content_browser;
|
||||
pub(crate) mod create_block;
|
||||
mod design_center;
|
||||
|
||||
@@ -186,8 +186,11 @@ pub fn extract_entities_to_doc_with_base(
|
||||
if handles.is_empty() {
|
||||
return Err(t!("No entities selected for WBLOCK.").into_owned());
|
||||
}
|
||||
let shift = crate::command::EntityTransform::Translate(-base_point);
|
||||
copy_with_dependencies(src, handles, &mut out, Some(&shift));
|
||||
// The objects keep their coordinates; the base point becomes the new
|
||||
// drawing's insertion base (INSBASE).
|
||||
copy_with_dependencies(src, handles, &mut out, None);
|
||||
out.header.model_space_insertion_base =
|
||||
codec::types::Vector3::new(base_point.x, base_point.y, base_point.z);
|
||||
if out.entities().count() == 0 {
|
||||
return Err(t!("None of the selected entities could be exported.").into_owned());
|
||||
}
|
||||
@@ -218,12 +221,16 @@ mod tests {
|
||||
|
||||
assert_eq!(out.header.insertion_units, 4);
|
||||
assert_eq!(out.entities().count(), 1);
|
||||
assert_eq!(
|
||||
out.header.model_space_insertion_base,
|
||||
Vector3::new(10.0, 20.0, 0.0)
|
||||
);
|
||||
let e = out.entities().next().unwrap();
|
||||
if let EntityType::Line(l) = e {
|
||||
assert!((l.start.x - 0.0).abs() < 1e-6);
|
||||
assert!((l.start.y - 0.0).abs() < 1e-6);
|
||||
assert!((l.end.x - 20.0).abs() < 1e-6);
|
||||
assert!((l.end.y - 20.0).abs() < 1e-6);
|
||||
assert!((l.start.x - 10.0).abs() < 1e-6);
|
||||
assert!((l.start.y - 20.0).abs() < 1e-6);
|
||||
assert!((l.end.x - 30.0).abs() < 1e-6);
|
||||
assert!((l.end.y - 40.0).abs() < 1e-6);
|
||||
} else {
|
||||
panic!("Expected Line entity");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user