Add support for line, xLine, yLine, xLineTo, yLineTo (#1754)
* Add support for line, xLine, yLine, xLineTo, yLineTo * Fix minor memory misalignment * Address PR comments
This commit is contained in:
1082
src/wasm-lib/Cargo.lock
generated
1082
src/wasm-lib/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ description = "A new executor for KCL which compiles to Execution Plans"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
||||
kcl-lib = { path = "../kcl" }
|
||||
kittycad = { workspace = true }
|
||||
kittycad-execution-plan = { workspace = true }
|
||||
@ -15,6 +16,7 @@ kittycad-modeling-cmds = { workspace = true }
|
||||
kittycad-modeling-session = { workspace = true }
|
||||
thiserror = "1.0.57"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
twenty-twenty = "0.7.0"
|
||||
uuid = "1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
|
BIN
src/wasm-lib/grackle/fixtures/cube_lineTo.png
Normal file
BIN
src/wasm-lib/grackle/fixtures/cube_lineTo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
BIN
src/wasm-lib/grackle/fixtures/cube_xyLine.png
Normal file
BIN
src/wasm-lib/grackle/fixtures/cube_xyLine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 KiB |
@ -113,6 +113,26 @@ impl BindingScope {
|
||||
"lineTo".into(),
|
||||
EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)),
|
||||
),
|
||||
(
|
||||
"line".into(),
|
||||
EpBinding::from(KclFunction::Line(native_functions::sketch::Line)),
|
||||
),
|
||||
(
|
||||
"xLineTo".into(),
|
||||
EpBinding::from(KclFunction::XLineTo(native_functions::sketch::XLineTo)),
|
||||
),
|
||||
(
|
||||
"xLine".into(),
|
||||
EpBinding::from(KclFunction::XLine(native_functions::sketch::XLine)),
|
||||
),
|
||||
(
|
||||
"yLineTo".into(),
|
||||
EpBinding::from(KclFunction::YLineTo(native_functions::sketch::YLineTo)),
|
||||
),
|
||||
(
|
||||
"yLine".into(),
|
||||
EpBinding::from(KclFunction::YLine(native_functions::sketch::YLine)),
|
||||
),
|
||||
(
|
||||
"extrude".into(),
|
||||
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),
|
||||
|
@ -76,7 +76,7 @@ impl From<ExecutionFailed> for Error {
|
||||
) -> Self {
|
||||
Self::Execution {
|
||||
error,
|
||||
instruction,
|
||||
instruction: instruction.expect("no instruction"),
|
||||
instruction_index,
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +262,11 @@ impl Planner {
|
||||
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Line(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::XLineTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::XLine(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::YLineTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::YLine(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Add(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Close(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::UserDefined(f) => {
|
||||
@ -631,6 +636,11 @@ enum KclFunction {
|
||||
Id(native_functions::Id),
|
||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||
LineTo(native_functions::sketch::LineTo),
|
||||
Line(native_functions::sketch::Line),
|
||||
XLineTo(native_functions::sketch::XLineTo),
|
||||
XLine(native_functions::sketch::XLine),
|
||||
YLineTo(native_functions::sketch::YLineTo),
|
||||
YLine(native_functions::sketch::YLine),
|
||||
Add(native_functions::Add),
|
||||
UserDefined(UserDefinedFunction),
|
||||
Extrude(native_functions::sketch::Extrude),
|
||||
|
@ -3,4 +3,4 @@
|
||||
pub mod helpers;
|
||||
pub mod stdlib_functions;
|
||||
|
||||
pub use stdlib_functions::{Close, Extrude, LineTo, StartSketchAt};
|
||||
pub use stdlib_functions::{Close, Extrude, Line, LineTo, StartSketchAt, XLine, XLineTo, YLine, YLineTo};
|
||||
|
@ -139,7 +139,7 @@ pub fn sequence_binding(
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a 2D point from an argument to a Cabble.
|
||||
/// Extract a 2D point from an argument to a KCL Function.
|
||||
pub fn arg_point2d(
|
||||
arg: EpBinding,
|
||||
fn_name: &'static str,
|
||||
@ -148,7 +148,7 @@ pub fn arg_point2d(
|
||||
arg_number: usize,
|
||||
) -> Result<Address, CompileError> {
|
||||
let expected = "2D point (array with length 2)";
|
||||
let elements = sequence_binding(arg, "startSketchAt", "an array of length 2", arg_number)?;
|
||||
let elements = sequence_binding(arg, fn_name, "an array of length 2", arg_number)?;
|
||||
if elements.len() != 2 {
|
||||
return Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
@ -165,12 +165,12 @@ pub fn arg_point2d(
|
||||
let start_z = start + 2;
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?,
|
||||
source: single_binding(elements[0].clone(), fn_name, "number", arg_number)?,
|
||||
destination: Destination::Address(start_x),
|
||||
length: 1,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?,
|
||||
source: single_binding(elements[1].clone(), fn_name, "number", arg_number)?,
|
||||
destination: Destination::Address(start_y),
|
||||
length: 1,
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
use kittycad_execution_plan::{
|
||||
api_request::ApiRequest,
|
||||
sketch_types::{self, Axes, BasePath, Plane, SketchGroup},
|
||||
Destination, Instruction,
|
||||
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand,
|
||||
};
|
||||
use kittycad_execution_plan_traits::{Address, InMemory, Primitive, Value};
|
||||
use kittycad_modeling_cmds::{
|
||||
@ -13,6 +13,22 @@ use uuid::Uuid;
|
||||
use super::helpers::{arg_point2d, no_arg_api_call, sg_binding, single_binding, stack_api_call};
|
||||
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum At {
|
||||
RelativeXY,
|
||||
AbsoluteXY,
|
||||
RelativeX,
|
||||
AbsoluteX,
|
||||
RelativeY,
|
||||
AbsoluteY,
|
||||
}
|
||||
|
||||
impl At {
|
||||
pub fn is_relative(&self) -> bool {
|
||||
*self == At::RelativeX || *self == At::RelativeY || *self == At::RelativeXY
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct Close;
|
||||
@ -140,25 +156,124 @@ impl Callable for LineTo {
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "lineTo", args, LineBareOptions { at: At::AbsoluteXY })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct Line;
|
||||
|
||||
impl Callable for Line {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "line", args, LineBareOptions { at: At::RelativeXY })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct XLineTo;
|
||||
|
||||
impl Callable for XLineTo {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "xLineTo", args, LineBareOptions { at: At::AbsoluteX })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct XLine;
|
||||
|
||||
impl Callable for XLine {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "xLine", args, LineBareOptions { at: At::RelativeX })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct YLineTo;
|
||||
|
||||
impl Callable for YLineTo {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "yLineTo", args, LineBareOptions { at: At::AbsoluteY })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct YLine;
|
||||
|
||||
impl Callable for YLine {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
LineBare::call(ctx, "yLine", args, LineBareOptions { at: At::RelativeY })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
/// Exposes all the possible arguments the `line` modeling command can take.
|
||||
/// Reduces code for the other line functions needed.
|
||||
/// We do not expose this to the developer since it does not align with
|
||||
/// the documentation (there is no "lineBare").
|
||||
pub struct LineBare;
|
||||
|
||||
/// Used to configure the call to handle different line variants.
|
||||
pub struct LineBareOptions {
|
||||
/// Where to start coordinates at, ex: At::RelativeXY.
|
||||
at: At,
|
||||
}
|
||||
|
||||
impl LineBare {
|
||||
fn call(
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
fn_name: &'static str,
|
||||
args: Vec<EpBinding>,
|
||||
opts: LineBareOptions,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
let mut instructions = Vec::new();
|
||||
let fn_name = "lineTo";
|
||||
// Get both required params.
|
||||
|
||||
let required = 2;
|
||||
|
||||
let mut args_iter = args.into_iter();
|
||||
|
||||
let Some(to) = args_iter.next() else {
|
||||
return Err(CompileError::NotEnoughArgs {
|
||||
fn_name: fn_name.into(),
|
||||
required: 2,
|
||||
actual: 0,
|
||||
required,
|
||||
actual: args_iter.count(),
|
||||
});
|
||||
};
|
||||
|
||||
let Some(sketch_group) = args_iter.next() else {
|
||||
return Err(CompileError::NotEnoughArgs {
|
||||
fn_name: fn_name.into(),
|
||||
required: 2,
|
||||
actual: 1,
|
||||
required,
|
||||
actual: args_iter.count(),
|
||||
});
|
||||
};
|
||||
|
||||
let tag = match args_iter.next() {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
@ -171,26 +286,90 @@ impl Callable for LineTo {
|
||||
EpBinding::Single(empty_string_addr)
|
||||
}
|
||||
};
|
||||
|
||||
// Check the type of required params.
|
||||
let to = arg_point2d(to, fn_name, &mut instructions, ctx, 0)?;
|
||||
// We don't check `to` here because it can take on either a
|
||||
// EpBinding::Sequence or EpBinding::Single.
|
||||
|
||||
let sg = sg_binding(sketch_group, fn_name, "sketch group", 1)?;
|
||||
let tag = single_binding(tag, fn_name, "string tag", 2)?;
|
||||
let id = Uuid::new_v4();
|
||||
|
||||
// Start of the path segment (which is a straight line).
|
||||
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
|
||||
let start_of_line = ctx.next_address.offset_by(1);
|
||||
|
||||
// Reserve space for the line's end, and the `relative: bool` field.
|
||||
ctx.next_address.offset_by(length_of_3d_point + 1);
|
||||
let new_sg_index = ctx.assign_sketch_group();
|
||||
|
||||
// Copy based on the options.
|
||||
match opts {
|
||||
LineBareOptions { at: At::AbsoluteXY, .. } | LineBareOptions { at: At::RelativeXY, .. } => {
|
||||
// Push the `to` 2D point onto the stack.
|
||||
let EpBinding::Sequence { elements, length_at: _ } = to.clone() else {
|
||||
return Err(CompileError::InvalidOperand("Must pass a list of length 2"));
|
||||
};
|
||||
let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() else {
|
||||
return Err(CompileError::InvalidOperand("Must pass a sequence here."));
|
||||
};
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
// X
|
||||
source: el0,
|
||||
length: 1,
|
||||
destination: Destination::StackPush,
|
||||
},
|
||||
Instruction::Copy {
|
||||
// Y
|
||||
source: el1,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::StackExtend { data: vec![0.0.into()] }, // Z
|
||||
]);
|
||||
}
|
||||
LineBareOptions { at: At::AbsoluteX, .. } | LineBareOptions { at: At::RelativeX, .. } => {
|
||||
let EpBinding::Single(addr) = to else {
|
||||
return Err(CompileError::InvalidOperand("Must pass a single value here."));
|
||||
};
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
// X
|
||||
source: addr,
|
||||
length: 1,
|
||||
destination: Destination::StackPush,
|
||||
},
|
||||
Instruction::StackExtend {
|
||||
data: vec![Primitive::from(0.0)],
|
||||
}, // Y
|
||||
Instruction::StackExtend {
|
||||
data: vec![Primitive::from(0.0)],
|
||||
}, // Z
|
||||
]);
|
||||
}
|
||||
LineBareOptions { at: At::AbsoluteY, .. } | LineBareOptions { at: At::RelativeY, .. } => {
|
||||
let EpBinding::Single(addr) = to else {
|
||||
return Err(CompileError::InvalidOperand("Must pass a single value here."));
|
||||
};
|
||||
instructions.extend([
|
||||
Instruction::StackPush {
|
||||
data: vec![Primitive::from(0.0)],
|
||||
}, // X
|
||||
Instruction::Copy {
|
||||
// Y
|
||||
source: addr,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::StackExtend {
|
||||
data: vec![Primitive::from(0.0)],
|
||||
}, // Z
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
instructions.extend([
|
||||
// Push the `to` 2D point onto the stack.
|
||||
Instruction::Copy {
|
||||
source: to,
|
||||
length: 2,
|
||||
destination: Destination::StackPush,
|
||||
},
|
||||
// Make it a 3D point.
|
||||
Instruction::StackExtend { data: vec![0.0.into()] },
|
||||
// Append the new path segment to memory.
|
||||
// First comes its tag.
|
||||
Instruction::SetPrimitive {
|
||||
@ -204,7 +383,7 @@ impl Callable for LineTo {
|
||||
// Then its `relative` field.
|
||||
Instruction::SetPrimitive {
|
||||
address: start_of_line + 1 + length_of_3d_point,
|
||||
value: false.into(),
|
||||
value: opts.at.is_relative().into(),
|
||||
},
|
||||
// Push the path ID onto the stack.
|
||||
Instruction::SketchGroupCopyFrom {
|
||||
@ -231,16 +410,159 @@ impl Callable for LineTo {
|
||||
data: vec![Primitive::from("ToPoint".to_owned())],
|
||||
},
|
||||
// `BasePath::from` point.
|
||||
// Place them in the secondary stack to prepare ToPoint structure.
|
||||
Instruction::SketchGroupGetLastPoint {
|
||||
source: sg,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
// `BasePath::to` point.
|
||||
Instruction::Copy {
|
||||
source: start_of_line + 1,
|
||||
length: 2,
|
||||
destination: Destination::StackExtend,
|
||||
]);
|
||||
|
||||
// Reserve space for the segment last point
|
||||
let to_point_from = ctx.next_address.offset_by(2);
|
||||
|
||||
instructions.extend([
|
||||
// Copy to the primary stack as well to be worked with.
|
||||
Instruction::SketchGroupGetLastPoint {
|
||||
source: sg,
|
||||
destination: Destination::Address(to_point_from),
|
||||
},
|
||||
]);
|
||||
|
||||
// `BasePath::to` point.
|
||||
|
||||
// The copy here depends on the incoming `to` data.
|
||||
// Sometimes it's a list, sometimes it's single datum.
|
||||
// And the relative/not relative matters. When relative, we need to
|
||||
// copy coords from `from` into the new `to` coord that don't change.
|
||||
// At least everything else can be built up from these "primitives".
|
||||
if let EpBinding::Sequence { elements, length_at: _ } = to.clone() {
|
||||
if let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() {
|
||||
match opts {
|
||||
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 + y2 } }
|
||||
LineBareOptions { at: At::RelativeXY, .. } => {
|
||||
instructions.extend([
|
||||
Instruction::BinaryArithmetic {
|
||||
arithmetic: BinaryArithmetic {
|
||||
operation: BinaryOperation::Add,
|
||||
operand0: Operand::Reference(to_point_from + 0),
|
||||
operand1: Operand::Reference(el0),
|
||||
},
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::BinaryArithmetic {
|
||||
arithmetic: BinaryArithmetic {
|
||||
operation: BinaryOperation::Add,
|
||||
operand0: Operand::Reference(to_point_from + 1),
|
||||
operand1: Operand::Reference(el1),
|
||||
},
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// ToPoint { from: { x1, y1 }, to: { x2, y2 } }
|
||||
LineBareOptions { at: At::AbsoluteXY, .. } => {
|
||||
// Otherwise just directly copy the new points.
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: el0,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: el1,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
_ => {
|
||||
return Err(CompileError::InvalidOperand(
|
||||
"A Sequence with At::...X or At::...Y is not valid here. Must be At::...XY.",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let EpBinding::Single(addr) = to {
|
||||
match opts {
|
||||
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 } }
|
||||
LineBareOptions { at: At::RelativeX } => {
|
||||
instructions.extend([
|
||||
Instruction::BinaryArithmetic {
|
||||
arithmetic: BinaryArithmetic {
|
||||
operation: BinaryOperation::Add,
|
||||
operand0: Operand::Reference(to_point_from + 0),
|
||||
operand1: Operand::Reference(addr),
|
||||
},
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: to_point_from + 1,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// ToPoint { from: { x1, y1 }, to: { x2, y1 } }
|
||||
LineBareOptions { at: At::AbsoluteX } => {
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: addr,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: to_point_from + 1,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// ToPoint { from: { x1, y1 }, to: { x1, y1 + y2 } }
|
||||
LineBareOptions { at: At::RelativeY } => {
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: to_point_from + 0,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::BinaryArithmetic {
|
||||
arithmetic: BinaryArithmetic {
|
||||
operation: BinaryOperation::Add,
|
||||
operand0: Operand::Reference(to_point_from + 1),
|
||||
operand1: Operand::Reference(addr),
|
||||
},
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
// ToPoint { from: { x1, y1 }, to: { x1, y2 } }
|
||||
LineBareOptions { at: At::AbsoluteY } => {
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: to_point_from + 0,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: addr,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
]);
|
||||
}
|
||||
_ => {
|
||||
return Err(CompileError::InvalidOperand(
|
||||
"A Single binding with At::...XY is not valid here.",
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(CompileError::InvalidOperand(
|
||||
"Must be a sequence or single value binding.",
|
||||
));
|
||||
}
|
||||
|
||||
instructions.extend([
|
||||
// `BasePath::name` string.
|
||||
Instruction::Copy {
|
||||
source: tag,
|
||||
|
@ -1048,14 +1048,10 @@ fn store_object_with_array_property() {
|
||||
|
||||
/// Write the program's plan to the KCVM debugger's normal input file.
|
||||
#[allow(unused)]
|
||||
fn kcvm_dbg(kcl_program: &str) {
|
||||
fn kcvm_dbg(kcl_program: &str, path: &str) {
|
||||
let (plan, _scope, _) = must_plan(kcl_program);
|
||||
let plan_json = serde_json::to_string_pretty(&plan).unwrap();
|
||||
std::fs::write(
|
||||
"/Users/adamchalmers/kc-repos/modeling-api/execution-plan-debugger/test_input.json",
|
||||
plan_json,
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(path, plan_json).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@ -1069,8 +1065,6 @@ async fn stdlib_cube_partial() {
|
||||
|> close(%)
|
||||
|> extrude(100.0, %)
|
||||
"#;
|
||||
let (_plan, _scope, last_address) = must_plan(program);
|
||||
assert_eq!(last_address, Address::ZERO + 66);
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
.ast()
|
||||
.unwrap();
|
||||
@ -1113,23 +1107,115 @@ async fn stdlib_cube_partial() {
|
||||
},
|
||||
]
|
||||
);
|
||||
// use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
||||
// let out = client
|
||||
// .unwrap()
|
||||
// .run_command(
|
||||
// uuid::Uuid::new_v4().into(),
|
||||
// each_cmd::TakeSnapshot {
|
||||
// format: ImageFormat::Png,
|
||||
// },
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let out = match out {
|
||||
// OkModelingCmdResponse::TakeSnapshot(b) => b,
|
||||
// other => panic!("wrong output: {other:?}"),
|
||||
// };
|
||||
// let out: Vec<u8> = out.contents.into();
|
||||
// std::fs::write("image.png", out).unwrap();
|
||||
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
||||
let out = client
|
||||
.unwrap()
|
||||
.run_command(
|
||||
uuid::Uuid::new_v4().into(),
|
||||
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
|
||||
format: ImageFormat::Png,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let out = match out {
|
||||
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
|
||||
other => panic!("wrong output: {other:?}"),
|
||||
};
|
||||
|
||||
use image::io::Reader as ImageReader;
|
||||
let img = ImageReader::new(std::io::Cursor::new(out))
|
||||
.with_guessed_format()
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image("fixtures/cube_lineTo.png", &img, 0.9999);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stdlib_cube_xline_yline() {
|
||||
let program = r#"
|
||||
let cube = startSketchAt([0.0, 0.0], "adam")
|
||||
|> xLine(210.0, %, "side0")
|
||||
|> yLine(210.0, %, "side1")
|
||||
|> xLine(-210.0, %, "side2")
|
||||
|> yLine(-210.0, %, "side3")
|
||||
|> close(%)
|
||||
|> extrude(100.0, %)
|
||||
"#;
|
||||
kcvm_dbg(
|
||||
program,
|
||||
"/home/lee/Code/Zoo/modeling-api/execution-plan-debugger/cube_xyline.json",
|
||||
);
|
||||
let (_plan, _scope, _last_address) = must_plan(program);
|
||||
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
.ast()
|
||||
.unwrap();
|
||||
let mut client = Some(test_client().await);
|
||||
let mem = match crate::execute(ast, &mut client).await {
|
||||
Ok(mem) => mem,
|
||||
Err(e) => panic!("{e}"),
|
||||
};
|
||||
let sg = &mem.sketch_groups.last().unwrap();
|
||||
assert_eq!(
|
||||
sg.path_rest,
|
||||
vec![
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 0.0, y: 0.0 },
|
||||
to: Point2d { x: 210.0, y: 0.0 },
|
||||
name: "side0".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 210.0, y: 0.0 },
|
||||
to: Point2d { x: 210.0, y: 210.0 },
|
||||
name: "side1".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 210.0, y: 210.0 },
|
||||
to: Point2d { x: 0.0, y: 210.0 },
|
||||
name: "side2".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 0.0, y: 210.0 },
|
||||
to: Point2d { x: 0.0, y: 0.0 },
|
||||
name: "side3".into(),
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
||||
let out = client
|
||||
.unwrap()
|
||||
.run_command(
|
||||
uuid::Uuid::new_v4().into(),
|
||||
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
|
||||
format: ImageFormat::Png,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let out = match out {
|
||||
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
|
||||
other => panic!("wrong output: {other:?}"),
|
||||
};
|
||||
|
||||
use image::io::Reader as ImageReader;
|
||||
let img = ImageReader::new(std::io::Cursor::new(out))
|
||||
.with_guessed_format()
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image("fixtures/cube_xyLine.png", &img, 0.9999);
|
||||
}
|
||||
|
||||
async fn test_client() -> Session {
|
||||
|
Reference in New Issue
Block a user