1use anyhow::Result;
4use kcl_derive_docs::stdlib;
5use kittycad_modeling_cmds::shared::Angle;
6
7use super::utils::untype_point;
8use crate::{
9    errors::{KclError, KclErrorDetails},
10    execution::{
11        types::{NumericType, PrimitiveType, RuntimeType},
12        ExecState, KclValue, Sketch, TagIdentifier,
13    },
14    std::{args::TyF64, utils::between, Args},
15};
16
17pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
19    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
20    let pt = inner_segment_end(&tag, exec_state, args.clone())?;
21
22    args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
23}
24
25#[stdlib {
51    name = "segEnd",
52    keywords = true,
53    unlabeled_first = true,
54    args = {
55        tag = { docs = "The line segment being queried by its tag"},
56    },
57    tags = ["sketch"]
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    let (p, ty) = path.end_point_components();
68    let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
70
71    Ok(point)
72}
73
74pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
76    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
77    let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
78
79    Ok(args.make_user_val_from_f64_with_type(result))
80}
81
82#[stdlib {
96    name = "segEndX",
97    keywords = true,
98    unlabeled_first = true,
99    args = {
100        tag = { docs = "The line segment being queried by its tag"},
101    },
102    tags = ["sketch"]
103}]
104fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
105    let line = args.get_tag_engine_info(exec_state, tag)?;
106    let path = line.path.clone().ok_or_else(|| {
107        KclError::Type(KclErrorDetails {
108            message: format!("Expected a line segment with a path, found `{:?}`", line),
109            source_ranges: vec![args.source_range],
110        })
111    })?;
112
113    Ok(TyF64::new(path.get_base().to[0], path.get_base().units.into()))
114}
115
116pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
118    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
119    let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
120
121    Ok(args.make_user_val_from_f64_with_type(result))
122}
123
124#[stdlib {
139    name = "segEndY",
140    keywords = true,
141    unlabeled_first = true,
142    args = {
143        tag = { docs = "The line segment being queried by its tag"},
144    },
145    tags = ["sketch"]
146}]
147fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
148    let line = args.get_tag_engine_info(exec_state, tag)?;
149    let path = line.path.clone().ok_or_else(|| {
150        KclError::Type(KclErrorDetails {
151            message: format!("Expected a line segment with a path, found `{:?}`", line),
152            source_ranges: vec![args.source_range],
153        })
154    })?;
155
156    Ok(path.get_to()[1].clone())
157}
158
159pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
161    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
162    let pt = inner_segment_start(&tag, exec_state, args.clone())?;
163
164    args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
165}
166
167#[stdlib {
193    name = "segStart",
194    keywords = true,
195    unlabeled_first = true,
196    args = {
197        tag = { docs = "The line segment being queried by its tag"},
198    },
199    tags = ["sketch"]
200}]
201fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
202    let line = args.get_tag_engine_info(exec_state, tag)?;
203    let path = line.path.clone().ok_or_else(|| {
204        KclError::Type(KclErrorDetails {
205            message: format!("Expected a line segment with a path, found `{:?}`", line),
206            source_ranges: vec![args.source_range],
207        })
208    })?;
209    let (p, ty) = path.start_point_components();
210    let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)];
212
213    Ok(point)
214}
215
216pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
218    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
219    let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
220
221    Ok(args.make_user_val_from_f64_with_type(result))
222}
223
224#[stdlib {
238    name = "segStartX",
239    keywords = true,
240    unlabeled_first = true,
241    args = {
242        tag = { docs = "The line segment being queried by its tag"},
243    },
244    tags = ["sketch"]
245}]
246fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
247    let line = args.get_tag_engine_info(exec_state, tag)?;
248    let path = line.path.clone().ok_or_else(|| {
249        KclError::Type(KclErrorDetails {
250            message: format!("Expected a line segment with a path, found `{:?}`", line),
251            source_ranges: vec![args.source_range],
252        })
253    })?;
254
255    Ok(path.get_from()[0].clone())
256}
257
258pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
260    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
261    let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
262
263    Ok(args.make_user_val_from_f64_with_type(result))
264}
265
266#[stdlib {
281    name = "segStartY",
282    keywords = true,
283    unlabeled_first = true,
284    args = {
285        tag = { docs = "The line segment being queried by its tag"},
286    },
287    tags = ["sketch"]
288}]
289fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
290    let line = args.get_tag_engine_info(exec_state, tag)?;
291    let path = line.path.clone().ok_or_else(|| {
292        KclError::Type(KclErrorDetails {
293            message: format!("Expected a line segment with a path, found `{:?}`", line),
294            source_ranges: vec![args.source_range],
295        })
296    })?;
297
298    Ok(path.get_from()[1].clone())
299}
300pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
302    let sketch =
303        args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
304    let result = inner_last_segment_x(sketch, args.clone())?;
305
306    Ok(args.make_user_val_from_f64_with_type(result))
307}
308
309#[stdlib {
324    name = "lastSegX",
325    keywords = true,
326    unlabeled_first = true,
327    args = {
328        sketch = { docs = "The sketch whose line segment is being queried"},
329    },
330    tags = ["sketch"]
331}]
332fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
333    let last_line = sketch
334        .paths
335        .last()
336        .ok_or_else(|| {
337            KclError::Type(KclErrorDetails {
338                message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
339                source_ranges: vec![args.source_range],
340            })
341        })?
342        .get_base();
343
344    Ok(TyF64::new(last_line.to[0], last_line.units.into()))
345}
346
347pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
349    let sketch =
350        args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
351    let result = inner_last_segment_y(sketch, args.clone())?;
352
353    Ok(args.make_user_val_from_f64_with_type(result))
354}
355
356#[stdlib {
371    name = "lastSegY",
372    keywords = true,
373    unlabeled_first = true,
374    args = {
375        sketch = { docs = "The sketch whose line segment is being queried"},
376    },
377    tags = ["sketch"]
378}]
379fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
380    let last_line = sketch
381        .paths
382        .last()
383        .ok_or_else(|| {
384            KclError::Type(KclErrorDetails {
385                message: format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
386                source_ranges: vec![args.source_range],
387            })
388        })?
389        .get_base();
390
391    Ok(TyF64::new(last_line.to[1], last_line.units.into()))
392}
393
394pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
396    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
397    let result = inner_segment_length(&tag, exec_state, args.clone())?;
398    Ok(args.make_user_val_from_f64_with_type(result))
399}
400
401#[stdlib {
421    name = "segLen",
422    keywords = true,
423    unlabeled_first = true,
424    args = {
425        tag = { docs = "The line segment being queried by its tag"},
426    },
427    tags = ["sketch"]
428}]
429fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
430    let line = args.get_tag_engine_info(exec_state, tag)?;
431    let path = line.path.clone().ok_or_else(|| {
432        KclError::Type(KclErrorDetails {
433            message: format!("Expected a line segment with a path, found `{:?}`", line),
434            source_ranges: vec![args.source_range],
435        })
436    })?;
437
438    Ok(path.length())
439}
440
441pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
443    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
444
445    let result = inner_segment_angle(&tag, exec_state, args.clone())?;
446    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
447}
448
449#[stdlib {
465    name = "segAng",
466    keywords = true,
467    unlabeled_first = true,
468    args = {
469        tag = { docs = "The line segment being queried by its tag"},
470    },
471    tags = ["sketch"]
472}]
473fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
474    let line = args.get_tag_engine_info(exec_state, tag)?;
475    let path = line.path.clone().ok_or_else(|| {
476        KclError::Type(KclErrorDetails {
477            message: format!("Expected a line segment with a path, found `{:?}`", line),
478            source_ranges: vec![args.source_range],
479        })
480    })?;
481
482    let result = between(path.get_base().from, path.get_base().to);
483
484    Ok(result.to_degrees())
485}
486
487pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
489    let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
490
491    let result = inner_tangent_to_end(&tag, exec_state, args.clone()).await?;
492    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
493}
494
495#[stdlib {
567    name = "tangentToEnd",
568    keywords = true,
569    unlabeled_first = true,
570    args = {
571        tag = { docs = "The line segment being queried by its tag"},
572    },
573    tags = ["sketch"]
574}]
575async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
576    let line = args.get_tag_engine_info(exec_state, tag)?;
577    let path = line.path.clone().ok_or_else(|| {
578        KclError::Type(KclErrorDetails {
579            message: format!("Expected a line segment with a path, found `{:?}`", line),
580            source_ranges: vec![args.source_range],
581        })
582    })?;
583
584    let from = untype_point(path.get_to()).0;
585
586    let tangent_info = path.get_tangential_info();
588    let tan_previous_point = tangent_info.tan_previous_point(from);
589
590    let previous_end_tangent = Angle::from_radians(f64::atan2(
593        from[1] - tan_previous_point[1],
594        from[0] - tan_previous_point[0],
595    ));
596
597    Ok(previous_end_tangent.to_degrees())
598}