feat(entities): render + property builder for loaded Helix entities
A helix loaded from a DWG (AcDbHelix) previously fell through to the fallback: it did not render, had no grips, could not be moved, and showed only a Type row. Add src/entities/helix.rs which drives all of these through the embedded spline (render/grips/transform) and exposes the generating parameters — base point, turns, turn height, height, base/top radius, turn slope, twist, constrain — as the Geometry group per the properties spec. Wire Helix into the to_truck / grips / apply_grip / geometry_properties / apply_geom_prop / apply_transform dispatch lists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4398341632
commit
0a9c27e39d
3 changed files with 140 additions and 6 deletions
133
src/entities/helix.rs
Normal file
133
src/entities/helix.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
// Helix entity (AcDbHelix): a spline curve plus its generating parameters.
|
||||||
|
//
|
||||||
|
// A loaded helix carries a fully evaluated `spline` (the NURBS the file
|
||||||
|
// stored) alongside the parameters that generated it (base point, axis,
|
||||||
|
// radius, turns, turn height, twist, constraint). We render and grip the
|
||||||
|
// entity through its embedded spline, and expose the generating parameters
|
||||||
|
// as the Geometry property group.
|
||||||
|
|
||||||
|
use acadrust::entities::{Helix, HelixConstraint};
|
||||||
|
|
||||||
|
use crate::command::EntityTransform;
|
||||||
|
use crate::entities::common::ro_prop as ro;
|
||||||
|
use crate::entities::traits::{Grippable, Transformable, TruckConvertible};
|
||||||
|
use crate::scene::convert::acad_to_truck::TruckEntity;
|
||||||
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
||||||
|
|
||||||
|
/// Perpendicular distance from `p` to the helix axis (line through
|
||||||
|
/// `axis_base_point` along `axis_vector`). Used to recover the top radius
|
||||||
|
/// from the last defining point of the spline.
|
||||||
|
fn radius_from_axis(helix: &Helix, p: &acadrust::types::Vector3) -> Option<f64> {
|
||||||
|
let base = &helix.axis_base_point;
|
||||||
|
let axis = helix.axis_vector.normalize();
|
||||||
|
// A zero axis vector cannot define a line.
|
||||||
|
if axis.length() < 1e-9 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let vx = p.x - base.x;
|
||||||
|
let vy = p.y - base.y;
|
||||||
|
let vz = p.z - base.z;
|
||||||
|
let proj = vx * axis.x + vy * axis.y + vz * axis.z;
|
||||||
|
let px = vx - proj * axis.x;
|
||||||
|
let py = vy - proj * axis.y;
|
||||||
|
let pz = vz - proj * axis.z;
|
||||||
|
Some((px * px + py * py + pz * pz).sqrt())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grips(helix: &Helix) -> Vec<GripDef> {
|
||||||
|
Grippable::grips(&helix.spline)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn properties(helix: &Helix) -> Vec<PropSection> {
|
||||||
|
let bp = &helix.axis_base_point;
|
||||||
|
let turns = helix.turns;
|
||||||
|
let turn_height = helix.turn_height;
|
||||||
|
// Overall height is turns × per-turn rise.
|
||||||
|
let height = turns * turn_height;
|
||||||
|
let base_radius = helix.radius;
|
||||||
|
// Top radius: the axial distance of the last defining point. For a
|
||||||
|
// cylindrical helix this equals the base radius.
|
||||||
|
let top_radius = helix
|
||||||
|
.spline
|
||||||
|
.control_points
|
||||||
|
.last()
|
||||||
|
.or_else(|| helix.spline.fit_points.last())
|
||||||
|
.and_then(|p| radius_from_axis(helix, p))
|
||||||
|
.unwrap_or(base_radius);
|
||||||
|
// Turn slope: rise angle of a single turn against the base circumference.
|
||||||
|
let turn_slope = if base_radius.abs() > 1e-9 {
|
||||||
|
(turn_height / (std::f64::consts::TAU * base_radius))
|
||||||
|
.atan()
|
||||||
|
.to_degrees()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let twist = if helix.handedness { "CW" } else { "CCW" };
|
||||||
|
let constrain = match helix.constraint {
|
||||||
|
HelixConstraint::TurnHeight => "Turn Height",
|
||||||
|
HelixConstraint::Turns => "Turns",
|
||||||
|
HelixConstraint::Height => "Height",
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![PropSection {
|
||||||
|
title: "Geometry".into(),
|
||||||
|
props: vec![
|
||||||
|
ro("Base point X", "base_x", format!("{:.4}", bp.x)),
|
||||||
|
ro("Base point Y", "base_y", format!("{:.4}", bp.y)),
|
||||||
|
ro("Base point Z", "base_z", format!("{:.4}", bp.z)),
|
||||||
|
ro("Number of turns", "num_turns", format!("{turns:.4}")),
|
||||||
|
ro("Turn height", "turn_height", format!("{turn_height:.4}")),
|
||||||
|
ro("Turns", "turns", format!("{turns:.4}")),
|
||||||
|
ro("Height", "height", format!("{height:.4}")),
|
||||||
|
ro("Base radius", "base_radius", format!("{base_radius:.4}")),
|
||||||
|
ro("Top radius", "top_radius", format!("{top_radius:.4}")),
|
||||||
|
ro("Turn slope", "turn_slope", format!("{turn_slope:.4}")),
|
||||||
|
ro("Twist", "twist", twist),
|
||||||
|
ro("Constrain", "constrain", constrain),
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helix geometry is derived from the stored spline; the generating
|
||||||
|
/// parameters are read-only (editing one would require regenerating the
|
||||||
|
/// spline, which the loaded entity does not carry a recipe for).
|
||||||
|
fn apply_geom_prop(_helix: &mut Helix, _field: &str, _value: &str) {}
|
||||||
|
|
||||||
|
fn apply_grip(helix: &mut Helix, grip_id: usize, apply: GripApply) {
|
||||||
|
Grippable::apply_grip(&mut helix.spline, grip_id, apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_transform(helix: &mut Helix, t: &EntityTransform) {
|
||||||
|
// Move the visible curve, then keep the generating points in step.
|
||||||
|
Transformable::apply_transform(&mut helix.spline, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TruckConvertible for Helix {
|
||||||
|
fn to_truck(&self, document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
||||||
|
self.spline.to_truck(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grippable for Helix {
|
||||||
|
fn grips(&self) -> Vec<GripDef> {
|
||||||
|
grips(self)
|
||||||
|
}
|
||||||
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
||||||
|
apply_grip(self, grip_id, apply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::entities::traits::PropertyEditable for Helix {
|
||||||
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
||||||
|
properties(self)
|
||||||
|
}
|
||||||
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
||||||
|
apply_geom_prop(self, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transformable for Helix {
|
||||||
|
fn apply_transform(&mut self, t: &EntityTransform) {
|
||||||
|
apply_transform(self, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod common;
|
||||||
pub mod dimension;
|
pub mod dimension;
|
||||||
pub mod ellipse;
|
pub mod ellipse;
|
||||||
pub mod hatch;
|
pub mod hatch;
|
||||||
|
pub mod helix;
|
||||||
pub mod insert;
|
pub mod insert;
|
||||||
pub mod leader;
|
pub mod leader;
|
||||||
pub mod line;
|
pub mod line;
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Wipeout, AttributeDefinition, AttributeEntity, MLine,
|
Wipeout, AttributeDefinition, AttributeEntity, MLine,
|
||||||
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
||||||
Table, Text, MText, Leader, MultiLeader, Underlay, Shape,
|
Table, Text, MText, Leader, MultiLeader, Underlay, Shape,
|
||||||
Ole2Frame,
|
Ole2Frame, Helix,
|
||||||
],
|
],
|
||||||
_ => None,
|
_ => None,
|
||||||
)
|
)
|
||||||
|
|
@ -288,7 +288,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
||||||
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
||||||
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
||||||
Underlay, Shape, Ole2Frame,
|
Underlay, Shape, Ole2Frame, Helix,
|
||||||
],
|
],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
)
|
)
|
||||||
|
|
@ -304,7 +304,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
||||||
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
||||||
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
||||||
Shape, Ole2Frame,
|
Shape, Ole2Frame, Helix,
|
||||||
],
|
],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
)
|
)
|
||||||
|
|
@ -320,7 +320,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
||||||
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
||||||
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
||||||
Shape, Ole2Frame,
|
Shape, Ole2Frame, Helix,
|
||||||
],
|
],
|
||||||
_ => {},
|
_ => {},
|
||||||
)
|
)
|
||||||
|
|
@ -336,7 +336,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
Solid, Solid3D, Region, Body, Face3D, PolygonMesh,
|
||||||
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
||||||
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
||||||
Underlay, Shape, Ole2Frame,
|
Underlay, Shape, Ole2Frame, Helix,
|
||||||
],
|
],
|
||||||
_ => {},
|
_ => {},
|
||||||
)
|
)
|
||||||
|
|
@ -425,7 +425,7 @@ impl EntityTypeOps for EntityType {
|
||||||
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
||||||
Table, MText, Point, Spline, Text, Viewport, Dimension,
|
Table, MText, Point, Spline, Text, Viewport, Dimension,
|
||||||
Leader, MultiLeader, Underlay, Shape, Ole2Frame,
|
Leader, MultiLeader, Underlay, Shape, Ole2Frame,
|
||||||
Solid3D, Region, Body, Surface,
|
Solid3D, Region, Body, Surface, Helix,
|
||||||
],
|
],
|
||||||
_ => {},
|
_ => {},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue