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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
kittycad = { workspace = true }
|
kittycad = { workspace = true }
|
||||||
kittycad-execution-plan = { workspace = true }
|
kittycad-execution-plan = { workspace = true }
|
||||||
@ -15,6 +16,7 @@ kittycad-modeling-cmds = { workspace = true }
|
|||||||
kittycad-modeling-session = { workspace = true }
|
kittycad-modeling-session = { workspace = true }
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||||
|
twenty-twenty = "0.7.0"
|
||||||
uuid = "1.7"
|
uuid = "1.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[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(),
|
"lineTo".into(),
|
||||||
EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)),
|
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(),
|
"extrude".into(),
|
||||||
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),
|
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),
|
||||||
|
@ -76,7 +76,7 @@ impl From<ExecutionFailed> for Error {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self::Execution {
|
Self::Execution {
|
||||||
error,
|
error,
|
||||||
instruction,
|
instruction: instruction.expect("no instruction"),
|
||||||
instruction_index,
|
instruction_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,6 +262,11 @@ impl Planner {
|
|||||||
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
|
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
|
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::LineTo(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::Add(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::Close(f) => f.call(&mut ctx, args)?,
|
KclFunction::Close(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::UserDefined(f) => {
|
KclFunction::UserDefined(f) => {
|
||||||
@ -631,6 +636,11 @@ enum KclFunction {
|
|||||||
Id(native_functions::Id),
|
Id(native_functions::Id),
|
||||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||||
LineTo(native_functions::sketch::LineTo),
|
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),
|
Add(native_functions::Add),
|
||||||
UserDefined(UserDefinedFunction),
|
UserDefined(UserDefinedFunction),
|
||||||
Extrude(native_functions::sketch::Extrude),
|
Extrude(native_functions::sketch::Extrude),
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod stdlib_functions;
|
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(
|
pub fn arg_point2d(
|
||||||
arg: EpBinding,
|
arg: EpBinding,
|
||||||
fn_name: &'static str,
|
fn_name: &'static str,
|
||||||
@ -148,7 +148,7 @@ pub fn arg_point2d(
|
|||||||
arg_number: usize,
|
arg_number: usize,
|
||||||
) -> Result<Address, CompileError> {
|
) -> Result<Address, CompileError> {
|
||||||
let expected = "2D point (array with length 2)";
|
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 {
|
if elements.len() != 2 {
|
||||||
return Err(CompileError::ArgWrongType {
|
return Err(CompileError::ArgWrongType {
|
||||||
fn_name,
|
fn_name,
|
||||||
@ -165,12 +165,12 @@ pub fn arg_point2d(
|
|||||||
let start_z = start + 2;
|
let start_z = start + 2;
|
||||||
instructions.extend([
|
instructions.extend([
|
||||||
Instruction::Copy {
|
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),
|
destination: Destination::Address(start_x),
|
||||||
length: 1,
|
length: 1,
|
||||||
},
|
},
|
||||||
Instruction::Copy {
|
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),
|
destination: Destination::Address(start_y),
|
||||||
length: 1,
|
length: 1,
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use kittycad_execution_plan::{
|
use kittycad_execution_plan::{
|
||||||
api_request::ApiRequest,
|
api_request::ApiRequest,
|
||||||
sketch_types::{self, Axes, BasePath, Plane, SketchGroup},
|
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_execution_plan_traits::{Address, InMemory, Primitive, Value};
|
||||||
use kittycad_modeling_cmds::{
|
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 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};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
pub struct Close;
|
pub struct Close;
|
||||||
@ -140,25 +156,124 @@ impl Callable for LineTo {
|
|||||||
&self,
|
&self,
|
||||||
ctx: &mut crate::native_functions::Context<'_>,
|
ctx: &mut crate::native_functions::Context<'_>,
|
||||||
args: Vec<EpBinding>,
|
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> {
|
) -> Result<EvalPlan, CompileError> {
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let fn_name = "lineTo";
|
|
||||||
// Get both required params.
|
let required = 2;
|
||||||
|
|
||||||
let mut args_iter = args.into_iter();
|
let mut args_iter = args.into_iter();
|
||||||
|
|
||||||
let Some(to) = args_iter.next() else {
|
let Some(to) = args_iter.next() else {
|
||||||
return Err(CompileError::NotEnoughArgs {
|
return Err(CompileError::NotEnoughArgs {
|
||||||
fn_name: fn_name.into(),
|
fn_name: fn_name.into(),
|
||||||
required: 2,
|
required,
|
||||||
actual: 0,
|
actual: args_iter.count(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(sketch_group) = args_iter.next() else {
|
let Some(sketch_group) = args_iter.next() else {
|
||||||
return Err(CompileError::NotEnoughArgs {
|
return Err(CompileError::NotEnoughArgs {
|
||||||
fn_name: fn_name.into(),
|
fn_name: fn_name.into(),
|
||||||
required: 2,
|
required,
|
||||||
actual: 1,
|
actual: args_iter.count(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let tag = match args_iter.next() {
|
let tag = match args_iter.next() {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
@ -171,26 +286,90 @@ impl Callable for LineTo {
|
|||||||
EpBinding::Single(empty_string_addr)
|
EpBinding::Single(empty_string_addr)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check the type of required params.
|
// 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 sg = sg_binding(sketch_group, fn_name, "sketch group", 1)?;
|
||||||
let tag = single_binding(tag, fn_name, "string tag", 2)?;
|
let tag = single_binding(tag, fn_name, "string tag", 2)?;
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
|
|
||||||
// Start of the path segment (which is a straight line).
|
// Start of the path segment (which is a straight line).
|
||||||
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
|
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
|
||||||
let start_of_line = ctx.next_address.offset_by(1);
|
let start_of_line = ctx.next_address.offset_by(1);
|
||||||
|
|
||||||
// Reserve space for the line's end, and the `relative: bool` field.
|
// Reserve space for the line's end, and the `relative: bool` field.
|
||||||
ctx.next_address.offset_by(length_of_3d_point + 1);
|
ctx.next_address.offset_by(length_of_3d_point + 1);
|
||||||
let new_sg_index = ctx.assign_sketch_group();
|
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([
|
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.
|
// Append the new path segment to memory.
|
||||||
// First comes its tag.
|
// First comes its tag.
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
@ -204,7 +383,7 @@ impl Callable for LineTo {
|
|||||||
// Then its `relative` field.
|
// Then its `relative` field.
|
||||||
Instruction::SetPrimitive {
|
Instruction::SetPrimitive {
|
||||||
address: start_of_line + 1 + length_of_3d_point,
|
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.
|
// Push the path ID onto the stack.
|
||||||
Instruction::SketchGroupCopyFrom {
|
Instruction::SketchGroupCopyFrom {
|
||||||
@ -231,16 +410,159 @@ impl Callable for LineTo {
|
|||||||
data: vec![Primitive::from("ToPoint".to_owned())],
|
data: vec![Primitive::from("ToPoint".to_owned())],
|
||||||
},
|
},
|
||||||
// `BasePath::from` point.
|
// `BasePath::from` point.
|
||||||
|
// Place them in the secondary stack to prepare ToPoint structure.
|
||||||
Instruction::SketchGroupGetLastPoint {
|
Instruction::SketchGroupGetLastPoint {
|
||||||
source: sg,
|
source: sg,
|
||||||
destination: Destination::StackExtend,
|
destination: Destination::StackExtend,
|
||||||
},
|
},
|
||||||
// `BasePath::to` point.
|
]);
|
||||||
Instruction::Copy {
|
|
||||||
source: start_of_line + 1,
|
// Reserve space for the segment last point
|
||||||
length: 2,
|
let to_point_from = ctx.next_address.offset_by(2);
|
||||||
destination: Destination::StackExtend,
|
|
||||||
|
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.
|
// `BasePath::name` string.
|
||||||
Instruction::Copy {
|
Instruction::Copy {
|
||||||
source: tag,
|
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.
|
/// Write the program's plan to the KCVM debugger's normal input file.
|
||||||
#[allow(unused)]
|
#[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, _scope, _) = must_plan(kcl_program);
|
||||||
let plan_json = serde_json::to_string_pretty(&plan).unwrap();
|
let plan_json = serde_json::to_string_pretty(&plan).unwrap();
|
||||||
std::fs::write(
|
std::fs::write(path, plan_json).unwrap();
|
||||||
"/Users/adamchalmers/kc-repos/modeling-api/execution-plan-debugger/test_input.json",
|
|
||||||
plan_json,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -1069,8 +1065,6 @@ async fn stdlib_cube_partial() {
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(100.0, %)
|
|> 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))
|
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||||
.ast()
|
.ast()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1113,23 +1107,115 @@ async fn stdlib_cube_partial() {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
// use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
||||||
// let out = client
|
let out = client
|
||||||
// .unwrap()
|
.unwrap()
|
||||||
// .run_command(
|
.run_command(
|
||||||
// uuid::Uuid::new_v4().into(),
|
uuid::Uuid::new_v4().into(),
|
||||||
// each_cmd::TakeSnapshot {
|
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
|
||||||
// format: ImageFormat::Png,
|
format: ImageFormat::Png,
|
||||||
// },
|
}),
|
||||||
// )
|
)
|
||||||
// .await
|
.await
|
||||||
// .unwrap();
|
.unwrap();
|
||||||
// let out = match out {
|
|
||||||
// OkModelingCmdResponse::TakeSnapshot(b) => b,
|
let out = match out {
|
||||||
// other => panic!("wrong output: {other:?}"),
|
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
|
||||||
// };
|
other => panic!("wrong output: {other:?}"),
|
||||||
// let out: Vec<u8> = out.contents.into();
|
};
|
||||||
// std::fs::write("image.png", out).unwrap();
|
|
||||||
|
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 {
|
async fn test_client() -> Session {
|
||||||
|
Reference in New Issue
Block a user