1pub use kcl_api::kcl_value_view::*;
2use serde::Serialize;
3
4use crate::exec::KclValue;
5use crate::execution::Metadata;
6use crate::execution::TagIdentifier;
7use crate::execution::geometry as runtime;
8use crate::parsing::ast::types::TagNode;
9
10fn json_view<T: Serialize>(value: &T) -> serde_json::Value {
11 serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
12}
13
14fn tag_declarator_view(tag: TagNode) -> TagDeclaratorView {
15 TagDeclaratorView {
16 comment_start: tag.comment_start,
17 end: tag.end,
18 module_id: tag.module_id,
19 start: tag.start,
20 type_: TagDeclaratorViewType::TagDeclarator,
21 name: tag.inner.name,
22 digest: tag.inner.digest,
23 }
24}
25
26fn tag_identifier_view(tag: TagIdentifier) -> TagIdentifierView {
27 TagIdentifierView {
28 type_: TagIdentifierViewType::TagIdentifier,
29 value: tag.value,
30 }
31}
32
33fn point3d_view(point: runtime::Point3d) -> Point3dView {
34 Point3dView {
35 x: point.x,
36 y: point.y,
37 z: point.z,
38 units: point.units,
39 }
40}
41
42fn plane_kind_view(kind: runtime::PlaneKind) -> PlaneKindView {
43 match kind {
44 runtime::PlaneKind::XY => PlaneKindView::XY,
45 runtime::PlaneKind::XZ => PlaneKindView::XZ,
46 runtime::PlaneKind::YZ => PlaneKindView::YZ,
47 runtime::PlaneKind::Custom => PlaneKindView::Custom,
48 }
49}
50
51fn plane_view(plane: runtime::Plane) -> PlaneView {
52 PlaneView {
53 id: plane.id,
54 artifact_id: plane.artifact_id,
55 object_id: plane.object_id,
56 kind: plane_kind_view(plane.kind),
57 origin: point3d_view(plane.info.origin),
58 x_axis: point3d_view(plane.info.x_axis),
59 y_axis: point3d_view(plane.info.y_axis),
60 z_axis: point3d_view(plane.info.z_axis),
61 }
62}
63
64fn face_view(face: runtime::Face) -> FaceView {
65 FaceView {
66 id: face.id,
67 artifact_id: face.artifact_id,
68 object_id: face.object_id,
69 value: face.value,
70 x_axis: point3d_view(face.x_axis),
71 y_axis: point3d_view(face.y_axis),
72 parent_solid: face_parent_solid_view(face.parent_solid),
73 units: face.units,
74 }
75}
76
77fn face_parent_solid_view(parent: runtime::FaceParentSolid) -> FaceParentSolidView {
78 FaceParentSolidView {
79 solid_id: parent.solid_id,
80 creator_sketch_id: parent.creator_sketch_id,
81 creator_sketch_is_closed: parent.creator_sketch_is_closed.map(profile_closed_view),
82 edge_cut_ids: parent.edge_cut_ids,
83 }
84}
85
86fn sketch_surface_view(surface: runtime::SketchSurface) -> SketchSurfaceView {
87 match surface {
88 runtime::SketchSurface::Plane(plane) => SketchSurfaceView::Plane(Box::new(plane_view(*plane))),
89 runtime::SketchSurface::Face(face) => SketchSurfaceView::Face(Box::new(face_view(*face))),
90 }
91}
92
93fn geo_meta_view(id: uuid::Uuid, metadata: Metadata) -> GeoMetaView {
94 GeoMetaView {
95 id,
96 source_range: metadata.source_range,
97 }
98}
99
100fn base_path_view(base: runtime::BasePath) -> BasePathView {
101 BasePathView {
102 from: base.from,
103 to: base.to,
104 units: base.units,
105 tag: base.tag.map(tag_declarator_view),
106 geo_meta: geo_meta_view(base.geo_meta.id, base.geo_meta.metadata),
107 }
108}
109
110fn path_view(path: runtime::Path) -> PathView {
111 match path {
112 runtime::Path::ToPoint { base } => PathView::ToPoint {
113 base: base_path_view(base),
114 },
115 runtime::Path::TangentialArcTo { base, center, ccw } => PathView::TangentialArcTo {
116 base: base_path_view(base),
117 center,
118 ccw,
119 },
120 runtime::Path::TangentialArc { base, center, ccw } => PathView::TangentialArc {
121 base: base_path_view(base),
122 center,
123 ccw,
124 },
125 runtime::Path::Circle {
126 base,
127 center,
128 radius,
129 ccw,
130 } => PathView::Circle {
131 base: base_path_view(base),
132 center,
133 radius,
134 ccw,
135 },
136 runtime::Path::CircleThreePoint { base, p1, p2, p3 } => PathView::CircleThreePoint {
137 base: base_path_view(base),
138 p1,
139 p2,
140 p3,
141 },
142 runtime::Path::ArcThreePoint { base, p1, p2, p3 } => PathView::ArcThreePoint {
143 base: base_path_view(base),
144 p1,
145 p2,
146 p3,
147 },
148 runtime::Path::Horizontal { base, x } => PathView::Horizontal {
149 base: base_path_view(base),
150 x,
151 },
152 runtime::Path::AngledLineTo { base, x, y } => PathView::AngledLineTo {
153 base: base_path_view(base),
154 x,
155 y,
156 },
157 runtime::Path::Base { base } => PathView::Base {
158 base: base_path_view(base),
159 },
160 runtime::Path::Arc {
161 base,
162 center,
163 radius,
164 ccw,
165 } => PathView::Arc {
166 base: base_path_view(base),
167 center,
168 radius,
169 ccw,
170 },
171 runtime::Path::Ellipse {
172 base,
173 center,
174 major_axis,
175 minor_radius,
176 ccw,
177 } => PathView::Ellipse {
178 base: base_path_view(base),
179 center,
180 major_axis,
181 minor_radius,
182 ccw,
183 },
184 runtime::Path::Conic { base } => PathView::Conic {
185 base: base_path_view(base),
186 },
187 runtime::Path::Bezier {
188 base,
189 control1,
190 control2,
191 } => PathView::Bezier {
192 base: base_path_view(base),
193 control1,
194 control2,
195 },
196 }
197}
198
199fn profile_closed_view(closed: runtime::ProfileClosed) -> ProfileClosedView {
200 match closed {
201 runtime::ProfileClosed::No => ProfileClosedView::No,
202 runtime::ProfileClosed::Maybe => ProfileClosedView::Maybe,
203 runtime::ProfileClosed::Implicitly => ProfileClosedView::Implicitly,
204 runtime::ProfileClosed::Explicitly => ProfileClosedView::Explicitly,
205 }
206}
207
208fn sketch_view(sketch: runtime::Sketch) -> SketchView {
209 SketchView {
210 type_: SketchViewType::Sketch,
211 id: sketch.id,
212 original_id: sketch.original_id,
213 paths: sketch.paths.into_iter().map(path_view).collect(),
214 inner_paths: sketch.inner_paths.into_iter().map(path_view).collect(),
215 on: sketch_surface_view(sketch.on),
216 start: base_path_view(sketch.start),
217 tags: sketch
218 .tags
219 .into_iter()
220 .map(|(name, tag)| (name, tag_identifier_view(tag)))
221 .collect(),
222 artifact_id: sketch.artifact_id,
223 units: sketch.units,
224 is_closed: profile_closed_view(sketch.is_closed),
225 }
226}
227
228fn surface_view(face_id: uuid::Uuid, tag: Option<TagNode>, geo_meta: runtime::GeoMeta) -> SurfaceView {
229 SurfaceView {
230 face_id,
231 tag: tag.map(tag_declarator_view),
232 geo_meta: geo_meta_view(geo_meta.id, geo_meta.metadata),
233 }
234}
235
236fn extrude_surface_view(surface: runtime::ExtrudeSurface) -> ExtrudeSurfaceView {
237 match surface {
238 runtime::ExtrudeSurface::ExtrudePlane(surface) => {
239 ExtrudeSurfaceView::ExtrudePlane(surface_view(surface.face_id, surface.tag, surface.geo_meta))
240 }
241 runtime::ExtrudeSurface::ExtrudeArc(surface) => {
242 ExtrudeSurfaceView::ExtrudeArc(surface_view(surface.face_id, surface.tag, surface.geo_meta))
243 }
244 runtime::ExtrudeSurface::Chamfer(surface) => {
245 ExtrudeSurfaceView::Chamfer(surface_view(surface.face_id, surface.tag, surface.geo_meta))
246 }
247 runtime::ExtrudeSurface::Fillet(surface) => {
248 ExtrudeSurfaceView::Fillet(surface_view(surface.face_id, surface.tag, surface.geo_meta))
249 }
250 }
251}
252
253fn solid_creator_view(creator: runtime::SolidCreator) -> SolidCreatorView {
254 match creator {
255 runtime::SolidCreator::Sketch(sketch) => SolidCreatorView::Sketch(sketch_view(sketch)),
256 runtime::SolidCreator::Face(face) => SolidCreatorView::Face {
257 face_id: face.face_id,
258 solid_id: face.solid_id,
259 sketch: sketch_view(face.sketch),
260 },
261 runtime::SolidCreator::Edge(edge) => SolidCreatorView::Edge {
262 edge_id: edge.edge_id,
263 body_id: edge.body_id,
264 },
265 runtime::SolidCreator::Procedural => SolidCreatorView::Procedural,
266 }
267}
268
269fn edge_cut_view(edge_cut: runtime::EdgeCut) -> EdgeCutView {
270 match edge_cut {
271 runtime::EdgeCut::Fillet {
272 id,
273 radius,
274 edge_id,
275 tag,
276 } => EdgeCutView::Fillet {
277 id,
278 radius: NumericValueView {
279 n: radius.n,
280 ty: radius.ty,
281 },
282 edge_id,
283 tag: (*tag).map(tag_declarator_view),
284 },
285 runtime::EdgeCut::Chamfer {
286 id,
287 length,
288 edge_id,
289 tag,
290 } => EdgeCutView::Chamfer {
291 id,
292 length: NumericValueView {
293 n: length.n,
294 ty: length.ty,
295 },
296 edge_id,
297 tag: (*tag).map(tag_declarator_view),
298 },
299 }
300}
301
302fn solid_view(solid: runtime::Solid) -> SolidView {
303 let original_id = solid.original_id();
304 let topology_id = solid.topology_id();
305 SolidView {
306 type_: SolidViewType::Solid,
307 id: solid.id,
308 original_id,
309 topology_id,
310 artifact_id: solid.artifact_id,
311 value: solid.value.into_iter().map(extrude_surface_view).collect(),
312 faces: solid
313 .faces
314 .into_iter()
315 .map(|(name, tag)| (name, tag_identifier_view(tag)))
316 .collect(),
317 creator: solid_creator_view(solid.creator),
318 start_cap_id: solid.start_cap_id,
319 end_cap_id: solid.end_cap_id,
320 edge_cuts: solid.edge_cuts.into_iter().map(edge_cut_view).collect(),
321 units: solid.units,
322 sectional: solid.sectional,
323 }
324}
325
326impl From<KclValue> for KclValueView {
327 fn from(full: KclValue) -> Self {
328 match full {
329 KclValue::Uuid { value, .. } => Self::Uuid { value },
330 KclValue::Bool { value, .. } => Self::Bool { value },
331 KclValue::Number { value, ty, .. } => Self::Number { value, ty },
332 KclValue::String { value, .. } => Self::String { value },
333 KclValue::Enum { value } => Self::Enum {
334 enum_name: value.enum_id().declared_name().to_owned(),
335 variant: value.variant().to_owned(),
336 },
337 KclValue::SketchVar { value } => Self::SketchVar {
338 value: Box::new(SketchVarView {
339 initial_value: value.initial_value,
340 ty: value.ty,
341 }),
342 },
343 KclValue::SketchConstraint { value } => Self::SketchConstraint {
344 value: json_view(&value),
345 },
346 KclValue::Tuple { value, .. } => Self::Tuple {
347 value: value.into_iter().map(Self::from).collect(),
348 },
349 KclValue::HomArray { value, .. } => Self::HomArray {
350 value: value.into_iter().map(Self::from).collect(),
351 },
352 KclValue::Object {
353 value, constrainable, ..
354 } => {
355 let mut fields: Vec<_> = value.into_iter().collect();
356 fields.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
357 Self::Object {
358 value: fields
359 .into_iter()
360 .map(|(name, value)| (name, Self::from(value)))
361 .collect(),
362 constrainable,
363 }
364 }
365 KclValue::TagIdentifier(tag) => Self::TagIdentifier(tag_identifier_view(*tag)),
366 KclValue::TagDeclarator(tag) => Self::TagDeclarator {
367 value: tag.inner.name.clone(),
368 },
369 KclValue::GdtAnnotation { value } => Self::GdtAnnotation {
370 value: Box::new(GdtAnnotationView { id: value.id }),
371 },
372 KclValue::CameraView { value } => Self::CameraView {
373 value: json_view(&value),
374 },
375 KclValue::NamedView { value } => Self::NamedView {
376 value: json_view(&value),
377 },
378 KclValue::Plane { value } => Self::Plane {
379 value: Box::new(plane_view(*value)),
380 },
381 KclValue::Face { value } => Self::Face {
382 value: Box::new(face_view(*value)),
383 },
384 KclValue::BoundedEdge { value, .. } => Self::BoundedEdge {
385 value: BoundedEdgeView {
386 face_id: value.face_id,
387 edge_id: value.edge_id,
388 lower_bound: value.lower_bound,
389 upper_bound: value.upper_bound,
390 },
391 },
392 KclValue::Segment { value } => Self::Segment {
393 value: json_view(&value),
394 },
395 KclValue::Sketch { value } => Self::Sketch {
396 value: Box::new(sketch_view(*value)),
397 },
398 KclValue::Solid { value } => Self::Solid {
399 value: Box::new(solid_view(*value)),
400 },
401 KclValue::Helix { value } => Self::Helix {
402 value: Box::new(HelixView {
403 value: value.value,
404 artifact_id: value.artifact_id,
405 revolutions: value.revolutions,
406 angle_start: value.angle_start,
407 ccw: value.ccw,
408 cylinder_id: value.cylinder_id,
409 units: value.units,
410 }),
411 },
412 KclValue::ImportedGeometry(value) => Self::ImportedGeometry(ImportedGeometryView {
413 id: value.id,
414 value: value.value,
415 }),
416 KclValue::Function { .. } => Self::Function {},
417 KclValue::Module { value, .. } => Self::Module { value },
418 KclValue::Type { experimental, .. } => Self::Type { experimental },
419 KclValue::KclNone { .. } => Self::KclNone {},
420 }
421 }
422}
423
424#[allow(dead_code)]
426pub trait PathViewExt {
427 fn get_base(&self) -> &BasePathView;
428 fn get_id(&self) -> uuid::Uuid {
429 self.get_base().geo_meta.id
430 }
431 fn get_tag(&self) -> Option<TagDeclaratorView> {
432 self.get_base().tag.clone()
433 }
434 fn arc_center_and_ccw(&self) -> Option<([f64; 2], bool)>;
435}
436
437impl PathViewExt for PathView {
438 fn get_base(&self) -> &BasePathView {
439 match self {
440 Self::ToPoint { base }
441 | Self::TangentialArcTo { base, .. }
442 | Self::TangentialArc { base, .. }
443 | Self::Circle { base, .. }
444 | Self::CircleThreePoint { base, .. }
445 | Self::ArcThreePoint { base, .. }
446 | Self::Horizontal { base, .. }
447 | Self::AngledLineTo { base, .. }
448 | Self::Base { base }
449 | Self::Arc { base, .. }
450 | Self::Ellipse { base, .. }
451 | Self::Conic { base }
452 | Self::Bezier { base, .. } => base,
453 }
454 }
455
456 fn arc_center_and_ccw(&self) -> Option<([f64; 2], bool)> {
457 match self {
458 Self::TangentialArcTo { center, ccw, .. }
459 | Self::TangentialArc { center, ccw, .. }
460 | Self::Arc { center, ccw, .. } => Some((*center, *ccw)),
461 Self::ArcThreePoint { p1, p2, p3, .. } => {
462 let circle = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]);
463 Some((circle.center, crate::std::utils::is_points_ccw(&[*p1, *p2, *p3]) > 0))
464 }
465 _ => None,
466 }
467 }
468}
469
470#[allow(dead_code)]
472pub trait SolidViewExt {
473 fn sketch(&self) -> Option<&SketchView>;
474 fn original_id(&self) -> uuid::Uuid;
475 fn topology_id(&self) -> uuid::Uuid;
476}
477
478impl SolidViewExt for SolidView {
479 fn sketch(&self) -> Option<&SketchView> {
480 match &self.creator {
481 SolidCreatorView::Sketch(sketch) | SolidCreatorView::Face { sketch, .. } => Some(sketch),
482 SolidCreatorView::Edge { .. } | SolidCreatorView::Procedural => None,
483 }
484 }
485
486 fn original_id(&self) -> uuid::Uuid {
487 self.original_id
488 }
489
490 fn topology_id(&self) -> uuid::Uuid {
491 self.topology_id
492 }
493}
494
495#[allow(dead_code)]
497pub trait ExtrudeSurfaceViewExt {
498 fn get_id(&self) -> uuid::Uuid;
499 fn get_tag(&self) -> Option<TagDeclaratorView>;
500 fn face_id(&self) -> uuid::Uuid;
501}
502
503impl ExtrudeSurfaceViewExt for ExtrudeSurfaceView {
504 fn get_id(&self) -> uuid::Uuid {
505 match self {
506 Self::ExtrudePlane(surface)
507 | Self::ExtrudeArc(surface)
508 | Self::Chamfer(surface)
509 | Self::Fillet(surface) => surface.geo_meta.id,
510 }
511 }
512
513 fn get_tag(&self) -> Option<TagDeclaratorView> {
514 match self {
515 Self::ExtrudePlane(surface)
516 | Self::ExtrudeArc(surface)
517 | Self::Chamfer(surface)
518 | Self::Fillet(surface) => surface.tag.clone(),
519 }
520 }
521
522 fn face_id(&self) -> uuid::Uuid {
523 match self {
524 Self::ExtrudePlane(surface)
525 | Self::ExtrudeArc(surface)
526 | Self::Chamfer(surface)
527 | Self::Fillet(surface) => surface.face_id,
528 }
529 }
530}
531
532#[allow(dead_code)]
534pub trait EdgeCutViewExt {
535 fn id(&self) -> uuid::Uuid;
536 fn edge_id(&self) -> uuid::Uuid;
537 fn tag(&self) -> Option<TagDeclaratorView>;
538}
539
540impl EdgeCutViewExt for EdgeCutView {
541 fn id(&self) -> uuid::Uuid {
542 match self {
543 Self::Fillet { id, .. } | Self::Chamfer { id, .. } => *id,
544 }
545 }
546
547 fn edge_id(&self) -> uuid::Uuid {
548 match self {
549 Self::Fillet { edge_id, .. } | Self::Chamfer { edge_id, .. } => *edge_id,
550 }
551 }
552
553 fn tag(&self) -> Option<TagDeclaratorView> {
554 match self {
555 Self::Fillet { tag, .. } | Self::Chamfer { tag, .. } => tag.clone(),
556 }
557 }
558}
559
560#[cfg(test)]
561mod tests {
562 use super::*;
563
564 fn number(value: f64) -> KclValue {
565 KclValue::Number {
566 value,
567 ty: Default::default(),
568 meta: Vec::new(),
569 }
570 }
571
572 #[test]
573 fn object_views_have_stable_field_order() {
574 let runtime_value = KclValue::Object {
575 value: std::collections::HashMap::from([
576 ("zeta".to_owned(), number(2.0)),
577 ("alpha".to_owned(), number(1.0)),
578 ]),
579 constrainable: false,
580 object_kind: Default::default(),
581 meta: Vec::new(),
582 };
583
584 let KclValueView::Object { value, .. } = KclValueView::from(runtime_value) else {
585 panic!("expected object view");
586 };
587
588 assert_eq!(value.keys().map(String::as_str).collect::<Vec<_>>(), ["alpha", "zeta"]);
589 }
590}