forked from 1b-agent/opencadstudio-upstream
fix(styles): apply active creation defaults
Resolve style properties before editors and previews open. Use absolute annotation multipliers for style-driven objects. Refs #401 Refs #431
This commit is contained in:
1 parent
53cf31f33d
commit
de2707a23e
14 files changed
+676
-129
No files matched your search
+50
-1
@@ -639,10 +639,33 @@ pub fn effective_annotation_scale_for(
|
||||
fallback: f32,
|
||||
scale_handle: Option<Handle>,
|
||||
) -> f32 {
|
||||
if !is_annotative(doc, entity) {
|
||||
let context_annotative = is_annotative(doc, entity);
|
||||
let style_annotative = annotation_style_is_annotative(doc, entity);
|
||||
if !context_annotative && !style_annotative {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if matches!(
|
||||
entity,
|
||||
EntityType::Dimension(_)
|
||||
| EntityType::Leader(_)
|
||||
| EntityType::Tolerance(_)
|
||||
| EntityType::Table(_)
|
||||
) {
|
||||
return fallback;
|
||||
}
|
||||
if style_annotative
|
||||
&& matches!(
|
||||
entity,
|
||||
EntityType::Text(_)
|
||||
| EntityType::MText(_)
|
||||
| EntityType::AttributeEntity(_)
|
||||
| EntityType::AttributeDefinition(_)
|
||||
)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// Unlike MTEXT / DIMENSION contexts, an MLEADER context carries its
|
||||
// already-scaled text height and overall scale factor. Keep the text
|
||||
// height as stored; make `ml.scale_factor * anno_scale` resolve to the
|
||||
@@ -1392,6 +1415,15 @@ fn mleader_style_annotative(doc: &CadDocument, handle: Option<Handle>) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
fn table_style_annotative(doc: &CadDocument, handle: Option<Handle>) -> bool {
|
||||
let Some(handle) = handle else {
|
||||
return false;
|
||||
};
|
||||
doc.objects.iter().any(|(object_handle, object)| {
|
||||
matches!(object, ObjectType::TableStyle(style) if *object_handle == handle && style.annotative)
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether an object carries a per-object annotation context with at least one
|
||||
/// per-scale representation — its extension dictionary holds an
|
||||
/// `AcDbContextDataManager` whose `ACDB_ANNOTATIONSCALES` collection is
|
||||
@@ -1454,6 +1486,7 @@ pub fn annotation_style_is_annotative(doc: &CadDocument, entity: &EntityType) ->
|
||||
EntityType::MultiLeader(leader) => {
|
||||
mleader_style_annotative(doc, leader.style_handle)
|
||||
}
|
||||
EntityType::Table(table) => table_style_annotative(doc, table.table_style_handle),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1473,6 +1506,8 @@ pub fn apply_mleader_style(
|
||||
entity.dogleg_length = style.landing_distance;
|
||||
entity.arrowhead_handle = style.arrowhead_handle;
|
||||
entity.arrowhead_size = style.arrowhead_size;
|
||||
entity.context.arrowhead_size = style.arrowhead_size;
|
||||
entity.context.landing_gap = style.landing_gap;
|
||||
entity.text_style_handle = style.text_style_handle;
|
||||
entity.text_color = style.text_color;
|
||||
entity.text_frame = style.text_frame;
|
||||
@@ -1509,6 +1544,20 @@ pub fn apply_mleader_style(
|
||||
entity.context.block_content_scale = entity.block_scale;
|
||||
entity.context.scale_factor = style.scale_factor;
|
||||
entity.enable_annotation_scale = style.is_annotative;
|
||||
for root in &mut entity.context.leader_roots {
|
||||
root.landing_distance = style.landing_distance;
|
||||
root.text_attachment_direction = entity.text_attachment_direction;
|
||||
for line in &mut root.lines {
|
||||
if line.override_flags.is_empty() {
|
||||
line.path_type = entity.path_type;
|
||||
line.line_color = style.line_color;
|
||||
line.line_type_handle = style.line_type_handle;
|
||||
line.line_weight = style.line_weight;
|
||||
line.arrowhead_handle = style.arrowhead_handle;
|
||||
line.arrowhead_size = style.arrowhead_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_mleader_style_to_object(
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
use acadrust::objects::ObjectType;
|
||||
use acadrust::tables::{DimStyle, TextStyle};
|
||||
use acadrust::{CadDocument, EntityType, Handle};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TextCreationDefaults {
|
||||
pub style_name: String,
|
||||
pub height: f64,
|
||||
pub width_factor: f64,
|
||||
pub oblique_angle: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DimensionCreationDefaults {
|
||||
pub style_name: String,
|
||||
pub style_handle: Handle,
|
||||
pub text_style_name: String,
|
||||
pub text_height: f64,
|
||||
pub gap: f64,
|
||||
pub arrow_size: f64,
|
||||
pub scale: f64,
|
||||
pub annotative: bool,
|
||||
}
|
||||
|
||||
fn named_text_style(doc: &CadDocument, name: &str) -> Option<TextStyle> {
|
||||
doc.text_styles
|
||||
.iter()
|
||||
.find(|style| style.name.eq_ignore_ascii_case(name))
|
||||
.or_else(|| {
|
||||
doc.text_styles
|
||||
.iter()
|
||||
.find(|style| style.name.eq_ignore_ascii_case("Standard"))
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn named_dim_style(doc: &CadDocument, name: &str) -> Option<DimStyle> {
|
||||
doc.dim_styles
|
||||
.iter()
|
||||
.find(|style| style.name.eq_ignore_ascii_case(name))
|
||||
.or_else(|| {
|
||||
doc.dim_styles
|
||||
.iter()
|
||||
.find(|style| style.name.eq_ignore_ascii_case("Standard"))
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn current_text_defaults(doc: &CadDocument) -> TextCreationDefaults {
|
||||
let requested = doc.header.current_text_style_name.trim();
|
||||
let style = named_text_style(doc, requested);
|
||||
let variable_height = if doc.header.text_height > 1.0e-9 {
|
||||
doc.header.text_height
|
||||
} else {
|
||||
style
|
||||
.as_ref()
|
||||
.map(|style| style.last_height)
|
||||
.filter(|height| *height > 1.0e-9)
|
||||
.unwrap_or(2.5)
|
||||
};
|
||||
TextCreationDefaults {
|
||||
style_name: style
|
||||
.as_ref()
|
||||
.map(|style| style.name.clone())
|
||||
.filter(|name| !name.trim().is_empty())
|
||||
.unwrap_or_else(|| "Standard".to_string()),
|
||||
height: style
|
||||
.as_ref()
|
||||
.map(|style| style.height)
|
||||
.filter(|height| *height > 1.0e-9)
|
||||
.unwrap_or(variable_height),
|
||||
width_factor: style.as_ref().map_or(1.0, |style| style.width_factor),
|
||||
oblique_angle: style.as_ref().map_or(0.0, |style| style.oblique_angle),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_dimension_defaults(doc: &CadDocument) -> DimensionCreationDefaults {
|
||||
let requested = doc.header.current_dimstyle_name.trim();
|
||||
let style = named_dim_style(doc, requested);
|
||||
DimensionCreationDefaults {
|
||||
style_name: style
|
||||
.as_ref()
|
||||
.map(|style| style.name.clone())
|
||||
.filter(|name| !name.trim().is_empty())
|
||||
.unwrap_or_else(|| "Standard".to_string()),
|
||||
style_handle: style.as_ref().map_or(Handle::NULL, |style| style.handle),
|
||||
text_style_name: style
|
||||
.as_ref()
|
||||
.map(|style| style.dimtxsty.clone())
|
||||
.filter(|name| !name.trim().is_empty())
|
||||
.unwrap_or_else(|| current_text_defaults(doc).style_name),
|
||||
text_height: style.as_ref().map_or(2.5, |style| style.dimtxt),
|
||||
gap: style.as_ref().map_or(0.625, |style| style.dimgap),
|
||||
arrow_size: style.as_ref().map_or(2.5, |style| style.dimasz),
|
||||
scale: style.as_ref().map_or(1.0, |style| {
|
||||
if style.annotative {
|
||||
1.0
|
||||
} else if style.dimscale > 1.0e-9 {
|
||||
style.dimscale
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}),
|
||||
annotative: style.as_ref().is_some_and(|style| style.annotative),
|
||||
}
|
||||
}
|
||||
|
||||
fn entity_uses_default_style(name: &str) -> bool {
|
||||
name.trim().is_empty() || name.eq_ignore_ascii_case("Standard")
|
||||
}
|
||||
|
||||
fn apply_text_defaults(doc: &CadDocument, entity: &mut EntityType) {
|
||||
let active = current_text_defaults(doc);
|
||||
let style_name = match entity {
|
||||
EntityType::Text(text) => &mut text.style,
|
||||
EntityType::MText(text) => &mut text.style,
|
||||
EntityType::AttributeEntity(attribute) => &mut attribute.text_style,
|
||||
EntityType::AttributeDefinition(attribute) => &mut attribute.text_style,
|
||||
_ => return,
|
||||
};
|
||||
if style_name.trim().is_empty() {
|
||||
style_name.clone_from(&active.style_name);
|
||||
}
|
||||
let resolved = named_text_style(doc, style_name)
|
||||
.unwrap_or_else(|| TextStyle::new(style_name.clone()));
|
||||
match entity {
|
||||
EntityType::Text(text) => {
|
||||
if resolved.height > 1.0e-9 {
|
||||
text.height = resolved.height;
|
||||
}
|
||||
if (text.width_factor - 1.0).abs() <= 1.0e-9 {
|
||||
text.width_factor = resolved.width_factor;
|
||||
}
|
||||
if text.oblique_angle.abs() <= 1.0e-9 {
|
||||
text.oblique_angle = resolved.oblique_angle;
|
||||
}
|
||||
if text.generation_flags == 0 {
|
||||
text.generation_flags = (if resolved.flags.backward { 2 } else { 0 })
|
||||
| (if resolved.flags.upside_down { 4 } else { 0 });
|
||||
}
|
||||
}
|
||||
EntityType::MText(text) => {
|
||||
if resolved.height > 1.0e-9 {
|
||||
text.height = resolved.height;
|
||||
}
|
||||
}
|
||||
EntityType::AttributeEntity(attribute) => {
|
||||
if resolved.height > 1.0e-9 {
|
||||
attribute.height = resolved.height;
|
||||
}
|
||||
if (attribute.width_factor - 1.0).abs() <= 1.0e-9 {
|
||||
attribute.width_factor = resolved.width_factor;
|
||||
}
|
||||
if attribute.oblique_angle.abs() <= 1.0e-9 {
|
||||
attribute.oblique_angle = resolved.oblique_angle;
|
||||
}
|
||||
if attribute.text_generation_flags == 0 {
|
||||
attribute.text_generation_flags =
|
||||
(if resolved.flags.backward { 2 } else { 0 })
|
||||
| (if resolved.flags.upside_down { 4 } else { 0 });
|
||||
}
|
||||
}
|
||||
EntityType::AttributeDefinition(attribute) => {
|
||||
if resolved.height > 1.0e-9 {
|
||||
attribute.height = resolved.height;
|
||||
}
|
||||
if (attribute.width_factor - 1.0).abs() <= 1.0e-9 {
|
||||
attribute.width_factor = resolved.width_factor;
|
||||
}
|
||||
if attribute.oblique_angle.abs() <= 1.0e-9 {
|
||||
attribute.oblique_angle = resolved.oblique_angle;
|
||||
}
|
||||
if attribute.text_generation_flags == 0 {
|
||||
attribute.text_generation_flags =
|
||||
(if resolved.flags.backward { 2 } else { 0 })
|
||||
| (if resolved.flags.upside_down { 4 } else { 0 });
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_dimension_defaults(doc: &CadDocument, entity: &mut EntityType) {
|
||||
let active = current_dimension_defaults(doc);
|
||||
match entity {
|
||||
EntityType::Dimension(dimension) => {
|
||||
if entity_uses_default_style(&dimension.base().style_name) {
|
||||
dimension.base_mut().style_name.clone_from(&active.style_name);
|
||||
}
|
||||
}
|
||||
EntityType::Leader(leader) => {
|
||||
if entity_uses_default_style(&leader.dimension_style) {
|
||||
leader.dimension_style.clone_from(&active.style_name);
|
||||
leader.text_height = active.text_height;
|
||||
leader.dimension_gap = active.gap;
|
||||
leader.arrow_size = active.arrow_size;
|
||||
}
|
||||
}
|
||||
EntityType::Tolerance(tolerance) => {
|
||||
if entity_uses_default_style(&tolerance.dimension_style_name) {
|
||||
tolerance.dimension_style_name.clone_from(&active.style_name);
|
||||
tolerance.dimension_style_handle = (!active.style_handle.is_null())
|
||||
.then_some(active.style_handle);
|
||||
tolerance.text_height = active.text_height;
|
||||
tolerance.dimension_gap = active.gap;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_object_defaults(doc: &CadDocument, entity: &mut EntityType) {
|
||||
match entity {
|
||||
EntityType::MultiLeader(leader) if leader.style_handle.is_none() => {
|
||||
let name = doc.header.current_mleader_style_name.trim();
|
||||
if let Some(style) = doc.objects.iter().find_map(|(handle, object)| match object {
|
||||
ObjectType::MultiLeaderStyle(style) if style.name.eq_ignore_ascii_case(name) => {
|
||||
let mut style = style.clone();
|
||||
style.handle = *handle;
|
||||
Some(style)
|
||||
}
|
||||
_ => None,
|
||||
}) {
|
||||
crate::scene::annotative::apply_mleader_style(leader, &style);
|
||||
}
|
||||
}
|
||||
EntityType::Table(table) if table.table_style_handle.is_none() => {
|
||||
let name = doc.header.current_table_style_name.trim();
|
||||
table.table_style_handle = doc.objects.iter().find_map(|(handle, object)| match object {
|
||||
ObjectType::TableStyle(style) if style.name.eq_ignore_ascii_case(name) => {
|
||||
Some(*handle)
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
}
|
||||
EntityType::MLine(mline) if mline.style_handle.is_none() => {
|
||||
let name = doc.header.multiline_style.trim();
|
||||
if let Some((handle, style_name)) =
|
||||
doc.objects.iter().find_map(|(handle, object)| match object {
|
||||
ObjectType::MLineStyle(style) if style.name.eq_ignore_ascii_case(name) => {
|
||||
Some((*handle, style.name.clone()))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
mline.style_handle = Some(handle);
|
||||
mline.style_name = style_name;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_current_creation_styles(doc: &CadDocument, entity: &mut EntityType) {
|
||||
apply_text_defaults(doc, entity);
|
||||
apply_dimension_defaults(doc, entity);
|
||||
apply_object_defaults(doc, entity);
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
pub mod annotative;
|
||||
pub mod cache;
|
||||
pub mod convert;
|
||||
pub mod creation_style;
|
||||
pub mod model;
|
||||
pub mod pick;
|
||||
pub mod pipeline;
|
||||
@@ -3800,6 +3801,28 @@ impl Scene {
|
||||
self.scale_handle_ensuring("1:1")
|
||||
}
|
||||
|
||||
pub(crate) fn creation_annotation_multiplier(&self) -> f64 {
|
||||
if self.current_layout == "Model" {
|
||||
return self.annotation_scale.max(1.0e-9) as f64;
|
||||
}
|
||||
let Some(handle) = self.active_viewport else {
|
||||
return 1.0;
|
||||
};
|
||||
let Some(EntityType::Viewport(viewport)) = self.document.get_entity(handle) else {
|
||||
return 1.0;
|
||||
};
|
||||
let paper_to_model = vp_effective_scale(
|
||||
viewport.custom_scale,
|
||||
viewport.view_height,
|
||||
viewport.height,
|
||||
);
|
||||
if paper_to_model.is_finite() && paper_to_model > 1.0e-9 {
|
||||
1.0 / paper_to_model
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_annotation_scale_named(&mut self, name: &str) -> Option<Handle> {
|
||||
let handle = self.scale_handle_ensuring(name)?;
|
||||
let ObjectType::Scale(scale) = self.document.objects.get(&handle)? else {
|
||||
|
||||
Reference in new issue
Block a user