* git mv src/wasm-lib rust Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv wasm-lib to workspace Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv kcl-lib Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv derive docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * resolve file paths Signed-off-by: Jess Frazelle <github@jessfraz.com> * clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> * move more shit Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix more paths Signed-off-by: Jess Frazelle <github@jessfraz.com> * make yarn build:wasm work Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix scripts Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixups Signed-off-by: Jess Frazelle <github@jessfraz.com> * better references Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix cargo ci Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix reference Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix more ci Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * cargo sort Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix script Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * fmt Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix a dep Signed-off-by: Jess Frazelle <github@jessfraz.com> * sort Signed-off-by: Jess Frazelle <github@jessfraz.com> * remove unused deps Signed-off-by: Jess Frazelle <github@jessfraz.com> * Revert "remove unused deps" This reverts commit fbabdb062e275fd5cbc1476f8480a1afee15d972. * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * deps; Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
164 lines
6.0 KiB
Rust
164 lines
6.0 KiB
Rust
//! Standard library lofts.
|
|
|
|
use std::num::NonZeroU32;
|
|
|
|
use anyhow::Result;
|
|
use kcl_derive_docs::stdlib;
|
|
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
|
use kittycad_modeling_cmds as kcmc;
|
|
|
|
use crate::{
|
|
errors::{KclError, KclErrorDetails},
|
|
execution::{ExecState, KclValue, Sketch, Solid},
|
|
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
|
};
|
|
|
|
const DEFAULT_V_DEGREE: u32 = 2;
|
|
|
|
/// Create a 3D surface or solid by interpolating between two or more sketches.
|
|
pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
|
let sketches = args.get_unlabeled_kw_arg("sketches")?;
|
|
let v_degree: NonZeroU32 = args
|
|
.get_kw_arg_opt("vDegree")?
|
|
.unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap());
|
|
// Attempt to approximate rational curves (such as arcs) using a bezier.
|
|
// This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios
|
|
// Over time, this field won't be necessary.
|
|
let bez_approximate_rational = args.get_kw_arg_opt("bezApproximateRational")?.unwrap_or(false);
|
|
// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
|
|
let base_curve_index: Option<u32> = args.get_kw_arg_opt("baseCurveIndex")?;
|
|
// Tolerance for the loft operation.
|
|
let tolerance: Option<f64> = args.get_kw_arg_opt("tolerance")?;
|
|
|
|
let value = inner_loft(
|
|
sketches,
|
|
v_degree,
|
|
bez_approximate_rational,
|
|
base_curve_index,
|
|
tolerance,
|
|
exec_state,
|
|
args,
|
|
)
|
|
.await?;
|
|
Ok(KclValue::Solid { value })
|
|
}
|
|
|
|
/// Create a 3D surface or solid by interpolating between two or more sketches.
|
|
///
|
|
/// The sketches need to closed and on the same plane.
|
|
///
|
|
/// ```no_run
|
|
/// // Loft a square and a triangle.
|
|
/// squareSketch = startSketchOn('XY')
|
|
/// |> startProfileAt([-100, 200], %)
|
|
/// |> line(end = [200, 0])
|
|
/// |> line(end = [0, -200])
|
|
/// |> line(end = [-200, 0])
|
|
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
/// |> close()
|
|
///
|
|
/// triangleSketch = startSketchOn(offsetPlane('XY', offset = 75))
|
|
/// |> startProfileAt([0, 125], %)
|
|
/// |> line(end = [-15, -30])
|
|
/// |> line(end = [30, 0])
|
|
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
/// |> close()
|
|
///
|
|
/// loft([squareSketch, triangleSketch])
|
|
/// ```
|
|
///
|
|
/// ```no_run
|
|
/// // Loft a square, a circle, and another circle.
|
|
/// squareSketch = startSketchOn('XY')
|
|
/// |> startProfileAt([-100, 200], %)
|
|
/// |> line(end = [200, 0])
|
|
/// |> line(end = [0, -200])
|
|
/// |> line(end = [-200, 0])
|
|
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
/// |> close()
|
|
///
|
|
/// circleSketch0 = startSketchOn(offsetPlane('XY', offset = 75))
|
|
/// |> circle( center = [0, 100], radius = 50 )
|
|
///
|
|
/// circleSketch1 = startSketchOn(offsetPlane('XY', offset = 150))
|
|
/// |> circle( center = [0, 100], radius = 20 )
|
|
///
|
|
/// loft([squareSketch, circleSketch0, circleSketch1])
|
|
/// ```
|
|
///
|
|
/// ```no_run
|
|
/// // Loft a square, a circle, and another circle with options.
|
|
/// squareSketch = startSketchOn('XY')
|
|
/// |> startProfileAt([-100, 200], %)
|
|
/// |> line(end = [200, 0])
|
|
/// |> line(end = [0, -200])
|
|
/// |> line(end = [-200, 0])
|
|
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
/// |> close()
|
|
///
|
|
/// circleSketch0 = startSketchOn(offsetPlane('XY', offset = 75))
|
|
/// |> circle( center = [0, 100], radius = 50 )
|
|
///
|
|
/// circleSketch1 = startSketchOn(offsetPlane('XY', offset = 150))
|
|
/// |> circle( center = [0, 100], radius = 20 )
|
|
///
|
|
/// loft([squareSketch, circleSketch0, circleSketch1],
|
|
/// baseCurveIndex = 0,
|
|
/// bezApproximateRational = false,
|
|
/// tolerance = 0.000001,
|
|
/// vDegree = 2,
|
|
/// )
|
|
/// ```
|
|
#[stdlib {
|
|
name = "loft",
|
|
feature_tree_operation = true,
|
|
keywords = true,
|
|
unlabeled_first = true,
|
|
args = {
|
|
sketches = {docs = "Which sketches to loft. Must include at least 2 sketches."},
|
|
v_degree = {docs = "Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified."},
|
|
bez_approximate_rational = {docs = "Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary."},
|
|
base_curve_index = {docs = "This can be set to override the automatically determined topological base curve, which is usually the first section encountered."},
|
|
tolerance = {docs = "Tolerance for the loft operation."},
|
|
}
|
|
}]
|
|
async fn inner_loft(
|
|
sketches: Vec<Sketch>,
|
|
v_degree: NonZeroU32,
|
|
bez_approximate_rational: bool,
|
|
base_curve_index: Option<u32>,
|
|
tolerance: Option<f64>,
|
|
exec_state: &mut ExecState,
|
|
args: Args,
|
|
) -> Result<Box<Solid>, KclError> {
|
|
// Make sure we have at least two sketches.
|
|
if sketches.len() < 2 {
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
message: format!(
|
|
"Loft requires at least two sketches, but only {} were provided.",
|
|
sketches.len()
|
|
),
|
|
source_ranges: vec![args.source_range],
|
|
}));
|
|
}
|
|
|
|
let id = exec_state.next_uuid();
|
|
args.batch_modeling_cmd(
|
|
id,
|
|
ModelingCmd::from(mcmd::Loft {
|
|
section_ids: sketches.iter().map(|group| group.id).collect(),
|
|
base_curve_index,
|
|
bez_approximate_rational,
|
|
tolerance: LengthUnit(tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
|
|
v_degree,
|
|
}),
|
|
)
|
|
.await?;
|
|
|
|
// Using the first sketch as the base curve, idk we might want to change this later.
|
|
let mut sketch = sketches[0].clone();
|
|
// Override its id with the loft id so we can get its faces later
|
|
sketch.id = id;
|
|
do_post_extrude(sketch, id.into(), 0.0, exec_state, args).await
|
|
}
|