forked from 1b-agent/opencadstudio-upstream
395 lines
12 KiB
Rust
395 lines
12 KiB
Rust
use acadrust::entities::{EmbeddedEntity, Solid3D};
|
|
use acadrust::objects::{
|
|
SolidHistoryBox, SolidHistoryBrep, SolidHistoryCylinder, SolidHistoryLoft,
|
|
SolidHistoryNodeBase, SolidHistoryOperation, SolidHistoryPyramid,
|
|
SolidHistoryRevolve, SolidHistorySphere, SolidHistorySweep, SolidHistoryTorus,
|
|
};
|
|
use acadrust::types::{Vector2, Vector3};
|
|
use acadrust::EntityType;
|
|
use cadkernel::brep::Body;
|
|
|
|
use crate::scene::model::object::{GripApply, GripDef, GripShape};
|
|
|
|
pub const GRIP_LENGTH: usize = 10_001;
|
|
pub const GRIP_WIDTH: usize = 10_002;
|
|
pub const GRIP_HEIGHT: usize = 10_003;
|
|
pub const GRIP_RADIUS: usize = 10_004;
|
|
pub const GRIP_MAJOR_RADIUS: usize = 10_005;
|
|
pub const GRIP_MINOR_RADIUS: usize = 10_006;
|
|
pub const GRIP_SIDES: usize = 10_007;
|
|
|
|
fn matrix(transform: [f64; 16]) -> Option<glam::DMat4> {
|
|
let matrix = glam::DMat4::from_cols_array(&transform);
|
|
(matrix.is_finite() && matrix.determinant().abs() > 1e-12).then_some(matrix)
|
|
}
|
|
|
|
fn world_point(transform: [f64; 16], point: [f64; 3]) -> Option<glam::DVec3> {
|
|
Some(matrix(transform)?.transform_point3(glam::DVec3::from_array(point)))
|
|
}
|
|
|
|
fn local_point(transform: [f64; 16], point: glam::DVec3) -> Option<glam::DVec3> {
|
|
Some(matrix(transform)?.inverse().transform_point3(point))
|
|
}
|
|
|
|
fn grip(id: usize, world: glam::DVec3, shape: GripShape) -> GripDef {
|
|
GripDef {
|
|
id,
|
|
world,
|
|
is_midpoint: false,
|
|
shape,
|
|
dir: None,
|
|
}
|
|
}
|
|
|
|
pub fn primitive_grips(
|
|
document: &acadrust::CadDocument,
|
|
handle: acadrust::Handle,
|
|
) -> Vec<GripDef> {
|
|
let Some(operation) = document.solid_history_operation(handle) else {
|
|
return Vec::new();
|
|
};
|
|
let mut grips = Vec::new();
|
|
let mut add = |id, transform, point, shape| {
|
|
if let Some(world) = world_point(transform, point) {
|
|
grips.push(grip(id, world, shape));
|
|
}
|
|
};
|
|
match operation {
|
|
SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => {
|
|
add(
|
|
GRIP_LENGTH,
|
|
value.base.transform,
|
|
[value.length, value.width * 0.5, 0.0],
|
|
GripShape::Square,
|
|
);
|
|
add(
|
|
GRIP_WIDTH,
|
|
value.base.transform,
|
|
[value.length * 0.5, value.width, 0.0],
|
|
GripShape::Square,
|
|
);
|
|
add(
|
|
GRIP_HEIGHT,
|
|
value.base.transform,
|
|
[value.length * 0.5, value.width * 0.5, value.height],
|
|
GripShape::Square,
|
|
);
|
|
}
|
|
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => {
|
|
add(
|
|
GRIP_RADIUS,
|
|
value.base.transform,
|
|
[value.major_radius, 0.0, value.height * 0.5],
|
|
GripShape::Square,
|
|
);
|
|
add(
|
|
GRIP_HEIGHT,
|
|
value.base.transform,
|
|
[0.0, 0.0, value.height],
|
|
GripShape::Square,
|
|
);
|
|
}
|
|
SolidHistoryOperation::Sphere(value) => add(
|
|
GRIP_RADIUS,
|
|
value.base.transform,
|
|
[value.radius, 0.0, 0.0],
|
|
GripShape::Square,
|
|
),
|
|
SolidHistoryOperation::Torus(value) => {
|
|
add(
|
|
GRIP_MAJOR_RADIUS,
|
|
value.base.transform,
|
|
[value.major_radius, 0.0, 0.0],
|
|
GripShape::Square,
|
|
);
|
|
add(
|
|
GRIP_MINOR_RADIUS,
|
|
value.base.transform,
|
|
[value.major_radius + value.minor_radius, 0.0, 0.0],
|
|
GripShape::Square,
|
|
);
|
|
}
|
|
SolidHistoryOperation::Pyramid(value) => {
|
|
add(
|
|
GRIP_RADIUS,
|
|
value.base.transform,
|
|
[value.radius, 0.0, 0.0],
|
|
GripShape::Square,
|
|
);
|
|
add(
|
|
GRIP_HEIGHT,
|
|
value.base.transform,
|
|
[0.0, 0.0, value.height],
|
|
GripShape::Square,
|
|
);
|
|
let angle = (value.sides.clamp(3, 71) as f64 * 5.0).to_radians();
|
|
add(
|
|
GRIP_SIDES,
|
|
value.base.transform,
|
|
[value.radius * angle.cos(), value.radius * angle.sin(), 0.0],
|
|
GripShape::Triangle,
|
|
);
|
|
}
|
|
_ => {}
|
|
}
|
|
grips
|
|
}
|
|
|
|
pub fn apply_primitive_grip(
|
|
operation: &mut SolidHistoryOperation,
|
|
grip_id: usize,
|
|
apply: GripApply,
|
|
) -> bool {
|
|
let GripApply::Absolute(world) = apply else {
|
|
return false;
|
|
};
|
|
let Some(transform) = operation.base().map(|base| base.transform) else {
|
|
return false;
|
|
};
|
|
let Some(local) = local_point(transform, world) else {
|
|
return false;
|
|
};
|
|
let positive = |value: f64| value.abs().max(1e-6);
|
|
match operation {
|
|
SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => {
|
|
match grip_id {
|
|
GRIP_LENGTH => value.length = positive(local.x),
|
|
GRIP_WIDTH => value.width = positive(local.y),
|
|
GRIP_HEIGHT => value.height = positive(local.z),
|
|
_ => return false,
|
|
}
|
|
}
|
|
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => {
|
|
match grip_id {
|
|
GRIP_RADIUS => {
|
|
let radius = local.x.hypot(local.y).max(1e-6);
|
|
value.major_radius = radius;
|
|
value.minor_radius = radius;
|
|
value.x_radius = radius;
|
|
}
|
|
GRIP_HEIGHT => value.height = positive(local.z),
|
|
_ => return false,
|
|
}
|
|
}
|
|
SolidHistoryOperation::Sphere(value) if grip_id == GRIP_RADIUS => {
|
|
value.radius = local.length().max(1e-6);
|
|
}
|
|
SolidHistoryOperation::Torus(value) => match grip_id {
|
|
GRIP_MAJOR_RADIUS => {
|
|
value.major_radius = local.x.hypot(local.y).max(1e-6)
|
|
}
|
|
GRIP_MINOR_RADIUS => {
|
|
value.minor_radius = (local.x.hypot(local.y) - value.major_radius)
|
|
.abs()
|
|
.max(1e-6)
|
|
}
|
|
_ => return false,
|
|
},
|
|
SolidHistoryOperation::Pyramid(value) => match grip_id {
|
|
GRIP_RADIUS => value.radius = local.x.hypot(local.y).max(1e-6),
|
|
GRIP_HEIGHT => value.height = positive(local.z),
|
|
GRIP_SIDES => {
|
|
let angle = local.y.atan2(local.x).rem_euclid(std::f64::consts::TAU);
|
|
value.sides = (angle.to_degrees() / 5.0).round() as i32;
|
|
value.sides = value.sides.clamp(3, 71);
|
|
}
|
|
_ => return false,
|
|
},
|
|
_ => return false,
|
|
}
|
|
true
|
|
}
|
|
|
|
fn base(transform: [f64; 16]) -> SolidHistoryNodeBase {
|
|
let mut base = SolidHistoryNodeBase::new(1);
|
|
base.transform = transform;
|
|
base
|
|
}
|
|
|
|
fn embedded(entity: &EntityType) -> Option<EmbeddedEntity> {
|
|
Some(match entity {
|
|
EntityType::Point(value) => EmbeddedEntity::Point(value.clone()),
|
|
EntityType::Line(value) => EmbeddedEntity::Line(value.clone()),
|
|
EntityType::Arc(value) => EmbeddedEntity::Arc(value.clone()),
|
|
EntityType::Circle(value) => EmbeddedEntity::Circle(value.clone()),
|
|
EntityType::Ellipse(value) => EmbeddedEntity::Ellipse(value.clone()),
|
|
EntityType::Spline(value) => EmbeddedEntity::Spline(value.clone()),
|
|
EntityType::LwPolyline(value) => EmbeddedEntity::LwPolyline(value.clone()),
|
|
EntityType::Ray(value) => EmbeddedEntity::Ray(value.clone()),
|
|
EntityType::XLine(value) => EmbeddedEntity::XLine(value.clone()),
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
pub fn box_op(
|
|
transform: [f64; 16],
|
|
length: f64,
|
|
width: f64,
|
|
height: f64,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Box(SolidHistoryBox {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
length,
|
|
width,
|
|
height,
|
|
..SolidHistoryBox::default()
|
|
})
|
|
}
|
|
|
|
pub fn wedge_op(
|
|
transform: [f64; 16],
|
|
length: f64,
|
|
width: f64,
|
|
height: f64,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Wedge(SolidHistoryBox {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
length,
|
|
width,
|
|
height,
|
|
..SolidHistoryBox::default()
|
|
})
|
|
}
|
|
|
|
pub fn cylinder_op(
|
|
transform: [f64; 16],
|
|
radius: f64,
|
|
height: f64,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Cylinder(SolidHistoryCylinder {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
height,
|
|
major_radius: radius,
|
|
minor_radius: radius,
|
|
x_radius: radius,
|
|
..SolidHistoryCylinder::default()
|
|
})
|
|
}
|
|
|
|
pub fn cone_op(
|
|
transform: [f64; 16],
|
|
radius: f64,
|
|
height: f64,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Cone(SolidHistoryCylinder {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
height,
|
|
major_radius: radius,
|
|
minor_radius: radius,
|
|
x_radius: radius,
|
|
..SolidHistoryCylinder::default()
|
|
})
|
|
}
|
|
|
|
pub fn sphere_op(transform: [f64; 16], radius: f64) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Sphere(SolidHistorySphere {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
radius,
|
|
..SolidHistorySphere::default()
|
|
})
|
|
}
|
|
|
|
pub fn torus_op(
|
|
transform: [f64; 16],
|
|
major_radius: f64,
|
|
minor_radius: f64,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Torus(SolidHistoryTorus {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
major_radius,
|
|
minor_radius,
|
|
..SolidHistoryTorus::default()
|
|
})
|
|
}
|
|
|
|
pub fn pyramid_op(
|
|
transform: [f64; 16],
|
|
radius: f64,
|
|
height: f64,
|
|
sides: usize,
|
|
) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Pyramid(SolidHistoryPyramid {
|
|
base: base(transform),
|
|
operation_major: 1,
|
|
height,
|
|
sides: sides as i32,
|
|
radius,
|
|
..SolidHistoryPyramid::default()
|
|
})
|
|
}
|
|
|
|
pub fn brep_op(body: &Body) -> SolidHistoryOperation {
|
|
let acis_data = crate::scene::convert::acis_export::planar_solid_to_sat(body)
|
|
.map(|document| {
|
|
let mut solid = Solid3D::new();
|
|
solid.set_sat_document(&document);
|
|
solid.acis_data
|
|
})
|
|
.unwrap_or_default();
|
|
SolidHistoryOperation::Brep(SolidHistoryBrep {
|
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
|
operation_major: 1,
|
|
acis_data,
|
|
..SolidHistoryBrep::default()
|
|
})
|
|
}
|
|
|
|
pub fn extrusion_op(profile: &EntityType, height: f64) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Extrusion(SolidHistorySweep {
|
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
|
operation_major: 1,
|
|
direction: Vector3::new(0.0, 0.0, height),
|
|
sweep_entity: embedded(profile),
|
|
scale_factor: 1.0,
|
|
sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
|
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
|
..SolidHistorySweep::default()
|
|
})
|
|
}
|
|
|
|
pub fn sweep_op(profile: &EntityType, path: &EntityType) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Sweep(SolidHistorySweep {
|
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
|
operation_major: 1,
|
|
sweep_entity: embedded(profile),
|
|
path_entity: embedded(path),
|
|
scale_factor: 1.0,
|
|
sweep_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
|
path_entity_transform: glam::DMat4::IDENTITY.to_cols_array(),
|
|
..SolidHistorySweep::default()
|
|
})
|
|
}
|
|
|
|
pub fn loft_op(profiles: &[EntityType]) -> SolidHistoryOperation {
|
|
SolidHistoryOperation::Loft(SolidHistoryLoft {
|
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
|
operation_major: 1,
|
|
cross_sections: profiles.iter().filter_map(embedded).collect(),
|
|
..SolidHistoryLoft::default()
|
|
})
|
|
}
|
|
|
|
pub fn revolve_op(
|
|
profile: &EntityType,
|
|
axis_start: [f64; 3],
|
|
axis_end: [f64; 3],
|
|
angle: f64,
|
|
) -> SolidHistoryOperation {
|
|
let direction = glam::DVec3::from_array(axis_end) - glam::DVec3::from_array(axis_start);
|
|
SolidHistoryOperation::Revolve(SolidHistoryRevolve {
|
|
base: base(glam::DMat4::IDENTITY.to_cols_array()),
|
|
operation_major: 1,
|
|
axis_point: Vector3::new(axis_start[0], axis_start[1], axis_start[2]),
|
|
direction: Vector2::new(direction.x, direction.y),
|
|
revolve_angle: angle,
|
|
sweep_entity: embedded(profile),
|
|
..SolidHistoryRevolve::default()
|
|
})
|
|
}
|