1use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kittycad_modeling_cmds::shared::Angle;
6
7use crate::{
8 errors::{KclError, KclErrorDetails},
9 execution::{
10 types::{NumericType, PrimitiveType, RuntimeType},
11 ExecState, KclValue, Sketch, TagIdentifier,
12 },
13 std::{args::TyF64, utils::between, Args},
14};
15
16use super::utils::untype_point;
17
18pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
20 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
21 let result = inner_segment_end(&tag, exec_state, args.clone())?;
22
23 args.make_user_val_from_point(result)
24}
25
26#[stdlib {
52 name = "segEnd",
53 keywords = true,
54 unlabeled_first = true,
55 args = {
56 tag = { docs = "The line segment being queried by its tag"},
57 }
58}]
59fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
60 let line = args.get_tag_engine_info(exec_state, tag)?;
61 let path = line.path.clone().ok_or_else(|| {
62 KclError::Type(KclErrorDetails {
63 message: format!("Expected a line segment with a path, found `{:?}`", line),
64 source_ranges: vec![args.source_range],
65 })
66 })?;
67
68 Ok(path.get_to().clone())
69}
70
71pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
73 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
74 let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
75
76 Ok(args.make_user_val_from_f64_with_type(result))
77}
78
79#[stdlib {
93 name = "segEndX",
94 keywords = true,
95 unlabeled_first = true,
96 args = {
97 tag = { docs = "The line segment being queried by its tag"},
98 }
99}]
100fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
101 let line = args.get_tag_engine_info(exec_state, tag)?;
102 let path = line.path.clone().ok_or_else(|| {
103 KclError::Type(KclErrorDetails {
104 message: format!("Expected a line segment with a path, found `{:?}`", line),
105 source_ranges: vec![args.source_range],
106 })
107 })?;
108
109 Ok(TyF64::new(path.get_base().to[0], path.get_base().units.into()))
110}
111
112pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
114 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
115 let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
116
117 Ok(args.make_user_val_from_f64_with_type(result))
118}
119
120#[stdlib {
135 name = "segEndY",
136 keywords = true,
137 unlabeled_first = true,
138 args = {
139 tag = { docs = "The line segment being queried by its tag"},
140 }
141}]
142fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
143 let line = args.get_tag_engine_info(exec_state, tag)?;
144 let path = line.path.clone().ok_or_else(|| {
145 KclError::Type(KclErrorDetails {
146 message: format!("Expected a line segment with a path, found `{:?}`", line),
147 source_ranges: vec![args.source_range],
148 })
149 })?;
150
151 Ok(path.get_to()[1].clone())
152}
153
154pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
156 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
157 let result = inner_segment_start(&tag, exec_state, args.clone())?;
158
159 args.make_user_val_from_point(result)
160}
161
162#[stdlib {
188 name = "segStart",
189 keywords = true,
190 unlabeled_first = true,
191 args = {
192 tag = { docs = "The line segment being queried by its tag"},
193 }
194}]
195fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
196 let line = args.get_tag_engine_info(exec_state, tag)?;
197 let path = line.path.clone().ok_or_else(|| {
198 KclError::Type(KclErrorDetails {
199 message: format!("Expected a line segment with a path, found `{:?}`", line),
200 source_ranges: vec![args.source_range],
201 })
202 })?;
203
204 Ok(path.get_from().to_owned())
205}
206
207pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
209 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
210 let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
211
212 Ok(args.make_user_val_from_f64_with_type(result))
213}
214
215#[stdlib {
229 name = "segStartX",
230 keywords = true,
231 unlabeled_first = true,
232 args = {
233 tag = { docs = "The line segment being queried by its tag"},
234 }
235}]
236fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
237 let line = args.get_tag_engine_info(exec_state, tag)?;
238 let path = line.path.clone().ok_or_else(|| {
239 KclError::Type(KclErrorDetails {
240 message: format!("Expected a line segment with a path, found `{:?}`", line),
241 source_ranges: vec![args.source_range],
242 })
243 })?;
244
245 Ok(path.get_from()[0].clone())
246}
247
248pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
250 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
251 let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
252
253 Ok(args.make_user_val_from_f64_with_type(result))
254}
255
256#[stdlib {
271 name = "segStartY",
272 keywords = true,
273 unlabeled_first = true,
274 args = {
275 tag = { docs = "The line segment being queried by its tag"},
276 }
277}]
278fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
279 let line = args.get_tag_engine_info(exec_state, tag)?;
280 let path = line.path.clone().ok_or_else(|| {
281 KclError::Type(KclErrorDetails {
282 message: format!("Expected a line segment with a path, found `{:?}`", line),
283 source_ranges: vec![args.source_range],
284 })
285 })?;
286
287 Ok(path.get_from()[1].clone())
288}
289pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
291 let sketch =
292 args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
293 let result = inner_last_segment_x(sketch, args.clone())?;
294
295 Ok(args.make_user_val_from_f64_with_type(result))
296}
297
298#[stdlib {
313 name = "lastSegX",
314 keywords = true,
315 unlabeled_first = true,
316 args = {
317 sketch = { docs = "The sketch whose line segment is being queried"},
318 }
319}]
320fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
321 let last_line = sketch
322 .paths
323 .last()
324 .ok_or_else(|| {
325 KclError::Type(KclErrorDetails {
326 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
327 source_ranges: vec![args.source_range],
328 })
329 })?
330 .get_base();
331
332 Ok(TyF64::new(last_line.to[0], last_line.units.into()))
333}
334
335pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
337 let sketch =
338 args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
339 let result = inner_last_segment_y(sketch, args.clone())?;
340
341 Ok(args.make_user_val_from_f64_with_type(result))
342}
343
344#[stdlib {
359 name = "lastSegY",
360 keywords = true,
361 unlabeled_first = true,
362 args = {
363 sketch = { docs = "The sketch whose line segment is being queried"},
364 }
365}]
366fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
367 let last_line = sketch
368 .paths
369 .last()
370 .ok_or_else(|| {
371 KclError::Type(KclErrorDetails {
372 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
373 source_ranges: vec![args.source_range],
374 })
375 })?
376 .get_base();
377
378 Ok(TyF64::new(last_line.to[1], last_line.units.into()))
379}
380
381pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
383 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
384 let result = inner_segment_length(&tag, exec_state, args.clone())?;
385 Ok(args.make_user_val_from_f64_with_type(result))
386}
387
388#[stdlib {
408 name = "segLen",
409 keywords = true,
410 unlabeled_first = true,
411 args = {
412 tag = { docs = "The line segment being queried by its tag"},
413 }
414}]
415fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
416 let line = args.get_tag_engine_info(exec_state, tag)?;
417 let path = line.path.clone().ok_or_else(|| {
418 KclError::Type(KclErrorDetails {
419 message: format!("Expected a line segment with a path, found `{:?}`", line),
420 source_ranges: vec![args.source_range],
421 })
422 })?;
423
424 Ok(path.length())
425}
426
427pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
429 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
430
431 let result = inner_segment_angle(&tag, exec_state, args.clone())?;
432 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
433}
434
435#[stdlib {
451 name = "segAng",
452 keywords = true,
453 unlabeled_first = true,
454 args = {
455 tag = { docs = "The line segment being queried by its tag"},
456 }
457}]
458fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
459 let line = args.get_tag_engine_info(exec_state, tag)?;
460 let path = line.path.clone().ok_or_else(|| {
461 KclError::Type(KclErrorDetails {
462 message: format!("Expected a line segment with a path, found `{:?}`", line),
463 source_ranges: vec![args.source_range],
464 })
465 })?;
466
467 let result = between(path.get_from().into(), path.get_to().into());
468
469 Ok(result.to_degrees())
470}
471
472pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
474 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
475
476 let result = inner_tangent_to_end(&tag, exec_state, args.clone()).await?;
477 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
478}
479
480#[stdlib {
551 name = "tangentToEnd",
552 keywords = true,
553 unlabeled_first = true,
554 args = {
555 tag = { docs = "The line segment being queried by its tag"},
556 }
557}]
558async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
559 let line = args.get_tag_engine_info(exec_state, tag)?;
560 let path = line.path.clone().ok_or_else(|| {
561 KclError::Type(KclErrorDetails {
562 message: format!("Expected a line segment with a path, found `{:?}`", line),
563 source_ranges: vec![args.source_range],
564 })
565 })?;
566
567 let from = untype_point(path.get_to()).0;
568
569 let tangent_info = path.get_tangential_info();
571 let tan_previous_point = tangent_info.tan_previous_point(from);
572
573 let previous_end_tangent = Angle::from_radians(f64::atan2(
576 from[1] - tan_previous_point[1],
577 from[0] - tan_previous_point[0],
578 ));
579
580 Ok(previous_end_tangent.to_degrees())
581}
582
583pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
585 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
586 let result = inner_angle_to_match_length_x(&tag, to.n, sketch, exec_state, args.clone())?;
587 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
588}
589
590#[stdlib {
605 name = "angleToMatchLengthX",
606}]
607fn inner_angle_to_match_length_x(
608 tag: &TagIdentifier,
609 to: f64,
610 sketch: Sketch,
611 exec_state: &mut ExecState,
612 args: Args,
613) -> Result<f64, KclError> {
614 let line = args.get_tag_engine_info(exec_state, tag)?;
615 let path = line.path.clone().ok_or_else(|| {
616 KclError::Type(KclErrorDetails {
617 message: format!("Expected a line segment with a path, found `{:?}`", line),
618 source_ranges: vec![args.source_range],
619 })
620 })?;
621
622 let length = path.length().n;
623
624 let last_line = sketch
625 .paths
626 .last()
627 .ok_or_else(|| {
628 KclError::Type(KclErrorDetails {
629 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
630 source_ranges: vec![args.source_range],
631 })
632 })?
633 .get_base();
634
635 let diff = (to - last_line.to[0]).abs();
637
638 let angle_r = (diff / length).acos();
639
640 if diff > length {
641 Ok(0.0)
642 } else {
643 Ok(angle_r.to_degrees())
644 }
645}
646
647pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
649 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
650 let result = inner_angle_to_match_length_y(&tag, to.n, sketch, exec_state, args.clone())?;
651 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
652}
653
654#[stdlib {
670 name = "angleToMatchLengthY",
671}]
672fn inner_angle_to_match_length_y(
673 tag: &TagIdentifier,
674 to: f64,
675 sketch: Sketch,
676 exec_state: &mut ExecState,
677 args: Args,
678) -> Result<f64, KclError> {
679 let line = args.get_tag_engine_info(exec_state, tag)?;
680 let path = line.path.clone().ok_or_else(|| {
681 KclError::Type(KclErrorDetails {
682 message: format!("Expected a line segment with a path, found `{:?}`", line),
683 source_ranges: vec![args.source_range],
684 })
685 })?;
686
687 let length = path.length().n;
688
689 let last_line = sketch
690 .paths
691 .last()
692 .ok_or_else(|| {
693 KclError::Type(KclErrorDetails {
694 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
695 source_ranges: vec![args.source_range],
696 })
697 })?
698 .get_base();
699
700 let diff = (to - last_line.to[1]).abs();
702
703 let angle_r = (diff / length).asin();
704
705 if diff > length {
706 Ok(0.0)
707 } else {
708 Ok(angle_r.to_degrees())
709 }
710}