1use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kittycad_modeling_cmds::shared::Angle;
6
7use crate::{
8 errors::{KclError, KclErrorDetails},
9 execution::{ExecState, KclValue, Point2d, Sketch, TagIdentifier},
10 std::{utils::between, Args},
11};
12
13pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
15 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
16 let result = inner_segment_end(&tag, exec_state, args.clone())?;
17
18 args.make_user_val_from_point(result)
19}
20
21#[stdlib {
47 name = "segEnd",
48 keywords = true,
49 unlabeled_first = true,
50 args = {
51 tag = { docs = "The line segment being queried by its tag"},
52 }
53}]
54fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
55 let line = args.get_tag_engine_info(exec_state, tag)?;
56 let path = line.path.clone().ok_or_else(|| {
57 KclError::Type(KclErrorDetails {
58 message: format!("Expected a line segment with a path, found `{:?}`", line),
59 source_ranges: vec![args.source_range],
60 })
61 })?;
62
63 Ok(path.get_base().to)
64}
65
66pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
68 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
69 let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
70
71 Ok(args.make_user_val_from_f64(result))
72}
73
74#[stdlib {
88 name = "segEndX",
89 keywords = true,
90 unlabeled_first = true,
91 args = {
92 tag = { docs = "The line segment being queried by its tag"},
93 }
94}]
95fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
96 let line = args.get_tag_engine_info(exec_state, tag)?;
97 let path = line.path.clone().ok_or_else(|| {
98 KclError::Type(KclErrorDetails {
99 message: format!("Expected a line segment with a path, found `{:?}`", line),
100 source_ranges: vec![args.source_range],
101 })
102 })?;
103
104 Ok(path.get_base().to[0])
105}
106
107pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
109 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
110 let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
111
112 Ok(args.make_user_val_from_f64(result))
113}
114
115#[stdlib {
130 name = "segEndY",
131 keywords = true,
132 unlabeled_first = true,
133 args = {
134 tag = { docs = "The line segment being queried by its tag"},
135 }
136}]
137fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
138 let line = args.get_tag_engine_info(exec_state, tag)?;
139 let path = line.path.clone().ok_or_else(|| {
140 KclError::Type(KclErrorDetails {
141 message: format!("Expected a line segment with a path, found `{:?}`", line),
142 source_ranges: vec![args.source_range],
143 })
144 })?;
145
146 Ok(path.get_to()[1])
147}
148
149pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
151 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
152 let result = inner_segment_start(&tag, exec_state, args.clone())?;
153
154 args.make_user_val_from_point(result)
155}
156
157#[stdlib {
183 name = "segStart",
184 keywords = true,
185 unlabeled_first = true,
186 args = {
187 tag = { docs = "The line segment being queried by its tag"},
188 }
189}]
190fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
191 let line = args.get_tag_engine_info(exec_state, tag)?;
192 let path = line.path.clone().ok_or_else(|| {
193 KclError::Type(KclErrorDetails {
194 message: format!("Expected a line segment with a path, found `{:?}`", line),
195 source_ranges: vec![args.source_range],
196 })
197 })?;
198
199 Ok(path.get_from().to_owned())
200}
201
202pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
204 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
205 let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
206
207 Ok(args.make_user_val_from_f64(result))
208}
209
210#[stdlib {
224 name = "segStartX",
225 keywords = true,
226 unlabeled_first = true,
227 args = {
228 tag = { docs = "The line segment being queried by its tag"},
229 }
230}]
231fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
232 let line = args.get_tag_engine_info(exec_state, tag)?;
233 let path = line.path.clone().ok_or_else(|| {
234 KclError::Type(KclErrorDetails {
235 message: format!("Expected a line segment with a path, found `{:?}`", line),
236 source_ranges: vec![args.source_range],
237 })
238 })?;
239
240 Ok(path.get_from()[0])
241}
242
243pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
245 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
246 let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
247
248 Ok(args.make_user_val_from_f64(result))
249}
250
251#[stdlib {
266 name = "segStartY",
267 keywords = true,
268 unlabeled_first = true,
269 args = {
270 tag = { docs = "The line segment being queried by its tag"},
271 }
272}]
273fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
274 let line = args.get_tag_engine_info(exec_state, tag)?;
275 let path = line.path.clone().ok_or_else(|| {
276 KclError::Type(KclErrorDetails {
277 message: format!("Expected a line segment with a path, found `{:?}`", line),
278 source_ranges: vec![args.source_range],
279 })
280 })?;
281
282 Ok(path.get_from()[1])
283}
284pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
286 let sketch = args.get_unlabeled_kw_arg("sketch")?;
287 let result = inner_last_segment_x(sketch, args.clone())?;
288
289 Ok(args.make_user_val_from_f64(result))
290}
291
292#[stdlib {
307 name = "lastSegX",
308 keywords = true,
309 unlabeled_first = true,
310 args = {
311 sketch = { docs = "The sketch whose line segment is being queried"},
312 }
313}]
314fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
315 let last_line = sketch
316 .paths
317 .last()
318 .ok_or_else(|| {
319 KclError::Type(KclErrorDetails {
320 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
321 source_ranges: vec![args.source_range],
322 })
323 })?
324 .get_base();
325
326 Ok(last_line.to[0])
327}
328
329pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
331 let sketch = args.get_unlabeled_kw_arg("sketch")?;
332 let result = inner_last_segment_y(sketch, args.clone())?;
333
334 Ok(args.make_user_val_from_f64(result))
335}
336
337#[stdlib {
352 name = "lastSegY",
353 keywords = true,
354 unlabeled_first = true,
355 args = {
356 sketch = { docs = "The sketch whose line segment is being queried"},
357 }
358}]
359fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
360 let last_line = sketch
361 .paths
362 .last()
363 .ok_or_else(|| {
364 KclError::Type(KclErrorDetails {
365 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
366 source_ranges: vec![args.source_range],
367 })
368 })?
369 .get_base();
370
371 Ok(last_line.to[1])
372}
373
374pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
376 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
377 let result = inner_segment_length(&tag, exec_state, args.clone())?;
378 Ok(args.make_user_val_from_f64(result))
379}
380
381#[stdlib {
403 name = "segLen",
404 keywords = true,
405 unlabeled_first = true,
406 args = {
407 tag = { docs = "The line segment being queried by its tag"},
408 }
409}]
410fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
411 let line = args.get_tag_engine_info(exec_state, tag)?;
412 let path = line.path.clone().ok_or_else(|| {
413 KclError::Type(KclErrorDetails {
414 message: format!("Expected a line segment with a path, found `{:?}`", line),
415 source_ranges: vec![args.source_range],
416 })
417 })?;
418
419 let result = path.length();
420
421 Ok(result)
422}
423
424pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
426 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
427
428 let result = inner_segment_angle(&tag, exec_state, args.clone())?;
429 Ok(args.make_user_val_from_f64(result))
430}
431
432#[stdlib {
448 name = "segAng",
449 keywords = true,
450 unlabeled_first = true,
451 args = {
452 tag = { docs = "The line segment being queried by its tag"},
453 }
454}]
455fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
456 let line = args.get_tag_engine_info(exec_state, tag)?;
457 let path = line.path.clone().ok_or_else(|| {
458 KclError::Type(KclErrorDetails {
459 message: format!("Expected a line segment with a path, found `{:?}`", line),
460 source_ranges: vec![args.source_range],
461 })
462 })?;
463
464 let result = between(path.get_from().into(), path.get_to().into());
465
466 Ok(result.to_degrees())
467}
468
469pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
471 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
472
473 let result = inner_tangent_to_end(&tag, exec_state, args.clone()).await?;
474 Ok(args.make_user_val_from_f64(result))
475}
476
477#[stdlib {
548 name = "tangentToEnd",
549 keywords = true,
550 unlabeled_first = true,
551 args = {
552 tag = { docs = "The line segment being queried by its tag"},
553 }
554}]
555async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
556 let line = args.get_tag_engine_info(exec_state, tag)?;
557 let path = line.path.clone().ok_or_else(|| {
558 KclError::Type(KclErrorDetails {
559 message: format!("Expected a line segment with a path, found `{:?}`", line),
560 source_ranges: vec![args.source_range],
561 })
562 })?;
563
564 let from = Point2d::from(path.get_to());
565
566 let tangent_info = path.get_tangential_info();
568 let tan_previous_point = tangent_info.tan_previous_point(from.into());
569
570 let previous_end_tangent = Angle::from_radians(f64::atan2(
573 from.y - tan_previous_point[1],
574 from.x - tan_previous_point[0],
575 ));
576
577 Ok(previous_end_tangent.to_degrees())
578}
579
580pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
582 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
583 let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
584 Ok(args.make_user_val_from_f64(result))
585}
586
587#[stdlib {
602 name = "angleToMatchLengthX",
603}]
604fn inner_angle_to_match_length_x(
605 tag: &TagIdentifier,
606 to: f64,
607 sketch: Sketch,
608 exec_state: &mut ExecState,
609 args: Args,
610) -> Result<f64, KclError> {
611 let line = args.get_tag_engine_info(exec_state, tag)?;
612 let path = line.path.clone().ok_or_else(|| {
613 KclError::Type(KclErrorDetails {
614 message: format!("Expected a line segment with a path, found `{:?}`", line),
615 source_ranges: vec![args.source_range],
616 })
617 })?;
618
619 let length = path.length();
620
621 let last_line = sketch
622 .paths
623 .last()
624 .ok_or_else(|| {
625 KclError::Type(KclErrorDetails {
626 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
627 source_ranges: vec![args.source_range],
628 })
629 })?
630 .get_base();
631
632 let diff = (to - last_line.to[0]).abs();
633
634 let angle_r = (diff / length).acos();
635
636 if diff > length {
637 Ok(0.0)
638 } else {
639 Ok(angle_r.to_degrees())
640 }
641}
642
643pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
645 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
646 let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
647 Ok(args.make_user_val_from_f64(result))
648}
649
650#[stdlib {
666 name = "angleToMatchLengthY",
667}]
668fn inner_angle_to_match_length_y(
669 tag: &TagIdentifier,
670 to: f64,
671 sketch: Sketch,
672 exec_state: &mut ExecState,
673 args: Args,
674) -> Result<f64, KclError> {
675 let line = args.get_tag_engine_info(exec_state, tag)?;
676 let path = line.path.clone().ok_or_else(|| {
677 KclError::Type(KclErrorDetails {
678 message: format!("Expected a line segment with a path, found `{:?}`", line),
679 source_ranges: vec![args.source_range],
680 })
681 })?;
682
683 let length = path.length();
684
685 let last_line = sketch
686 .paths
687 .last()
688 .ok_or_else(|| {
689 KclError::Type(KclErrorDetails {
690 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
691 source_ranges: vec![args.source_range],
692 })
693 })?
694 .get_base();
695
696 let diff = (to - last_line.to[1]).abs();
697
698 let angle_r = (diff / length).asin();
699
700 if diff > length {
701 Ok(0.0)
702 } else {
703 Ok(angle_r.to_degrees())
704 }
705}