forked from 1b-agent/opencadstudio-upstream
DWF and DGN underlays: attach, display, snaps, frames and references
DWFATTACH / -DWFATTACH and DGNATTACH / -DGNATTACH attach a DWF / DWFx sheet or a DGN (V7 or V8) model with the reference's prompts; ATTACH routes the file types to them. The sheet or model is read into vector paths, drawn as a transparent underlay raster (contrast, fade, monochrome and background adjustment apply) and offers its geometry to object snaps. Definitions are kept in ACAD_DWFDEFINITIONS / ACAD_DGNDEFINITIONS, the External References palette lists and reloads them, and image textures get line-keeping mip levels so thin underlay lines stay visible when zoomed out. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7f8f8457c7
commit
cb7860e50e
46 files changed
+2723
-129
No files matched your search
@@ -613,12 +613,12 @@ impl OpenCADStudio {
|
||||
crate::app::commands::pdf_import::PdfImportSource::File(import),
|
||||
);
|
||||
}
|
||||
CmdResult::AttachPdfPages { path, pages } => {
|
||||
CmdResult::AttachPdfPages { kind, path, pages } => {
|
||||
let label = self.history_label_from_active_cmd(i, "PDFATTACH");
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.attach_pdf_pages(i, label, &path, pages);
|
||||
self.attach_pdf_pages(i, label, kind, &path, pages);
|
||||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
CmdResult::PdfImport(request) => {
|
||||
|
||||
@@ -218,14 +218,16 @@ impl OpenCADStudio {
|
||||
&mut self,
|
||||
i: usize,
|
||||
label: String,
|
||||
kind: codec::entities::UnderlayType,
|
||||
path: &str,
|
||||
pages: Vec<(String, codec::EntityType)>,
|
||||
) {
|
||||
let pending = self.begin_undo(i, label, pages.len(), false);
|
||||
|
||||
for (page, mut entity) in pages {
|
||||
let definition = crate::modules::insert::pdf_attach::ensure_pdf_definition(
|
||||
let definition = crate::modules::insert::pdf_attach::ensure_underlay_definition(
|
||||
&mut self.tabs[i].scene.document,
|
||||
kind,
|
||||
path,
|
||||
&page,
|
||||
);
|
||||
|
||||
@@ -1022,6 +1022,8 @@ impl OpenCADStudio {
|
||||
| "DWFFRAME"
|
||||
| "DGNFRAME"
|
||||
| "PDFOSNAP"
|
||||
| "DWFOSNAP"
|
||||
| "DGNOSNAP"
|
||||
| "UOSNAP"
|
||||
| "PDFIMPORTMODE"
|
||||
| "PDFIMPORTFILTER"
|
||||
@@ -1181,7 +1183,9 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if matches!(name.as_str(), "PDFOSNAP" | "UOSNAP") {
|
||||
// ponytail: one flag for every underlay kind; split it if a drawing
|
||||
// needs PDF snaps on while DWF/DGN snaps are off.
|
||||
if matches!(name.as_str(), "PDFOSNAP" | "DWFOSNAP" | "DGNOSNAP" | "UOSNAP") {
|
||||
let current = i16::from(crate::scene::model::pdf_vector::pdf_osnap());
|
||||
if let Some(value) = &value {
|
||||
match value.parse::<i16>().ok().filter(|value| (0..=1).contains(value)) {
|
||||
|
||||
@@ -12,6 +12,26 @@ use crate::modules::insert::xref_cmd::display_path;
|
||||
use crate::ui::window::xref_attach::{XrefAttachMsg, XrefAttachState};
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// DWFATTACH / DGNATTACH on the command line: with a file, from its
|
||||
/// sheet / model prompt; without one, from the path prompt.
|
||||
pub(super) fn start_underlay_attach(
|
||||
&mut self,
|
||||
i: usize,
|
||||
kind: codec::entities::UnderlayType,
|
||||
file: &str,
|
||||
) {
|
||||
use crate::command::CadCommand;
|
||||
let insunits = self.tabs[i].scene.document.header.insertion_units;
|
||||
let mut command = crate::modules::insert::pdf_attach::PdfAttachCommand::for_kind(kind, insunits);
|
||||
if !file.trim().is_empty() {
|
||||
if let Some(crate::command::CmdResult::ReportError(message)) = command.on_text_input(file) {
|
||||
self.command_line.push_error(&message);
|
||||
}
|
||||
}
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
/// Commands owned here; `None` defers to the other families.
|
||||
pub(super) fn dispatch_xref_attach(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
let upper = cmd.to_ascii_uppercase();
|
||||
@@ -21,6 +41,22 @@ impl OpenCADStudio {
|
||||
};
|
||||
match (verb.as_str(), rest.is_empty()) {
|
||||
("ATTACH", true) => Some(Task::done(Message::AttachPick)),
|
||||
("DWFATTACH", true) => Some(Task::done(Message::UnderlayAttachPick(
|
||||
codec::entities::UnderlayType::Dwf,
|
||||
))),
|
||||
("DGNATTACH", true) => Some(Task::done(Message::UnderlayAttachPick(
|
||||
codec::entities::UnderlayType::Dgn,
|
||||
))),
|
||||
// A file after DWFATTACH / DGNATTACH, or the - forms, go on the
|
||||
// command line.
|
||||
("DWFATTACH" | "-DWFATTACH", _) => {
|
||||
self.start_underlay_attach(i, codec::entities::UnderlayType::Dwf, rest);
|
||||
Some(Task::none())
|
||||
}
|
||||
("DGNATTACH" | "-DGNATTACH", _) => {
|
||||
self.start_underlay_attach(i, codec::entities::UnderlayType::Dgn, rest);
|
||||
Some(Task::none())
|
||||
}
|
||||
("XATTACH", true) => Some(Task::done(Message::XAttachPick)),
|
||||
// A file named on the command line skips the picker.
|
||||
// Handled at once, so the placement it starts belongs to this
|
||||
@@ -299,6 +335,26 @@ impl OpenCADStudio {
|
||||
/// Messages of the ATTACH picker and the dialog.
|
||||
pub(in crate::app) fn update_xref_attach(&mut self, message: Message) -> Task<Message> {
|
||||
match message {
|
||||
Message::UnderlayAttachPick(kind) => Task::perform(
|
||||
async move {
|
||||
let (title, filter, extensions): (&str, &str, &[&str]) = match kind {
|
||||
codec::entities::UnderlayType::Dgn => {
|
||||
("Select DGN File", "DGN Files", &["dgn"])
|
||||
}
|
||||
_ => ("Select DWF File", "DWF Files", &["dwf", "dwfx"]),
|
||||
};
|
||||
let handle = crate::sys::file_dialog()
|
||||
.set_title(crate::t!(title).as_ref())
|
||||
.add_filter(crate::t!(filter).as_ref(), extensions)
|
||||
.pick_file()
|
||||
.await;
|
||||
match handle {
|
||||
Some(h) => Ok(crate::sys::handle_path(&h)),
|
||||
None => Err("Cancelled".to_string()),
|
||||
}
|
||||
},
|
||||
Message::AttachPickResult,
|
||||
),
|
||||
Message::AttachPick => Task::perform(
|
||||
async {
|
||||
let handle = crate::sys::file_dialog()
|
||||
@@ -306,11 +362,14 @@ impl OpenCADStudio {
|
||||
.add_filter(
|
||||
crate::t!("All Reference Files").as_ref(),
|
||||
&[
|
||||
"dwg", "dxf", "pdf", "png", "jpg", "jpeg", "bmp", "tif", "tiff",
|
||||
"dwg", "dxf", "pdf", "dwf", "dwfx", "dgn", "png", "jpg", "jpeg",
|
||||
"bmp", "tif", "tiff",
|
||||
],
|
||||
)
|
||||
.add_filter(crate::t!("Drawing").as_ref(), &["dwg", "dxf"])
|
||||
.add_filter(crate::t!("PDF Files").as_ref(), &["pdf"])
|
||||
.add_filter(crate::t!("DWF Files").as_ref(), &["dwf", "dwfx"])
|
||||
.add_filter(crate::t!("DGN Files").as_ref(), &["dgn"])
|
||||
.add_filter(
|
||||
crate::t!("Images").as_ref(),
|
||||
&["png", "jpg", "jpeg", "bmp", "tif", "tiff"],
|
||||
@@ -337,6 +396,16 @@ impl OpenCADStudio {
|
||||
)))),
|
||||
Err(e) => self.update(Message::PdfAttachPickResult(Err(e.to_string()))),
|
||||
},
|
||||
"dwf" | "dwfx" | "dgn" => {
|
||||
let kind = if ext == "dgn" {
|
||||
codec::entities::UnderlayType::Dgn
|
||||
} else {
|
||||
codec::entities::UnderlayType::Dwf
|
||||
};
|
||||
let i = self.active_tab;
|
||||
self.start_underlay_attach(i, kind, &path.to_string_lossy());
|
||||
Task::none()
|
||||
}
|
||||
"png" | "jpg" | "jpeg" | "bmp" | "tif" | "tiff" => {
|
||||
let result = image::image_dimensions(&path)
|
||||
.map(|(w, h)| (path.clone(), w, h))
|
||||
|
||||
@@ -3851,6 +3851,8 @@ pub enum Message {
|
||||
XAttachPickResult(Result<std::path::PathBuf, String>),
|
||||
/// ATTACH: pick a drawing, image or PDF to reference.
|
||||
AttachPick,
|
||||
/// DWFATTACH / DGNATTACH: pick the file (the result goes the ATTACH way).
|
||||
UnderlayAttachPick(codec::entities::UnderlayType),
|
||||
/// The Reference slide-out's xref fading: amount dragged, drag done,
|
||||
/// switch.
|
||||
XrefFadeSlide(u8),
|
||||
|
||||
@@ -867,8 +867,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
Some(entity.common().handle)
|
||||
}
|
||||
codec::EntityType::Underlay(underlay)
|
||||
if matches!(underlay.underlay_type, codec::entities::UnderlayType::Pdf)
|
||||
&& self.tabs[i].xref_unloaded.is_unloaded(underlay.definition_handle.value()) =>
|
||||
if self.tabs[i].xref_unloaded.is_unloaded(underlay.definition_handle.value()) =>
|
||||
{
|
||||
Some(entity.common().handle)
|
||||
}
|
||||
@@ -1021,8 +1020,8 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
"XREF: cannot reload nested reference '{}'. Reload it in its host drawing.",
|
||||
name
|
||||
).as_ref());
|
||||
} else if *kind == crate::io::xref_model::RefKind::Pdf {
|
||||
// A PDF reloads by clearing its definition's unloaded state.
|
||||
} else if *kind == crate::io::xref_model::RefKind::Underlay {
|
||||
// An underlay reloads by clearing its definition's unloaded state.
|
||||
for (key, row_name, row_kind, _) in &picked {
|
||||
if row_kind != kind || row_name != name {
|
||||
continue;
|
||||
|
||||
@@ -1426,6 +1426,7 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
message @ (Message::AttachPick
|
||||
| Message::UnderlayAttachPick(_)
|
||||
| Message::AttachPickResult(_)
|
||||
| Message::XrefAttach(_)
|
||||
| Message::XrefAttachBrowseResult(_)) => self.update_xref_attach(message),
|
||||
|
||||
Reference in new issue
Block a user