forked from 1b-agent/opencadstudio-upstream
feat(mtext): evaluate dynamic text fields at render time
Re-evaluate AcDbField-driven MTEXT — date/login/document-property fields, table formulas and object properties — against the current system instead of showing the frozen last-save cache. The evaluation engine lives in `acadrust::fields`; OCS supplies only the environment (system clock, OS login, env vars) through a FieldContext implementation, and hooks the resolved text into MTEXT rendering, falling back to the cached value for anything it can't evaluate. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
b30f9b9741
commit
1b6a3a6f5b
4 files changed
+83
-3
No files matched your search
Generated
+1
-1
@@ -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",
|
||||
|
||||
@@ -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<String> {
|
||||
#[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<String> {
|
||||
#[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<String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
+10
-2
@@ -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<GlyphBox> {
|
||||
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<GlyphBox>
|
||||
} 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,
|
||||
|
||||
Reference in new issue
Block a user