Restore revolve profile and axis grips

This commit is contained in:
ramox81 committed 2026-09-02 17:01:35 +03:00
1 parent bde9cc2715
commit 15c44c0f3a
5 files changed
+229 -21

No files matched your search

Generated
+3 -3
View File
@@ -71,8 +71,8 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "acadrust"
version = "0.4.1"
source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=a0f7d444f1607bc4b2c881060cbe7ea1014253cb#a0f7d444f1607bc4b2c881060cbe7ea1014253cb"
version = "0.5.1"
source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=ad9b1a25ec0d3dc217e24d9c525709ec18484603#ad9b1a25ec0d3dc217e24d9c525709ec18484603"
dependencies = [
"ahash 0.8.12",
"anyhow",
@@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]]
name = "cadkernel"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8#a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=76ae7f836f5e6d7aa81cefb79330698e38adf95a#76ae7f836f5e6d7aa81cefb79330698e38adf95a"
dependencies = [
"acadrust",
"cavalier_contours",
+3 -3
View File
@@ -27,8 +27,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
rfd = "0.17"
clap = { version = "4", features = ["derive"] }
env_logger = "0.11"
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "a809cb32cc6d90fd0e9c4199c3b4977cd56b7fa8", features = ["acis", "offset"] }
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "ad9b1a25ec0d3dc217e24d9c525709ec18484603", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "76ae7f836f5e6d7aa81cefb79330698e38adf95a", features = ["acis", "offset"] }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
@@ -61,7 +61,7 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa
iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
[patch."https://github.com/HakanSeven12/cadcodec.git"]
acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb" }
acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "ad9b1a25ec0d3dc217e24d9c525709ec18484603" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+21 -12
View File
@@ -3566,18 +3566,27 @@ impl OpenCADStudio {
let entity_opt = self.tabs[i].scene.document.get_entity(handle).cloned();
if let Some(entity) = entity_opt {
let result = sweep_model::revolved(
&entity,
[
axis_start.x as f64,
axis_start.y as f64,
axis_start.z as f64,
],
[axis_end.x as f64, axis_end.y as f64, axis_end.z as f64],
(angle_deg as f64).to_radians(),
);
if let Some(solid) = result {
let history = crate::scene::model::solid_history::brep_op(&solid);
let from = [
axis_start.x as f64,
axis_start.y as f64,
axis_start.z as f64,
];
let to = [axis_end.x as f64, axis_end.y as f64, axis_end.z as f64];
let angle = (angle_deg as f64).to_radians();
let result = sweep_model::revolve_history(&entity, from, to, angle)
.and_then(|history| {
cadkernel::acis::rebuild_body(&history)
.ok()
.map(|solid| (solid, history))
})
.or_else(|| {
sweep_model::revolved(&entity, from, to, angle).map(|solid| {
let history =
crate::scene::model::solid_history::brep_op(&solid);
(solid, history)
})
});
if let Some((solid, history)) = result {
let pending = self.begin_undo(i, "REVOLVE", 1, true);
let created = self.add_solid_model(empty_solid3d(), solid, history);
if !created.is_null() {
+149 -2
View File
@@ -1,12 +1,15 @@
use acadrust::entities::Solid3D;
use acadrust::entities::{EmbeddedEntity, Solid3D};
use acadrust::objects::{
DynamicBlockData, ObjectType, SolidHistoryBox, SolidHistoryBrep, SolidHistoryCone,
SolidHistoryCylinder, SolidHistoryNodeBase, SolidHistoryOperation,
SolidHistoryPyramid, SolidHistorySphere, SolidHistorySweep, SolidHistoryTorus,
SolidHistoryPyramid, SolidHistoryRevolve, SolidHistorySphere, SolidHistorySweep,
SolidHistoryTorus,
};
use acadrust::EntityType;
use cadkernel::brep::Body;
use crate::command::EntityTransform;
use crate::entities::traits::EntityTypeOps;
use crate::scene::model::object::{
GripApply, GripDef, GripShape, PropSection, PropValue, Property,
};
@@ -23,6 +26,8 @@ pub const GRIP_MAJOR_RADIUS: usize = 10_008;
pub const GRIP_MINOR_RADIUS: usize = 10_009;
pub const GRIP_TOP_RADIUS: usize = 10_010;
pub const GRIP_POSITION: usize = 10_011;
pub const GRIP_REVOLVE_AXIS: usize = 10_012;
pub const GRIP_REVOLVE_PROFILE_FIRST: usize = 50_000;
pub const GRIP_BOX_CORNER_FIRST: usize = 10_100;
pub const GRIP_BOX_FACE_X_MIN: usize = 10_110;
pub const GRIP_BOX_FACE_X_MAX: usize = 10_111;
@@ -1641,6 +1646,130 @@ fn local_point(transform: [f64; 16], point: glam::DVec3) -> Option<glam::DVec3>
Some(matrix(transform)?.inverse().transform_point3(point))
}
fn embedded_entity(value: &EmbeddedEntity) -> Option<EntityType> {
Some(match value {
EmbeddedEntity::Point(value) => EntityType::Point(value.clone()),
EmbeddedEntity::Line(value) => EntityType::Line(value.clone()),
EmbeddedEntity::Arc(value) => EntityType::Arc(value.clone()),
EmbeddedEntity::Circle(value) => EntityType::Circle(value.clone()),
EmbeddedEntity::Ellipse(value) => EntityType::Ellipse(value.clone()),
EmbeddedEntity::Spline(value) => EntityType::Spline(value.clone()),
EmbeddedEntity::LwPolyline(value) => EntityType::LwPolyline(value.clone()),
EmbeddedEntity::Ray(value) => EntityType::Ray(value.clone()),
EmbeddedEntity::XLine(value) => EntityType::XLine(value.clone()),
EmbeddedEntity::Unknown { .. } => return None,
})
}
fn into_embedded_entity(value: EntityType) -> Option<EmbeddedEntity> {
Some(match value {
EntityType::Point(value) => EmbeddedEntity::Point(value),
EntityType::Line(value) => EmbeddedEntity::Line(value),
EntityType::Arc(value) => EmbeddedEntity::Arc(value),
EntityType::Circle(value) => EmbeddedEntity::Circle(value),
EntityType::Ellipse(value) => EmbeddedEntity::Ellipse(value),
EntityType::Spline(value) => EmbeddedEntity::Spline(value),
EntityType::LwPolyline(value) => EmbeddedEntity::LwPolyline(value),
EntityType::Ray(value) => EmbeddedEntity::Ray(value),
EntityType::XLine(value) => EmbeddedEntity::XLine(value),
_ => return None,
})
}
fn revolve_profile_matrix(value: &SolidHistoryRevolve) -> Option<glam::DMat4> {
let base = matrix(value.base.transform)?;
let axis_point = glam::DVec3::new(
value.axis_point.x,
value.axis_point.y,
value.axis_point.z,
);
let axis = glam::DVec3::new(value.direction.x, value.direction.y, value.direction.z)
.normalize_or_zero();
if !axis_point.is_finite() || axis.length_squared() <= 1e-12 {
return None;
}
Some(
base
* glam::DMat4::from_translation(axis_point)
* glam::DMat4::from_axis_angle(axis, value.start_angle)
* glam::DMat4::from_translation(-axis_point),
)
}
fn revolve_profile_grips(value: &SolidHistoryRevolve) -> Vec<GripDef> {
let Some(profile) = value.sweep_entity.as_ref().and_then(embedded_entity) else {
return Vec::new();
};
let Some(transform) = revolve_profile_matrix(value) else {
return Vec::new();
};
profile
.grips()
.into_iter()
.enumerate()
.filter_map(|(index, source)| {
let world = transform.transform_point3(source.world);
if !world.is_finite() {
return None;
}
let transform_vector = |vector: glam::DVec3| {
let vector = transform.transform_vector3(vector);
(vector.length_squared() > 1e-12).then(|| vector.normalize())
};
Some(GripDef {
id: GRIP_REVOLVE_PROFILE_FIRST + index,
world,
is_midpoint: source.is_midpoint,
shape: source.shape,
dir: source.dir.and_then(transform_vector),
axis: source.axis.and_then(transform_vector),
})
})
.collect()
}
fn apply_revolve_profile_grip(
value: &mut SolidHistoryRevolve,
index: usize,
apply: GripApply,
) -> bool {
let Some(mut profile) = value.sweep_entity.as_ref().and_then(embedded_entity) else {
return false;
};
let Some(source) = profile.grips().get(index).cloned() else {
return false;
};
let Some(transform) = revolve_profile_matrix(value) else {
return false;
};
let inverse = transform.inverse();
if !inverse.is_finite() {
return false;
}
let apply = match apply {
GripApply::Absolute(world) => {
let local = inverse.transform_point3(world);
if !local.is_finite() {
return false;
}
GripApply::Absolute(local)
}
GripApply::Translate(delta) => {
let local = inverse.transform_vector3(delta);
if !local.is_finite() || local.length_squared() <= 1e-24 {
return false;
}
GripApply::Translate(local)
}
};
profile.apply_grip(source.id, apply);
let Some(profile) = into_embedded_entity(profile) else {
return false;
};
value.sweep_entity = Some(profile);
true
}
fn grip(
id: usize,
world: glam::DVec3,
@@ -1873,6 +2002,16 @@ pub fn primitive_grips(
GripShape::Square,
None,
),
SolidHistoryOperation::Revolve(value) => {
add(
GRIP_REVOLVE_AXIS,
value.base.transform,
[value.axis_point.x, value.axis_point.y, value.axis_point.z],
GripShape::Square,
None,
);
grips.extend(revolve_profile_grips(value));
}
_ => {}
}
grips
@@ -1883,6 +2022,11 @@ pub fn apply_primitive_grip(
grip_id: usize,
apply: GripApply,
) -> bool {
if let SolidHistoryOperation::Revolve(value) = operation {
if let Some(index) = grip_id.checked_sub(GRIP_REVOLVE_PROFILE_FIRST) {
return apply_revolve_profile_grip(value, index, apply);
}
}
let GripApply::Absolute(world) = apply else {
return false;
};
@@ -2032,6 +2176,9 @@ pub fn apply_primitive_grip(
value.base.transform =
(glam::DMat4::from_translation(delta) * current).to_cols_array();
}
SolidHistoryOperation::Revolve(value) if grip_id == GRIP_REVOLVE_AXIS => {
value.axis_point = acadrust::types::Vector3::new(local.x, local.y, local.z);
}
_ => return false,
}
true
+53 -1
View File
@@ -5,7 +5,7 @@ use cadkernel::geom2d::{Arc, Curve, EllipseArc, Line};
use cadkernel::space::{PlanarCurve, Plane, Vec3};
use acadrust::entities::{EmbeddedEntity, LwPolyline, LwVertex};
use acadrust::objects::{
SolidHistoryNodeBase, SolidHistoryOperation, SolidHistorySweep,
SolidHistoryNodeBase, SolidHistoryOperation, SolidHistoryRevolve, SolidHistorySweep,
};
use acadrust::types::{Vector2, Vector3};
use acadrust::EntityType;
@@ -139,6 +139,58 @@ pub fn revolved(
)
}
fn profile_bounds_center(profile: &Profile) -> Option<Vec3> {
let mut low = Vec3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY);
let mut high = Vec3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY);
let mut found = false;
for piece in &profile.pieces {
for point in piece.tessellate_within(1e-4) {
let point = Vec3::from(profile.plane.point_at(point));
if !point.is_finite() {
continue;
}
low.x = low.x.min(point.x);
low.y = low.y.min(point.y);
low.z = low.z.min(point.z);
high.x = high.x.max(point.x);
high.y = high.y.max(point.y);
high.z = high.z.max(point.z);
found = true;
}
}
found.then(|| (low + high) * 0.5)
}
/// Stores the source profile and canonical axis required to rebuild and edit
/// a revolved solid.
pub fn revolve_history(
entity: &EntityType,
from: [f64; 3],
to: [f64; 3],
angle: f64,
) -> Option<SolidHistoryOperation> {
if !angle.is_finite() || angle.abs() <= 1e-12 {
return None;
}
let profile = profile_of(entity)?;
let from = Vec3::from(from);
let direction = (Vec3::from(to) - from).normalize()?;
let center = profile_bounds_center(&profile)?;
let axis_point = from + direction * (center - from).dot(direction);
let mut base = SolidHistoryNodeBase::new(1);
base.transform = glam::DMat4::IDENTITY.to_cols_array();
Some(SolidHistoryOperation::Revolve(SolidHistoryRevolve {
base,
operation_major: 1,
axis_point: Vector3::new(axis_point.x, axis_point.y, axis_point.z),
direction: Vector3::new(direction.x, direction.y, direction.z),
revolve_angle: angle,
flag_290: true,
sweep_entity: Some(embedded_path(entity)?),
..SolidHistoryRevolve::default()
}))
}
/// SWEEP as an exact B-rep along straight and circular path pieces.
pub fn swept(profile: &EntityType, path: &EntityType) -> Option<Body> {
let profile = profile_of(profile)?;