Extend works on any surface: natural (continuing the face's own surface), tangent (G1 run-off) or curvature (G2 run-off), by a distance, up to a plane or face, or back to the surface's bounds, with neighbouring edges lengthened to meet. Untrim and Extend share face parameter-loop helpers. The new SurfaceExtend fields are noted under format 21. Closes #760.
This commit is contained in:
1 parent
41f0c3d0c6
commit
b4edf05373
18 files changed
+2271
-263
No files matched your search
@@ -194,6 +194,7 @@ impl crate::Feature {
|
||||
| K::SurfaceTrim { plane, .. } => checked_plane(plane, 0)?,
|
||||
K::Draft { neutral, .. } => checked_plane(neutral, 0)?,
|
||||
K::ReplaceFace { target, .. } => checked_plane(target, 0)?,
|
||||
K::SurfaceExtend { up_to: Some(crate::ExtendTo::Plane(plane)), .. } => checked_plane(plane, 0)?,
|
||||
K::ConstructionAxis { def } => {
|
||||
for r in def.refs() {
|
||||
checked_geo(r, 0)?;
|
||||
@@ -248,6 +249,7 @@ impl crate::Feature {
|
||||
| K::SurfaceTrim { plane, .. } => remap_plane(plane, ids, names, bodies, 0)?,
|
||||
K::Draft { neutral, .. } => remap_plane(neutral, ids, names, bodies, 0)?,
|
||||
K::ReplaceFace { target, .. } => remap_plane(target, ids, names, bodies, 0)?,
|
||||
K::SurfaceExtend { up_to: Some(crate::ExtendTo::Plane(plane)), .. } => remap_plane(plane, ids, names, bodies, 0)?,
|
||||
K::ConstructionAxis { def } => {
|
||||
for r in def.refs_mut() {
|
||||
remap_geo(r, ids, names, bodies, 0)?;
|
||||
|
||||
@@ -1137,11 +1137,20 @@ pub enum FeatureKind {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
face: Option<Vec3>,
|
||||
},
|
||||
/// A planar surface body extended outward by `distance` past the edges through `edges`.
|
||||
/// A surface body's boundary edges through `edges` extended outward: by `distance`, or up
|
||||
/// to `up_to`; the surface continued (`mode` natural) or a run-off face along each edge
|
||||
/// (tangent, curvature). `neighbours`: the edges next to an extended edge are lengthened to
|
||||
/// meet it (else the extension's sides run square to the edge).
|
||||
SurfaceExtend {
|
||||
body: String,
|
||||
edges: Vec<Vec3>,
|
||||
distance: String,
|
||||
#[serde(default, skip_serializing_if = "ExtendMode::is_natural")]
|
||||
mode: ExtendMode,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
up_to: Option<ExtendTo>,
|
||||
#[serde(default = "yes", skip_serializing_if = "is_yes")]
|
||||
neighbours: bool,
|
||||
},
|
||||
/// A surface body trimmed by a plane, or by a face of a body (`tool`), keeping the side of
|
||||
/// `keep`.
|
||||
@@ -1189,6 +1198,37 @@ pub enum FeatureKind {
|
||||
},
|
||||
}
|
||||
|
||||
/// How Extend continues a surface past its edges.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExtendMode {
|
||||
/// The surface itself, continued.
|
||||
#[default]
|
||||
Natural,
|
||||
/// A ruled run-off in the surface's tangent plane (G1).
|
||||
Tangent,
|
||||
/// A run-off that keeps the surface's curvature across the edge (G2).
|
||||
Curvature,
|
||||
}
|
||||
|
||||
impl ExtendMode {
|
||||
pub fn is_natural(&self) -> bool {
|
||||
*self == ExtendMode::Natural
|
||||
}
|
||||
}
|
||||
|
||||
/// What Extend extends up to instead of a distance.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExtendTo {
|
||||
/// A plane.
|
||||
Plane(PlaneRef),
|
||||
/// The surface of a body's face (extended).
|
||||
Face(FaceAt),
|
||||
/// The surface's own bounds (natural only): a trim undone.
|
||||
Bounds,
|
||||
}
|
||||
|
||||
/// How a fillet's size is given.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
|
||||
@@ -78,11 +78,30 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SurfaceExtend { body, edges, distance } => {
|
||||
let d = val(vals, distance, Kind::Length)?;
|
||||
FeatureKind::SurfaceExtend { body, edges, distance, mode, up_to, neighbours } => {
|
||||
let i = st.bodies.iter().position(|b| &b.name == body).ok_or_else(|| DocError::Unknown(format!("body `{body}`")))?;
|
||||
let Some(mb) = st.bodies.get(i).cloned() else { return Err(DocError::Invalid("body".into())) };
|
||||
let out = kernel::extend(&mb.body, edges, d)?;
|
||||
let mode = match mode {
|
||||
crate::ExtendMode::Natural => kernel::ExtendMode::Natural,
|
||||
crate::ExtendMode::Tangent => kernel::ExtendMode::Tangent,
|
||||
crate::ExtendMode::Curvature => kernel::ExtendMode::Curvature,
|
||||
};
|
||||
let at = |limit| kernel::ExtendOptions { mode, limit, neighbours: *neighbours };
|
||||
let out = match up_to {
|
||||
None => kernel::extend_surface(&mb.body, edges, &at(kernel::ExtendLimit::Distance(val(vals, distance, Kind::Length)?)))?,
|
||||
Some(crate::ExtendTo::Bounds) => kernel::extend_surface(&mb.body, edges, &at(kernel::ExtendLimit::Bounds))?,
|
||||
Some(crate::ExtendTo::Plane(plane)) => {
|
||||
let tool = kernel::SplitTool::Plane(doc.resolve_plane_in(vals, plane, 0, Some(st))?);
|
||||
kernel::extend_surface(&mb.body, edges, &at(kernel::ExtendLimit::UpTo(&tool)))?
|
||||
}
|
||||
Some(crate::ExtendTo::Face(t)) => {
|
||||
let tb = st.body(&t.body).ok_or_else(|| DocError::Unknown(format!("body `{}`", t.body)))?;
|
||||
let face = crate::appearance::face_index_at(tb, t.point)
|
||||
.ok_or_else(|| DocError::Invalid(format!("no face of `{}` at the target point", t.body)))?;
|
||||
let tool = kernel::SplitTool::Face { body: &tb.body, face };
|
||||
kernel::extend_surface(&mb.body, edges, &at(kernel::ExtendLimit::UpTo(&tool)))?
|
||||
}
|
||||
};
|
||||
if let Some(slot) = st.bodies.get_mut(i) {
|
||||
*slot = ModelBody::new(mb.name, out, mb.feature);
|
||||
}
|
||||
|
||||
@@ -139,11 +139,21 @@ macro_rules! geometry_walk {
|
||||
f(Pos(start));
|
||||
}
|
||||
}
|
||||
FeatureKind::Chamfer { edges, .. } | FeatureKind::SheetHem { edges, .. } | FeatureKind::SurfaceExtend { edges, .. } => {
|
||||
FeatureKind::Chamfer { edges, .. } | FeatureKind::SheetHem { edges, .. } => {
|
||||
for e in edges {
|
||||
f(Pos(e));
|
||||
}
|
||||
}
|
||||
FeatureKind::SurfaceExtend { edges, up_to, .. } => {
|
||||
for e in edges {
|
||||
f(Pos(e));
|
||||
}
|
||||
match up_to {
|
||||
Some(crate::ExtendTo::Plane(plane)) => f(Plane(plane)),
|
||||
Some(crate::ExtendTo::Face(t)) => f(Pos(& $($m)? t.point)),
|
||||
Some(crate::ExtendTo::Bounds) | None => {}
|
||||
}
|
||||
}
|
||||
FeatureKind::Shell { faces, .. }
|
||||
| FeatureKind::OffsetFace { faces, .. }
|
||||
| FeatureKind::DeleteFace { faces, .. }
|
||||
|
||||
@@ -175,8 +175,15 @@ macro_rules! feature_walk {
|
||||
}
|
||||
}
|
||||
FeatureKind::SurfaceUntrim { face, .. } => f(B(& $($m)? face.body)),
|
||||
FeatureKind::SurfaceExtend { body, up_to, .. } => {
|
||||
f(B(body));
|
||||
match up_to {
|
||||
Some(crate::ExtendTo::Plane(plane)) => $plane(plane, f),
|
||||
Some(crate::ExtendTo::Face(t)) => f(B(& $($m)? t.body)),
|
||||
Some(crate::ExtendTo::Bounds) | None => {}
|
||||
}
|
||||
}
|
||||
FeatureKind::SheetConvert { body, .. }
|
||||
| FeatureKind::SurfaceExtend { body, .. }
|
||||
| FeatureKind::SurfaceRuled { body, .. }
|
||||
| FeatureKind::Unstitch { body, .. }
|
||||
| FeatureKind::ReverseNormal { body, .. }
|
||||
|
||||
@@ -153,6 +153,14 @@ pub(super) fn fixtures() -> Vec<(&'static str, Value)> {
|
||||
("reverse_normal", json!({"type": "reverse_normal", "body": "B"})),
|
||||
("surface_untrim", json!({"type": "surface_untrim", "face": {"body": "B", "point": o}, "holes": [o], "outer": true})),
|
||||
("surface_extend", json!({"type": "surface_extend", "body": "B", "edges": [o], "distance": "extend.distance"})),
|
||||
(
|
||||
"surface_extend_to_plane",
|
||||
json!({"type": "surface_extend", "body": "B", "edges": [o], "distance": "extend.distance", "mode": "curvature", "up_to": {"plane": plane("extend")}}),
|
||||
),
|
||||
(
|
||||
"surface_extend_to_face",
|
||||
json!({"type": "surface_extend", "body": "B", "edges": [o], "distance": "extend.distance", "up_to": {"face": {"body": "B", "point": o}}, "neighbours": false}),
|
||||
),
|
||||
(
|
||||
"surface_ruled",
|
||||
json!({"type": "surface_ruled", "body": "B", "edges": [o], "distance": "ruled.distance", "mode": "angle", "angle": "ruled.angle",
|
||||
|
||||
@@ -330,6 +330,16 @@ surface_extend
|
||||
inputs:
|
||||
Distance = extend.distance (Length)
|
||||
expressions: extend.distance
|
||||
surface_extend_to_plane
|
||||
inputs:
|
||||
Distance = extend.distance (Length)
|
||||
Angle = extend.angle (Angle)
|
||||
Offset = extend.offset (Length)
|
||||
expressions: extend.distance, extend.angle, extend.offset
|
||||
surface_extend_to_face
|
||||
inputs:
|
||||
Distance = extend.distance (Length)
|
||||
expressions: extend.distance
|
||||
surface_ruled
|
||||
inputs:
|
||||
Distance = ruled.distance (Length)
|
||||
|
||||
@@ -155,6 +155,10 @@ surface_untrim
|
||||
face points: [0, 0, 0]
|
||||
surface_extend
|
||||
moves: edges[0][0], edges[0][1], edges[0][2]
|
||||
surface_extend_to_plane
|
||||
moves: edges[0][0], edges[0][1], edges[0][2], up_to.plane.axis_dir[0], up_to.plane.axis_dir[1], up_to.plane.axis_origin[0], up_to.plane.axis_origin[1], up_to.plane.axis_origin[2]
|
||||
surface_extend_to_face
|
||||
moves: edges[0][0], edges[0][1], edges[0][2], up_to.face.point[0], up_to.face.point[1], up_to.face.point[2]
|
||||
surface_ruled
|
||||
moves: direction[0], direction[2], edges[0][0], edges[0][1], edges[0][2], face[1], face[2]
|
||||
face points: [1, 0, 0]
|
||||
|
||||
@@ -172,6 +172,11 @@ surface_untrim
|
||||
remap_bodies: face.body
|
||||
surface_extend
|
||||
remap_bodies: body
|
||||
surface_extend_to_plane
|
||||
remap_refs: up_to.plane.base.base.name
|
||||
remap_bodies: body
|
||||
surface_extend_to_face
|
||||
remap_bodies: body, up_to.face.body
|
||||
surface_ruled
|
||||
remap_bodies: body
|
||||
surface_trim
|
||||
|
||||
@@ -173,6 +173,12 @@
|
||||
//! A `SurfaceRuled` feature (Ruled Surface: `body`, `edges` points, `distance`, optional
|
||||
//! `mode`, `angle`, `direction`, `flip`, `corners` and `face`) is also first saved in 21;
|
||||
//! 0.11.0 asks for an update instead of meeting a feature it can't read.
|
||||
//! Under the same version, a `SurfaceExtend` can say how it continues the surface (`mode`:
|
||||
//! `tangent` or `curvature`; `natural` when absent), extend up to something instead of its
|
||||
//! `distance` (`up_to`: a `plane`, a `face` as `body` and `point`, or `bounds`) and keep the
|
||||
//! neighbouring edges as they are (`neighbours: false`). An Extend without them builds the
|
||||
//! planar extension it always did and saves as before; 0.11.0 asks for an update instead of
|
||||
//! dropping them and extending a plane by the distance.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
|
||||
@@ -353,7 +353,12 @@ macro_rules! feature_walk {
|
||||
FeatureKind::MeshConvert { tolerance, .. } => f("Tolerance", tolerance, L, I),
|
||||
FeatureKind::Thicken { thickness, .. } => f("Thickness", thickness, L, I),
|
||||
FeatureKind::SurfaceTrim { plane, .. } => $plane(plane, f),
|
||||
FeatureKind::SurfaceExtend { distance, .. } => f("Distance", distance, L, I),
|
||||
FeatureKind::SurfaceExtend { distance, up_to, .. } => {
|
||||
f("Distance", distance, L, I);
|
||||
if let Some(crate::ExtendTo::Plane(plane)) = up_to {
|
||||
$plane(plane, f);
|
||||
}
|
||||
}
|
||||
FeatureKind::SurfaceRuled { distance, angle, .. } => {
|
||||
f("Distance", distance, L, I);
|
||||
if let Some(a) = angle {
|
||||
|
||||
Reference in new issue
Block a user