feat: DATAEXTRACTION — export entity data to CSV
- DATAEXTRACTION / EATTEXT / ATTEXT commands trigger an async save dialog
- Extracts all model-space entities with: Type, Handle, Layer, Color,
Linetype, and a geometry summary per entity type (coords, radius, etc.)
- DataExtractionSave / DataExtractionSaveResult message pair drives
the rfd save-file dialog asynchronously
- CSV fields are properly escaped (commas, quotes, newlines)
- ROADMAP.md: 12 DATAEXTRACTION marked ✅
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b15be3af3d
commit
f80d694818
4 changed files with 118 additions and 1 deletions
|
|
@ -1612,6 +1612,11 @@ impl H7CAD {
|
|||
self.command_line.push_output(&format!("COUNT: {total} entity(ies) total."));
|
||||
}
|
||||
|
||||
"DATAEXTRACTION" | "EATTEXT" | "ATTEXT" => {
|
||||
let csv = build_data_extraction_csv(&self.tabs[i].scene.document);
|
||||
return Task::done(Message::DataExtractionSave(csv));
|
||||
}
|
||||
|
||||
// ── Find / Replace ────────────────────────────────────────────────
|
||||
// FIND <search> — list all Text/MText/Dimension containing <search>
|
||||
// FIND <search> REPLACE <rep> — replace first occurrence (case-insensitive)
|
||||
|
|
@ -3613,3 +3618,71 @@ fn replace_entity_text(entity: &mut acadrust::EntityType, search: &str, rep: &st
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// ── DATAEXTRACTION ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Build a CSV string with one row per entity in model space.
|
||||
/// Columns: Type, Handle, Layer, Color, Linetype, ExtraInfo
|
||||
fn build_data_extraction_csv(doc: &acadrust::CadDocument) -> String {
|
||||
use acadrust::EntityType;
|
||||
|
||||
let mut out = String::from("Type,Handle,Layer,Color,Linetype,ExtraInfo\n");
|
||||
|
||||
let ms_handle = doc.header.model_space_block_handle;
|
||||
for e in doc.entities() {
|
||||
// Skip Block/EndBlock sentinels and paper-space entities.
|
||||
if matches!(e, EntityType::Block(_) | EntityType::BlockEnd(_)) {
|
||||
continue;
|
||||
}
|
||||
if !ms_handle.is_null() && e.common().owner_handle != ms_handle {
|
||||
continue;
|
||||
}
|
||||
let type_name = entity_type_name(e);
|
||||
let handle = format!("{:X}", e.common().handle.value());
|
||||
let layer = csv_escape(&e.common().layer);
|
||||
let color = format!("{}", e.common().color);
|
||||
let lt = csv_escape(&e.common().linetype);
|
||||
let extra = csv_escape(&entity_extra_info(e));
|
||||
out.push_str(&format!("{type_name},{handle},{layer},{color},{lt},{extra}\n"));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Return a short geometry summary for CSV ExtraInfo column.
|
||||
fn entity_extra_info(entity: &acadrust::EntityType) -> String {
|
||||
use acadrust::EntityType;
|
||||
match entity {
|
||||
EntityType::Line(e) => format!(
|
||||
"({:.3},{:.3})-({:.3},{:.3})",
|
||||
e.start.x, e.start.y, e.end.x, e.end.y
|
||||
),
|
||||
EntityType::Circle(e) => format!(
|
||||
"C({:.3},{:.3}) R={:.3}",
|
||||
e.center.x, e.center.y, e.radius
|
||||
),
|
||||
EntityType::Arc(e) => format!(
|
||||
"C({:.3},{:.3}) R={:.3} {:.1}°-{:.1}°",
|
||||
e.center.x, e.center.y, e.radius, e.start_angle, e.end_angle
|
||||
),
|
||||
EntityType::Text(e) => e.value.clone(),
|
||||
EntityType::MText(e) => e.value.chars().take(60).collect(),
|
||||
EntityType::Insert(e) => format!("BLK={} @({:.3},{:.3})", e.block_name, e.insert_point.x, e.insert_point.y),
|
||||
EntityType::LwPolyline(e) => format!("{} vertices", e.vertices.len()),
|
||||
EntityType::Polyline(e) => format!("{} vertices", e.vertices.len()),
|
||||
EntityType::Polyline2D(e) => format!("{} vertices", e.vertices.len()),
|
||||
EntityType::Polyline3D(e) => format!("{} vertices", e.vertices.len()),
|
||||
EntityType::Hatch(e) => format!("PAT={}", e.pattern.name),
|
||||
EntityType::Dimension(e) => format!("{:.3}", e.base().actual_measurement),
|
||||
EntityType::Spline(e) => format!("{} ctrl pts", e.control_points.len()),
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Escape a string for a CSV field (wrap in quotes if it contains comma/quote/newline).
|
||||
fn csv_escape(s: &str) -> String {
|
||||
if s.contains(',') || s.contains('"') || s.contains('\n') {
|
||||
format!("\"{}\"", s.replace('"', "\"\""))
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue