forked from 1b-agent/opencadstudio-upstream
The codec and kernel repositories and crates are renamed opencadcodec and opencadkernel. Inside OCS they are depended on under short keys, so code reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node graph engine). File-format and protocol spellings stay as they were: the ACIS product id `acadrust`, the ACADRUST_* switches and the plugin version keys `acadrust_version` / `acadrust_source`. A button left of MCP on the command line opens a translucent node graph over the viewport, with a palette of object, input, math, logic, list, text and geometry nodes added by click or drag. An object node owns drawing entities and its rows are the Properties panel's own rows for them: the single-object page is extracted from refresh_properties as entity_property_sections, values use the control protocol's property_json, and writes go through set_property_value with a property target override, so the panel, MCP and the graph share one read/write path. A list reaching an object row makes one entity per element. Each graph action is one undo step; the canvas pans and zooms. Co-Authored-By: Claude Opus 5.5 <[email protected]>
108 lines
3.9 KiB
Rust
108 lines
3.9 KiB
Rust
// Regression for #385: text and dimension text must reach the PDF / print
|
|
// export. From v0.8.2 text renders on-screen only as GPU SDF glyph quads
|
|
// (`WireModel::text_verts`); the CPU PDF exporter used to draw only wire
|
|
// stroke `points`, so all text — standalone TEXT/MTEXT and dimension text —
|
|
// vanished from exported PDFs (present in v0.7.6).
|
|
//
|
|
// The op-level placement checks live in `pdf_export`'s own unit tests, which can
|
|
// see `emit_text`'s ops. What only an integration test can cover is that the
|
|
// real entity -> scene.entity_wires() -> export_pdf path carries text at all,
|
|
// per entity kind — so that is what this asserts.
|
|
use codec::entities::{Dimension, DimensionLinear, Text};
|
|
use codec::types::Vector3;
|
|
use codec::EntityType;
|
|
use OpenCADStudio::io::pdf_export::{
|
|
export_pdf, PdfPageInput, PdfPlotOptions, PlotContent, PlotWire,
|
|
};
|
|
use OpenCADStudio::scene::Scene;
|
|
|
|
#[test]
|
|
fn text_and_dim_reach_pdf_export() {
|
|
let mut scene = Scene::new();
|
|
|
|
let t = Text::with_value("HELLO", Vector3::new(10.0, 10.0, 0.0)).with_height(5.0);
|
|
let text_h = scene.add_entity(EntityType::Text(t));
|
|
|
|
// A linear dimension — its measurement value is synthesized as SDF text too,
|
|
// and dimension text is what #385 was actually filed about.
|
|
let dim = DimensionLinear::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(40.0, 0.0, 0.0));
|
|
let dim_h = scene.add_entity(EntityType::Dimension(Dimension::Linear(dim)));
|
|
|
|
let wires = scene.entity_wires();
|
|
|
|
// Assert per entity, not "some wire somewhere has text": both asserts below
|
|
// would otherwise be satisfied by the TEXT alone, leaving a dimension-text
|
|
// regression — the actual reported bug — green.
|
|
// Tessellation names each wire after its entity handle (see the
|
|
// `w.name.parse::<u64>()` lookup in the render pipeline).
|
|
let has_text = |h: codec::types::Handle| {
|
|
let want = h.value().to_string();
|
|
wires
|
|
.iter()
|
|
.any(|w| w.name == want && !w.text_verts.is_empty())
|
|
};
|
|
assert!(has_text(text_h), "TEXT entity emitted no text glyphs");
|
|
assert!(
|
|
has_text(dim_h),
|
|
"DIMENSION entity emitted no measurement-value text glyphs"
|
|
);
|
|
|
|
// End-to-end: the same wire set with and without text. A private temp dir
|
|
// keeps concurrent runs (and other users on a shared build host) from
|
|
// colliding on a fixed name in the shared temp root.
|
|
let dir = std::env::temp_dir().join(format!("ocs385-{}", std::process::id()));
|
|
std::fs::create_dir_all(&dir).expect("temp dir");
|
|
|
|
let plot_wires: Vec<PlotWire> = wires
|
|
.iter()
|
|
.map(|w| PlotWire {
|
|
wire: w.clone(),
|
|
draw_depth: 0.0,
|
|
})
|
|
.collect();
|
|
|
|
let p_text = dir.join("with_text.pdf");
|
|
let mut page = PdfPageInput {
|
|
content: PlotContent {
|
|
wires: std::sync::Arc::new(plot_wires),
|
|
..Default::default()
|
|
},
|
|
paper_w: 210.0,
|
|
paper_h: 297.0,
|
|
offset_x: 0.0,
|
|
offset_y: 0.0,
|
|
rotation_deg: 0,
|
|
scale: 1.0,
|
|
clip: None,
|
|
options: PdfPlotOptions::default(),
|
|
plot_style: None,
|
|
};
|
|
export_pdf(&page, &p_text).expect("export with text");
|
|
let with_text = std::fs::read(&p_text).expect("read pdf");
|
|
assert!(with_text.starts_with(b"%PDF"), "not a PDF");
|
|
|
|
let stripped: Vec<_> = page
|
|
.content
|
|
.wires
|
|
.iter()
|
|
.cloned()
|
|
.map(|mut w| {
|
|
w.wire.text_verts.clear();
|
|
w
|
|
})
|
|
.collect();
|
|
let p_bare = dir.join("no_text.pdf");
|
|
page.content.wires = std::sync::Arc::new(stripped);
|
|
export_pdf(&page, &p_bare).expect("export without text");
|
|
let no_text = std::fs::read(&p_bare).expect("read pdf");
|
|
|
|
assert!(
|
|
with_text.len() > no_text.len(),
|
|
"text added no content to the PDF: {} !> {}",
|
|
with_text.len(),
|
|
no_text.len()
|
|
);
|
|
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|