forked from 1b-agent/opencadstudio-upstream
Merge pull request #1484 from ramox81/codex/pdf-import-followups
PDF import and underlay follow-ups
This commit is contained in:
commit
e2e2c36b6a
12 files changed
+412
-83
No files matched your search
@@ -622,6 +622,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"PDFIMPORTMODE",
|
||||
"PDFIMPORTFILTER",
|
||||
"PDFIMPORTLAYERS",
|
||||
"PDFIMPORTIMAGEPATH",
|
||||
"XDWGFADECTL",
|
||||
"POINTCLOUDCLIPFRAME",
|
||||
"XCLIPFRAME",
|
||||
|
||||
@@ -77,18 +77,12 @@ impl OpenCADStudio {
|
||||
};
|
||||
let naming = pdf_import::LayerNaming {
|
||||
settings,
|
||||
prefix: import_prefix(&path),
|
||||
prefix: import_prefix(),
|
||||
current: current_layer.clone(),
|
||||
};
|
||||
let mut result = pdf_import::convert(&content, &underlay, &area, &naming);
|
||||
let images = std::mem::take(&mut result.images);
|
||||
let image_dir = self.tabs[i]
|
||||
.current_path
|
||||
.as_deref()
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.to_path_buf())
|
||||
.or_else(|| std::path::Path::new(&path).parent().map(|p| p.to_path_buf()))
|
||||
.map(|dir| dir.join("PDF Images"));
|
||||
let image_dir = pdf_import::image_dir(&path);
|
||||
|
||||
self.push_undo_snapshot(i, "PDFIMPORT");
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
@@ -100,8 +94,8 @@ impl OpenCADStudio {
|
||||
lt.pattern_length = 1.0;
|
||||
let _ = scene.document.line_types.add(lt);
|
||||
}
|
||||
// Raster images: each written as a PNG under "PDF Images" next to the
|
||||
// drawing (or the PDF while the drawing is unsaved), then referenced.
|
||||
// Raster images: each written as a PNG in the PDFIMPORTIMAGEPATH
|
||||
// folder, named after the PDF with 8 hex digits, then referenced.
|
||||
let stem = std::path::Path::new(&path.replace('\\', "/"))
|
||||
.file_stem()
|
||||
.map(|s| s.to_string_lossy().into_owned())
|
||||
@@ -117,8 +111,14 @@ impl OpenCADStudio {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Rebuilt from its components: one separator style, the platform's.
|
||||
let stored = file
|
||||
.components()
|
||||
.collect::<std::path::PathBuf>()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let mut raster = codec::entities::RasterImage::with_size(
|
||||
file.to_string_lossy().as_ref(),
|
||||
&stored,
|
||||
image.insertion,
|
||||
image.width as f64,
|
||||
image.height as f64,
|
||||
@@ -128,7 +128,9 @@ impl OpenCADStudio {
|
||||
raster.u_vector = image.u;
|
||||
raster.v_vector = image.v;
|
||||
raster.flags = codec::entities::ImageDisplayFlags::SHOW_IMAGE
|
||||
| codec::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY;
|
||||
| codec::entities::ImageDisplayFlags::SHOW_NOT_ALIGNED
|
||||
| codec::entities::ImageDisplayFlags::USE_CLIPPING_BOUNDARY
|
||||
| codec::entities::ImageDisplayFlags::TRANSPARENCY_ON;
|
||||
raster.common.layer = image.layer.clone();
|
||||
result.entities.push(codec::EntityType::RasterImage(raster));
|
||||
}
|
||||
@@ -154,8 +156,8 @@ impl OpenCADStudio {
|
||||
let mut name = stem.clone();
|
||||
let mut n = 1;
|
||||
while scene.document.block_records.get(&name).is_some() {
|
||||
name = format!("{stem}{n}");
|
||||
n += 1;
|
||||
name = format!("{stem}({n})");
|
||||
}
|
||||
if scene
|
||||
.define_block_from_owned_entities(result.entities, &name, glam::DVec3::ZERO)
|
||||
@@ -239,24 +241,15 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
/// The layer prefix of an imported file: "PDF_" for the first file of the
|
||||
/// session, "PDF2_", "PDF3_" … for each other file.
|
||||
// ponytail: remembered for the session only; a reopened drawing starts from
|
||||
/// The layer prefix of an import: "PDF_" for the first import of the
|
||||
/// session, "PDF2_", "PDF3_" … for each later one, of the same file or not.
|
||||
// ponytail: counted for the session only; a reopened drawing starts from
|
||||
// "PDF_" again.
|
||||
fn import_prefix(path: &str) -> String {
|
||||
static FILES: std::sync::Mutex<Vec<String>> = std::sync::Mutex::new(Vec::new());
|
||||
let mut files = FILES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let index = match files.iter().position(|f| f.eq_ignore_ascii_case(path)) {
|
||||
Some(at) => at,
|
||||
None => {
|
||||
files.push(path.to_string());
|
||||
files.len() - 1
|
||||
}
|
||||
};
|
||||
if index == 0 {
|
||||
"PDF_".to_string()
|
||||
} else {
|
||||
format!("PDF{}_", index + 1)
|
||||
fn import_prefix() -> String {
|
||||
static IMPORTS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
||||
match IMPORTS.fetch_add(1, std::sync::atomic::Ordering::Relaxed) {
|
||||
0 => "PDF_".to_string(),
|
||||
n => format!("PDF{}_", n + 1),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1022,6 +1022,7 @@ impl OpenCADStudio {
|
||||
| "PDFIMPORTMODE"
|
||||
| "PDFIMPORTFILTER"
|
||||
| "PDFIMPORTLAYERS"
|
||||
| "PDFIMPORTIMAGEPATH"
|
||||
| "XDWGFADECTL"
|
||||
| "POINTCLOUDCLIPFRAME"
|
||||
| "XCLIPFRAME"
|
||||
@@ -1158,6 +1159,24 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
// A folder name; "." clears it.
|
||||
if name == "PDFIMPORTIMAGEPATH" {
|
||||
use crate::modules::insert::pdf_import::{image_path, set_image_path};
|
||||
match &value {
|
||||
Some(value) => {
|
||||
let value = value.trim().trim_matches('"');
|
||||
set_image_path(if value == "." { String::new() } else { value.to_string() });
|
||||
}
|
||||
None => {
|
||||
self.command_line.push_output(&format!(
|
||||
"Enter new value for PDFIMPORTIMAGEPATH, or . for none <\"{}\">:",
|
||||
image_path()
|
||||
));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if matches!(name.as_str(), "PDFOSNAP" | "UOSNAP") {
|
||||
let current = i16::from(crate::scene::model::pdf_vector::pdf_osnap());
|
||||
if let Some(value) = &value {
|
||||
|
||||
@@ -459,6 +459,7 @@ impl OpenCADStudio {
|
||||
| "commands"
|
||||
| "properties"
|
||||
| "measure"
|
||||
| "snap"
|
||||
| "query"
|
||||
| "records"
|
||||
| "record_schema"
|
||||
@@ -705,6 +706,28 @@ impl OpenCADStudio {
|
||||
let response = match op {
|
||||
"properties" => self.control_properties(),
|
||||
"measure" => self.control_measure(&req),
|
||||
// The object snap a cursor over `point` (world) would get;
|
||||
// `from` is the base for perpendicular and tangent.
|
||||
"snap" => {
|
||||
let p = &req["point"];
|
||||
let world = glam::DVec3::new(
|
||||
p[0].as_f64().unwrap_or(0.0),
|
||||
p[1].as_f64().unwrap_or(0.0),
|
||||
p[2].as_f64().unwrap_or(0.0),
|
||||
);
|
||||
let from = req["from"].as_array().map(|f| {
|
||||
glam::DVec3::new(
|
||||
f.first().and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
f.get(1).and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
f.get(2).and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
)
|
||||
});
|
||||
let i = self.active_tab;
|
||||
match self.snap_query(i, world, from) {
|
||||
Some(hit) => json!({"ok":true,"snap":format!("{:?}", hit.snap_type),"world":[hit.world.x,hit.world.y,hit.world.z]}),
|
||||
None => json!({"ok":true,"snap":null}),
|
||||
}
|
||||
}
|
||||
"xdata_get" => self.xdata_read(&req),
|
||||
"get_selection" => self.control_get_selection(),
|
||||
"history" => {
|
||||
|
||||
@@ -2872,6 +2872,49 @@ impl OpenCADStudio {
|
||||
)
|
||||
}
|
||||
|
||||
/// The object snap the cursor would get over `world` in the model view,
|
||||
/// through the same candidate gathering and snapper as a cursor move
|
||||
/// (automation's `snap` query).
|
||||
pub(in crate::app) fn snap_query(
|
||||
&mut self,
|
||||
i: usize,
|
||||
world: glam::DVec3,
|
||||
from: Option<glam::DVec3>,
|
||||
) -> Option<crate::snap::SnapResult> {
|
||||
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
|
||||
if vw <= 1.0 || vh <= 1.0 {
|
||||
return None;
|
||||
}
|
||||
let tile_b = self.tabs[i].scene.active_model_tile_bounds(vw, vh);
|
||||
let bounds = iced::Rectangle {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: tile_b.width,
|
||||
height: tile_b.height,
|
||||
};
|
||||
let (view_rot, eye, px) = {
|
||||
let cam = self.tabs[i].scene.camera.borrow();
|
||||
(cam.view_proj_rte(bounds), cam.eye(), cam.project(world, bounds)?)
|
||||
};
|
||||
let p = Point::new(px.x, px.y);
|
||||
let raw = self.cursor_model_point(i, &None, p, bounds);
|
||||
let all_wires = self.tabs[i].scene.hit_test_wires();
|
||||
let candidates = self.tabs[i].scene.interaction_candidates_near(
|
||||
all_wires,
|
||||
raw,
|
||||
view_rot,
|
||||
eye,
|
||||
bounds,
|
||||
self.snapper.osnap_radius_px,
|
||||
);
|
||||
// Perpendicular and tangent measure from `from`, else the running
|
||||
// command's last point.
|
||||
self.snapper.from_point = from.or(self.last_point).map(|p| p.as_vec3());
|
||||
let (go, gr) = self.drafting_grid_basis(i);
|
||||
self.snapper
|
||||
.snap(raw, p, &candidates, view_rot, eye, bounds, go, gr, None)
|
||||
}
|
||||
|
||||
/// Apply one frame of a UCS-icon grip drag: map the cursor onto the active
|
||||
/// UCS plane (so the move stays in-plane) and either slide the origin there
|
||||
/// or rotate the chosen axis to point at it, keeping a right-handed frame
|
||||
|
||||
Reference in new issue
Block a user