forked from 1b-agent/opencadstudio-upstream
Fix extruded solid grip preview and taper range
This commit is contained in:
1 parent
3ad0423393
commit
ea6eb58c53
8 files changed
+87
-10
No files matched your search
@@ -1476,6 +1476,9 @@ pub struct Scene {
|
||||
pub selection_filter: HashSet<String>,
|
||||
/// In-progress preview wires while a command is active (rubber-band + object ghosts).
|
||||
pub preview_wires: Vec<WireModel>,
|
||||
/// Candidate wireframes produced by editable solid-history grips. The
|
||||
/// resident body remains untouched until the grip is committed.
|
||||
solid_history_preview_wires: HashMap<Handle, Vec<WireModel>>,
|
||||
/// Live hatch fill used by interactive edits such as dragging the pattern
|
||||
/// origin grip. Kept separate from the resident hatch set so moving one
|
||||
/// pattern never re-uploads every hatch in a large drawing.
|
||||
@@ -1883,6 +1886,7 @@ impl Scene {
|
||||
model_lineweight_scale: 1.0,
|
||||
selection_filter: HashSet::default(),
|
||||
preview_wires: vec![],
|
||||
solid_history_preview_wires: HashMap::default(),
|
||||
preview_hatches: Arc::new(Vec::new()),
|
||||
preview_text: vec![],
|
||||
interim_wire: None,
|
||||
|
||||
@@ -2314,7 +2314,7 @@ fn apply_extrusion_draft_grip(value: &mut SolidHistorySweep, apply: GripApply) -
|
||||
let GripApply::Absolute(world) = apply else {
|
||||
return false;
|
||||
};
|
||||
let Some((_, base_top, radial, profile_radius, height)) = extrusion_draft_grip(value) else {
|
||||
let Some((_, base_top, radial, _, height)) = extrusion_draft_grip(value) else {
|
||||
return false;
|
||||
};
|
||||
let top_offset = (world - base_top).dot(radial);
|
||||
@@ -2322,10 +2322,27 @@ fn apply_extrusion_draft_grip(value: &mut SolidHistorySweep, apply: GripApply) -
|
||||
return false;
|
||||
}
|
||||
let limit = 89.0_f64.to_radians();
|
||||
let inward_limit = ((profile_radius - 1e-6).max(1e-6) / height)
|
||||
.atan()
|
||||
.min(limit);
|
||||
let angle = (-top_offset).atan2(height).clamp(-limit, inward_limit);
|
||||
let desired = (-top_offset).atan2(height).clamp(-limit, limit);
|
||||
let angle = if desired > 0.0 && !extrusion_draft_rebuilds(value, desired) {
|
||||
// The maximum inset is profile-dependent. A corner radius is too
|
||||
// restrictive for many outlines, while allowing the pointer through
|
||||
// a collapsed outline makes the top grow again. Search the actual
|
||||
// rebuild boundary so every supported profile reaches its smallest
|
||||
// valid top without crossing into an inverted result.
|
||||
let mut valid = 0.0;
|
||||
let mut invalid = limit;
|
||||
for _ in 0..16 {
|
||||
let candidate = (valid + invalid) * 0.5;
|
||||
if extrusion_draft_rebuilds(value, candidate) {
|
||||
valid = candidate;
|
||||
} else {
|
||||
invalid = candidate;
|
||||
}
|
||||
}
|
||||
valid
|
||||
} else {
|
||||
desired
|
||||
};
|
||||
if !angle.is_finite() || (angle - value.draft_angle).abs() <= 1e-12 {
|
||||
return false;
|
||||
}
|
||||
@@ -2333,6 +2350,12 @@ fn apply_extrusion_draft_grip(value: &mut SolidHistorySweep, apply: GripApply) -
|
||||
true
|
||||
}
|
||||
|
||||
fn extrusion_draft_rebuilds(value: &SolidHistorySweep, angle: f64) -> bool {
|
||||
let mut candidate = value.clone();
|
||||
candidate.draft_angle = angle;
|
||||
cadkernel::acis::rebuild_body(&SolidHistoryOperation::Extrusion(candidate)).is_ok()
|
||||
}
|
||||
|
||||
fn grip(
|
||||
id: usize,
|
||||
world: glam::DVec3,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use cadkernel::brep::{self, Body, Curve3, EdgeKey, FaceKey, Surface};
|
||||
|
||||
use crate::scene::model::mesh_model::{MeshLodSet, MeshModel};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
|
||||
/// What counts as the same point when the kernel checks a body over.
|
||||
const TOL: f64 = 1e-9;
|
||||
@@ -243,6 +244,27 @@ pub fn edge_wires(body: &Body) -> Vec<acadrust::entities::Wire> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// White wireframe used while a solid-history grip is hot.
|
||||
///
|
||||
/// This deliberately does not touch the resident solid mesh or its entity
|
||||
/// wires: the selected source stays visible in blue while the candidate body
|
||||
/// is presented as a separate, non-pickable outline until placement.
|
||||
pub fn grip_preview_wires(body: &Body, handle: acadrust::Handle) -> Vec<WireModel> {
|
||||
tessellation(body)
|
||||
.edges
|
||||
.into_iter()
|
||||
.filter(|edge| edge.positions.len() >= 2)
|
||||
.map(|edge| {
|
||||
WireModel::solid_f64(
|
||||
format!("{}-GRIP-PREVIEW", handle.value()),
|
||||
edge.positions,
|
||||
WireModel::WHITE,
|
||||
false,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// B-rep edge nearest a world-space surface pick.
|
||||
pub fn nearest_edge(body: &Body, pick: [f64; 3]) -> Option<EdgeKey> {
|
||||
let pick = cadkernel::space::Vec3::from(pick);
|
||||
|
||||
+6
-1
@@ -841,6 +841,7 @@ impl Scene {
|
||||
handle: Handle,
|
||||
operation: acadrust::objects::SolidHistoryOperation,
|
||||
) -> bool {
|
||||
self.solid_history_preview_wires.remove(&handle);
|
||||
let Ok(body) = cadkernel::acis::rebuild_body(&operation) else {
|
||||
return false;
|
||||
};
|
||||
@@ -879,6 +880,7 @@ impl Scene {
|
||||
};
|
||||
entity.set_sat_document(&document);
|
||||
self.sync_solid_reference_point(handle);
|
||||
self.solid_history_preview_wires.remove(&handle);
|
||||
self.register_solid_model(handle, body);
|
||||
true
|
||||
}
|
||||
@@ -901,7 +903,10 @@ impl Scene {
|
||||
let Some(EntityType::Solid3D(_)) = self.document.get_entity(handle) else {
|
||||
return false;
|
||||
};
|
||||
self.register_solid_model(handle, body);
|
||||
self.solid_history_preview_wires.insert(
|
||||
handle,
|
||||
crate::scene::model::solid_model::grip_preview_wires(&body, handle),
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -272,6 +272,7 @@ impl Scene {
|
||||
// flips the wire content id back to the base tessellation id, which
|
||||
// re-uploads the base wires (without the preview) on the next frame.
|
||||
self.preview_wires = vec![];
|
||||
self.solid_history_preview_wires.clear();
|
||||
if !self.preview_hatches.is_empty() {
|
||||
self.preview_hatches = std::sync::Arc::new(Vec::new());
|
||||
}
|
||||
@@ -302,6 +303,21 @@ impl Scene {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Current candidate geometry for a hot grip. Solid-history candidates
|
||||
/// live outside the document until placement; ordinary entities continue
|
||||
/// to use their current tessellation.
|
||||
pub fn grip_wire_models_for(&self, handles: &[acadrust::Handle]) -> Vec<WireModel> {
|
||||
handles
|
||||
.iter()
|
||||
.flat_map(|handle| {
|
||||
self.solid_history_preview_wires
|
||||
.get(handle)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| self.wire_models_for(std::slice::from_ref(handle)))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Split a MIRROR selection into plain ghost wires (reflected wholesale) and
|
||||
/// text ghosts paired with their bounding-box centre. Lets the preview match
|
||||
/// the commit for TEXT: MIRRTEXT on → true glyph mirror (full reflection, same
|
||||
|
||||
Reference in new issue
Block a user