forked from 1b-agent/opencadstudio-upstream
Merge pull request #1131 from ramox81/codex/thicken-surface-grips
This commit is contained in:
commit
4201b0a031
18 files changed
+536
-67
No files matched your search
@@ -3756,10 +3756,36 @@ impl OpenCADStudio {
|
||||
let direction = direction
|
||||
.or(path_direction)
|
||||
.unwrap_or(glam::DVec3::ZERO);
|
||||
self.add_surface_model(
|
||||
empty_extruded_surface(direction, taper_angle),
|
||||
body,
|
||||
)
|
||||
let history = path
|
||||
.is_none()
|
||||
.then(|| {
|
||||
sweep_model::extrusion_history(
|
||||
&entity,
|
||||
None,
|
||||
direction.to_array(),
|
||||
taper_angle,
|
||||
profile.plane.origin,
|
||||
)
|
||||
})
|
||||
.flatten();
|
||||
let mut surface = empty_extruded_surface(direction, taper_angle);
|
||||
if let (
|
||||
Some(acadrust::objects::SolidHistoryOperation::Extrusion(value)),
|
||||
acadrust::EntityType::Surface(entity),
|
||||
) = (&history, &mut surface)
|
||||
{
|
||||
if let Some(data) =
|
||||
crate::scene::model::solid_history::extrusion_surface_data(value)
|
||||
{
|
||||
entity.surface_data = data;
|
||||
}
|
||||
}
|
||||
match history {
|
||||
Some(history) => {
|
||||
self.add_surface_model_with_history(surface, body, history)
|
||||
}
|
||||
None => self.add_surface_model(surface, body),
|
||||
}
|
||||
} else {
|
||||
let direction = direction.unwrap_or(glam::DVec3::ZERO);
|
||||
let history = path
|
||||
|
||||
@@ -402,6 +402,24 @@ impl super::OpenCADStudio {
|
||||
self.add_surface_model_inner(entity, surface, true)
|
||||
}
|
||||
|
||||
pub(super) fn add_surface_model_with_history(
|
||||
&mut self,
|
||||
entity: EntityType,
|
||||
surface: Body,
|
||||
history: SolidHistoryOperation,
|
||||
) -> Handle {
|
||||
let i = self.active_tab;
|
||||
let handle = self.add_surface_model_inner(entity, surface, true);
|
||||
if handle.is_null() {
|
||||
return handle;
|
||||
}
|
||||
if !self.tabs[i].scene.create_solid_history(handle, history) {
|
||||
self.tabs[i].scene.rollback_new_entities(&[handle]);
|
||||
return Handle::NULL;
|
||||
}
|
||||
handle
|
||||
}
|
||||
|
||||
fn add_surface_model_preserving_style(
|
||||
&mut self,
|
||||
entity: EntityType,
|
||||
|
||||
+24
-9
@@ -2142,6 +2142,17 @@ impl OpenCADStudio {
|
||||
entity_type_label(entity)
|
||||
}
|
||||
}
|
||||
acadrust::EntityType::Surface(_)
|
||||
if matches!(
|
||||
crate::scene::model::solid_history::primitive_property_operation(
|
||||
&self.tabs[i].scene.document,
|
||||
handle,
|
||||
),
|
||||
Some(acadrust::objects::SolidHistoryOperation::Extrusion(_))
|
||||
) =>
|
||||
{
|
||||
format!("{} ({})", t!("Surface"), t!("Extrusion"))
|
||||
}
|
||||
_ => entity_type_label(entity),
|
||||
};
|
||||
ui::PropertiesPanel {
|
||||
@@ -2249,23 +2260,22 @@ filter={:.1} local={:.1} aggregate={:.1} entities={}",
|
||||
);
|
||||
}
|
||||
}
|
||||
if local_refs.iter().all(|(handle, _)| {
|
||||
let compact_solids = local_refs.iter().all(|(handle, _)| {
|
||||
crate::scene::model::solid_history::has_compact_solid_properties(
|
||||
&self.tabs[i].scene.document,
|
||||
*handle,
|
||||
)
|
||||
}) {
|
||||
sections.retain(|section| {
|
||||
!section.props.iter().any(|property| {
|
||||
property.field.starts_with("acis_")
|
||||
|| property.field.starts_with("s3d_")
|
||||
})
|
||||
});
|
||||
});
|
||||
if compact_solids {
|
||||
retain_compact_solid_sections(&mut sections);
|
||||
}
|
||||
sections.extend(aggregate_solid_history_sections(
|
||||
&self.tabs[i].scene.document,
|
||||
&local_refs.iter().map(|(handle, _)| *handle).collect::<Vec<_>>(),
|
||||
));
|
||||
if compact_solids {
|
||||
retain_compact_solid_sections(&mut sections);
|
||||
}
|
||||
ui::PropertiesPanel {
|
||||
choice_combos: sections
|
||||
.iter()
|
||||
@@ -3205,6 +3215,10 @@ fn retain_compact_solid_sections(
|
||||
sections: &mut Vec<crate::scene::model::object::PropSection>,
|
||||
) {
|
||||
sections.iter_mut().for_each(|section| {
|
||||
let internal_surface_section = section
|
||||
.props
|
||||
.iter()
|
||||
.any(|property| property.field == "srf_kind");
|
||||
section.props.retain(|property| {
|
||||
matches!(
|
||||
property.field,
|
||||
@@ -3217,7 +3231,8 @@ fn retain_compact_solid_sections(
|
||||
| "transparency"
|
||||
| "hyperlink"
|
||||
| "material"
|
||||
) || crate::scene::model::solid_history::is_specialized_property(property.field)
|
||||
) || (!internal_surface_section
|
||||
&& crate::scene::model::solid_history::is_specialized_property(property.field))
|
||||
});
|
||||
});
|
||||
sections.retain(|section| !section.props.is_empty());
|
||||
|
||||
@@ -2003,7 +2003,48 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
|
||||
if crate::scene::model::solid_history::is_loft_geometry_choice(field) {
|
||||
if crate::scene::model::solid_history::is_surface_property_choice(field) {
|
||||
use crate::scene::model::solid_history::{
|
||||
PROP_SURFACE_MAINTAIN_ASSOCIATIVITY, PROP_SURFACE_SHOW_ASSOCIATIVITY,
|
||||
PROP_SURFACE_WIREFRAME_TYPE,
|
||||
};
|
||||
for &handle in &handles {
|
||||
if self.tabs[i].scene.is_layer_locked(handle) {
|
||||
continue;
|
||||
}
|
||||
let Some(mut state) = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(|entity| match entity {
|
||||
acadrust::EntityType::Surface(surface) => Some(
|
||||
crate::entities::solid3d::surface_property_state(surface),
|
||||
),
|
||||
_ => None,
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
match field {
|
||||
PROP_SURFACE_WIREFRAME_TYPE => {
|
||||
state.isolines = !value.eq_ignore_ascii_case("Isoparms")
|
||||
}
|
||||
PROP_SURFACE_MAINTAIN_ASSOCIATIVITY => {
|
||||
state.maintain_associativity = value.eq_ignore_ascii_case("Yes")
|
||||
}
|
||||
PROP_SURFACE_SHOW_ASSOCIATIVITY => {
|
||||
state.show_associativity = value.eq_ignore_ascii_case("Yes")
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
crate::scene::view::dispatch::set_entity_xdata(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
crate::entities::solid3d::SURFACE_PROPERTIES_APP,
|
||||
Some(crate::entities::solid3d::surface_property_xdata_values(state)),
|
||||
);
|
||||
}
|
||||
} else if crate::scene::model::solid_history::is_loft_geometry_choice(field) {
|
||||
// These choices change the generated body, not just history
|
||||
// flags. Use the same transactional rebuild as numeric edits.
|
||||
for &handle in &handles {
|
||||
|
||||
@@ -1535,15 +1535,30 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
if let Some(axis) = grip.axis {
|
||||
snapped = cursor_on_projected_axis(
|
||||
p,
|
||||
bounds,
|
||||
view_rot,
|
||||
eye,
|
||||
grip.origin_world,
|
||||
axis,
|
||||
)
|
||||
.unwrap_or(snapped);
|
||||
let acquired_point = otrack_hit.is_some()
|
||||
|| snap_hit.is_some_and(|hit| {
|
||||
hit.snap_type != crate::snap::SnapType::Grid
|
||||
});
|
||||
if acquired_point {
|
||||
if let Some(axis) = axis.try_normalize() {
|
||||
// An axis grip still moves on its construction axis, but
|
||||
// an acquired object point supplies the exact coordinate
|
||||
// along that axis. This lets height and draft arrows seat
|
||||
// back onto existing surface/solid geometry.
|
||||
snapped = grip.origin_world
|
||||
+ axis * (snapped - grip.origin_world).dot(axis);
|
||||
}
|
||||
} else {
|
||||
snapped = cursor_on_projected_axis(
|
||||
p,
|
||||
bounds,
|
||||
view_rot,
|
||||
eye,
|
||||
grip.origin_world,
|
||||
axis,
|
||||
)
|
||||
.unwrap_or(snapped);
|
||||
}
|
||||
}
|
||||
|
||||
let snap_ms = snap_started.elapsed().as_secs_f64() * 1000.0;
|
||||
@@ -1751,6 +1766,26 @@ impl OpenCADStudio {
|
||||
let preview_ms = preview_started.elapsed().as_secs_f64() * 1000.0;
|
||||
let geometry_ms = grip_started.elapsed().as_secs_f64() * 1000.0;
|
||||
self.refresh_selected_grips();
|
||||
// A history rebuild can clamp a requested value or move a derived
|
||||
// construction handle non-linearly. Rebase each active target onto
|
||||
// the freshly rebuilt grip so the hot arrow remains attached to the
|
||||
// live surface and reverses immediately from a geometric limit.
|
||||
let refreshed_targets: Vec<_> = self.tabs[i]
|
||||
.selected_grip_handles
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(self.tabs[i].selected_grips.iter())
|
||||
.map(|(handle, grip)| ((handle, grip.id), grip.world))
|
||||
.collect();
|
||||
if let Some(active) = self.tabs[i].active_grip.as_mut() {
|
||||
for target in &mut active.targets {
|
||||
if let Some((_, world)) = refreshed_targets.iter().find(|((handle, id), _)| {
|
||||
*handle == target.handle && *id == target.grip_id
|
||||
}) {
|
||||
target.last_world = *world;
|
||||
}
|
||||
}
|
||||
}
|
||||
let grips_ms = grip_started.elapsed().as_secs_f64() * 1000.0 - geometry_ms;
|
||||
// Properties are refreshed when the grip is committed or cancelled.
|
||||
// Rebuilding the inspector on every pointer event adds no drawing
|
||||
|
||||
Reference in new issue
Block a user