geop_ops_model/
sketch3d.rs1use crate::{
9 Chain, Curve, Line, NamedModel, Path, Point,
10 datums::Datums,
11 model,
12 sketch::{PlacedSketch, chain_of_pieces},
13};
14use geop_core_editor::{one_of, selectable::SelectableElement, view::drawn};
15use geop_core_geometry::{nurb_curve::NurbCurve3D, shape::Axis};
16use geop_core_math::{
17 geop_error::{GeopError, GeopResult, WithContext},
18 vector::Vector3,
19 with_context,
20};
21use geop_core_program::{
22 element::{ElementReference, HasElements, Kind, PickedElement, whole},
23 reader::Reader,
24 target::{Target, TargetReference},
25};
26
27pub type Sketch3d = geop_core_sketch::space::Sketch3d;
29pub type Constraint3d = geop_core_sketch::space::Constraint3d;
31
32one_of! {
33 pub enum PointSource -> Vector3 {
36 Vertex(ElementReference<NamedModel, Point>),
37 Datum(ElementReference<Datums, Point>),
38 Sketch(ElementReference<PlacedSketch, Point>),
39 }
40}
41
42one_of! {
43 pub enum CurveSource -> Chain<NurbCurve3D> {
46 Edge(ElementReference<NamedModel, Curve>),
47 Sketch(ElementReference<PlacedSketch, Curve>),
48 }
49}
50
51one_of! {
52 pub enum LineSource -> Axis {
55 Edge(ElementReference<NamedModel, Line>),
56 Datum(ElementReference<Datums, Line>),
57 Sketch(ElementReference<PlacedSketch, Line>),
58 }
59}
60
61#[derive(Clone, Debug, PartialEq)]
67pub struct PlacedSketch3d {
68 pub name: String,
69 pub sketch: Sketch3d,
70 pub edges: Vec<String>,
71}
72
73const PREFIX: &str = "sketch3d:";
75
76pub fn sketch3d(name: &str) -> TargetReference<PlacedSketch3d> {
78 TargetReference::new(format!("{PREFIX}{name}"))
79}
80
81pub fn sketch3d_path(name: &str) -> ElementReference<PlacedSketch3d, Path> {
83 ElementReference::whole(sketch3d(name))
84}
85
86impl PlacedSketch3d {
87 pub fn path(&self) -> GeopResult<Chain<NurbCurve3D>> {
90 let ctx = with_context!("the curves of the 3-D sketch {:?}", self.name);
91 let chains = self.sketch.chains().with_context(ctx)?;
92 let [chain] = chains.as_slice() else {
93 return Err(GeopError::new(format!(
94 "it has {} chains of curves: a path is one chain of curves, or one loop",
95 chains.len()
96 )))
97 .with_context(ctx);
98 };
99 let geometry = self.sketch.enclose().with_context(ctx)?;
100 let pieces = chain.to_nurbs(&self.sketch, &geometry).with_context(ctx)?;
101 Ok(chain_of_pieces(&self.name, pieces, chain.closed))
102 }
103}
104
105impl Target for PlacedSketch3d {}
107
108impl Kind for PlacedSketch3d {
110 const NAME: &'static str = "sketch3d";
111}
112
113impl HasElements<PlacedSketch3d> for PlacedSketch3d {
114 type Element<'a> = &'a PlacedSketch3d;
115
116 fn element(&self, name: &str) -> GeopResult<&PlacedSketch3d> {
117 whole(self, name)
118 }
119
120 fn names_target(name: &str) -> bool {
121 name.starts_with(PREFIX)
122 }
123}
124
125impl HasElements<Path> for PlacedSketch3d {
126 type Element<'a> = Chain<NurbCurve3D>;
127
128 fn element(&self, name: &str) -> GeopResult<Chain<NurbCurve3D>> {
129 whole(self, name)?.path()
130 }
131
132 fn names_target(name: &str) -> bool {
133 name.starts_with(PREFIX)
134 }
135}
136
137impl SelectableElement<PlacedSketch3d> for Path {
140 const PICKED_FROM: &'static [&'static str] = &[drawn::EDGE];
141
142 fn select(
143 reader: &Reader,
144 hit: &PickedElement,
145 ) -> Option<ElementReference<PlacedSketch3d, Self>> {
146 (hit.kind == drawn::EDGE && hit.target == *model().location()).then_some(())?;
147 reader
148 .targets_of::<PlacedSketch3d>()
149 .into_iter()
150 .find(|target| {
151 let whole =
152 ElementReference::<PlacedSketch3d, PlacedSketch3d>::whole(target.clone());
153 (reader.retrieve_element(&whole))
154 .is_ok_and(|placed| placed.edges.contains(&hit.name))
155 })
156 .map(ElementReference::whole)
157 }
158
159 fn drawn(reader: &Reader, path: &ElementReference<PlacedSketch3d, Self>) -> Vec<PickedElement> {
160 let whole =
161 ElementReference::<PlacedSketch3d, PlacedSketch3d>::whole(path.target().clone());
162 let edges = (reader.retrieve_element(&whole).ok()).map(|placed| placed.edges.clone());
163 (edges.unwrap_or_default().into_iter())
164 .map(|edge| PickedElement {
165 target: model().location().clone(),
166 kind: drawn::EDGE.to_string(),
167 name: edge,
168 })
169 .collect()
170 }
171}