2025-03-24 21:55:24 +13:00
use std ::num ::NonZeroU32 ;
2024-07-19 20:30:13 -05:00
2024-08-21 12:12:56 -07:00
use anyhow ::Result ;
2025-02-21 12:36:21 +13:00
use schemars ::JsonSchema ;
2025-04-07 16:13:15 +12:00
use serde ::Serialize ;
2024-07-29 13:18:55 -07:00
2025-06-10 21:30:48 -04:00
use super ::fillet ::EdgeReference ;
2025-05-19 15:11:35 -07:00
pub use crate ::execution ::fn_call ::Args ;
2024-07-16 07:45:43 -05:00
use crate ::{
2025-06-26 17:02:54 -05:00
ModuleId ,
2024-07-16 07:45:43 -05:00
errors ::{ KclError , KclErrorDetails } ,
2024-12-07 07:16:04 +13:00
execution ::{
2025-05-19 16:50:15 +12:00
ExecState , ExtrudeSurface , Helix , KclObjectFields , KclValue , Metadata , PlaneInfo , Sketch , SketchSurface , Solid ,
TagIdentifier ,
2025-06-26 17:02:54 -05:00
kcl_value ::FunctionSource ,
types ::{ NumericType , PrimitiveType , RuntimeType , UnitAngle , UnitLen , UnitType } ,
2024-07-16 07:45:43 -05:00
} ,
2024-12-05 17:56:49 +13:00
parsing ::ast ::types ::TagNode ,
2024-12-03 16:39:51 +13:00
source_range ::SourceRange ,
2025-03-13 21:59:39 -07:00
std ::{
shapes ::{ PolygonType , SketchOrSurface } ,
sketch ::FaceTag ,
sweep ::SweepPath ,
} ,
2024-07-16 07:45:43 -05:00
} ;
2025-04-17 17:22:19 -07:00
const ERROR_STRING_SKETCH_TO_SOLID_HELPER : & str =
" You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve` " ;
2025-04-07 16:13:15 +12:00
#[ derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS) ]
2025-02-21 12:36:21 +13:00
#[ ts(export) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct TyF64 {
pub n : f64 ,
pub ty : NumericType ,
}
impl TyF64 {
pub fn new ( n : f64 , ty : NumericType ) -> Self {
Self { n , ty }
}
2025-04-23 10:58:35 +12:00
pub fn to_mm ( & self ) -> f64 {
self . to_length_units ( UnitLen ::Mm )
}
pub fn to_length_units ( & self , units : UnitLen ) -> f64 {
let len = match & self . ty {
NumericType ::Default { len , .. } = > * len ,
NumericType ::Known ( UnitType ::Length ( len ) ) = > * len ,
t = > unreachable! ( " expected length, found {t:?} " ) ,
} ;
2025-05-16 10:58:21 +12:00
debug_assert_ne! ( len , UnitLen ::Unknown ) ;
2025-04-23 10:58:35 +12:00
len . adjust_to ( self . n , units ) . 0
}
pub fn to_degrees ( & self ) -> f64 {
let angle = match self . ty {
NumericType ::Default { angle , .. } = > angle ,
NumericType ::Known ( UnitType ::Angle ( angle ) ) = > angle ,
_ = > unreachable! ( ) ,
} ;
2025-05-16 10:58:21 +12:00
debug_assert_ne! ( angle , UnitAngle ::Unknown ) ;
2025-04-23 10:58:35 +12:00
angle . adjust_to ( self . n , UnitAngle ::Degrees ) . 0
}
2025-04-30 12:40:11 +12:00
pub fn to_radians ( & self ) -> f64 {
let angle = match self . ty {
NumericType ::Default { angle , .. } = > angle ,
NumericType ::Known ( UnitType ::Angle ( angle ) ) = > angle ,
_ = > unreachable! ( ) ,
} ;
2025-05-16 10:58:21 +12:00
debug_assert_ne! ( angle , UnitAngle ::Unknown ) ;
2025-04-30 12:40:11 +12:00
angle . adjust_to ( self . n , UnitAngle ::Radians ) . 0
}
2025-02-21 12:36:21 +13:00
pub fn count ( n : f64 ) -> Self {
Self {
n ,
ty : NumericType ::count ( ) ,
}
}
2025-04-07 16:13:15 +12:00
pub fn map_value ( mut self , n : f64 ) -> Self {
2025-02-21 12:36:21 +13:00
self . n = n ;
self
}
}
2025-03-31 18:02:48 -07:00
impl JsonSchema for TyF64 {
fn schema_name ( ) -> String {
" TyF64 " . to_string ( )
}
2025-06-26 17:02:54 -05:00
fn json_schema ( r#gen : & mut schemars ::r#gen ::SchemaGenerator ) -> schemars ::schema ::Schema {
r#gen . subschema_for ::< f64 > ( )
2025-03-31 18:02:48 -07:00
}
}
2024-07-16 07:45:43 -05:00
impl Args {
2025-06-06 10:45:58 +12:00
pub ( crate ) fn get_kw_arg_opt < T > (
2025-04-03 22:44:52 +13:00
& self ,
label : & str ,
ty : & RuntimeType ,
exec_state : & mut ExecState ,
) -> Result < Option < T > , KclError >
where
T : for < ' a > FromKclValue < ' a > ,
{
2025-06-05 07:41:01 +12:00
match self . kw_args . labeled . get ( label ) {
None = > return Ok ( None ) ,
Some ( a ) = > {
if let KclValue ::KclNone { .. } = & a . value {
return Ok ( None ) ;
}
}
}
2025-04-03 22:44:52 +13:00
2025-06-06 10:45:58 +12:00
self . get_kw_arg ( label , ty , exec_state ) . map ( Some )
2025-04-03 22:44:52 +13:00
}
2025-06-06 10:45:58 +12:00
pub ( crate ) fn get_kw_arg < T > ( & self , label : & str , ty : & RuntimeType , exec_state : & mut ExecState ) -> Result < T , KclError >
2025-03-19 19:26:57 -07:00
where
T : for < ' a > FromKclValue < ' a > ,
{
let Some ( arg ) = self . kw_args . labeled . get ( label ) else {
2025-06-02 13:30:57 -05:00
return Err ( KclError ::new_semantic ( KclErrorDetails ::new (
2025-06-05 07:41:01 +12:00
format! ( " This function requires a keyword argument ` {label} ` " ) ,
2025-05-19 14:13:10 -04:00
vec! [ self . source_range ] ,
) ) ) ;
2025-03-19 19:26:57 -07:00
} ;
2025-05-21 17:22:30 -04:00
let arg = arg . value . coerce ( ty , true , exec_state ) . map_err ( | _ | {
2025-05-14 10:04:51 -04:00
let actual_type = arg . value . principal_type ( ) ;
let actual_type_name = actual_type
. as_ref ( )
. map ( | t | t . to_string ( ) )
. unwrap_or_else ( | | arg . value . human_friendly_type ( ) . to_owned ( ) ) ;
2025-03-19 19:26:57 -07:00
let msg_base = format! (
2025-06-05 07:41:01 +12:00
" This function expected its `{label}` argument to be {} but it's actually of type {actual_type_name} " ,
2025-03-19 19:26:57 -07:00
ty . human_friendly_type ( ) ,
) ;
2025-05-14 10:04:51 -04:00
let suggestion = match ( ty , actual_type ) {
( RuntimeType ::Primitive ( PrimitiveType ::Solid ) , Some ( RuntimeType ::Primitive ( PrimitiveType ::Sketch ) ) ) = > {
Some ( ERROR_STRING_SKETCH_TO_SOLID_HELPER )
}
( RuntimeType ::Array ( t , _ ) , Some ( RuntimeType ::Primitive ( PrimitiveType ::Sketch ) ) )
if * * t = = RuntimeType ::Primitive ( PrimitiveType ::Solid ) = >
{
2025-04-17 17:22:19 -07:00
Some ( ERROR_STRING_SKETCH_TO_SOLID_HELPER )
}
2025-03-19 19:26:57 -07:00
_ = > None ,
} ;
2025-04-17 17:22:19 -07:00
let mut message = match suggestion {
2025-03-19 19:26:57 -07:00
None = > msg_base ,
Some ( sugg ) = > format! ( " {msg_base} . {sugg} " ) ,
} ;
2025-06-13 02:20:04 +12:00
if message . contains ( " one or more Solids or ImportedGeometry but it's actually of type Sketch " ) {
2025-04-17 17:22:19 -07:00
message = format! ( " {message} . {ERROR_STRING_SKETCH_TO_SOLID_HELPER} " ) ;
}
2025-06-02 13:30:57 -05:00
KclError ::new_semantic ( KclErrorDetails ::new ( message , arg . source_ranges ( ) ) )
2025-03-19 19:26:57 -07:00
} ) ? ;
2025-06-13 14:02:14 -04:00
T ::from_kcl_val ( & arg ) . ok_or_else ( | | {
KclError ::new_internal ( KclErrorDetails ::new (
format! ( " Mismatch between type coercion and value extraction (this isn't your fault). \n To assist in bug-reporting, expected type: {ty:?} ; actual value: {arg:?} " ) ,
vec! [ self . source_range ] ,
) )
} )
2025-03-19 19:26:57 -07:00
}
2025-03-12 11:24:27 -05:00
/// Get a labelled keyword arg, check it's an array, and return all items in the array
/// plus their source range.
2025-05-29 16:48:47 +12:00
pub ( crate ) fn kw_arg_edge_array_and_source (
& self ,
label : & str ,
) -> Result < Vec < ( EdgeReference , SourceRange ) > , KclError > {
2025-03-12 11:24:27 -05:00
let Some ( arg ) = self . kw_args . labeled . get ( label ) else {
2025-06-02 13:30:57 -05:00
let err = KclError ::new_semantic ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " This function requires a keyword argument ' {label} ' " ) ,
vec! [ self . source_range ] ,
) ) ;
2025-03-12 11:24:27 -05:00
return Err ( err ) ;
} ;
2025-05-28 16:29:23 +12:00
arg . value
. clone ( )
. into_array ( )
2025-03-12 11:24:27 -05:00
. iter ( )
. map ( | item | {
let source = SourceRange ::from ( item ) ;
let val = FromKclValue ::from_kcl_val ( item ) . ok_or_else ( | | {
2025-06-02 13:30:57 -05:00
KclError ::new_semantic ( KclErrorDetails ::new (
2025-05-29 16:48:47 +12:00
format! ( " Expected an Edge but found {} " , arg . value . human_friendly_type ( ) ) ,
2025-05-19 14:13:10 -04:00
arg . source_ranges ( ) ,
) )
2025-03-12 11:24:27 -05:00
} ) ? ;
Ok ( ( val , source ) )
} )
. collect ::< Result < Vec < _ > , _ > > ( )
}
2025-05-28 16:29:23 +12:00
pub ( crate ) fn get_unlabeled_kw_arg_array_and_type (
& self ,
label : & str ,
exec_state : & mut ExecState ,
) -> Result < ( Vec < KclValue > , RuntimeType ) , KclError > {
2025-06-06 10:45:58 +12:00
let value = self . get_unlabeled_kw_arg ( label , & RuntimeType ::any_array ( ) , exec_state ) ? ;
2025-05-28 16:29:23 +12:00
Ok ( match value {
KclValue ::HomArray { value , ty } = > ( value , ty ) ,
KclValue ::Tuple { value , .. } = > ( value , RuntimeType ::any ( ) ) ,
val = > ( vec! [ val ] , RuntimeType ::any ( ) ) ,
} )
}
2025-03-17 17:57:26 +13:00
/// Get the unlabeled keyword argument. If not set, returns Err. If it
/// can't be converted to the given type, returns Err.
2025-06-06 10:45:58 +12:00
pub ( crate ) fn get_unlabeled_kw_arg < T > (
2025-03-17 17:57:26 +13:00
& self ,
label : & str ,
ty : & RuntimeType ,
exec_state : & mut ExecState ,
) -> Result < T , KclError >
where
T : for < ' a > FromKclValue < ' a > ,
{
let arg = self
. unlabeled_kw_arg_unconverted ( )
2025-06-02 13:30:57 -05:00
. ok_or ( KclError ::new_semantic ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " This function requires a value for the special unlabeled first parameter, ' {label} ' " ) ,
vec! [ self . source_range ] ,
) ) ) ? ;
2025-03-17 17:57:26 +13:00
2025-05-21 17:22:30 -04:00
let arg = arg . value . coerce ( ty , true , exec_state ) . map_err ( | _ | {
2025-05-14 10:04:51 -04:00
let actual_type = arg . value . principal_type ( ) ;
let actual_type_name = actual_type
. as_ref ( )
. map ( | t | t . to_string ( ) )
. unwrap_or_else ( | | arg . value . human_friendly_type ( ) . to_owned ( ) ) ;
2025-03-17 17:57:26 +13:00
let msg_base = format! (
" This function expected the input argument to be {} but it's actually of type {actual_type_name} " ,
ty . human_friendly_type ( ) ,
) ;
2025-05-14 10:04:51 -04:00
let suggestion = match ( ty , actual_type ) {
( RuntimeType ::Primitive ( PrimitiveType ::Solid ) , Some ( RuntimeType ::Primitive ( PrimitiveType ::Sketch ) ) ) = > {
Some ( ERROR_STRING_SKETCH_TO_SOLID_HELPER )
}
( RuntimeType ::Array ( ty , _ ) , Some ( RuntimeType ::Primitive ( PrimitiveType ::Sketch ) ) )
if * * ty = = RuntimeType ::Primitive ( PrimitiveType ::Solid ) = >
{
2025-04-17 17:22:19 -07:00
Some ( ERROR_STRING_SKETCH_TO_SOLID_HELPER )
}
2025-03-10 22:53:16 -05:00
_ = > None ,
} ;
2025-04-17 17:22:19 -07:00
let mut message = match suggestion {
2025-03-10 22:53:16 -05:00
None = > msg_base ,
Some ( sugg ) = > format! ( " {msg_base} . {sugg} " ) ,
} ;
2025-04-17 17:22:19 -07:00
2025-06-13 02:20:04 +12:00
if message . contains ( " one or more Solids or ImportedGeometry but it's actually of type Sketch " ) {
2025-04-17 17:22:19 -07:00
message = format! ( " {message} . {ERROR_STRING_SKETCH_TO_SOLID_HELPER} " ) ;
}
2025-06-02 13:30:57 -05:00
KclError ::new_semantic ( KclErrorDetails ::new ( message , arg . source_ranges ( ) ) )
2025-03-17 17:57:26 +13:00
} ) ? ;
2025-05-10 13:00:14 -07:00
T ::from_kcl_val ( & arg ) . ok_or_else ( | | {
2025-06-02 13:30:57 -05:00
KclError ::new_internal ( KclErrorDetails ::new (
2025-05-28 16:29:23 +12:00
format! ( " Mismatch between type coercion and value extraction (this isn't your fault). \n To assist in bug-reporting, expected type: {ty:?} ; actual value: {arg:?} " ) ,
2025-05-19 14:13:10 -04:00
vec! [ self . source_range ] ,
) )
2025-05-10 13:00:14 -07:00
} )
2024-12-05 14:27:51 -06:00
}
2025-06-10 21:30:48 -04:00
// TODO: Move this to the modeling module.
2024-09-16 15:10:33 -04:00
fn get_tag_info_from_memory < ' a , ' e > (
2024-07-27 22:56:46 -07:00
& ' a self ,
2024-09-16 15:10:33 -04:00
exec_state : & ' e mut ExecState ,
2024-07-27 22:56:46 -07:00
tag : & ' a TagIdentifier ,
2024-12-07 07:16:04 +13:00
) -> Result < & ' e crate ::execution ::TagEngineInfo , KclError > {
2025-03-17 12:28:51 +13:00
if let ( epoch , KclValue ::TagIdentifier ( t ) ) =
exec_state . stack ( ) . get_from_call_stack ( & tag . value , self . source_range ) ?
{
let info = t . get_info ( epoch ) . ok_or_else ( | | {
2025-06-02 13:30:57 -05:00
KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Tag ` {} ` does not have engine info " , tag . value ) ,
vec! [ self . source_range ] ,
) )
2025-03-17 12:28:51 +13:00
} ) ? ;
Ok ( info )
2024-07-27 22:56:46 -07:00
} else {
2025-06-02 13:30:57 -05:00
Err ( KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Tag ` {} ` does not exist " , tag . value ) ,
vec! [ self . source_range ] ,
) ) )
2024-07-27 22:56:46 -07:00
}
}
2025-06-10 21:30:48 -04:00
// TODO: Move this to the modeling module.
2024-09-16 15:10:33 -04:00
pub ( crate ) fn get_tag_engine_info < ' a , ' e > (
2024-07-27 22:56:46 -07:00
& ' a self ,
2024-09-16 15:10:33 -04:00
exec_state : & ' e mut ExecState ,
2024-07-27 22:56:46 -07:00
tag : & ' a TagIdentifier ,
2024-12-07 07:16:04 +13:00
) -> Result < & ' a crate ::execution ::TagEngineInfo , KclError >
2024-09-16 15:10:33 -04:00
where
' e : ' a ,
{
2025-03-17 12:28:51 +13:00
if let Some ( info ) = tag . get_cur_info ( ) {
2024-07-27 22:56:46 -07:00
return Ok ( info ) ;
}
2024-09-16 15:10:33 -04:00
self . get_tag_info_from_memory ( exec_state , tag )
2024-07-27 22:56:46 -07:00
}
2025-06-10 21:30:48 -04:00
// TODO: Move this to the modeling module.
2024-09-16 15:10:33 -04:00
fn get_tag_engine_info_check_surface < ' a , ' e > (
2024-07-27 22:56:46 -07:00
& ' a self ,
2024-09-16 15:10:33 -04:00
exec_state : & ' e mut ExecState ,
2024-07-27 22:56:46 -07:00
tag : & ' a TagIdentifier ,
2024-12-07 07:16:04 +13:00
) -> Result < & ' a crate ::execution ::TagEngineInfo , KclError >
2024-09-16 15:10:33 -04:00
where
' e : ' a ,
{
2025-03-17 12:28:51 +13:00
if let Some ( info ) = tag . get_cur_info ( ) {
2024-10-28 20:20:45 -04:00
if info . surface . is_some ( ) {
2024-07-27 22:56:46 -07:00
return Ok ( info ) ;
}
}
2024-09-16 15:10:33 -04:00
self . get_tag_info_from_memory ( exec_state , tag )
2024-07-27 22:56:46 -07:00
}
2025-05-11 23:57:31 -04:00
pub ( crate ) fn make_kcl_val_from_point ( & self , p : [ f64 ; 2 ] , ty : NumericType ) -> Result < KclValue , KclError > {
2024-11-14 17:27:19 -06:00
let meta = Metadata {
source_range : self . source_range ,
} ;
let x = KclValue ::Number {
2025-05-11 23:57:31 -04:00
value : p [ 0 ] ,
2024-11-14 17:27:19 -06:00
meta : vec ! [ meta ] ,
2025-07-01 12:42:12 -05:00
ty ,
2024-11-14 17:27:19 -06:00
} ;
let y = KclValue ::Number {
2025-05-11 23:57:31 -04:00
value : p [ 1 ] ,
2024-11-14 17:27:19 -06:00
meta : vec ! [ meta ] ,
2025-07-01 12:42:12 -05:00
ty ,
2024-11-14 17:27:19 -06:00
} ;
2025-05-11 23:57:31 -04:00
let ty = RuntimeType ::Primitive ( PrimitiveType ::Number ( ty ) ) ;
Ok ( KclValue ::HomArray { value : vec ! [ x , y ] , ty } )
2024-07-16 07:45:43 -05:00
}
2025-04-07 16:13:15 +12:00
pub ( super ) fn make_user_val_from_f64_with_type ( & self , f : TyF64 ) -> KclValue {
2025-02-14 13:03:23 +13:00
KclValue ::from_number_with_type (
2025-02-21 12:36:21 +13:00
f . n ,
f . ty ,
2025-02-14 13:03:23 +13:00
vec! [ Metadata {
source_range : self . source_range ,
} ] ,
)
}
2025-06-10 21:30:48 -04:00
// TODO: Move this to the modeling module.
2024-07-28 00:30:04 -07:00
pub ( crate ) async fn get_adjacent_face_to_tag (
2024-07-16 07:45:43 -05:00
& self ,
2024-09-16 15:10:33 -04:00
exec_state : & mut ExecState ,
2024-07-16 07:45:43 -05:00
tag : & TagIdentifier ,
must_be_planar : bool ,
) -> Result < uuid ::Uuid , KclError > {
if tag . value . is_empty ( ) {
2025-06-02 13:30:57 -05:00
return Err ( KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
" Expected a non-empty tag for the face " . to_string ( ) ,
vec! [ self . source_range ] ,
) ) ) ;
2024-07-16 07:45:43 -05:00
}
2024-09-16 15:10:33 -04:00
let engine_info = self . get_tag_engine_info_check_surface ( exec_state , tag ) ? ;
2024-07-27 22:56:46 -07:00
2024-10-28 20:20:45 -04:00
let surface = engine_info . surface . as_ref ( ) . ok_or_else ( | | {
2025-06-02 13:30:57 -05:00
KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Tag ` {} ` does not have a surface " , tag . value ) ,
vec! [ self . source_range ] ,
) )
2024-07-27 22:56:46 -07:00
} ) ? ;
if let Some ( face_from_surface ) = match surface {
ExtrudeSurface ::ExtrudePlane ( extrude_plane ) = > {
if let Some ( plane_tag ) = & extrude_plane . tag {
if plane_tag . name = = tag . value {
Some ( Ok ( extrude_plane . face_id ) )
2024-07-16 07:45:43 -05:00
} else {
None
}
2024-07-27 22:56:46 -07:00
} else {
None
2024-07-16 07:45:43 -05:00
}
2024-07-27 22:56:46 -07:00
}
// The must be planar check must be called before the arc check.
2025-06-02 13:30:57 -05:00
ExtrudeSurface ::ExtrudeArc ( _ ) if must_be_planar = > Some ( Err ( KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Tag ` {} ` is a non-planar surface " , tag . value ) ,
vec! [ self . source_range ] ,
) ) ) ) ,
2024-07-27 22:56:46 -07:00
ExtrudeSurface ::ExtrudeArc ( extrude_arc ) = > {
if let Some ( arc_tag ) = & extrude_arc . tag {
if arc_tag . name = = tag . value {
Some ( Ok ( extrude_arc . face_id ) )
2024-07-16 07:45:43 -05:00
} else {
None
}
2024-07-27 22:56:46 -07:00
} else {
None
2024-07-16 07:45:43 -05:00
}
2024-07-27 22:56:46 -07:00
}
2024-07-28 00:30:04 -07:00
ExtrudeSurface ::Chamfer ( chamfer ) = > {
if let Some ( chamfer_tag ) = & chamfer . tag {
if chamfer_tag . name = = tag . value {
Some ( Ok ( chamfer . face_id ) )
} else {
None
}
2024-07-16 07:45:43 -05:00
} else {
None
}
}
2024-07-28 00:30:04 -07:00
// The must be planar check must be called before the fillet check.
2025-06-02 13:30:57 -05:00
ExtrudeSurface ::Fillet ( _ ) if must_be_planar = > Some ( Err ( KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Tag ` {} ` is a non-planar surface " , tag . value ) ,
vec! [ self . source_range ] ,
) ) ) ) ,
2024-07-28 00:30:04 -07:00
ExtrudeSurface ::Fillet ( fillet ) = > {
if let Some ( fillet_tag ) = & fillet . tag {
if fillet_tag . name = = tag . value {
Some ( Ok ( fillet . face_id ) )
} else {
None
}
} else {
None
}
}
} {
return face_from_surface ;
}
2024-07-16 07:45:43 -05:00
// If we still haven't found the face, return an error.
2025-06-02 13:30:57 -05:00
Err ( KclError ::new_type ( KclErrorDetails ::new (
2025-05-19 14:13:10 -04:00
format! ( " Expected a face with the tag ` {} ` " , tag . value ) ,
vec! [ self . source_range ] ,
) ) )
2024-07-16 07:45:43 -05:00
}
}
2024-07-19 20:30:13 -05:00
2024-08-12 16:53:24 -05:00
/// Types which impl this trait can be extracted from a `KclValue`.
pub trait FromKclValue < ' a > : Sized {
/// Try to convert a KclValue into this type.
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > ;
2024-07-19 20:30:13 -05:00
}
2024-10-30 16:52:17 -04:00
impl < ' a > FromKclValue < ' a > for TagNode {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-07-19 20:30:13 -05:00
arg . get_tag_declarator ( ) . ok ( )
}
}
2024-08-12 16:53:24 -05:00
impl < ' a > FromKclValue < ' a > for TagIdentifier {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-07-19 20:30:13 -05:00
arg . get_tag_identifier ( ) . ok ( )
}
}
2025-03-19 15:12:27 -07:00
impl < ' a > FromKclValue < ' a > for Vec < TagIdentifier > {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
KclValue ::HomArray { value , .. } = > {
let tags = value . iter ( ) . map ( | v | v . get_tag_identifier ( ) . unwrap ( ) ) . collect ( ) ;
Some ( tags )
}
2025-05-11 23:57:31 -04:00
KclValue ::Tuple { value , .. } = > {
2025-03-19 15:12:27 -07:00
let tags = value . iter ( ) . map ( | v | v . get_tag_identifier ( ) . unwrap ( ) ) . collect ( ) ;
Some ( tags )
}
_ = > None ,
}
}
}
2025-04-25 19:09:03 -05:00
impl < ' a > FromKclValue < ' a > for Vec < KclValue > {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-05-28 16:29:23 +12:00
Some ( arg . clone ( ) . into_array ( ) )
2025-04-25 19:09:03 -05:00
}
}
2024-10-01 08:50:23 -05:00
impl < ' a > FromKclValue < ' a > for KclValue {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-10-01 08:50:23 -05:00
Some ( arg . clone ( ) )
}
}
2024-11-16 11:19:00 -06:00
macro_rules ! let_field_of {
// Optional field
( $obj :ident , $field :ident ? ) = > {
let $field = $obj . get ( stringify! ( $field ) ) . and_then ( FromKclValue ::from_kcl_val ) ;
2024-11-14 17:27:19 -06:00
} ;
KCL: Patterns of patterns can use the original sketch/solid as target (#5284)
Right now, if you model something like this box with a button:
<img width="413" alt="Screenshot 2025-02-06 at 3 08 03 PM" src="https://github.com/user-attachments/assets/04818a70-7cf3-4ee3-b8c5-df5959ac10db" />
Let's say you want to pattern the button, and repeat it a second time. If you try, you'll actually pattern the entire model (box + button).
<img width="486" alt="Screenshot 2025-02-06 at 3 08 52 PM" src="https://github.com/user-attachments/assets/09fc28d9-5d80-4ab3-b4dc-b8de2945fcba" />
Why? Because right now, when you sketch on a face (like the button was), both the box and the button share the same ID. All extrusions from a solid will share the same ID, because they all refer to the same composite solid.
This is helpful in some ways -- arguably the solid _is_ just one big complex shape now -- but it's not helpful in other ways. What if I want to only pattern the button? Luckily there's an original ID for the button part, which is still stored. So we just need a way to tell the pattern stdlib functions whether to use the target's main ID or its original ID. This PR adds a new optional bool, `useOriginal`, to patterns. It's false by default, to keep backwards-compatibility (make sure that old KCL code doesn't change).
This PR is based on https://github.com/KittyCAD/modeling-app/pull/3914. It's based on work Serena and I are doing to fix a bug (engine does not allow patterning a 3D solid which was sketched on a face of another solid). @gserena01 our test program is now:
```
w = 400
case = startSketchOn('XY')
|> startProfileAt([-w, -w], %)
|> line(endAbsolute = [-w, w])
|> line(endAbsolute = [w, -w])
|> line(endAbsolute = [-w, -w])
|> close()
|> extrude(length = 200)
bump1 = startSketchOn(case, 'end')
|> circle({ center = [-50, -50], radius = 40 }, %)
|> extrude(length = 20)
// We pass in "bump1" here since we want to pattern just this object on the face.
useOriginal = true
target = bump1
transform = {
axis = [1, 0, 0],
instances = 3,
distance = -100
}
patternLinear3d(transform, target, useOriginal)
```
If you change the `useOriginal = true` to `false` you can see the difference.
2025-02-06 17:46:47 -06:00
// Optional field but with a different string used as the key
( $obj :ident , $field :ident ? $key :literal ) = > {
let $field = $obj . get ( $key ) . and_then ( FromKclValue ::from_kcl_val ) ;
} ;
2024-11-16 11:19:00 -06:00
// Mandatory field, but with a different string used as the key.
( $obj :ident , $field :ident $key :literal ) = > {
let $field = $obj . get ( $key ) . and_then ( FromKclValue ::from_kcl_val ) ? ;
2024-11-14 17:27:19 -06:00
} ;
2024-11-16 11:19:00 -06:00
// Mandatory field, optionally with a type annotation
( $obj :ident , $field :ident $(, $annotation :ty ) ? ) = > {
let $field $( : $annotation ) ? = $obj . get ( stringify! ( $field ) ) . and_then ( FromKclValue ::from_kcl_val ) ? ;
2024-11-14 17:27:19 -06:00
} ;
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::Plane {
2024-11-18 16:25:25 -05:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-01-22 09:42:09 +13:00
arg . as_plane ( ) . cloned ( )
2024-11-18 16:25:25 -05:00
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::PlaneType {
2024-11-18 16:25:25 -05:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
let plane_type = match arg . as_str ( ) ? {
" XY " | " xy " = > Self ::XY ,
" XZ " | " xz " = > Self ::XZ ,
" YZ " | " yz " = > Self ::YZ ,
" Custom " = > Self ::Custom ,
_ = > return None ,
} ;
Some ( plane_type )
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for kittycad_modeling_cmds ::units ::UnitLength {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let s = arg . as_str ( ) ? ;
s . parse ( ) . ok ( )
}
}
impl < ' a > FromKclValue < ' a > for kittycad_modeling_cmds ::coord ::System {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , forward ) ;
let_field_of! ( obj , up ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { forward , up } )
}
}
impl < ' a > FromKclValue < ' a > for kittycad_modeling_cmds ::coord ::AxisDirectionPair {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , axis ) ;
let_field_of! ( obj , direction ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { axis , direction } )
}
}
impl < ' a > FromKclValue < ' a > for kittycad_modeling_cmds ::coord ::Axis {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let s = arg . as_str ( ) ? ;
match s {
" y " = > Some ( Self ::Y ) ,
" z " = > Some ( Self ::Z ) ,
_ = > None ,
}
}
}
impl < ' a > FromKclValue < ' a > for PolygonType {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let s = arg . as_str ( ) ? ;
match s {
" inscribed " = > Some ( Self ::Inscribed ) ,
_ = > Some ( Self ::Circumscribed ) ,
}
}
}
impl < ' a > FromKclValue < ' a > for kittycad_modeling_cmds ::coord ::Direction {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let s = arg . as_str ( ) ? ;
match s {
" positive " = > Some ( Self ::Positive ) ,
" negative " = > Some ( Self ::Negative ) ,
_ = > None ,
}
2025-04-23 21:26:09 -07:00
}
}
impl < ' a > FromKclValue < ' a > for crate ::execution ::Geometry {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
KclValue ::Sketch { value } = > Some ( Self ::Sketch ( * value . to_owned ( ) ) ) ,
KclValue ::Solid { value } = > Some ( Self ::Solid ( * value . to_owned ( ) ) ) ,
_ = > None ,
}
}
}
impl < ' a > FromKclValue < ' a > for crate ::execution ::GeometryWithImportedGeometry {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
KclValue ::Sketch { value } = > Some ( Self ::Sketch ( * value . to_owned ( ) ) ) ,
KclValue ::Solid { value } = > Some ( Self ::Solid ( * value . to_owned ( ) ) ) ,
KclValue ::ImportedGeometry ( value ) = > Some ( Self ::ImportedGeometry ( Box ::new ( value . clone ( ) ) ) ) ,
_ = > None ,
}
2024-11-14 17:27:19 -06:00
}
}
impl < ' a > FromKclValue < ' a > for FaceTag {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let case1 = | | match arg . as_str ( ) {
Some ( " start " | " START " ) = > Some ( Self ::StartOrEnd ( super ::sketch ::StartOrEnd ::Start ) ) ,
Some ( " end " | " END " ) = > Some ( Self ::StartOrEnd ( super ::sketch ::StartOrEnd ::End ) ) ,
_ = > None ,
} ;
let case2 = | | {
2024-11-16 11:19:00 -06:00
let tag = TagIdentifier ::from_kcl_val ( arg ) ? ;
2024-11-14 17:27:19 -06:00
Some ( Self ::Tag ( Box ::new ( tag ) ) )
} ;
case1 ( ) . or_else ( case2 )
2024-10-01 08:50:23 -05:00
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for super ::sketch ::TangentialArcData {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , radius ) ;
let_field_of! ( obj , offset ) ;
2024-11-14 17:27:19 -06:00
Some ( Self ::RadiusAndOffset { radius , offset } )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::Point3d {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
// Case 1: object with x/y/z fields
if let Some ( obj ) = arg . as_object ( ) {
2025-04-14 05:58:19 -04:00
let_field_of! ( obj , x , TyF64 ) ;
let_field_of! ( obj , y , TyF64 ) ;
let_field_of! ( obj , z , TyF64 ) ;
2025-04-29 12:24:18 +12:00
// TODO here and below we could use coercing combination.
2025-04-14 05:58:19 -04:00
let ( a , ty ) = NumericType ::combine_eq_array ( & [ x , y , z ] ) ;
return Some ( Self {
x : a [ 0 ] ,
y : a [ 1 ] ,
z : a [ 2 ] ,
units : ty . as_length ( ) . unwrap_or ( UnitLen ::Unknown ) ,
} ) ;
2024-11-14 17:27:19 -06:00
}
// Case 2: Array of 3 numbers.
2025-04-14 05:58:19 -04:00
let [ x , y , z ] : [ TyF64 ; 3 ] = FromKclValue ::from_kcl_val ( arg ) ? ;
let ( a , ty ) = NumericType ::combine_eq_array ( & [ x , y , z ] ) ;
Some ( Self {
x : a [ 0 ] ,
y : a [ 1 ] ,
z : a [ 2 ] ,
units : ty . as_length ( ) . unwrap_or ( UnitLen ::Unknown ) ,
} )
2024-11-14 17:27:19 -06:00
}
}
impl < ' a > FromKclValue < ' a > for super ::sketch ::PlaneData {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
// Case 0: actual plane
2025-01-22 09:42:09 +13:00
if let KclValue ::Plane { value } = arg {
2025-04-29 19:11:02 -07:00
return Some ( Self ::Plane ( PlaneInfo {
origin : value . info . origin ,
x_axis : value . info . x_axis ,
y_axis : value . info . y_axis ,
} ) ) ;
2024-11-14 17:27:19 -06:00
}
// Case 1: predefined plane
if let Some ( s ) = arg . as_str ( ) {
return match s {
" XY " | " xy " = > Some ( Self ::XY ) ,
" -XY " | " -xy " = > Some ( Self ::NegXY ) ,
" XZ " | " xz " = > Some ( Self ::XZ ) ,
" -XZ " | " -xz " = > Some ( Self ::NegXZ ) ,
" YZ " | " yz " = > Some ( Self ::YZ ) ,
" -YZ " | " -yz " = > Some ( Self ::NegYZ ) ,
_ = > None ,
} ;
}
// Case 2: custom plane
let obj = arg . as_object ( ) ? ;
2024-11-26 09:51:43 -06:00
let_field_of! ( obj , plane , & KclObjectFields ) ;
2025-02-27 15:58:58 +13:00
let origin = plane . get ( " origin " ) . and_then ( FromKclValue ::from_kcl_val ) ? ;
let x_axis = plane . get ( " xAxis " ) . and_then ( FromKclValue ::from_kcl_val ) ? ;
let y_axis = plane . get ( " yAxis " ) . and_then ( FromKclValue ::from_kcl_val ) ? ;
2025-04-29 19:11:02 -07:00
Some ( Self ::Plane ( PlaneInfo { origin , x_axis , y_axis } ) )
2024-11-14 17:27:19 -06:00
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::ExtrudePlane {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , face_id " faceId " ) ;
let tag = FromKclValue ::from_kcl_val ( obj . get ( " tag " ) ? ) ;
let_field_of! ( obj , geo_meta " geoMeta " ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { face_id , tag , geo_meta } )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::ExtrudeArc {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , face_id " faceId " ) ;
let tag = FromKclValue ::from_kcl_val ( obj . get ( " tag " ) ? ) ;
let_field_of! ( obj , geo_meta " geoMeta " ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { face_id , tag , geo_meta } )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::GeoMeta {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , id ) ;
let_field_of! ( obj , source_range " sourceRange " ) ;
2024-11-14 17:27:19 -06:00
Some ( Self {
id ,
metadata : Metadata { source_range } ,
} )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::ChamferSurface {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , face_id " faceId " ) ;
let tag = FromKclValue ::from_kcl_val ( obj . get ( " tag " ) ? ) ;
let_field_of! ( obj , geo_meta " geoMeta " ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { face_id , tag , geo_meta } )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::FilletSurface {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , face_id " faceId " ) ;
let tag = FromKclValue ::from_kcl_val ( obj . get ( " tag " ) ? ) ;
let_field_of! ( obj , geo_meta " geoMeta " ) ;
2024-11-14 17:27:19 -06:00
Some ( Self { face_id , tag , geo_meta } )
}
}
impl < ' a > FromKclValue < ' a > for ExtrudeSurface {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-12-07 07:16:04 +13:00
let case1 = crate ::execution ::ExtrudePlane ::from_kcl_val ;
let case2 = crate ::execution ::ExtrudeArc ::from_kcl_val ;
let case3 = crate ::execution ::ChamferSurface ::from_kcl_val ;
let case4 = crate ::execution ::FilletSurface ::from_kcl_val ;
2024-11-14 17:27:19 -06:00
case1 ( arg )
. map ( Self ::ExtrudePlane )
. or_else ( | | case2 ( arg ) . map ( Self ::ExtrudeArc ) )
. or_else ( | | case3 ( arg ) . map ( Self ::Chamfer ) )
. or_else ( | | case4 ( arg ) . map ( Self ::Fillet ) )
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::EdgeCut {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let obj = arg . as_object ( ) ? ;
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , typ " type " ) ;
let tag = Box ::new ( obj . get ( " tag " ) . and_then ( FromKclValue ::from_kcl_val ) ) ;
let_field_of! ( obj , edge_id " edgeId " ) ;
let_field_of! ( obj , id ) ;
2024-11-14 17:27:19 -06:00
match typ {
" fillet " = > {
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , radius ) ;
2024-11-14 17:27:19 -06:00
Some ( Self ::Fillet {
edge_id ,
tag ,
id ,
radius ,
} )
}
" chamfer " = > {
2024-11-16 11:19:00 -06:00
let_field_of! ( obj , length ) ;
2024-11-14 17:27:19 -06:00
Some ( Self ::Chamfer {
id ,
length ,
edge_id ,
tag ,
} )
}
_ = > None ,
}
}
}
macro_rules ! impl_from_kcl_for_vec {
( $typ :path ) = > {
impl < ' a > FromKclValue < ' a > for Vec < $typ > {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-05-28 16:29:23 +12:00
arg . clone ( )
. into_array ( )
2024-11-14 17:27:19 -06:00
. iter ( )
2024-11-16 11:19:00 -06:00
. map ( | value | FromKclValue ::from_kcl_val ( value ) )
2024-11-14 17:27:19 -06:00
. collect ::< Option < _ > > ( )
2024-07-19 20:30:13 -05:00
}
}
} ;
}
2024-11-14 17:27:19 -06:00
impl_from_kcl_for_vec! ( FaceTag ) ;
2024-12-07 07:16:04 +13:00
impl_from_kcl_for_vec! ( crate ::execution ::EdgeCut ) ;
impl_from_kcl_for_vec! ( crate ::execution ::Metadata ) ;
2024-11-14 17:27:19 -06:00
impl_from_kcl_for_vec! ( super ::fillet ::EdgeReference ) ;
impl_from_kcl_for_vec! ( ExtrudeSurface ) ;
2025-04-26 19:33:41 -04:00
impl_from_kcl_for_vec! ( TyF64 ) ;
2025-05-28 16:29:23 +12:00
impl_from_kcl_for_vec! ( Solid ) ;
impl_from_kcl_for_vec! ( Sketch ) ;
2024-07-19 20:30:13 -05:00
2024-12-03 16:39:51 +13:00
impl < ' a > FromKclValue < ' a > for SourceRange {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-05-11 23:57:31 -04:00
let value = match arg {
KclValue ::Tuple { value , .. } | KclValue ::HomArray { value , .. } = > value ,
_ = > {
return None ;
}
2024-12-03 16:39:51 +13:00
} ;
if value . len ( ) ! = 3 {
return None ;
}
let v0 = value . first ( ) ? ;
let v1 = value . get ( 1 ) ? ;
let v2 = value . get ( 2 ) ? ;
Some ( SourceRange ::new (
v0 . as_usize ( ) ? ,
v1 . as_usize ( ) ? ,
ModuleId ::from_usize ( v2 . as_usize ( ) ? ) ,
) )
2024-11-14 17:27:19 -06:00
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::Metadata {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
FromKclValue ::from_kcl_val ( arg ) . map ( | sr | Self { source_range : sr } )
2024-11-14 17:27:19 -06:00
}
}
2024-12-07 07:16:04 +13:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::Solid {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
arg . as_solid ( ) . cloned ( )
}
}
2025-03-18 16:36:48 -07:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::SolidOrSketchOrImportedGeometry {
2025-03-11 18:23:21 -07:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
2025-03-17 17:57:26 +13:00
KclValue ::Solid { value } = > Some ( Self ::SolidSet ( vec! [ ( * * value ) . clone ( ) ] ) ) ,
2025-03-18 16:36:48 -07:00
KclValue ::Sketch { value } = > Some ( Self ::SketchSet ( vec! [ ( * * value ) . clone ( ) ] ) ) ,
KclValue ::HomArray { value , .. } = > {
let mut solids = vec! [ ] ;
let mut sketches = vec! [ ] ;
for item in value {
match item {
KclValue ::Solid { value } = > solids . push ( ( * * value ) . clone ( ) ) ,
KclValue ::Sketch { value } = > sketches . push ( ( * * value ) . clone ( ) ) ,
_ = > return None ,
}
}
if ! solids . is_empty ( ) {
Some ( Self ::SolidSet ( solids ) )
} else {
Some ( Self ::SketchSet ( sketches ) )
}
}
2025-03-11 18:23:21 -07:00
KclValue ::ImportedGeometry ( value ) = > Some ( Self ::ImportedGeometry ( Box ::new ( value . clone ( ) ) ) ) ,
_ = > None ,
}
}
}
2025-04-17 17:22:19 -07:00
impl < ' a > FromKclValue < ' a > for crate ::execution ::SolidOrImportedGeometry {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
KclValue ::Solid { value } = > Some ( Self ::SolidSet ( vec! [ ( * * value ) . clone ( ) ] ) ) ,
KclValue ::HomArray { value , .. } = > {
let mut solids = vec! [ ] ;
for item in value {
match item {
KclValue ::Solid { value } = > solids . push ( ( * * value ) . clone ( ) ) ,
_ = > return None ,
}
}
Some ( Self ::SolidSet ( solids ) )
}
KclValue ::ImportedGeometry ( value ) = > Some ( Self ::ImportedGeometry ( Box ::new ( value . clone ( ) ) ) ) ,
_ = > None ,
}
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for super ::sketch ::SketchData {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-18 16:25:25 -05:00
// Order is critical since PlaneData is a subset of Plane.
2024-12-07 07:16:04 +13:00
let case1 = crate ::execution ::Plane ::from_kcl_val ;
2024-11-18 16:25:25 -05:00
let case2 = super ::sketch ::PlaneData ::from_kcl_val ;
2024-12-07 07:16:04 +13:00
let case3 = crate ::execution ::Solid ::from_kcl_val ;
2025-03-17 17:57:26 +13:00
let case4 = < Vec < Solid > > ::from_kcl_val ;
2024-11-14 17:27:19 -06:00
case1 ( arg )
2024-11-18 16:25:25 -05:00
. map ( Box ::new )
2024-11-14 17:27:19 -06:00
. map ( Self ::Plane )
2024-11-18 16:25:25 -05:00
. or_else ( | | case2 ( arg ) . map ( Self ::PlaneOrientation ) )
. or_else ( | | case3 ( arg ) . map ( Box ::new ) . map ( Self ::Solid ) )
2025-03-17 17:57:26 +13:00
. or_else ( | | case4 ( arg ) . map ( | v | Box ::new ( v [ 0 ] . clone ( ) ) ) . map ( Self ::Solid ) )
2024-11-14 17:27:19 -06:00
}
}
impl < ' a > FromKclValue < ' a > for super ::fillet ::EdgeReference {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let id = arg . as_uuid ( ) . map ( Self ::Uuid ) ;
2024-11-16 11:19:00 -06:00
let tag = | | TagIdentifier ::from_kcl_val ( arg ) . map ( Box ::new ) . map ( Self ::Tag ) ;
2024-11-14 17:27:19 -06:00
id . or_else ( tag )
}
}
2025-01-07 19:10:53 -08:00
impl < ' a > FromKclValue < ' a > for super ::axis_or_reference ::Axis2dOrEdgeReference {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-04-03 22:44:52 +13:00
let case1 = | arg : & KclValue | {
let obj = arg . as_object ( ) ? ;
let_field_of! ( obj , direction ) ;
let_field_of! ( obj , origin ) ;
Some ( Self ::Axis { direction , origin } )
} ;
2025-01-07 19:10:53 -08:00
let case2 = super ::fillet ::EdgeReference ::from_kcl_val ;
2025-04-03 22:44:52 +13:00
case1 ( arg ) . or_else ( | | case2 ( arg ) . map ( Self ::Edge ) )
2025-01-07 19:10:53 -08:00
}
}
impl < ' a > FromKclValue < ' a > for super ::axis_or_reference ::Axis3dOrEdgeReference {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-04-03 22:44:52 +13:00
let case1 = | arg : & KclValue | {
let obj = arg . as_object ( ) ? ;
let_field_of! ( obj , direction ) ;
let_field_of! ( obj , origin ) ;
Some ( Self ::Axis { direction , origin } )
} ;
2024-11-16 11:19:00 -06:00
let case2 = super ::fillet ::EdgeReference ::from_kcl_val ;
2025-04-03 22:44:52 +13:00
case1 ( arg ) . or_else ( | | case2 ( arg ) . map ( Self ::Edge ) )
2024-11-14 17:27:19 -06:00
}
}
2025-05-18 19:25:35 -07:00
impl < ' a > FromKclValue < ' a > for super ::axis_or_reference ::Axis2dOrPoint2d {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
let case1 = | arg : & KclValue | {
let obj = arg . as_object ( ) ? ;
let_field_of! ( obj , direction ) ;
let_field_of! ( obj , origin ) ;
Some ( Self ::Axis { direction , origin } )
} ;
let case2 = < [ TyF64 ; 2 ] > ::from_kcl_val ;
case1 ( arg ) . or_else ( | | case2 ( arg ) . map ( Self ::Point ) )
}
}
impl < ' a > FromKclValue < ' a > for super ::axis_or_reference ::Axis3dOrPoint3d {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
let case1 = | arg : & KclValue | {
let obj = arg . as_object ( ) ? ;
let_field_of! ( obj , direction ) ;
let_field_of! ( obj , origin ) ;
Some ( Self ::Axis { direction , origin } )
} ;
let case2 = < [ TyF64 ; 3 ] > ::from_kcl_val ;
case1 ( arg ) . or_else ( | | case2 ( arg ) . map ( Self ::Point ) )
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for i64 {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-25 10:50:43 +13:00
match arg {
2025-02-14 13:03:23 +13:00
KclValue ::Number { value , .. } = > crate ::try_f64_to_i64 ( * value ) ,
2024-11-25 10:50:43 +13:00
_ = > None ,
}
2024-11-14 17:27:19 -06:00
}
}
2024-11-16 11:19:00 -06:00
impl < ' a > FromKclValue < ' a > for & ' a str {
2024-11-26 09:51:43 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-16 11:19:00 -06:00
let KclValue ::String { value , meta : _ } = arg else {
return None ;
} ;
Some ( value )
}
}
2024-11-26 09:51:43 -06:00
impl < ' a > FromKclValue < ' a > for & ' a KclObjectFields {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-16 11:19:00 -06:00
let KclValue ::Object { value , meta : _ } = arg else {
return None ;
} ;
Some ( value )
}
}
impl < ' a > FromKclValue < ' a > for uuid ::Uuid {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
let KclValue ::Uuid { value , meta : _ } = arg else {
return None ;
} ;
Some ( * value )
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for u32 {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-25 10:50:43 +13:00
match arg {
2025-02-14 13:03:23 +13:00
KclValue ::Number { value , .. } = > crate ::try_f64_to_u32 ( * value ) ,
2024-11-25 10:50:43 +13:00
_ = > None ,
}
2024-11-14 17:27:19 -06:00
}
}
impl < ' a > FromKclValue < ' a > for NonZeroU32 {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
u32 ::from_kcl_val ( arg ) . and_then ( | x | x . try_into ( ) . ok ( ) )
2024-11-14 17:27:19 -06:00
}
}
impl < ' a > FromKclValue < ' a > for u64 {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-25 10:50:43 +13:00
match arg {
2025-02-14 13:03:23 +13:00
KclValue ::Number { value , .. } = > crate ::try_f64_to_u64 ( * value ) ,
2024-11-25 10:50:43 +13:00
_ = > None ,
}
2024-11-14 17:27:19 -06:00
}
}
2025-04-14 05:58:19 -04:00
impl < ' a > FromKclValue < ' a > for TyF64 {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
2025-07-01 12:42:12 -05:00
KclValue ::Number { value , ty , .. } = > Some ( TyF64 ::new ( * value , * ty ) ) ,
2025-04-14 05:58:19 -04:00
_ = > None ,
}
}
}
impl < ' a > FromKclValue < ' a > for [ TyF64 ; 2 ] {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
match arg {
2025-05-11 23:57:31 -04:00
KclValue ::Tuple { value , meta : _ } | KclValue ::HomArray { value , .. } = > {
2025-04-14 05:58:19 -04:00
if value . len ( ) ! = 2 {
return None ;
}
let v0 = value . first ( ) ? ;
let v1 = value . get ( 1 ) ? ;
let array = [ v0 . as_ty_f64 ( ) ? , v1 . as_ty_f64 ( ) ? ] ;
Some ( array )
}
2025-02-14 13:03:23 +13:00
_ = > None ,
}
}
}
2025-04-14 05:58:19 -04:00
impl < ' a > FromKclValue < ' a > for [ TyF64 ; 3 ] {
2025-02-14 13:03:23 +13:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
match arg {
2025-05-11 23:57:31 -04:00
KclValue ::Tuple { value , meta : _ } | KclValue ::HomArray { value , .. } = > {
2025-04-14 05:58:19 -04:00
if value . len ( ) ! = 3 {
return None ;
}
let v0 = value . first ( ) ? ;
let v1 = value . get ( 1 ) ? ;
let v2 = value . get ( 2 ) ? ;
let array = [ v0 . as_ty_f64 ( ) ? , v1 . as_ty_f64 ( ) ? , v2 . as_ty_f64 ( ) ? ] ;
Some ( array )
}
2024-11-14 17:27:19 -06:00
_ = > None ,
}
}
}
2025-03-17 17:57:26 +13:00
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for Sketch {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let KclValue ::Sketch { value } = arg else {
return None ;
} ;
Some ( value . as_ref ( ) . to_owned ( ) )
}
}
2025-01-07 19:10:53 -08:00
impl < ' a > FromKclValue < ' a > for Helix {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-01-22 09:42:09 +13:00
let KclValue ::Helix { value } = arg else {
2025-01-07 19:10:53 -08:00
return None ;
} ;
Some ( value . as_ref ( ) . to_owned ( ) )
}
}
2025-03-17 17:57:26 +13:00
2025-01-07 19:10:53 -08:00
impl < ' a > FromKclValue < ' a > for SweepPath {
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
let case1 = Sketch ::from_kcl_val ;
2025-03-17 17:57:26 +13:00
let case2 = < Vec < Sketch > > ::from_kcl_val ;
let case3 = Helix ::from_kcl_val ;
2025-01-07 19:10:53 -08:00
case1 ( arg )
. map ( Self ::Sketch )
2025-03-17 17:57:26 +13:00
. or_else ( | | case2 ( arg ) . map ( | arg0 : Vec < Sketch > | Self ::Sketch ( arg0 [ 0 ] . clone ( ) ) ) )
. or_else ( | | case3 ( arg ) . map ( | arg0 : Helix | Self ::Helix ( Box ::new ( arg0 ) ) ) )
2025-01-07 19:10:53 -08:00
}
}
2024-11-14 17:27:19 -06:00
impl < ' a > FromKclValue < ' a > for String {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let KclValue ::String { value , meta : _ } = arg else {
return None ;
} ;
Some ( value . to_owned ( ) )
}
}
2024-12-05 17:56:49 +13:00
impl < ' a > FromKclValue < ' a > for crate ::parsing ::ast ::types ::KclNone {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let KclValue ::KclNone { value , meta : _ } = arg else {
return None ;
} ;
Some ( value . to_owned ( ) )
}
}
impl < ' a > FromKclValue < ' a > for bool {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-11-14 17:27:19 -06:00
let KclValue ::Bool { value , meta : _ } = arg else {
return None ;
} ;
Some ( * value )
}
}
2025-03-17 17:57:26 +13:00
impl < ' a > FromKclValue < ' a > for Box < Solid > {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-03-17 17:57:26 +13:00
let KclValue ::Solid { value } = arg else {
return None ;
} ;
Some ( value . to_owned ( ) )
2024-07-19 20:30:13 -05:00
}
}
2025-06-06 10:45:58 +12:00
impl < ' a > FromKclValue < ' a > for FunctionSource {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2025-06-06 10:45:58 +12:00
arg . as_function ( ) . cloned ( )
2024-07-19 20:30:13 -05:00
}
}
2024-11-14 17:27:19 -06:00
2024-09-27 15:44:44 -07:00
impl < ' a > FromKclValue < ' a > for SketchOrSurface {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-07-19 20:30:13 -05:00
match arg {
2024-11-14 17:27:19 -06:00
KclValue ::Sketch { value : sg } = > Some ( Self ::Sketch ( sg . to_owned ( ) ) ) ,
2025-01-22 09:42:09 +13:00
KclValue ::Plane { value } = > Some ( Self ::SketchSurface ( SketchSurface ::Plane ( value . clone ( ) ) ) ) ,
KclValue ::Face { value } = > Some ( Self ::SketchSurface ( SketchSurface ::Face ( value . clone ( ) ) ) ) ,
2024-07-19 20:30:13 -05:00
_ = > None ,
}
}
}
2024-08-12 16:53:24 -05:00
impl < ' a > FromKclValue < ' a > for SketchSurface {
2024-11-16 11:19:00 -06:00
fn from_kcl_val ( arg : & ' a KclValue ) -> Option < Self > {
2024-07-19 20:30:13 -05:00
match arg {
2025-01-22 09:42:09 +13:00
KclValue ::Plane { value } = > Some ( Self ::Plane ( value . clone ( ) ) ) ,
KclValue ::Face { value } = > Some ( Self ::Face ( value . clone ( ) ) ) ,
2024-07-19 20:30:13 -05:00
_ = > None ,
}
}
}
2024-11-05 14:10:35 -06:00
2024-11-14 17:27:19 -06:00
impl From < Args > for Metadata {
fn from ( value : Args ) -> Self {
Self {
source_range : value . source_range ,
}
}
}
impl From < Args > for Vec < Metadata > {
fn from ( value : Args ) -> Self {
vec! [ Metadata {
source_range : value . source_range ,
} ]
}
2024-11-05 14:10:35 -06:00
}