diff --git a/Cargo.lock b/Cargo.lock index 88ff809b2..96d9d9628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.4.0" -source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#a925f1bf2e929233c79f376ee6aeb9aa4511d262" +source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#885a22bacf8e30e5218e7b5856a2175241593271" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/src/entities/field.rs b/src/entities/field.rs new file mode 100644 index 000000000..8ddee9256 --- /dev/null +++ b/src/entities/field.rs @@ -0,0 +1,71 @@ +//! Host-side glue for dynamic text field evaluation. +//! +//! The field language (DIESEL + AcVar), the field structure/linkage, and all +//! date math live in the reader library ([`acadrust::fields`]). OCS only +//! supplies the **environment** — the current clock, OS login and environment +//! variables — through [`FieldContext`]. A DWG library must stay deterministic +//! and platform-neutral, so the one genuinely system-specific bit (reading the +//! clock / OS user) lives here in the app instead. + +use acadrust::fields::FieldContext; +use acadrust::types::Handle; +use acadrust::CadDocument; + +/// OCS's environment provider for field evaluation. +struct OcsFieldContext; + +impl FieldContext for OcsFieldContext { + fn now_julian(&self) -> f64 { + // Unix epoch is Julian Day 2440587.5. + epoch_secs() as f64 / 86_400.0 + 2_440_587.5 + } + + fn login(&self) -> Option { + #[cfg(not(target_arch = "wasm32"))] + { + for var in ["USER", "LOGNAME", "USERNAME"] { + if let Ok(v) = std::env::var(var) { + if !v.is_empty() { + return Some(v); + } + } + } + } + None + } + + fn getenv(&self, name: &str) -> Option { + #[cfg(not(target_arch = "wasm32"))] + { + return std::env::var(name).ok().filter(|v| !v.is_empty()); + } + #[cfg(target_arch = "wasm32")] + { + let _ = name; + None + } + } +} + +/// Re-evaluate the field hosted by entity `host` (usually an MTEXT), or `None` +/// to keep the cached text. Thin wrapper over the library engine. +pub fn resolve(document: &CadDocument, host: Handle) -> Option { + acadrust::fields::resolve(document, host, &OcsFieldContext) +} + +/// Seconds since the Unix epoch. Native uses the system clock; wasm uses the JS +/// `Date` clock. This platform-specific read stays in the app, not the library. +fn epoch_secs() -> i64 { + #[cfg(not(target_arch = "wasm32"))] + { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs() as i64) + .unwrap_or(0) + } + #[cfg(target_arch = "wasm32")] + { + (js_sys::Date::now() / 1000.0) as i64 + } +} diff --git a/src/entities/mod.rs b/src/entities/mod.rs index 8e9acb215..8542d26fb 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -5,6 +5,7 @@ pub mod common; pub mod dim_override; pub mod dimension; pub mod ellipse; +pub mod field; pub mod hatch; pub mod helix; pub mod insert; diff --git a/src/entities/mtext.rs b/src/entities/mtext.rs index a82ac51bd..537de57a5 100644 --- a/src/entities/mtext.rs +++ b/src/entities/mtext.rs @@ -71,6 +71,12 @@ fn drawing_dir_str(d: &DrawingDirection) -> &'static str { /// Per-visible-character world-space boxes for the MText editor's /// click-to-select preview. Uses the exact same layout opts as `to_truck` /// so the boxes line up with the rendered glyphs. +/// The MTEXT string to render: the live re-evaluated value when the entity +/// hosts a dynamic field, otherwise its stored (cached) value. +fn display_value(t: &MText, document: &acadrust::CadDocument) -> String { + crate::entities::field::resolve(document, t.common.handle).unwrap_or_else(|| t.value.clone()) +} + pub fn glyph_boxes(t: &MText, document: &acadrust::CadDocument) -> Vec { let resolved_style = resolve_text_style(&t.style, document); let attach_h_anchor: f32 = match t.attachment_point { @@ -98,8 +104,9 @@ pub fn glyph_boxes(t: &MText, document: &acadrust::CadDocument) -> Vec } else { t.rotation as f32 }; + let display = display_value(t, document); let layout = layout_mtext(&MTextRenderOpts { - value: &t.value, + value: &display, insertion: [ t.insertion_point.x, t.insertion_point.y, @@ -146,8 +153,9 @@ fn to_truck(t: &MText, document: &acadrust::CadDocument) -> TruckEntity { } else { t.rotation as f32 }; + let display = display_value(t, document); let layout = layout_mtext(&MTextRenderOpts { - value: &t.value, + value: &display, insertion: [ t.insertion_point.x, t.insertion_point.y,