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::{PrimitiveType, RuntimeType},
11 ExecState, KclValue, Point2d, Sketch, TagIdentifier,
12 },
13 std::{utils::between, Args},
14};
15
16pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
18 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
19 let result = inner_segment_end(&tag, exec_state, args.clone())?;
20
21 args.make_user_val_from_point(result)
22}
23
24#[stdlib {
50 name = "segEnd",
51 keywords = true,
52 unlabeled_first = true,
53 args = {
54 tag = { docs = "The line segment being queried by its tag"},
55 }
56}]
57fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
58 let line = args.get_tag_engine_info(exec_state, tag)?;
59 let path = line.path.clone().ok_or_else(|| {
60 KclError::Type(KclErrorDetails {
61 message: format!("Expected a line segment with a path, found `{:?}`", line),
62 source_ranges: vec![args.source_range],
63 })
64 })?;
65
66 Ok(path.get_base().to)
67}
68
69pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
71 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
72 let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
73
74 Ok(args.make_user_val_from_f64(result))
75}
76
77#[stdlib {
91 name = "segEndX",
92 keywords = true,
93 unlabeled_first = true,
94 args = {
95 tag = { docs = "The line segment being queried by its tag"},
96 }
97}]
98fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
99 let line = args.get_tag_engine_info(exec_state, tag)?;
100 let path = line.path.clone().ok_or_else(|| {
101 KclError::Type(KclErrorDetails {
102 message: format!("Expected a line segment with a path, found `{:?}`", line),
103 source_ranges: vec![args.source_range],
104 })
105 })?;
106
107 Ok(path.get_base().to[0])
108}
109
110pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
112 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
113 let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
114
115 Ok(args.make_user_val_from_f64(result))
116}
117
118#[stdlib {
133 name = "segEndY",
134 keywords = true,
135 unlabeled_first = true,
136 args = {
137 tag = { docs = "The line segment being queried by its tag"},
138 }
139}]
140fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
141 let line = args.get_tag_engine_info(exec_state, tag)?;
142 let path = line.path.clone().ok_or_else(|| {
143 KclError::Type(KclErrorDetails {
144 message: format!("Expected a line segment with a path, found `{:?}`", line),
145 source_ranges: vec![args.source_range],
146 })
147 })?;
148
149 Ok(path.get_to()[1])
150}
151
152pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
154 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
155 let result = inner_segment_start(&tag, exec_state, args.clone())?;
156
157 args.make_user_val_from_point(result)
158}
159
160#[stdlib {
186 name = "segStart",
187 keywords = true,
188 unlabeled_first = true,
189 args = {
190 tag = { docs = "The line segment being queried by its tag"},
191 }
192}]
193fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[f64; 2], KclError> {
194 let line = args.get_tag_engine_info(exec_state, tag)?;
195 let path = line.path.clone().ok_or_else(|| {
196 KclError::Type(KclErrorDetails {
197 message: format!("Expected a line segment with a path, found `{:?}`", line),
198 source_ranges: vec![args.source_range],
199 })
200 })?;
201
202 Ok(path.get_from().to_owned())
203}
204
205pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
207 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
208 let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
209
210 Ok(args.make_user_val_from_f64(result))
211}
212
213#[stdlib {
227 name = "segStartX",
228 keywords = true,
229 unlabeled_first = true,
230 args = {
231 tag = { docs = "The line segment being queried by its tag"},
232 }
233}]
234fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
235 let line = args.get_tag_engine_info(exec_state, tag)?;
236 let path = line.path.clone().ok_or_else(|| {
237 KclError::Type(KclErrorDetails {
238 message: format!("Expected a line segment with a path, found `{:?}`", line),
239 source_ranges: vec![args.source_range],
240 })
241 })?;
242
243 Ok(path.get_from()[0])
244}
245
246pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
248 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
249 let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
250
251 Ok(args.make_user_val_from_f64(result))
252}
253
254#[stdlib {
269 name = "segStartY",
270 keywords = true,
271 unlabeled_first = true,
272 args = {
273 tag = { docs = "The line segment being queried by its tag"},
274 }
275}]
276fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
277 let line = args.get_tag_engine_info(exec_state, tag)?;
278 let path = line.path.clone().ok_or_else(|| {
279 KclError::Type(KclErrorDetails {
280 message: format!("Expected a line segment with a path, found `{:?}`", line),
281 source_ranges: vec![args.source_range],
282 })
283 })?;
284
285 Ok(path.get_from()[1])
286}
287pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
289 let sketch =
290 args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
291 let result = inner_last_segment_x(sketch, args.clone())?;
292
293 Ok(args.make_user_val_from_f64(result))
294}
295
296#[stdlib {
311 name = "lastSegX",
312 keywords = true,
313 unlabeled_first = true,
314 args = {
315 sketch = { docs = "The sketch whose line segment is being queried"},
316 }
317}]
318fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
319 let last_line = sketch
320 .paths
321 .last()
322 .ok_or_else(|| {
323 KclError::Type(KclErrorDetails {
324 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
325 source_ranges: vec![args.source_range],
326 })
327 })?
328 .get_base();
329
330 Ok(last_line.to[0])
331}
332
333pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
335 let sketch =
336 args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
337 let result = inner_last_segment_y(sketch, args.clone())?;
338
339 Ok(args.make_user_val_from_f64(result))
340}
341
342#[stdlib {
357 name = "lastSegY",
358 keywords = true,
359 unlabeled_first = true,
360 args = {
361 sketch = { docs = "The sketch whose line segment is being queried"},
362 }
363}]
364fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
365 let last_line = sketch
366 .paths
367 .last()
368 .ok_or_else(|| {
369 KclError::Type(KclErrorDetails {
370 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
371 source_ranges: vec![args.source_range],
372 })
373 })?
374 .get_base();
375
376 Ok(last_line.to[1])
377}
378
379pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
381 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
382 let result = inner_segment_length(&tag, exec_state, args.clone())?;
383 Ok(args.make_user_val_from_f64(result))
384}
385
386#[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<f64, 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 let result = path.length();
425
426 Ok(result)
427}
428
429pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
431 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
432
433 let result = inner_segment_angle(&tag, exec_state, args.clone())?;
434 Ok(args.make_user_val_from_f64(result))
435}
436
437#[stdlib {
453 name = "segAng",
454 keywords = true,
455 unlabeled_first = true,
456 args = {
457 tag = { docs = "The line segment being queried by its tag"},
458 }
459}]
460fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
461 let line = args.get_tag_engine_info(exec_state, tag)?;
462 let path = line.path.clone().ok_or_else(|| {
463 KclError::Type(KclErrorDetails {
464 message: format!("Expected a line segment with a path, found `{:?}`", line),
465 source_ranges: vec![args.source_range],
466 })
467 })?;
468
469 let result = between(path.get_from().into(), path.get_to().into());
470
471 Ok(result.to_degrees())
472}
473
474pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
476 let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
477
478 let result = inner_tangent_to_end(&tag, exec_state, args.clone()).await?;
479 Ok(args.make_user_val_from_f64(result))
480}
481
482#[stdlib {
553 name = "tangentToEnd",
554 keywords = true,
555 unlabeled_first = true,
556 args = {
557 tag = { docs = "The line segment being queried by its tag"},
558 }
559}]
560async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
561 let line = args.get_tag_engine_info(exec_state, tag)?;
562 let path = line.path.clone().ok_or_else(|| {
563 KclError::Type(KclErrorDetails {
564 message: format!("Expected a line segment with a path, found `{:?}`", line),
565 source_ranges: vec![args.source_range],
566 })
567 })?;
568
569 let from = Point2d::from(path.get_to());
570
571 let tangent_info = path.get_tangential_info();
573 let tan_previous_point = tangent_info.tan_previous_point(from.into());
574
575 let previous_end_tangent = Angle::from_radians(f64::atan2(
578 from.y - tan_previous_point[1],
579 from.x - tan_previous_point[0],
580 ));
581
582 Ok(previous_end_tangent.to_degrees())
583}
584
585pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
587 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
588 let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
589 Ok(args.make_user_val_from_f64(result))
590}
591
592#[stdlib {
607 name = "angleToMatchLengthX",
608}]
609fn inner_angle_to_match_length_x(
610 tag: &TagIdentifier,
611 to: f64,
612 sketch: Sketch,
613 exec_state: &mut ExecState,
614 args: Args,
615) -> Result<f64, KclError> {
616 let line = args.get_tag_engine_info(exec_state, tag)?;
617 let path = line.path.clone().ok_or_else(|| {
618 KclError::Type(KclErrorDetails {
619 message: format!("Expected a line segment with a path, found `{:?}`", line),
620 source_ranges: vec![args.source_range],
621 })
622 })?;
623
624 let length = path.length();
625
626 let last_line = sketch
627 .paths
628 .last()
629 .ok_or_else(|| {
630 KclError::Type(KclErrorDetails {
631 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
632 source_ranges: vec![args.source_range],
633 })
634 })?
635 .get_base();
636
637 let diff = (to - last_line.to[0]).abs();
638
639 let angle_r = (diff / length).acos();
640
641 if diff > length {
642 Ok(0.0)
643 } else {
644 Ok(angle_r.to_degrees())
645 }
646}
647
648pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
650 let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
651 let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
652 Ok(args.make_user_val_from_f64(result))
653}
654
655#[stdlib {
671 name = "angleToMatchLengthY",
672}]
673fn inner_angle_to_match_length_y(
674 tag: &TagIdentifier,
675 to: f64,
676 sketch: Sketch,
677 exec_state: &mut ExecState,
678 args: Args,
679) -> Result<f64, KclError> {
680 let line = args.get_tag_engine_info(exec_state, tag)?;
681 let path = line.path.clone().ok_or_else(|| {
682 KclError::Type(KclErrorDetails {
683 message: format!("Expected a line segment with a path, found `{:?}`", line),
684 source_ranges: vec![args.source_range],
685 })
686 })?;
687
688 let length = path.length();
689
690 let last_line = sketch
691 .paths
692 .last()
693 .ok_or_else(|| {
694 KclError::Type(KclErrorDetails {
695 message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
696 source_ranges: vec![args.source_range],
697 })
698 })?
699 .get_base();
700
701 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}