forked from 1b-agent/opencadstudio-upstream
Show drawing defaults when nothing is selected and disable layout controls on Start. Refs #514 Refs #515
2722 lines
110 KiB
Rust
2722 lines
110 KiB
Rust
//! OCS-facing integration for non-graphical DWG object graphs.
|
||
//!
|
||
//! Dynamic-block evaluation records, solid construction history and
|
||
//! associative networks do not draw standalone geometry. Their native place in
|
||
//! OCS is the selected entity's semantic/property view, while the evaluated
|
||
//! entity geometry continues through the normal render pipeline.
|
||
|
||
use acadrust::objects::{
|
||
AssociativeData, BlockEvalValue, DynamicBlockData, ObjectType,
|
||
SolidHistoryNodeBase, SolidHistoryOperation,
|
||
};
|
||
use acadrust::{CadDocument, EntityType, Handle};
|
||
|
||
use crate::scene::model::object::{PropSection, PropValue, Property};
|
||
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct ObjectDataCache {
|
||
document_sections: std::sync::Arc<Vec<PropSection>>,
|
||
dynamic_objects: std::sync::Arc<Vec<Handle>>,
|
||
associative_objects: std::sync::Arc<Vec<Handle>>,
|
||
light_entities: std::sync::Arc<Vec<Handle>>,
|
||
sun_objects: std::sync::Arc<Vec<Handle>>,
|
||
geo_objects: std::sync::Arc<Vec<Handle>>,
|
||
}
|
||
|
||
pub fn build_cache(document: &CadDocument) -> ObjectDataCache {
|
||
let mut dynamic_objects = Vec::new();
|
||
let mut associative_objects = Vec::new();
|
||
let mut ordered_lights = Vec::new();
|
||
let mut sun_objects = Vec::new();
|
||
let mut geo_objects = Vec::new();
|
||
for (handle, object) in &document.objects {
|
||
match object {
|
||
ObjectType::DynamicBlock(_) => dynamic_objects.push(*handle),
|
||
ObjectType::Associative(_) => associative_objects.push(*handle),
|
||
ObjectType::GeoData(_) => geo_objects.push(*handle),
|
||
ObjectType::ClassObject(value) => match &value.data {
|
||
acadrust::objects::ClassObjectData::LightList(list) => {
|
||
ordered_lights.extend(list.lights.iter().map(|entry| entry.handle));
|
||
}
|
||
acadrust::objects::ClassObjectData::Sun(_) => sun_objects.push(*handle),
|
||
_ => {}
|
||
},
|
||
_ => {}
|
||
}
|
||
}
|
||
let mut seen_lights = rustc_hash::FxHashSet::default();
|
||
ordered_lights.retain(|handle| seen_lights.insert(*handle));
|
||
for entity in document.entities() {
|
||
if let EntityType::Light(light) = entity {
|
||
if seen_lights.insert(light.common.handle) {
|
||
ordered_lights.push(light.common.handle);
|
||
}
|
||
}
|
||
}
|
||
dynamic_objects.sort_by_key(|handle| handle.value());
|
||
associative_objects.sort_by_key(|handle| handle.value());
|
||
sun_objects.sort_by_key(|handle| handle.value());
|
||
geo_objects.sort_by_key(|handle| handle.value());
|
||
ObjectDataCache {
|
||
document_sections: std::sync::Arc::new(build_document_sections(document)),
|
||
dynamic_objects: std::sync::Arc::new(dynamic_objects),
|
||
associative_objects: std::sync::Arc::new(associative_objects),
|
||
light_entities: std::sync::Arc::new(ordered_lights),
|
||
sun_objects: std::sync::Arc::new(sun_objects),
|
||
geo_objects: std::sync::Arc::new(geo_objects),
|
||
}
|
||
}
|
||
|
||
pub fn cache_is_prepared(cache: &ObjectDataCache) -> bool {
|
||
!cache.document_sections.is_empty()
|
||
}
|
||
|
||
pub fn light_entities(cache: &ObjectDataCache) -> &[Handle] {
|
||
&cache.light_entities
|
||
}
|
||
|
||
pub fn sun_objects(cache: &ObjectDataCache) -> &[Handle] {
|
||
&cache.sun_objects
|
||
}
|
||
|
||
pub fn geo_objects(cache: &ObjectDataCache) -> &[Handle] {
|
||
&cache.geo_objects
|
||
}
|
||
|
||
pub fn update_light_entity(
|
||
cache: &mut ObjectDataCache,
|
||
handle: Handle,
|
||
exists: bool,
|
||
) {
|
||
let lights = std::sync::Arc::make_mut(&mut cache.light_entities);
|
||
if exists {
|
||
if !lights.contains(&handle) {
|
||
lights.push(handle);
|
||
}
|
||
} else {
|
||
lights.retain(|candidate| *candidate != handle);
|
||
}
|
||
}
|
||
|
||
fn ro(label: impl Into<String>, value: impl Into<String>) -> Property {
|
||
Property {
|
||
label: label.into(),
|
||
field: "dwg_object_data",
|
||
value: PropValue::ReadOnly(value.into()),
|
||
}
|
||
}
|
||
|
||
fn handle_text(handle: Handle) -> String {
|
||
if handle.is_valid() {
|
||
format!("{:X}", handle.value())
|
||
} else {
|
||
"None".to_string()
|
||
}
|
||
}
|
||
|
||
fn entity_history_handle(entity: &EntityType) -> Option<Handle> {
|
||
match entity {
|
||
EntityType::Solid3D(value) => value.history_handle,
|
||
EntityType::Region(value) => value.history_handle,
|
||
EntityType::Body(value) => value.history_handle,
|
||
EntityType::Surface(value) => value.history_handle,
|
||
_ => None,
|
||
}
|
||
.filter(|handle| handle.is_valid())
|
||
}
|
||
|
||
fn history_operation_name(operation: &SolidHistoryOperation) -> &'static str {
|
||
match operation {
|
||
SolidHistoryOperation::Unknown => "Unknown",
|
||
SolidHistoryOperation::Box(_) => "Box",
|
||
SolidHistoryOperation::Wedge(_) => "Wedge",
|
||
SolidHistoryOperation::Sphere(_) => "Sphere",
|
||
SolidHistoryOperation::Cylinder(_) => "Cylinder",
|
||
SolidHistoryOperation::Cone(_) => "Cone",
|
||
SolidHistoryOperation::Pyramid(_) => "Pyramid",
|
||
SolidHistoryOperation::Torus(_) => "Torus",
|
||
SolidHistoryOperation::Boolean(_) => "Boolean",
|
||
SolidHistoryOperation::Brep(_) => "Brep",
|
||
SolidHistoryOperation::Fillet(_) => "Fillet",
|
||
SolidHistoryOperation::Chamfer(_) => "Chamfer",
|
||
SolidHistoryOperation::Sweep(_) => "Sweep",
|
||
SolidHistoryOperation::Extrusion(_) => "Extrusion",
|
||
SolidHistoryOperation::Loft(_) => "Loft",
|
||
SolidHistoryOperation::Revolve(_) => "Revolve",
|
||
}
|
||
}
|
||
|
||
fn history_step_id(operation: &SolidHistoryOperation) -> Option<i32> {
|
||
let base = match operation {
|
||
SolidHistoryOperation::Unknown => return None,
|
||
SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => &value.base,
|
||
SolidHistoryOperation::Sphere(value) => &value.base,
|
||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => &value.base,
|
||
SolidHistoryOperation::Pyramid(value) => &value.base,
|
||
SolidHistoryOperation::Torus(value) => &value.base,
|
||
SolidHistoryOperation::Boolean(value) => &value.base,
|
||
SolidHistoryOperation::Brep(value) => &value.base,
|
||
SolidHistoryOperation::Fillet(value) => &value.base,
|
||
SolidHistoryOperation::Chamfer(value) => &value.base,
|
||
SolidHistoryOperation::Sweep(value) | SolidHistoryOperation::Extrusion(value) => {
|
||
&value.base
|
||
}
|
||
SolidHistoryOperation::Loft(value) => &value.base,
|
||
SolidHistoryOperation::Revolve(value) => &value.base,
|
||
};
|
||
Some(base.step_id)
|
||
}
|
||
|
||
fn history_base_text(base: &SolidHistoryNodeBase) -> String {
|
||
format!(
|
||
"node {}; version {}.{}; material {}; color {:?}; transform [{:.4}, {:.4}, {:.4}, {:.4}; {:.4}, {:.4}, {:.4}, {:.4}; {:.4}, {:.4}, {:.4}, {:.4}; {:.4}, {:.4}, {:.4}, {:.4}]",
|
||
base.eval.node_id,
|
||
base.major,
|
||
base.minor,
|
||
handle_text(base.material),
|
||
base.color,
|
||
base.transform[0],
|
||
base.transform[1],
|
||
base.transform[2],
|
||
base.transform[3],
|
||
base.transform[4],
|
||
base.transform[5],
|
||
base.transform[6],
|
||
base.transform[7],
|
||
base.transform[8],
|
||
base.transform[9],
|
||
base.transform[10],
|
||
base.transform[11],
|
||
base.transform[12],
|
||
base.transform[13],
|
||
base.transform[14],
|
||
base.transform[15],
|
||
)
|
||
}
|
||
|
||
fn embedded_name(value: Option<&acadrust::entities::EmbeddedEntity>) -> &'static str {
|
||
match value {
|
||
Some(acadrust::entities::EmbeddedEntity::Point(_)) => "Point",
|
||
Some(acadrust::entities::EmbeddedEntity::Line(_)) => "Line",
|
||
Some(acadrust::entities::EmbeddedEntity::Arc(_)) => "Arc",
|
||
Some(acadrust::entities::EmbeddedEntity::Circle(_)) => "Circle",
|
||
Some(acadrust::entities::EmbeddedEntity::Ellipse(_)) => "Ellipse",
|
||
Some(acadrust::entities::EmbeddedEntity::Spline(_)) => "Spline",
|
||
Some(acadrust::entities::EmbeddedEntity::LwPolyline(_)) => "Polyline",
|
||
Some(acadrust::entities::EmbeddedEntity::Ray(_)) => "Ray",
|
||
Some(acadrust::entities::EmbeddedEntity::XLine(_)) => "XLine",
|
||
Some(acadrust::entities::EmbeddedEntity::Unknown { .. }) => "Unknown",
|
||
None => "None",
|
||
}
|
||
}
|
||
|
||
fn history_operation_text(operation: &SolidHistoryOperation) -> String {
|
||
match operation {
|
||
SolidHistoryOperation::Unknown => "Unknown operation".to_string(),
|
||
SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => format!(
|
||
"{}; length {:.6}; width {:.6}; height {:.6}",
|
||
history_base_text(&value.base),
|
||
value.length,
|
||
value.width,
|
||
value.height
|
||
),
|
||
SolidHistoryOperation::Sphere(value) => format!(
|
||
"{}; radius {:.6}",
|
||
history_base_text(&value.base),
|
||
value.radius
|
||
),
|
||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => format!(
|
||
"{}; height {:.6}; radii {:.6}/{:.6}; x-radius {:.6}",
|
||
history_base_text(&value.base),
|
||
value.height,
|
||
value.major_radius,
|
||
value.minor_radius,
|
||
value.x_radius
|
||
),
|
||
SolidHistoryOperation::Pyramid(value) => format!(
|
||
"{}; height {:.6}; sides {}; radii {:.6}/{:.6}",
|
||
history_base_text(&value.base),
|
||
value.height,
|
||
value.sides,
|
||
value.radius,
|
||
value.top_radius
|
||
),
|
||
SolidHistoryOperation::Torus(value) => format!(
|
||
"{}; radii {:.6}/{:.6}",
|
||
history_base_text(&value.base),
|
||
value.major_radius,
|
||
value.minor_radius
|
||
),
|
||
SolidHistoryOperation::Boolean(value) => format!(
|
||
"{}; operation {}; operands {}/{}",
|
||
history_base_text(&value.base),
|
||
value.operation,
|
||
value.first_operand,
|
||
value.second_operand
|
||
),
|
||
SolidHistoryOperation::Brep(value) => format!(
|
||
"{}; ACIS {} bytes; materials {}",
|
||
history_base_text(&value.base),
|
||
value.acis_data.sat_data.len() + value.acis_data.sab_data.len(),
|
||
value.acis_data.materials.len()
|
||
),
|
||
SolidHistoryOperation::Fillet(value) => format!(
|
||
"{}; method {}; edges {}; radii {}; setbacks {}/{}",
|
||
history_base_text(&value.base),
|
||
value.method,
|
||
value.edges.len(),
|
||
value.radii.len(),
|
||
value.start_setbacks.len(),
|
||
value.end_setbacks.len()
|
||
),
|
||
SolidHistoryOperation::Chamfer(value) => format!(
|
||
"{}; method {}; distances {:.6}/{:.6}; edges {}; base face {}",
|
||
history_base_text(&value.base),
|
||
value.method,
|
||
value.base_distance,
|
||
value.other_distance,
|
||
value.edges.len(),
|
||
value.base_face
|
||
),
|
||
SolidHistoryOperation::Sweep(value) | SolidHistoryOperation::Extrusion(value) => format!(
|
||
"{}; sweep {}; path {}; direction {:.6},{:.6},{:.6}; draft {:.6}; scale {:.6}; twist {:.6}; align {:.6}; bank {}; intersections {}",
|
||
history_base_text(&value.base),
|
||
embedded_name(value.sweep_entity.as_ref()),
|
||
embedded_name(value.path_entity.as_ref()),
|
||
value.direction.x,
|
||
value.direction.y,
|
||
value.direction.z,
|
||
value.draft_angle,
|
||
value.scale_factor,
|
||
value.twist_angle,
|
||
value.align_angle,
|
||
value.bank,
|
||
value.check_intersections
|
||
),
|
||
SolidHistoryOperation::Loft(value) => format!(
|
||
"{}; cross sections {}; guides {}",
|
||
history_base_text(&value.base),
|
||
value.cross_sections.len(),
|
||
value.guides.len()
|
||
),
|
||
SolidHistoryOperation::Revolve(value) => format!(
|
||
"{}; profile {}; axis point {:.6},{:.6},{:.6}; direction {:.6},{:.6}; angle {:.6}; start {:.6}; draft {:.6}; twist {:.6}; close-to-axis {}",
|
||
history_base_text(&value.base),
|
||
embedded_name(value.sweep_entity.as_ref()),
|
||
value.axis_point.x,
|
||
value.axis_point.y,
|
||
value.axis_point.z,
|
||
value.direction.x,
|
||
value.direction.y,
|
||
value.revolve_angle,
|
||
value.start_angle,
|
||
value.draft_angle,
|
||
value.twist_angle,
|
||
value.close_to_axis
|
||
),
|
||
}
|
||
}
|
||
|
||
fn solid_history_sections(
|
||
document: &CadDocument,
|
||
cache: &ObjectDataCache,
|
||
entity: &EntityType,
|
||
) -> Vec<PropSection> {
|
||
let Some(history_handle) = entity_history_handle(entity) else {
|
||
return Vec::new();
|
||
};
|
||
let Some(ObjectType::DynamicBlock(history_object)) =
|
||
document.objects.get(&history_handle)
|
||
else {
|
||
return vec![PropSection {
|
||
title: "Solid History".to_string(),
|
||
props: vec![
|
||
ro("Handle", handle_text(history_handle)),
|
||
ro("Status", "Referenced history object is unresolved"),
|
||
],
|
||
}];
|
||
};
|
||
let DynamicBlockData::SolidHistory(history) = &history_object.data else {
|
||
return vec![PropSection {
|
||
title: "Solid History".to_string(),
|
||
props: vec![
|
||
ro("Handle", handle_text(history_handle)),
|
||
ro("Status", "Referenced object is not a solid history"),
|
||
],
|
||
}];
|
||
};
|
||
let mut nodes = cache
|
||
.dynamic_objects
|
||
.iter()
|
||
.filter_map(|handle| match document.objects.get(handle) {
|
||
Some(ObjectType::DynamicBlock(value))
|
||
if document.owner_chain_reaches(value.owner, history_handle) =>
|
||
{
|
||
match &value.data {
|
||
DynamicBlockData::SolidHistoryNode(node) => Some(node),
|
||
_ => None,
|
||
}
|
||
}
|
||
_ => None,
|
||
})
|
||
.collect::<Vec<_>>();
|
||
nodes.sort_by_key(|node| history_step_id(node).unwrap_or(i32::MAX));
|
||
|
||
let mut sections = vec![PropSection {
|
||
title: "Solid History".to_string(),
|
||
props: vec![
|
||
ro("Handle", handle_text(history_handle)),
|
||
ro("Version", format!("{}.{}", history.major, history.minor)),
|
||
ro("Root node", history.history_node_id.to_string()),
|
||
ro("Show history", history.show_history.to_string()),
|
||
ro("Record history", history.record_history.to_string()),
|
||
ro("Steps", nodes.len().to_string()),
|
||
],
|
||
}];
|
||
if !nodes.is_empty() {
|
||
sections.push(PropSection {
|
||
title: "Construction Steps".to_string(),
|
||
props: nodes
|
||
.into_iter()
|
||
.map(|node| {
|
||
let step = history_step_id(node)
|
||
.map(|value| value.to_string())
|
||
.unwrap_or_else(|| "?".to_string());
|
||
ro(
|
||
format!("Step {step} · {}", history_operation_name(node)),
|
||
history_operation_text(node),
|
||
)
|
||
})
|
||
.collect(),
|
||
});
|
||
}
|
||
sections
|
||
}
|
||
|
||
fn block_value_text(value: &BlockEvalValue) -> String {
|
||
match value {
|
||
BlockEvalValue::None => "None".to_string(),
|
||
BlockEvalValue::Real(value) => format!("{value:.6}"),
|
||
BlockEvalValue::Point(value) => format!("{:.6}, {:.6}", value[0], value[1]),
|
||
BlockEvalValue::Text(value) => value.clone(),
|
||
BlockEvalValue::Long(value) => value.to_string(),
|
||
BlockEvalValue::Handle(value) => handle_text(*value),
|
||
BlockEvalValue::Short(value) => value.to_string(),
|
||
}
|
||
}
|
||
|
||
fn dynamic_data_text(data: &DynamicBlockData) -> String {
|
||
match data {
|
||
DynamicBlockData::Unknown => "Unknown".to_string(),
|
||
DynamicBlockData::Representation(value) => {
|
||
format!("block {}; flags {}", handle_text(value.block), value.flags)
|
||
}
|
||
DynamicBlockData::ProxyNode(value) => format!(
|
||
"node {}; parent {}; version {}.{}; value {}",
|
||
value.node_id,
|
||
value.parent_id,
|
||
value.major,
|
||
value.minor,
|
||
block_value_text(&value.value)
|
||
),
|
||
DynamicBlockData::GripLocationComponent(value) => format!(
|
||
"grip type {}; expression {}; value {}",
|
||
value.grip_type,
|
||
value.expression,
|
||
block_value_text(&value.eval.value)
|
||
),
|
||
DynamicBlockData::AlignmentGrip(value) | DynamicBlockData::LinearGrip(value) => format!(
|
||
"{} at {:.6},{:.6},{:.6}; orientation {:.6},{:.6},{:.6}; cycling {}",
|
||
value.grip.element.name,
|
||
value.grip.location.x,
|
||
value.grip.location.y,
|
||
value.grip.location.z,
|
||
value.orientation.x,
|
||
value.orientation.y,
|
||
value.orientation.z,
|
||
value.grip.insert_cycling
|
||
),
|
||
DynamicBlockData::FlipGrip(value) => format!(
|
||
"{} at {:.6},{:.6},{:.6}; state {}; orientation {:.6},{:.6},{:.6}",
|
||
value.grip.element.name,
|
||
value.grip.location.x,
|
||
value.grip.location.y,
|
||
value.grip.location.z,
|
||
value.combined_state,
|
||
value.orientation.x,
|
||
value.orientation.y,
|
||
value.orientation.z
|
||
),
|
||
DynamicBlockData::LookupGrip(value)
|
||
| DynamicBlockData::PolarGrip(value)
|
||
| DynamicBlockData::RotationGrip(value)
|
||
| DynamicBlockData::VisibilityGrip(value)
|
||
| DynamicBlockData::XYGrip(value)
|
||
| DynamicBlockData::PropertiesTableGrip(value) => format!(
|
||
"{} at {:.6},{:.6},{:.6}; flags {}/{}; cycling {} weight {}",
|
||
value.element.name,
|
||
value.location.x,
|
||
value.location.y,
|
||
value.location.z,
|
||
value.flags_91,
|
||
value.flags_92,
|
||
value.insert_cycling,
|
||
value.insert_cycling_weight
|
||
),
|
||
DynamicBlockData::AlignmentParameter(value) => format!(
|
||
"{}; perpendicular {}; base {:.6},{:.6},{:.6}; end {:.6},{:.6},{:.6}",
|
||
value.parameter.parameter.element.name,
|
||
value.align_perpendicular,
|
||
value.parameter.definition_base_point.x,
|
||
value.parameter.definition_base_point.y,
|
||
value.parameter.definition_base_point.z,
|
||
value.parameter.definition_end_point.x,
|
||
value.parameter.definition_end_point.y,
|
||
value.parameter.definition_end_point.z
|
||
),
|
||
DynamicBlockData::BasePointParameter(value) => format!(
|
||
"{}; point {:.6},{:.6},{:.6}; base {:.6},{:.6},{:.6}",
|
||
value.parameter.parameter.element.name,
|
||
value.point.x,
|
||
value.point.y,
|
||
value.point.z,
|
||
value.base_point.x,
|
||
value.base_point.y,
|
||
value.base_point.z
|
||
),
|
||
DynamicBlockData::FlipParameter(value) => format!(
|
||
"{}; labels {}/{}; tooltip {}",
|
||
value.parameter.parameter.element.name,
|
||
value.base_state_label,
|
||
value.flipped_state_label,
|
||
value.tooltip
|
||
),
|
||
DynamicBlockData::LinearParameter(value) => format!(
|
||
"{}; {} = {:.6}; range {:.6}..{:.6}; increment {:.6}; values {}",
|
||
value.parameter.parameter.element.name,
|
||
value.distance_name,
|
||
value.distance,
|
||
value.value_set.minimum,
|
||
value.value_set.maximum,
|
||
value.value_set.increment,
|
||
value.value_set.values.len()
|
||
),
|
||
DynamicBlockData::LookupParameter(value) => format!(
|
||
"{}; {} index {}; description {}",
|
||
value.parameter.parameter.element.name,
|
||
value.lookup_name,
|
||
value.index,
|
||
value.lookup_description
|
||
),
|
||
DynamicBlockData::PointParameter(value) => format!(
|
||
"{}; {}; label at {:.6},{:.6},{:.6}",
|
||
value.parameter.parameter.element.name,
|
||
value.position_name,
|
||
value.definition_label_point.x,
|
||
value.definition_label_point.y,
|
||
value.definition_label_point.z
|
||
),
|
||
DynamicBlockData::PolarParameter(value) => format!(
|
||
"{}; angle {}; distance {}; offset {:.6}",
|
||
value.parameter.parameter.element.name,
|
||
value.angle_name,
|
||
value.distance_name,
|
||
value.offset
|
||
),
|
||
DynamicBlockData::RotationParameter(value) => format!(
|
||
"{}; {} = {:.6}; range {:.6}..{:.6}; values {}",
|
||
value.parameter.parameter.element.name,
|
||
value.angle_name,
|
||
value.angle,
|
||
value.value_set.minimum,
|
||
value.value_set.maximum,
|
||
value.value_set.values.len()
|
||
),
|
||
DynamicBlockData::UserParameter(value) => format!(
|
||
"{}; expression {}; value {}; variable {}",
|
||
value.parameter.parameter.element.name,
|
||
value.expression,
|
||
block_value_text(&value.value),
|
||
handle_text(value.associated_variable)
|
||
),
|
||
DynamicBlockData::VisibilityParameter(value) => format!(
|
||
"{}; states {}; members {}; point {:.6},{:.6},{:.6}",
|
||
value.name,
|
||
value.states.len(),
|
||
value.all_blocks.len(),
|
||
value.def_point.x,
|
||
value.def_point.y,
|
||
value.def_point.z
|
||
),
|
||
DynamicBlockData::XYParameter(value) => format!(
|
||
"{}; {} = {:.6}; {} = {:.6}",
|
||
value.parameter.parameter.element.name,
|
||
value.x_label,
|
||
value.x_value,
|
||
value.y_label,
|
||
value.y_value
|
||
),
|
||
DynamicBlockData::AngularConstraintParameter(value) => format!(
|
||
"{} = {:.6}; dependency {}",
|
||
value.expression_name,
|
||
value.angle,
|
||
handle_text(value.constraint.dependency)
|
||
),
|
||
DynamicBlockData::DiametricConstraintParameter(value)
|
||
| DynamicBlockData::RadialConstraintParameter(value) => format!(
|
||
"{} = {:.6}; dependency {}",
|
||
value.expression_name,
|
||
value.distance,
|
||
handle_text(value.constraint.dependency)
|
||
),
|
||
DynamicBlockData::AlignedConstraintParameter(value)
|
||
| DynamicBlockData::LinearConstraintParameter(value)
|
||
| DynamicBlockData::HorizontalConstraintParameter(value)
|
||
| DynamicBlockData::VerticalConstraintParameter(value) => format!(
|
||
"{} = {:.6}; dependency {}",
|
||
value.expression_name,
|
||
value.value,
|
||
handle_text(value.constraint.dependency)
|
||
),
|
||
DynamicBlockData::ParameterDependencyBody(value) => format!(
|
||
"{}; dependency version {}; dimension version {}; class version {}",
|
||
value.name,
|
||
value.dependency_body_version,
|
||
value.dimension_base_version,
|
||
value.class_version
|
||
),
|
||
DynamicBlockData::MoveAction(value) => format!(
|
||
"{}; dependencies {}; actions {}; offsets {:.6},{:.6}; angle {:.6}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.action.action_ids.len(),
|
||
value.offsets.offset_x,
|
||
value.offsets.offset_y,
|
||
value.offsets.angle_offset
|
||
),
|
||
DynamicBlockData::FlipAction(value) => format!(
|
||
"{}; dependencies {}; actions {}; connections {}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.action.action_ids.len(),
|
||
value.connections.len()
|
||
),
|
||
DynamicBlockData::RotateAction(value) | DynamicBlockData::ScaleAction(value) => format!(
|
||
"{}; dependencies {}; actions {}; base {:.6},{:.6},{:.6}; dependent {}",
|
||
value.action.action.element.name,
|
||
value.action.action.dependencies.len(),
|
||
value.action.action.action_ids.len(),
|
||
value.action.base_point.x,
|
||
value.action.base_point.y,
|
||
value.action.base_point.z,
|
||
value.action.dependent
|
||
),
|
||
DynamicBlockData::ArrayAction(value) => format!(
|
||
"{}; dependencies {}; columns offset {:.6}; rows offset {:.6}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.column_offset,
|
||
value.row_offset
|
||
),
|
||
DynamicBlockData::LookupAction(value) => format!(
|
||
"{}; dependencies {}; table {}×{}; expressions {}; rows {}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.row_count,
|
||
value.column_count,
|
||
value.expressions.len(),
|
||
value.rows.len()
|
||
),
|
||
DynamicBlockData::StretchAction(value) => format!(
|
||
"{}; dependencies {}; points {}; handles {}; codes {}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.points.len(),
|
||
value.handles.len(),
|
||
value.codes.len()
|
||
),
|
||
DynamicBlockData::PolarStretchAction(value) => format!(
|
||
"{}; dependencies {}; points {}; handles {}; codes {}",
|
||
value.action.element.name,
|
||
value.action.dependencies.len(),
|
||
value.points.len(),
|
||
value.handles.len(),
|
||
value.codes.len()
|
||
),
|
||
DynamicBlockData::EvaluationGraph(value) => format!(
|
||
"first node {}; nodes {}; edges {}",
|
||
value.first_node_id,
|
||
value.nodes.len(),
|
||
value.edges.len()
|
||
),
|
||
DynamicBlockData::AngularConstraintParameterEntity(value) => format!(
|
||
"{} = {:.6}; dependency {}",
|
||
value.expression_name,
|
||
value.angle,
|
||
handle_text(value.constraint.dependency)
|
||
),
|
||
DynamicBlockData::SolidHistory(value) => format!(
|
||
"version {}.{}; root {}; show {}; record {}",
|
||
value.major,
|
||
value.minor,
|
||
value.history_node_id,
|
||
value.show_history,
|
||
value.record_history
|
||
),
|
||
DynamicBlockData::SolidHistoryNode(value) => history_operation_text(value),
|
||
DynamicBlockData::PropertiesTable
|
||
| DynamicBlockData::AlignmentParameterEntity
|
||
| DynamicBlockData::BasePointParameterEntity
|
||
| DynamicBlockData::FlipParameterEntity
|
||
| DynamicBlockData::LinearParameterEntity
|
||
| DynamicBlockData::PointParameterEntity
|
||
| DynamicBlockData::RotationParameterEntity
|
||
| DynamicBlockData::VisibilityParameterEntity
|
||
| DynamicBlockData::XYParameterEntity
|
||
| DynamicBlockData::FlipGripEntity
|
||
| DynamicBlockData::LinearGripEntity
|
||
| DynamicBlockData::PolarGripEntity
|
||
| DynamicBlockData::RotationGripEntity
|
||
| DynamicBlockData::VisibilityGripEntity
|
||
| DynamicBlockData::XYGripEntity => "Semantic entity marker".to_string(),
|
||
}
|
||
}
|
||
|
||
fn dynamic_block_sections(
|
||
document: &CadDocument,
|
||
cache: &ObjectDataCache,
|
||
handle: Handle,
|
||
) -> Vec<PropSection> {
|
||
let Some(definition) = document.dynamic_definition_for_insert(handle) else {
|
||
return Vec::new();
|
||
};
|
||
let mut objects = cache
|
||
.dynamic_objects
|
||
.iter()
|
||
.filter_map(|object_handle| match document.objects.get(object_handle) {
|
||
Some(ObjectType::DynamicBlock(value))
|
||
if document.owner_chain_reaches(value.owner, definition) =>
|
||
{
|
||
Some(value)
|
||
}
|
||
_ => None,
|
||
})
|
||
.collect::<Vec<_>>();
|
||
let visibility = document.block_visibility_param_for_def(definition);
|
||
if objects.is_empty() && visibility.is_none() {
|
||
return Vec::new();
|
||
}
|
||
objects.sort_by_key(|object| object.handle.value());
|
||
|
||
let mut overview = vec![
|
||
ro("Definition", handle_text(definition)),
|
||
ro("Native records", objects.len().to_string()),
|
||
];
|
||
if let Some(parameter) = visibility {
|
||
overview.push(ro("Visibility", parameter.name.clone()));
|
||
overview.push(ro(
|
||
"Visibility states",
|
||
parameter
|
||
.states
|
||
.iter()
|
||
.map(|state| state.name.as_str())
|
||
.collect::<Vec<_>>()
|
||
.join(", "),
|
||
));
|
||
}
|
||
|
||
let mut sections = vec![PropSection {
|
||
title: "Dynamic Block".to_string(),
|
||
props: overview,
|
||
}];
|
||
if !objects.is_empty() {
|
||
sections.push(PropSection {
|
||
title: "Dynamic Parameters / Actions".to_string(),
|
||
props: objects
|
||
.into_iter()
|
||
.filter(|object| {
|
||
!matches!(
|
||
object.data,
|
||
DynamicBlockData::SolidHistory(_)
|
||
| DynamicBlockData::SolidHistoryNode(_)
|
||
)
|
||
})
|
||
.map(|object| {
|
||
let class = if object.dxf_name.is_empty() {
|
||
object.cpp_class_name.as_str()
|
||
} else {
|
||
object.dxf_name.as_str()
|
||
};
|
||
ro(
|
||
format!("{} · {}", handle_text(object.handle), class),
|
||
dynamic_data_text(&object.data),
|
||
)
|
||
})
|
||
.collect(),
|
||
});
|
||
}
|
||
sections
|
||
}
|
||
|
||
fn associative_data_text(data: &AssociativeData) -> String {
|
||
match data {
|
||
AssociativeData::Unknown => "Unknown".to_string(),
|
||
AssociativeData::Dependency(value) => format!(
|
||
"status {}; read {}; write {}; attached {}; target {}; body {}",
|
||
value.status,
|
||
value.is_read_dependency,
|
||
value.is_write_dependency,
|
||
value.is_attached_to_object,
|
||
handle_text(value.dependent_on),
|
||
handle_text(value.dependency_body)
|
||
),
|
||
AssociativeData::ValueDependency(value) => format!(
|
||
"{}; target {}; value {:?}",
|
||
value.name,
|
||
handle_text(value.dependency.dependent_on),
|
||
value.value.value
|
||
),
|
||
AssociativeData::GeomDependency(value) => format!(
|
||
"target {}; enabled {}; subentity {}",
|
||
handle_text(value.dependency.dependent_on),
|
||
value.enabled,
|
||
value.persistent_subent.class_name
|
||
),
|
||
AssociativeData::Action(value) => format!(
|
||
"status {}; network {}; body {}; dependencies {}; parameters {}; values {}",
|
||
value.geometry_status,
|
||
handle_text(value.owning_network),
|
||
handle_text(value.action_body),
|
||
value.dependencies.len(),
|
||
value.owned_parameters.len(),
|
||
value.values.len()
|
||
),
|
||
AssociativeData::Network(value) => format!(
|
||
"actions {}; owned {}; dependencies {}; values {}",
|
||
value.actions.len(),
|
||
value.owned_actions.len(),
|
||
value.action.dependencies.len(),
|
||
value.action.values.len()
|
||
),
|
||
AssociativeData::ConstraintGroup(value) => format!(
|
||
"status {}; dependency {}; actions {}; nodes {}",
|
||
value.action.geometry_status,
|
||
handle_text(value.dependency),
|
||
value.actions.len(),
|
||
value.nodes.len()
|
||
),
|
||
AssociativeData::Variable(value) => format!(
|
||
"{} = {:?}; expression {}; evaluator {}; values {}",
|
||
value.name,
|
||
value.value.value,
|
||
value.expression,
|
||
value.evaluator,
|
||
value.action.values.len()
|
||
),
|
||
AssociativeData::ArrayParameters(value) => format!(
|
||
"{}; items {}; rows {}; levels {}",
|
||
value.class_name,
|
||
value.items.len(),
|
||
value.row_count,
|
||
value.level_count
|
||
),
|
||
AssociativeData::ArrayActionBody(value) => format!(
|
||
"parameter block {}; dependencies {}; value parameters {}",
|
||
value.parameter_block,
|
||
value.parameter_body.dependencies.len(),
|
||
value.parameter_body.values.len()
|
||
),
|
||
AssociativeData::ArrayModifyActionBody(value) => format!(
|
||
"items {}; dependencies {}; value parameters {}",
|
||
value.item_locations.len(),
|
||
value.body.parameter_body.dependencies.len(),
|
||
value.body.parameter_body.values.len()
|
||
),
|
||
AssociativeData::DimensionAssociation(value) => format!(
|
||
"dimension {}; associativity {}; references {}",
|
||
handle_text(value.dimension),
|
||
value.associativity,
|
||
value.references.iter().flatten().count()
|
||
),
|
||
other => format!("{} semantic payload", associative_data_name(other)),
|
||
}
|
||
}
|
||
|
||
fn associative_data_name(data: &AssociativeData) -> &'static str {
|
||
match data {
|
||
AssociativeData::Unknown => "Unknown",
|
||
AssociativeData::Dependency(_) => "Dependency",
|
||
AssociativeData::ValueDependency(_) => "Value Dependency",
|
||
AssociativeData::GeomDependency(_) => "Geometry Dependency",
|
||
AssociativeData::SurfaceActionBody(_) => "Surface Action",
|
||
AssociativeData::Action(_) => "Action",
|
||
AssociativeData::Network(_) => "Network",
|
||
AssociativeData::AnnotationActionBody(_) => "Annotation Action",
|
||
AssociativeData::PersSubentManager(_) => "Persistent Subentity Manager",
|
||
AssociativeData::EdgeActionParam(_) => "Edge Parameter",
|
||
AssociativeData::ConstraintGroup(_) => "Constraint Group",
|
||
AssociativeData::Variable(_) => "Variable",
|
||
AssociativeData::ActionParam(_) => "Action Parameter",
|
||
AssociativeData::CompoundActionParam(_) => "Compound Parameter",
|
||
AssociativeData::OsnapPointRefActionParam(_) => "Object Snap Parameter",
|
||
AssociativeData::PointRefActionParam(_) => "Point Reference",
|
||
AssociativeData::ObjectActionParam(_) => "Object Parameter",
|
||
AssociativeData::PathActionParam(_) => "Path Parameter",
|
||
AssociativeData::DimDependencyBody(_) => "Dimension Dependency",
|
||
AssociativeData::FaceActionParam(_) => "Face Parameter",
|
||
AssociativeData::VertexActionParam(_) => "Vertex Parameter",
|
||
AssociativeData::AsmBodyActionParam(_) => "ASM Body Parameter",
|
||
AssociativeData::ArrayParameters(_) => "Array Parameters",
|
||
AssociativeData::ArrayActionBody(_) => "Array Action",
|
||
AssociativeData::ArrayModifyActionBody(_) => "Array Modify Action",
|
||
AssociativeData::DimensionAssociation(_) => "Dimension Association",
|
||
AssociativeData::PersSubentManagerStatic(_) => "Static Subentity Manager",
|
||
AssociativeData::ViewRepActionBody(_) => "View Representation Action",
|
||
AssociativeData::ViewObjectActionParam(_) => "View Object Parameter",
|
||
AssociativeData::ViewRepHatchManager(_) => "View Hatch Manager",
|
||
AssociativeData::ViewRepHatchActionParam(_) => "View Hatch Parameter",
|
||
AssociativeData::ViewLabelActionParam(_) => "View Label Parameter",
|
||
}
|
||
}
|
||
|
||
fn associative_sections(
|
||
document: &CadDocument,
|
||
cache: &ObjectDataCache,
|
||
handle: Handle,
|
||
) -> Vec<PropSection> {
|
||
let mut objects = cache
|
||
.associative_objects
|
||
.iter()
|
||
.filter_map(|object_handle| match document.objects.get(object_handle) {
|
||
Some(ObjectType::Associative(value))
|
||
if document.owner_chain_reaches(value.owner, handle)
|
||
|| value.references_handle(handle) =>
|
||
{
|
||
Some(value)
|
||
}
|
||
_ => None,
|
||
})
|
||
.collect::<Vec<_>>();
|
||
if objects.is_empty() {
|
||
return Vec::new();
|
||
}
|
||
objects.sort_by_key(|object| object.handle.value());
|
||
vec![PropSection {
|
||
title: "Associative Data".to_string(),
|
||
props: objects
|
||
.into_iter()
|
||
.map(|object| {
|
||
let class = if object.dxf_name.is_empty() {
|
||
object.cpp_class_name.as_str()
|
||
} else {
|
||
object.dxf_name.as_str()
|
||
};
|
||
ro(
|
||
format!("{} · {}", handle_text(object.handle), class),
|
||
associative_data_text(&object.data),
|
||
)
|
||
})
|
||
.collect(),
|
||
}]
|
||
}
|
||
|
||
/// Semantic DWG object sections connected to one selected entity.
|
||
pub fn sections(
|
||
document: &CadDocument,
|
||
cache: &ObjectDataCache,
|
||
handle: Handle,
|
||
entity: &EntityType,
|
||
) -> Vec<PropSection> {
|
||
let mut sections = solid_history_sections(document, cache, entity);
|
||
if matches!(entity, EntityType::Insert(_)) {
|
||
sections.extend(dynamic_block_sections(document, cache, handle));
|
||
}
|
||
if let EntityType::Table(table) = entity {
|
||
let mut linked = rustc_hash::FxHashSet::default();
|
||
for row in &table.rows {
|
||
for cell in &row.cells {
|
||
if let Some(link_handle) = cell.data_link_handle {
|
||
if linked.insert(link_handle) {
|
||
if let Some(ObjectType::ClassObject(object)) =
|
||
document.objects.get(&link_handle)
|
||
{
|
||
if let Some(section) = class_object_section(&object.data) {
|
||
sections.push(section);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
sections.extend(associative_sections(document, cache, handle));
|
||
|
||
// A directly attached typed object can still be useful when its semantic
|
||
// graph contains no backlink (for example a sparse extension dictionary).
|
||
if sections.is_empty() {
|
||
if let Some(ObjectType::DynamicBlock(object)) = document.objects.get(&handle) {
|
||
sections.push(PropSection {
|
||
title: "Dynamic Object".to_string(),
|
||
props: vec![ro("Decoded data", dynamic_data_text(&object.data))],
|
||
});
|
||
}
|
||
}
|
||
sections
|
||
}
|
||
|
||
fn preview_text(value: &str, limit: usize) -> String {
|
||
if value.chars().count() <= limit {
|
||
return value.to_string();
|
||
}
|
||
let mut preview: String = value.chars().take(limit).collect();
|
||
preview.push_str(&format!("… ({} chars)", value.chars().count()));
|
||
preview
|
||
}
|
||
|
||
fn bounded_join<I>(values: I, limit: usize) -> String
|
||
where
|
||
I: IntoIterator<Item = String>,
|
||
{
|
||
let mut values = values.into_iter();
|
||
let mut visible = Vec::new();
|
||
for _ in 0..limit {
|
||
let Some(value) = values.next() else {
|
||
break;
|
||
};
|
||
visible.push(value);
|
||
}
|
||
let hidden = values.count();
|
||
let mut result = visible.join("; ");
|
||
if hidden != 0 {
|
||
result.push_str(&format!("; … +{hidden}"));
|
||
}
|
||
result
|
||
}
|
||
|
||
fn class_object_section(
|
||
data: &acadrust::objects::ClassObjectData,
|
||
) -> Option<PropSection> {
|
||
use acadrust::objects::ClassObjectData;
|
||
|
||
let section = match data {
|
||
ClassObjectData::Empty => return None,
|
||
ClassObjectData::SpatialIndex(value) => PropSection {
|
||
title: "Spatial Index".to_string(),
|
||
props: vec![
|
||
ro(
|
||
"Updated",
|
||
format!(
|
||
"Julian {}; {} ms",
|
||
value.last_updated_julian_day,
|
||
value.last_updated_milliseconds
|
||
),
|
||
),
|
||
ro(
|
||
"Extents",
|
||
format!(
|
||
"{:.6},{:.6},{:.6} → {:.6},{:.6},{:.6}",
|
||
value.min_corner.x,
|
||
value.min_corner.y,
|
||
value.min_corner.z,
|
||
value.max_corner.x,
|
||
value.max_corner.y,
|
||
value.max_corner.z
|
||
),
|
||
),
|
||
ro("Indexed Objects", value.indexed_objects.len().to_string()),
|
||
],
|
||
},
|
||
ClassObjectData::LayerFilter(value) => PropSection {
|
||
title: "Layer Filter".to_string(),
|
||
props: vec![ro("Names", bounded_join(value.names.iter().cloned(), 64))],
|
||
},
|
||
ClassObjectData::PartialViewingIndex(value) => PropSection {
|
||
title: "Partial Viewing Index".to_string(),
|
||
props: vec![
|
||
ro("Has Entries", value.has_entries.to_string()),
|
||
ro(
|
||
"Entries",
|
||
bounded_join(
|
||
value.entries.iter().map(|entry| {
|
||
format!(
|
||
"{}: {:.4},{:.4},{:.4} → {:.4},{:.4},{:.4}",
|
||
handle_text(entry.object),
|
||
entry.extents_min.x,
|
||
entry.extents_min.y,
|
||
entry.extents_min.z,
|
||
entry.extents_max.x,
|
||
entry.extents_max.y,
|
||
entry.extents_max.z
|
||
)
|
||
}),
|
||
32,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::VbaProject(value) => PropSection {
|
||
title: "VBA Project".to_string(),
|
||
props: vec![
|
||
ro(
|
||
"Detached Records",
|
||
format!(
|
||
"{} leading / {} bytes; {} trailing / {} bytes",
|
||
value.storage.leading_records.len(),
|
||
value
|
||
.storage
|
||
.leading_records
|
||
.iter()
|
||
.map(|record| record.data.len())
|
||
.sum::<usize>(),
|
||
value.storage.trailing_records.len(),
|
||
value
|
||
.storage
|
||
.trailing_records
|
||
.iter()
|
||
.map(|record| record.data.len())
|
||
.sum::<usize>()
|
||
),
|
||
),
|
||
ro(
|
||
"Streams",
|
||
value
|
||
.storage
|
||
.compound_file
|
||
.as_ref()
|
||
.map(|storage| storage.root.entries.len())
|
||
.unwrap_or_default()
|
||
.to_string(),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::SectionManager(value) => PropSection {
|
||
title: "Section Manager".to_string(),
|
||
props: vec![
|
||
ro("Live", value.is_live.to_string()),
|
||
ro(
|
||
"Sections",
|
||
bounded_join(
|
||
value.sections.iter().map(|handle| handle_text(*handle)),
|
||
64,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::SectionSettings(value) => PropSection {
|
||
title: "Section Settings".to_string(),
|
||
props: vec![
|
||
ro("Current Type", value.current_type.to_string()),
|
||
ro(
|
||
"Types",
|
||
bounded_join(
|
||
value.types.iter().map(|setting| {
|
||
let geometry = bounded_join(
|
||
setting.geometry.iter().map(|geometry| {
|
||
format!(
|
||
"#{} flags {} layer '{}' linetype '{}' scale {:.4} weight {} transparency {}/{} hatch {} '{}' angle {:.4} spacing {:.4} scale {:.4} color {:?}",
|
||
geometry.index,
|
||
geometry.flags,
|
||
geometry.layer,
|
||
geometry.linetype,
|
||
geometry.linetype_scale,
|
||
geometry.lineweight,
|
||
geometry.face_transparency,
|
||
geometry.edge_transparency,
|
||
geometry.hatch_type,
|
||
geometry.hatch_pattern,
|
||
geometry.hatch_angle,
|
||
geometry.hatch_spacing,
|
||
geometry.hatch_scale,
|
||
geometry.color
|
||
)
|
||
}),
|
||
16,
|
||
);
|
||
format!(
|
||
"type {} generation {} sources [{}] destination {} file '{}' geometry {} [{}]",
|
||
setting.section_type,
|
||
setting.generation,
|
||
bounded_join(
|
||
setting.sources.iter().map(|handle| handle_text(*handle)),
|
||
32
|
||
),
|
||
handle_text(setting.destination_block),
|
||
setting.destination_file,
|
||
setting.geometry.len(),
|
||
geometry
|
||
)
|
||
}),
|
||
16,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::GradientBackground(value) => PropSection {
|
||
title: "Gradient Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Colors",
|
||
format!(
|
||
"#{:06X} / #{:06X} / #{:06X}",
|
||
value.color_top & 0x00FF_FFFF,
|
||
value.color_middle & 0x00FF_FFFF,
|
||
value.color_bottom & 0x00FF_FFFF
|
||
),
|
||
),
|
||
ro(
|
||
"Geometry",
|
||
format!(
|
||
"horizon {:.6}; height {:.6}; rotation {:.6}",
|
||
value.horizon, value.height, value.rotation
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::GroundPlaneBackground(value) => PropSection {
|
||
title: "Ground Plane Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Colors",
|
||
format!(
|
||
"sky #{:06X}/#{:06X}; underground #{:06X}/#{:06X}; near/far #{:06X}/#{:06X}",
|
||
value.color_sky_zenith & 0x00FF_FFFF,
|
||
value.color_sky_horizon & 0x00FF_FFFF,
|
||
value.color_underground_horizon & 0x00FF_FFFF,
|
||
value.color_underground_azimuth & 0x00FF_FFFF,
|
||
value.color_near & 0x00FF_FFFF,
|
||
value.color_far & 0x00FF_FFFF
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::IblBackground(value) => PropSection {
|
||
title: "Image Based Lighting Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("Enabled", value.enabled.to_string()),
|
||
ro("Name", value.name.clone()),
|
||
ro("Rotation", format!("{:.6}", value.rotation)),
|
||
ro("Display Image", value.display_image.to_string()),
|
||
ro(
|
||
"Secondary Background",
|
||
handle_text(value.secondary_background),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::ImageBackground(value) => PropSection {
|
||
title: "Image Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("File", value.filename.clone()),
|
||
ro(
|
||
"Display",
|
||
format!(
|
||
"fit {}; aspect {}; tile {}",
|
||
value.fit_to_screen,
|
||
value.maintain_aspect_ratio,
|
||
value.use_tiling
|
||
),
|
||
),
|
||
ro(
|
||
"Offset / Scale",
|
||
format!(
|
||
"{:.6},{:.6} / {:.6},{:.6}",
|
||
value.offset.x, value.offset.y, value.scale.x, value.scale.y
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::SkyLightBackground(value) => PropSection {
|
||
title: "Sky Light Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("Sun", handle_text(value.sun)),
|
||
],
|
||
},
|
||
ClassObjectData::SolidBackground(value) => PropSection {
|
||
title: "Solid Background".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("Color", format!("#{:06X}", value.color & 0x00FF_FFFF)),
|
||
],
|
||
},
|
||
ClassObjectData::RenderEntry(value) => PropSection {
|
||
title: "Render History Entry".to_string(),
|
||
props: vec![
|
||
ro("Image", value.image_filename.clone()),
|
||
ro(
|
||
"Preset / View",
|
||
format!("{} / {}", value.preset_name, value.view_name),
|
||
),
|
||
ro("Size", format!("{}×{}", value.width, value.height)),
|
||
ro(
|
||
"Started",
|
||
format!(
|
||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
|
||
value.start_year,
|
||
value.start_month,
|
||
value.start_day,
|
||
value.start_hour,
|
||
value.start_minute,
|
||
value.start_second,
|
||
value.start_millisecond
|
||
),
|
||
),
|
||
ro(
|
||
"Finished",
|
||
format!(
|
||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
|
||
value.end_year,
|
||
value.end_month,
|
||
value.end_day,
|
||
value.end_hour,
|
||
value.end_minute,
|
||
value.end_second,
|
||
value.end_millisecond
|
||
),
|
||
),
|
||
ro(
|
||
"Statistics",
|
||
format!(
|
||
"{:.4}s; {} memory; {} materials; {} lights; {} triangles; index {}",
|
||
value.render_time,
|
||
value.memory_amount,
|
||
value.material_count,
|
||
value.light_count,
|
||
value.triangle_count,
|
||
value.display_index
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::MotionPath(value) => PropSection {
|
||
title: "Motion Path".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Camera / Target / View",
|
||
format!(
|
||
"{} / {} / {}",
|
||
handle_text(value.camera_path),
|
||
handle_text(value.target_path),
|
||
handle_text(value.view)
|
||
),
|
||
),
|
||
ro(
|
||
"Playback",
|
||
format!(
|
||
"{} frames at {} fps; corner deceleration {}",
|
||
value.frames, value.frame_rate, value.corner_deceleration
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::CurvePath(value) => PropSection {
|
||
title: "Curve Path".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("Entity", handle_text(value.entity)),
|
||
],
|
||
},
|
||
ClassObjectData::PointPath(value) => PropSection {
|
||
title: "Point Path".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Point",
|
||
format!("{:.8}, {:.8}, {:.8}", value.point.x, value.point.y, value.point.z),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::TvDeviceProperties(value) => PropSection {
|
||
title: "Graphics Device Properties".to_string(),
|
||
props: vec![
|
||
ro("Flags", value.flags.to_string()),
|
||
ro("Max Regen Threads", value.max_regen_threads.to_string()),
|
||
ro(
|
||
"Palette / Highlight",
|
||
format!(
|
||
"{} / {} / {}",
|
||
value.use_lut_palette,
|
||
value.alternate_highlight,
|
||
value.alternate_highlight_color
|
||
),
|
||
),
|
||
ro(
|
||
"GPU",
|
||
format!(
|
||
"geometry shader {}; blending {}; AA {:.4}; reserved {:.4}",
|
||
value.geometry_shader_usage,
|
||
value.blending_mode,
|
||
value.antialiasing_level,
|
||
value.reserved_double
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::PointCloudDefinition(value)
|
||
| ClassObjectData::PointCloudDefinitionEx(value) => PropSection {
|
||
title: "Point Cloud Definition".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("File", value.source_filename.clone()),
|
||
ro("Loaded", value.is_loaded.to_string()),
|
||
ro("Points", value.point_count.to_string()),
|
||
ro(
|
||
"Extents",
|
||
format!(
|
||
"{:.6},{:.6},{:.6} → {:.6},{:.6},{:.6}",
|
||
value.extents_min.x,
|
||
value.extents_min.y,
|
||
value.extents_min.z,
|
||
value.extents_max.x,
|
||
value.extents_max.y,
|
||
value.extents_max.z
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::PointCloudDefinitionReactor(value)
|
||
| ClassObjectData::PointCloudDefinitionReactorEx(value) => PropSection {
|
||
title: "Point Cloud Reactor".to_string(),
|
||
props: vec![ro("Version", value.class_version.to_string())],
|
||
},
|
||
ClassObjectData::PointCloudColorMap(value) => PropSection {
|
||
title: "Point Cloud Color Map".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Defaults",
|
||
format!(
|
||
"intensity '{}'; elevation '{}'; classification '{}'",
|
||
value.default_intensity_scheme,
|
||
value.default_elevation_scheme,
|
||
value.default_classification_scheme
|
||
),
|
||
),
|
||
ro(
|
||
"Ramps",
|
||
bounded_join(
|
||
value.color_ramps.iter().map(|ramp| {
|
||
format!(
|
||
"v{} [{}]",
|
||
ramp.class_version,
|
||
ramp.color_schemes.join(", ")
|
||
)
|
||
}),
|
||
32,
|
||
),
|
||
),
|
||
ro(
|
||
"Classification Ramps",
|
||
bounded_join(
|
||
value.classification_color_ramps.iter().map(|ramp| {
|
||
format!(
|
||
"v{} [{}]",
|
||
ramp.class_version,
|
||
ramp.color_schemes.join(", ")
|
||
)
|
||
}),
|
||
32,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::NavisworksModelDefinition(value) => PropSection {
|
||
title: "Coordination Model Definition".to_string(),
|
||
props: vec![
|
||
ro("Path", value.path.clone()),
|
||
ro(
|
||
"State",
|
||
format!(
|
||
"flags {}; status {}; host visible {}",
|
||
value.flags, value.status, value.host_drawing_visibility
|
||
),
|
||
),
|
||
ro(
|
||
"Extents",
|
||
format!(
|
||
"{:.6},{:.6},{:.6} → {:.6},{:.6},{:.6}",
|
||
value.extents_min.x,
|
||
value.extents_min.y,
|
||
value.extents_min.z,
|
||
value.extents_max.x,
|
||
value.extents_max.y,
|
||
value.extents_max.z
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::ContextDataManager(value) => PropSection {
|
||
title: "Context Data Manager".to_string(),
|
||
props: vec![
|
||
ro("Object Context", handle_text(value.object_context)),
|
||
ro(
|
||
"Sub-Managers",
|
||
bounded_join(
|
||
value.sub_managers.iter().map(|manager| {
|
||
format!(
|
||
"{} [{}]",
|
||
handle_text(manager.handle),
|
||
bounded_join(
|
||
manager.entries.iter().map(|entry| format!(
|
||
"{}={}",
|
||
entry.name,
|
||
handle_text(entry.item)
|
||
)),
|
||
32
|
||
)
|
||
)
|
||
}),
|
||
32,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::SunStudy(value) => PropSection {
|
||
title: "Sun Study".to_string(),
|
||
props: vec![
|
||
ro("Name", value.setup_name.clone()),
|
||
ro("Description", value.description.clone()),
|
||
ro(
|
||
"Output",
|
||
format!(
|
||
"type {}; subset {} '{} / {}'; calendar {}; range {}",
|
||
value.output_type,
|
||
value.use_subset,
|
||
value.sheet_set_name,
|
||
value.sheet_subset_name,
|
||
value.select_dates_from_calendar,
|
||
value.select_range_of_dates
|
||
),
|
||
),
|
||
ro(
|
||
"Dates",
|
||
bounded_join(
|
||
value.dates.iter().map(|date| {
|
||
format!("Julian {} @ {} ms", date.julian_day, date.milliseconds)
|
||
}),
|
||
64,
|
||
),
|
||
),
|
||
ro(
|
||
"Time",
|
||
format!(
|
||
"{}..{} every {}; active hours {}",
|
||
value.start_time,
|
||
value.end_time,
|
||
value.interval,
|
||
value.hours.iter().filter(|enabled| **enabled).count()
|
||
),
|
||
),
|
||
ro(
|
||
"Layout",
|
||
format!(
|
||
"shade {}; viewports {}; {}×{} spacing {:.6}; lock {}; labels {}",
|
||
value.shade_plot_type,
|
||
value.viewport_count,
|
||
value.rows,
|
||
value.columns,
|
||
value.spacing,
|
||
value.lock_viewports,
|
||
value.label_viewports
|
||
),
|
||
),
|
||
ro(
|
||
"References",
|
||
format!(
|
||
"wizard {}; view {}; visual style {}; text style {}",
|
||
handle_text(value.page_setup_wizard),
|
||
handle_text(value.view),
|
||
handle_text(value.visual_style),
|
||
handle_text(value.text_style)
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::DataTable(value) => PropSection {
|
||
title: format!("Data Table · {}", value.name),
|
||
props: vec![
|
||
ro(
|
||
"Shape",
|
||
format!(
|
||
"{} rows; {} columns; flags {}",
|
||
value.row_count,
|
||
value.columns.len(),
|
||
value.flags
|
||
),
|
||
),
|
||
ro(
|
||
"Columns",
|
||
bounded_join(
|
||
value.columns.iter().map(|column| {
|
||
format!(
|
||
"{} type {} rows [{}]",
|
||
column.name,
|
||
column.value_type,
|
||
bounded_join(
|
||
column.rows.iter().map(|cell| format!(
|
||
"{} | {:.8} | {}",
|
||
cell.integer,
|
||
cell.real,
|
||
preview_text(&cell.text, 128)
|
||
)),
|
||
32
|
||
)
|
||
)
|
||
}),
|
||
32,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::DataLink(value) => PropSection {
|
||
title: "Data Link".to_string(),
|
||
props: vec![
|
||
ro("Adapter", value.data_adapter.clone()),
|
||
ro("Description", value.description.clone()),
|
||
ro("Tooltip", value.tooltip.clone()),
|
||
ro(
|
||
"Connection",
|
||
preview_text(&value.connection_string, 1024),
|
||
),
|
||
ro(
|
||
"Options",
|
||
format!(
|
||
"{} / update {}; flags {}; path {}; status {} '{}'",
|
||
value.option,
|
||
value.update_option,
|
||
value.flags,
|
||
value.path_option,
|
||
value.status_flags,
|
||
value.update_status
|
||
),
|
||
),
|
||
ro(
|
||
"Updated",
|
||
format!(
|
||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
|
||
value.year,
|
||
value.month,
|
||
value.day,
|
||
value.hour,
|
||
value.minute,
|
||
value.second,
|
||
value.millisecond
|
||
),
|
||
),
|
||
ro("Owner", handle_text(value.hard_owner)),
|
||
ro(
|
||
"Custom Data",
|
||
bounded_join(
|
||
value.custom_data.iter().map(|entry| {
|
||
format!(
|
||
"{}='{}'",
|
||
handle_text(entry.target),
|
||
preview_text(&entry.value, 256)
|
||
)
|
||
}),
|
||
64,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::PersistentSubentityManager(value) => PropSection {
|
||
title: "Persistent Subentity Manager".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Reserved",
|
||
format!("{}/{}", value.reserved_zero, value.reserved_two),
|
||
),
|
||
ro(
|
||
"Steps",
|
||
format!(
|
||
"{} declared; [{}]",
|
||
value.associated_step_count,
|
||
bounded_join(value.steps.iter().map(ToString::to_string), 128)
|
||
),
|
||
),
|
||
ro(
|
||
"Subentities",
|
||
format!(
|
||
"{} declared; [{}]",
|
||
value.associated_subentity_count,
|
||
bounded_join(value.subentities.iter().map(ToString::to_string), 128)
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::GeoMapImage(value) => PropSection {
|
||
title: "Geographic Map Image".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro(
|
||
"Origin / Size",
|
||
format!(
|
||
"{:.8},{:.8},{:.8} / {:.4}×{:.4}",
|
||
value.origin.x,
|
||
value.origin.y,
|
||
value.origin.z,
|
||
value.image_size.x,
|
||
value.image_size.y
|
||
),
|
||
),
|
||
ro(
|
||
"Display",
|
||
format!(
|
||
"properties {}; clip {}; brightness {}; contrast {}; fade {}",
|
||
value.display_properties,
|
||
value.clipping_enabled,
|
||
value.brightness,
|
||
value.contrast,
|
||
value.fade
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::DetailViewStyle(value) => PropSection {
|
||
title: format!("Detail View Style · {}", value.base.display_name),
|
||
props: vec![
|
||
ro("Description", value.base.description.clone()),
|
||
ro(
|
||
"State",
|
||
format!(
|
||
"base v{} flags {}; modified {}; v{} flags {}",
|
||
value.base.class_version,
|
||
value.base.flags,
|
||
value.base.modified_for_recompute,
|
||
value.class_version,
|
||
value.flags
|
||
),
|
||
),
|
||
ro(
|
||
"Identifier",
|
||
format!(
|
||
"style {}; color {:?}; height {:.6}; excluded '{}'; offset {:.6}; placement {}",
|
||
handle_text(value.identifier_style),
|
||
value.identifier_color,
|
||
value.identifier_height,
|
||
value.identifier_excluded_characters,
|
||
value.identifier_offset,
|
||
value.identifier_placement
|
||
),
|
||
),
|
||
ro(
|
||
"Arrow / Boundary",
|
||
format!(
|
||
"symbol {} color {:?} size {:.6}; boundary {} weight {} color {:?}",
|
||
handle_text(value.arrow_symbol),
|
||
value.arrow_symbol_color,
|
||
value.arrow_symbol_size,
|
||
handle_text(value.boundary_linetype),
|
||
value.boundary_lineweight,
|
||
value.boundary_color
|
||
),
|
||
),
|
||
ro(
|
||
"View Label",
|
||
format!(
|
||
"style {} color {:?} height {:.6}; attachment {}; offset {:.6}; alignment {}; pattern '{}'",
|
||
handle_text(value.view_label_text_style),
|
||
value.view_label_text_color,
|
||
value.view_label_text_height,
|
||
value.view_label_attachment,
|
||
value.view_label_offset,
|
||
value.view_label_alignment,
|
||
value.view_label_pattern
|
||
),
|
||
),
|
||
ro(
|
||
"Connection / Border",
|
||
format!(
|
||
"{} weight {} color {:?}; {} weight {} color {:?}; model edge {}",
|
||
handle_text(value.connection_linetype),
|
||
value.connection_lineweight,
|
||
value.connection_color,
|
||
handle_text(value.border_linetype),
|
||
value.border_lineweight,
|
||
value.border_color,
|
||
value.model_edge
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::SectionViewStyle(value) => PropSection {
|
||
title: format!("Section View Style · {}", value.base.display_name),
|
||
props: vec![
|
||
ro("Description", value.base.description.clone()),
|
||
ro(
|
||
"State",
|
||
format!(
|
||
"base v{} flags {}; modified {}; v{} flags {}; reserved {:?}",
|
||
value.base.class_version,
|
||
value.base.flags,
|
||
value.base.modified_for_recompute,
|
||
value.class_version,
|
||
value.flags,
|
||
value.reserved_flags
|
||
),
|
||
),
|
||
ro(
|
||
"Identifier / Arrows",
|
||
format!(
|
||
"style {} color {:?} height {:.6}; symbols {}/{} color {:?} size {:.6}; excluded '{}'; extension {:.6}",
|
||
handle_text(value.identifier_style),
|
||
value.identifier_color,
|
||
value.identifier_height,
|
||
handle_text(value.arrow_start_symbol),
|
||
handle_text(value.arrow_end_symbol),
|
||
value.arrow_symbol_color,
|
||
value.arrow_symbol_size,
|
||
value.identifier_excluded_characters,
|
||
value.arrow_symbol_extension_length
|
||
),
|
||
),
|
||
ro(
|
||
"Plane / Bend",
|
||
format!(
|
||
"plane {} weight {} color {:?}; bend {} weight {} color {:?}; lengths {:.6}/{:.6}",
|
||
handle_text(value.plane_linetype),
|
||
value.plane_lineweight,
|
||
value.plane_color,
|
||
handle_text(value.bend_linetype),
|
||
value.bend_lineweight,
|
||
value.bend_color,
|
||
value.bend_line_length,
|
||
value.end_line_length
|
||
),
|
||
),
|
||
ro(
|
||
"View Label",
|
||
format!(
|
||
"style {} color {:?} height {:.6}; attachment {}; offset {:.6}; alignment {}; pattern '{}'",
|
||
handle_text(value.view_label_text_style),
|
||
value.view_label_text_color,
|
||
value.view_label_text_height,
|
||
value.view_label_attachment,
|
||
value.view_label_offset,
|
||
value.view_label_alignment,
|
||
value.view_label_pattern
|
||
),
|
||
),
|
||
ro(
|
||
"Hatch",
|
||
format!(
|
||
"color {:?}; background {:?}; pattern '{}'; scale {:.6}; transparency {}; angles [{}]",
|
||
value.hatch_color,
|
||
value.hatch_background_color,
|
||
value.hatch_pattern,
|
||
value.hatch_scale,
|
||
value.hatch_transparency,
|
||
bounded_join(value.hatch_angles.iter().map(|angle| format!("{angle:.6}")), 64)
|
||
),
|
||
),
|
||
ro(
|
||
"Placement",
|
||
format!(
|
||
"identifier {} offset {:.6}; arrow {}; overshoot {:.6}",
|
||
value.identifier_position,
|
||
value.identifier_offset,
|
||
value.arrow_position,
|
||
value.end_line_overshoot
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::AcMeCommandHistory(value) => PropSection {
|
||
title: "Model Documentation Command History".to_string(),
|
||
props: vec![ro("Version", value.class_version.to_string())],
|
||
},
|
||
ClassObjectData::AcMeScope(value) => PropSection {
|
||
title: "Model Documentation Scope".to_string(),
|
||
props: vec![ro("Version", value.class_version.to_string())],
|
||
},
|
||
ClassObjectData::AcMeStateManager(value) => PropSection {
|
||
title: "Model Documentation State Manager".to_string(),
|
||
props: vec![ro("Version", value.class_version.to_string())],
|
||
},
|
||
ClassObjectData::CsacDocumentOptions(value) => PropSection {
|
||
title: "Model Documentation Options".to_string(),
|
||
props: vec![ro("Version", value.class_version.to_string())],
|
||
},
|
||
ClassObjectData::ViewRepSourceManager(value) => PropSection {
|
||
title: "View Representation Source Manager".to_string(),
|
||
props: vec![
|
||
ro("Has Source", value.has_source.to_string()),
|
||
ro("Source", handle_text(value.source)),
|
||
ro("Status", value.status.to_string()),
|
||
],
|
||
},
|
||
ClassObjectData::ViewRepStandard(value) => PropSection {
|
||
title: "View Representation Standard".to_string(),
|
||
props: vec![ro("Values", format!("{:?}", value.values))],
|
||
},
|
||
ClassObjectData::ViewRepOrientationDefinition => PropSection {
|
||
title: "View Representation Orientation Definition".to_string(),
|
||
props: Vec::new(),
|
||
},
|
||
ClassObjectData::ViewRepOrientation(value) => PropSection {
|
||
title: "View Representation Orientation".to_string(),
|
||
props: vec![
|
||
ro(
|
||
"Camera",
|
||
format!("{:.8},{:.8},{:.8}", value.camera.x, value.camera.y, value.camera.z),
|
||
),
|
||
ro(
|
||
"Target",
|
||
format!("{:.8},{:.8},{:.8}", value.target.x, value.target.y, value.target.z),
|
||
),
|
||
ro(
|
||
"Normal",
|
||
format!("{:.8},{:.8},{:.8}", value.normal.x, value.normal.y, value.normal.z),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::ViewRepSectionDefinition(value) => PropSection {
|
||
title: "View Representation Section Definition".to_string(),
|
||
props: vec![
|
||
ro("Version", value.version.to_string()),
|
||
ro("Depth", format!("{:.8}", value.section_depth)),
|
||
ro("Flags", format!("{:?}", value.flags)),
|
||
],
|
||
},
|
||
ClassObjectData::ViewRepModelSpaceViewSelectionSet(value) => PropSection {
|
||
title: "View Representation Selection Set".to_string(),
|
||
props: vec![
|
||
ro("Version", value.version.to_string()),
|
||
ro(
|
||
"Entities",
|
||
bounded_join(
|
||
value.entities.iter().map(|handle| handle_text(*handle)),
|
||
128,
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::ViewRep(value) => PropSection {
|
||
title: format!("View Representation · {}", value.name),
|
||
props: vec![
|
||
ro(
|
||
"Header",
|
||
format!(
|
||
"{:?}; scale {}; status {}; description '{}'",
|
||
value.header_values, value.scale, value.header_status, value.description
|
||
),
|
||
),
|
||
ro(
|
||
"Source",
|
||
format!(
|
||
"id {}; enabled {}; version {}; model {}; guid {:08X}-{:04X}-{:04X}-{:?}",
|
||
value.source_id,
|
||
value.source_enabled,
|
||
value.source_version,
|
||
value.model_id,
|
||
value.guid.data1,
|
||
value.guid.data2,
|
||
value.guid.data3,
|
||
value.guid.data4
|
||
),
|
||
),
|
||
ro(
|
||
"Geometry",
|
||
format!(
|
||
"marker {}; transform v{} {:?}; database {}; geometry v{} marker {}; sketches {}",
|
||
value.marker,
|
||
value.transform_version,
|
||
value.transform,
|
||
value.database_id,
|
||
value.geometry_version,
|
||
value.geometry_marker,
|
||
value.sketches.len()
|
||
),
|
||
),
|
||
ro(
|
||
"References",
|
||
format!(
|
||
"related {:?}; source manager {}; owned {:?}; optional {:?}; orientation {}; linked {:?}; style {}",
|
||
value.related_objects.map(handle_text),
|
||
handle_text(value.source_manager),
|
||
value.owned_objects.map(handle_text),
|
||
value.optional_objects.map(handle_text),
|
||
handle_text(value.orientation),
|
||
value.linked_views.map(handle_text),
|
||
handle_text(value.style)
|
||
),
|
||
),
|
||
ro(
|
||
"Placement",
|
||
format!(
|
||
"{:.8},{:.8}; rotation {:.8}; active {}; projection {}",
|
||
value.position.x,
|
||
value.position.y,
|
||
value.rotation,
|
||
value.is_active,
|
||
value.projection
|
||
),
|
||
),
|
||
ro(
|
||
"Actions / Parent",
|
||
format!(
|
||
"mode {}; action {}; has parent {}; parent {}; section paths {}",
|
||
value.action_mode,
|
||
value.action.map(handle_text).unwrap_or_else(|| "None".to_string()),
|
||
value.has_parent,
|
||
handle_text(value.parent),
|
||
value.section_sketches.len()
|
||
),
|
||
),
|
||
ro(
|
||
"Tail / Block Path",
|
||
format!(
|
||
"version {}; state {}; id {}; path count {}; version {}; id {}; enabled {}; entries {}",
|
||
value.tail_version,
|
||
value.tail_state,
|
||
value.tail_id,
|
||
value.path_count,
|
||
value.path_version,
|
||
value.path_id,
|
||
value.has_block_path,
|
||
value.block_path.as_ref().map(|path| path.entries.len()).unwrap_or_default()
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::ViewRepModelSpaceSource(value) => PropSection {
|
||
title: "View Representation Model Source".to_string(),
|
||
props: vec![
|
||
ro(
|
||
"Header",
|
||
format!(
|
||
"enabled {}; values {:?}; source v{} status {}; tail {:?}",
|
||
value.enabled,
|
||
value.header_values,
|
||
value.source_version,
|
||
value.source_status,
|
||
value.tail_values
|
||
),
|
||
),
|
||
ro("Transform", format!("{:?}", value.transform)),
|
||
ro(
|
||
"References",
|
||
format!(
|
||
"model {}; references {:?}; orientation {}",
|
||
handle_text(value.model),
|
||
value.references.map(handle_text),
|
||
handle_text(value.orientation)
|
||
),
|
||
),
|
||
ro(
|
||
"GUID",
|
||
format!(
|
||
"{:08X}-{:04X}-{:04X}-{:?}",
|
||
value.guid.data1,
|
||
value.guid.data2,
|
||
value.guid.data3,
|
||
value.guid.data4
|
||
),
|
||
),
|
||
],
|
||
},
|
||
ClassObjectData::LightList(_)
|
||
| ClassObjectData::Sun(_)
|
||
| ClassObjectData::RenderSettings(_)
|
||
| ClassObjectData::MentalRayRenderSettings(_)
|
||
| ClassObjectData::RapidRtRenderSettings(_)
|
||
| ClassObjectData::RenderEnvironment(_)
|
||
| ClassObjectData::RenderGlobal(_) => return None,
|
||
};
|
||
Some(section)
|
||
}
|
||
|
||
fn semantic_property_text(
|
||
value: &acadrust::objects::SemanticPropertyValue,
|
||
) -> String {
|
||
use acadrust::objects::SemanticPropertyValue;
|
||
match value {
|
||
SemanticPropertyValue::Text(value) => preview_text(value, 512),
|
||
SemanticPropertyValue::Bool(value) => value.to_string(),
|
||
SemanticPropertyValue::Byte(value) => value.to_string(),
|
||
SemanticPropertyValue::Int16(value) => value.to_string(),
|
||
SemanticPropertyValue::Int32(value) => value.to_string(),
|
||
SemanticPropertyValue::Int64(value) => value.to_string(),
|
||
SemanticPropertyValue::Double(value) => format!("{value:.12}"),
|
||
SemanticPropertyValue::Handle(value) => handle_text(*value),
|
||
SemanticPropertyValue::Binary(value) => format!("{} bytes", value.len()),
|
||
}
|
||
}
|
||
|
||
fn auxiliary_object_property(
|
||
object: &ObjectType,
|
||
) -> Option<Property> {
|
||
use acadrust::objects::DataObjectData;
|
||
|
||
let (label, value) = match object {
|
||
ObjectType::TableContent(table) => (
|
||
format!("Table Content {}", handle_text(table.common.handle)),
|
||
format!(
|
||
"{} rows × {} columns; {} merged ranges; {} fields",
|
||
table.rows.len(),
|
||
table.columns.len(),
|
||
table.merged_ranges.len(),
|
||
table.field_handles.len()
|
||
),
|
||
),
|
||
ObjectType::ImageDefinitionReactor(value) => (
|
||
format!("Image Reactor {}", handle_text(value.handle)),
|
||
format!(
|
||
"owner {}; image {}",
|
||
handle_text(value.owner),
|
||
handle_text(value.image_handle)
|
||
),
|
||
),
|
||
ObjectType::BookColor(value) => (
|
||
format!("Book Color {}", handle_text(value.handle)),
|
||
format!("{} / {} = {:?}", value.book_name, value.color_name, value.color),
|
||
),
|
||
ObjectType::PlaceHolder(value) => (
|
||
format!("Placeholder {}", handle_text(value.handle)),
|
||
format!("owner {}", handle_text(value.owner)),
|
||
),
|
||
ObjectType::FieldList(value) => (
|
||
format!("Field List {}", handle_text(value.handle)),
|
||
format!(
|
||
"owner {}; unknown {}; fields [{}]",
|
||
handle_text(value.owner),
|
||
value.unknown,
|
||
bounded_join(value.fields.iter().map(|handle| handle_text(*handle)), 128)
|
||
),
|
||
),
|
||
ObjectType::DataObject(value) => {
|
||
let text = match &value.data {
|
||
DataObjectData::BreakPointRef => "break point reference".to_string(),
|
||
DataObjectData::BreakData(data) => format!(
|
||
"v{} dimension {}; reserved {}; point references {}",
|
||
data.version,
|
||
handle_text(data.dimension_reference),
|
||
handle_text(data.reserved_reference),
|
||
data.point_references.len()
|
||
),
|
||
DataObjectData::CellStyleMap(data) => {
|
||
format!("{} named cell styles", data.cells.len())
|
||
}
|
||
DataObjectData::AcDsRecord => "AcDs record".to_string(),
|
||
DataObjectData::AcDsSchema => "AcDs schema".to_string(),
|
||
DataObjectData::Dummy => "dummy helper".to_string(),
|
||
DataObjectData::IdBuffer(data) => format!(
|
||
"flags {}; objects [{}]",
|
||
data.flags,
|
||
bounded_join(
|
||
data.object_ids.iter().map(|handle| handle_text(*handle)),
|
||
128
|
||
)
|
||
),
|
||
DataObjectData::Index(data) => format!(
|
||
"updated Julian {} @ {} ms",
|
||
data.last_updated_julian_day, data.last_updated_milliseconds
|
||
),
|
||
DataObjectData::LayerIndex(data) => format!(
|
||
"updated Julian {} @ {} ms; entries [{}]",
|
||
data.last_updated_julian_day,
|
||
data.last_updated_milliseconds,
|
||
bounded_join(
|
||
data.entries.iter().map(|entry| format!(
|
||
"{}: {} layers, buffer {}",
|
||
entry.name,
|
||
entry.layer_count,
|
||
handle_text(entry.id_buffer)
|
||
)),
|
||
64
|
||
)
|
||
),
|
||
DataObjectData::LongTransaction => "long transaction".to_string(),
|
||
DataObjectData::ObjectPointer => "object pointer".to_string(),
|
||
DataObjectData::PartialViewingFilter(_) => {
|
||
"partial viewing filter".to_string()
|
||
}
|
||
DataObjectData::TableGeometry(data) => format!(
|
||
"{} rows × {} columns; {} cell geometry records; {} primitives",
|
||
data.rows,
|
||
data.columns,
|
||
data.cells.len(),
|
||
data.cells.iter().map(|cell| cell.geometry.len()).sum::<usize>()
|
||
),
|
||
};
|
||
(
|
||
format!("{} {}", value.dxf_name(), handle_text(value.handle)),
|
||
format!("owner {}; {text}", handle_text(value.owner)),
|
||
)
|
||
}
|
||
ObjectType::RegisteredClass(value) => (
|
||
format!(
|
||
"{} {}",
|
||
if value.dxf_name.is_empty() {
|
||
value.cpp_class_name.as_str()
|
||
} else {
|
||
value.dxf_name.as_str()
|
||
},
|
||
handle_text(value.handle)
|
||
),
|
||
format!(
|
||
"owner {}; properties [{}]; payload {} bits / {} records; references {}",
|
||
handle_text(value.owner),
|
||
bounded_join(
|
||
value.properties.iter().map(|property| format!(
|
||
"{}:{}={}",
|
||
property.subclass,
|
||
property.code,
|
||
semantic_property_text(&property.value)
|
||
)),
|
||
64
|
||
),
|
||
value.payload.bit_count,
|
||
value.payload.records.len(),
|
||
value.object_ids.len()
|
||
),
|
||
),
|
||
ObjectType::ProxyObject(value) => (
|
||
format!(
|
||
"Proxy Object {}",
|
||
handle_text(value.handle)
|
||
),
|
||
format!(
|
||
"class {} / id {}; subclass '{}'; version {}/{} maintenance {}; DXF {}; payload {} + {} bits; references {}",
|
||
value.class_id,
|
||
value.proxy_id,
|
||
value.dxf_subclass,
|
||
value.version,
|
||
value.dwg_version,
|
||
value.maintenance_version,
|
||
value.from_dxf,
|
||
value.payload.bit_count,
|
||
value.text_payload.bit_count,
|
||
value.object_ids.len()
|
||
),
|
||
),
|
||
ObjectType::Unknown {
|
||
type_name,
|
||
handle,
|
||
owner,
|
||
raw_dxf_codes,
|
||
raw_dwg_data,
|
||
raw_dwg_handle_bits,
|
||
..
|
||
} => (
|
||
format!("Unknown Object {}", handle_text(*handle)),
|
||
format!(
|
||
"{}; owner {}; DXF codes {}; DWG bytes {}; handle bits {}",
|
||
type_name,
|
||
handle_text(*owner),
|
||
raw_dxf_codes.as_ref().map(Vec::len).unwrap_or_default(),
|
||
raw_dwg_data.as_ref().map(Vec::len).unwrap_or_default(),
|
||
raw_dwg_handle_bits
|
||
),
|
||
),
|
||
_ => return None,
|
||
};
|
||
Some(ro(label, value))
|
||
}
|
||
|
||
/// Drawing-level standard object data prepared on the file-open worker.
|
||
fn build_document_sections(document: &CadDocument) -> Vec<PropSection> {
|
||
use acadrust::objects::ClassObjectData;
|
||
|
||
let mut sections = vec![PropSection {
|
||
title: "Drawing".to_string(),
|
||
props: vec![
|
||
ro("Version", format!("{:?}", document.version)),
|
||
ro("Entities", document.entity_count().to_string()),
|
||
ro("Objects", document.objects.len().to_string()),
|
||
ro("Layers", document.layers.len().to_string()),
|
||
ro("Blocks", document.block_records.len().to_string()),
|
||
],
|
||
}];
|
||
|
||
let mut objects = document.objects.iter().collect::<Vec<_>>();
|
||
objects.sort_by_key(|(handle, _)| handle.value());
|
||
for (_, object) in objects {
|
||
match object {
|
||
ObjectType::GeoData(value) => {
|
||
sections.push(PropSection {
|
||
title: "Geographic Location".to_string(),
|
||
props: vec![
|
||
ro("Version", value.version.to_string()),
|
||
ro("Coordinate Type", value.coordinate_type.to_string()),
|
||
ro(
|
||
"Design Point",
|
||
format!(
|
||
"{:.8}, {:.8}, {:.8}",
|
||
value.design_point.x,
|
||
value.design_point.y,
|
||
value.design_point.z
|
||
),
|
||
),
|
||
ro(
|
||
"Reference Point",
|
||
format!(
|
||
"{:.8}, {:.8}, {:.8}",
|
||
value.reference_point.x,
|
||
value.reference_point.y,
|
||
value.reference_point.z
|
||
),
|
||
),
|
||
ro(
|
||
"North",
|
||
format!(
|
||
"{:.8}, {:.8}",
|
||
value.north_direction.x, value.north_direction.y
|
||
),
|
||
),
|
||
ro(
|
||
"Up",
|
||
format!(
|
||
"{:.8}, {:.8}, {:.8}",
|
||
value.up_direction.x,
|
||
value.up_direction.y,
|
||
value.up_direction.z
|
||
),
|
||
),
|
||
ro(
|
||
"Units / Scale",
|
||
format!(
|
||
"horizontal {} × {:.12}; vertical {} × {:.12}; user {:.12}",
|
||
value.horizontal_units,
|
||
value.horizontal_unit_scale,
|
||
value.vertical_units,
|
||
value.vertical_unit_scale,
|
||
value.user_scale_factor
|
||
),
|
||
),
|
||
ro(
|
||
"Sea Level",
|
||
format!(
|
||
"enabled {}; elevation {:.8}; radius {:.8}",
|
||
value.sea_level_correction,
|
||
value.sea_level_elevation,
|
||
value.coordinate_projection_radius
|
||
),
|
||
),
|
||
ro(
|
||
"Coordinate System",
|
||
preview_text(&value.coordinate_system_definition, 1024),
|
||
),
|
||
ro("Datum", preview_text(&value.coordinate_system_datum, 512)),
|
||
ro("WKT", preview_text(&value.coordinate_system_wkt, 1024)),
|
||
ro(
|
||
"Transformation Mesh",
|
||
format!(
|
||
"{} points; {} faces",
|
||
value.mesh_points.len(),
|
||
value.mesh_faces.len()
|
||
),
|
||
),
|
||
ro(
|
||
"Observation",
|
||
format!(
|
||
"{} → {}; coverage {}; RSS {}",
|
||
value.observation_from_tag,
|
||
value.observation_to_tag,
|
||
value.observation_coverage_tag,
|
||
value.geo_rss_tag
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ObjectType::RasterVariables(value) => {
|
||
sections.push(PropSection {
|
||
title: "Raster Settings".to_string(),
|
||
props: vec![
|
||
ro("Version", value.class_version.to_string()),
|
||
ro("Display Frame", value.display_image_frame.to_string()),
|
||
ro("Quality", value.image_quality.to_string()),
|
||
ro("Units", value.units.to_string()),
|
||
],
|
||
});
|
||
}
|
||
ObjectType::WipeoutVariables(value) => {
|
||
sections.push(PropSection {
|
||
title: "Wipeout Settings".to_string(),
|
||
props: vec![ro("Display Frame", value.display_frame.to_string())],
|
||
});
|
||
}
|
||
ObjectType::ClassObject(value) => match &value.data {
|
||
ClassObjectData::LightList(list) => {
|
||
sections.push(PropSection {
|
||
title: "Light List".to_string(),
|
||
props: vec![
|
||
ro("Version", list.class_version.to_string()),
|
||
ro("Lights", list.lights.len().to_string()),
|
||
ro(
|
||
"Entries",
|
||
bounded_join(
|
||
list.lights.iter().map(|light| {
|
||
format!(
|
||
"{}={}",
|
||
light.name,
|
||
handle_text(light.handle)
|
||
)
|
||
}),
|
||
64,
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::Sun(sun) => {
|
||
sections.push(PropSection {
|
||
title: "Sun".to_string(),
|
||
props: vec![
|
||
ro("Version", sun.class_version.to_string()),
|
||
ro("On", sun.is_on.to_string()),
|
||
ro("Color", format!("{:?}", sun.color)),
|
||
ro("Intensity", format!("{:.6}", sun.intensity)),
|
||
ro(
|
||
"Date / Time",
|
||
format!(
|
||
"Julian {}; {} ms; DST {}",
|
||
sun.julian_day,
|
||
sun.milliseconds,
|
||
sun.is_daylight_savings_on
|
||
),
|
||
),
|
||
ro(
|
||
"Shadows",
|
||
format!(
|
||
"enabled {}; type {}; map {}; softness {}",
|
||
sun.has_shadow,
|
||
sun.shadow_type,
|
||
sun.shadow_map_size,
|
||
sun.shadow_softness
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::RenderSettings(settings) => {
|
||
sections.push(PropSection {
|
||
title: "Render Settings".to_string(),
|
||
props: vec![
|
||
ro("Version", settings.class_version.to_string()),
|
||
ro("Name", settings.name.clone()),
|
||
ro("Description", settings.description.clone()),
|
||
ro(
|
||
"Flags",
|
||
format!(
|
||
"fog {}; fog background {}; backfaces {}; environment {}",
|
||
settings.fog_enabled,
|
||
settings.fog_background_enabled,
|
||
settings.backfaces_enabled,
|
||
settings.environment_image_enabled
|
||
),
|
||
),
|
||
ro(
|
||
"Environment Image",
|
||
settings.environment_image_filename.clone(),
|
||
),
|
||
ro(
|
||
"Display",
|
||
format!(
|
||
"index {}; predefined {}",
|
||
settings.display_index, settings.has_predefined
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::MentalRayRenderSettings(settings) => {
|
||
sections.push(PropSection {
|
||
title: "Mental Ray".to_string(),
|
||
props: vec![
|
||
ro("Preset", settings.base.name.clone()),
|
||
ro(
|
||
"Version",
|
||
format!(
|
||
"base {}; mental ray {}",
|
||
settings.base.class_version, settings.version
|
||
),
|
||
),
|
||
ro(
|
||
"Preset State",
|
||
format!(
|
||
"description '{}'; display {}; predefined {}; fog {}/{}; backfaces {}; environment {} '{}'",
|
||
settings.base.description,
|
||
settings.base.display_index,
|
||
settings.base.has_predefined,
|
||
settings.base.fog_enabled,
|
||
settings.base.fog_background_enabled,
|
||
settings.base.backfaces_enabled,
|
||
settings.base.environment_image_enabled,
|
||
settings.base.environment_image_filename
|
||
),
|
||
),
|
||
ro(
|
||
"Sampling",
|
||
format!(
|
||
"{}..{}; filter {} {:.4}×{:.4}; contrast {:?}",
|
||
settings.sampling_min,
|
||
settings.sampling_max,
|
||
settings.sampling_filter,
|
||
settings.sampling_filter_width,
|
||
settings.sampling_filter_height,
|
||
settings.sampling_contrast
|
||
),
|
||
),
|
||
ro(
|
||
"Ray Tracing",
|
||
format!(
|
||
"{}; depth {:?}; shadows {} / maps {}",
|
||
settings.ray_tracing_enabled,
|
||
settings.ray_trace_depth,
|
||
settings.shadow_mode,
|
||
settings.shadow_maps_enabled
|
||
),
|
||
),
|
||
ro(
|
||
"Global Illumination",
|
||
format!(
|
||
"{}; samples {}; radius {} {:.6}; photons {}; trace {:?}",
|
||
settings.global_illumination_enabled,
|
||
settings.global_illumination_sample_count,
|
||
settings.global_illumination_sample_radius_enabled,
|
||
settings.global_illumination_sample_radius,
|
||
settings.photons_per_light,
|
||
settings.photon_trace_depth
|
||
),
|
||
),
|
||
ro(
|
||
"Final Gather",
|
||
format!(
|
||
"{}; rays {}; state {:?}; radius {:?}",
|
||
settings.final_gathering_enabled,
|
||
settings.final_gathering_ray_count,
|
||
settings.final_gathering_sample_radius_state,
|
||
settings.final_gathering_sample_radius
|
||
),
|
||
),
|
||
ro(
|
||
"Diagnostics",
|
||
format!(
|
||
"mode {}; grid {} size {:.6}; photon {}; BSP {}; samples {}; luminance {:.6}",
|
||
settings.diagnostics_mode,
|
||
settings.diagnostics_grid_mode,
|
||
settings.diagnostics_grid_size,
|
||
settings.diagnostics_photon_mode,
|
||
settings.diagnostics_bsp_mode,
|
||
settings.diagnostics_samples_mode,
|
||
settings.light_luminance_scale
|
||
),
|
||
),
|
||
ro(
|
||
"Execution",
|
||
format!(
|
||
"export {}; description '{}'; tile {} order {}; memory {}; energy {:.6}",
|
||
settings.export_mi_enabled,
|
||
settings.description,
|
||
settings.tile_size,
|
||
settings.tile_order,
|
||
settings.memory_limit,
|
||
settings.energy_multiplier
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::RapidRtRenderSettings(settings) => {
|
||
sections.push(PropSection {
|
||
title: "Rapid RT".to_string(),
|
||
props: vec![
|
||
ro("Preset", settings.base.name.clone()),
|
||
ro(
|
||
"Version",
|
||
format!(
|
||
"base {}; Rapid RT {}",
|
||
settings.base.class_version, settings.version
|
||
),
|
||
),
|
||
ro(
|
||
"Preset State",
|
||
format!(
|
||
"description '{}'; display {}; predefined {}; fog {}/{}; backfaces {}; environment {} '{}'",
|
||
settings.base.description,
|
||
settings.base.display_index,
|
||
settings.base.has_predefined,
|
||
settings.base.fog_enabled,
|
||
settings.base.fog_background_enabled,
|
||
settings.base.backfaces_enabled,
|
||
settings.base.environment_image_enabled,
|
||
settings.base.environment_image_filename
|
||
),
|
||
),
|
||
ro(
|
||
"Target / Level / Time",
|
||
format!(
|
||
"{} / {} / {}",
|
||
settings.render_target,
|
||
settings.render_level,
|
||
settings.render_time
|
||
),
|
||
),
|
||
ro(
|
||
"Lighting / Filter",
|
||
format!(
|
||
"{} / {} {:.4}×{:.4}",
|
||
settings.lighting_model,
|
||
settings.filter_type,
|
||
settings.filter_width,
|
||
settings.filter_height
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::RenderEnvironment(environment) => {
|
||
sections.push(PropSection {
|
||
title: "Render Environment".to_string(),
|
||
props: vec![
|
||
ro("Version", environment.class_version.to_string()),
|
||
ro(
|
||
"Fog",
|
||
format!(
|
||
"{}; background {}; color {:?}; density {:.4}..{:.4}; distance {:.4}..{:.4}",
|
||
environment.fog_enabled,
|
||
environment.fog_background_enabled,
|
||
environment.fog_color,
|
||
environment.fog_density_near,
|
||
environment.fog_density_far,
|
||
environment.fog_distance_near,
|
||
environment.fog_distance_far
|
||
),
|
||
),
|
||
ro(
|
||
"Environment Image",
|
||
format!(
|
||
"{} · {}",
|
||
environment.environment_image_enabled,
|
||
environment.environment_image_filename
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
ClassObjectData::RenderGlobal(global) => {
|
||
sections.push(PropSection {
|
||
title: "Render Output".to_string(),
|
||
props: vec![
|
||
ro("Version", global.class_version.to_string()),
|
||
ro(
|
||
"Procedure / Destination",
|
||
format!("{} / {}", global.procedure, global.destination),
|
||
),
|
||
ro(
|
||
"Image",
|
||
format!(
|
||
"{}×{}; save {} · {}",
|
||
global.image_width,
|
||
global.image_height,
|
||
global.save_enabled,
|
||
global.save_filename
|
||
),
|
||
),
|
||
ro(
|
||
"Display",
|
||
format!(
|
||
"predefined first {}; high-level info {}",
|
||
global.predefined_presets_first,
|
||
global.high_level_info
|
||
),
|
||
),
|
||
],
|
||
});
|
||
}
|
||
other => {
|
||
if let Some(section) = class_object_section(other) {
|
||
sections.push(section);
|
||
}
|
||
}
|
||
},
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
if !document.dgn_ls_definitions.is_empty() {
|
||
let mut definitions = document
|
||
.dgn_ls_definitions
|
||
.values()
|
||
.collect::<Vec<_>>();
|
||
definitions.sort_by_key(|definition| definition.handle.value());
|
||
let omitted = definitions.len().saturating_sub(128);
|
||
let mut props = definitions
|
||
.into_iter()
|
||
.take(128)
|
||
.map(|definition| {
|
||
ro(
|
||
definition.name.clone(),
|
||
format!(
|
||
"handle {}; root {}; components {}",
|
||
handle_text(definition.handle),
|
||
handle_text(definition.root_component),
|
||
document.dgn_ls_components.len()
|
||
),
|
||
)
|
||
})
|
||
.collect::<Vec<_>>();
|
||
if omitted != 0 {
|
||
props.push(ro("More Definitions", format!("{omitted} omitted")));
|
||
}
|
||
sections.push(PropSection {
|
||
title: "DGN Line Styles".to_string(),
|
||
props,
|
||
});
|
||
}
|
||
let mut auxiliary = document
|
||
.objects
|
||
.values()
|
||
.filter_map(auxiliary_object_property)
|
||
.collect::<Vec<_>>();
|
||
auxiliary.sort_by(|left, right| left.label.cmp(&right.label));
|
||
let auxiliary_omitted = auxiliary.len().saturating_sub(256);
|
||
auxiliary.truncate(256);
|
||
if auxiliary_omitted != 0 {
|
||
auxiliary.push(ro(
|
||
"More Standard Objects",
|
||
format!("{auxiliary_omitted} omitted"),
|
||
));
|
||
}
|
||
if !auxiliary.is_empty() {
|
||
sections.push(PropSection {
|
||
title: "Standard Object Data".to_string(),
|
||
props: auxiliary,
|
||
});
|
||
}
|
||
let mut per_title = rustc_hash::FxHashMap::default();
|
||
let mut omitted = rustc_hash::FxHashMap::default();
|
||
sections.retain(|section| {
|
||
if section.title == "Drawing" {
|
||
return true;
|
||
}
|
||
let shown = per_title.entry(section.title.clone()).or_insert(0usize);
|
||
if *shown < 4 {
|
||
*shown += 1;
|
||
true
|
||
} else {
|
||
*omitted.entry(section.title.clone()).or_insert(0usize) += 1;
|
||
false
|
||
}
|
||
});
|
||
if !omitted.is_empty() {
|
||
let mut entries = omitted.into_iter().collect::<Vec<_>>();
|
||
entries.sort_by(|left, right| left.0.cmp(&right.0));
|
||
sections.push(PropSection {
|
||
title: "Additional Standard Objects".to_string(),
|
||
props: entries
|
||
.into_iter()
|
||
.map(|(name, count)| ro(name, format!("{count} more records")))
|
||
.collect(),
|
||
});
|
||
}
|
||
sections
|
||
}
|