1use geop_core_geometry::nurb_curve::{NurbCurve2D, NurbCurve3D};
9use geop_core_math::{
10 geop_error::GeopResult,
11 scalars::Scalar,
12 vector::{Vector2, Vector3},
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum ViewKind {
20 Front,
21 Top,
22 Right,
23 Left,
24 Bottom,
25 Back,
26 Iso,
28}
29
30impl ViewKind {
31 pub const ALL: [ViewKind; 7] = [
32 ViewKind::Front,
33 ViewKind::Top,
34 ViewKind::Right,
35 ViewKind::Left,
36 ViewKind::Bottom,
37 ViewKind::Back,
38 ViewKind::Iso,
39 ];
40
41 pub fn name(self) -> &'static str {
43 match self {
44 ViewKind::Front => "front",
45 ViewKind::Top => "top",
46 ViewKind::Right => "right",
47 ViewKind::Left => "left",
48 ViewKind::Bottom => "bottom",
49 ViewKind::Back => "back",
50 ViewKind::Iso => "iso",
51 }
52 }
53
54 pub fn from_name(name: &str) -> Option<ViewKind> {
56 ViewKind::ALL.into_iter().find(|k| k.name() == name)
57 }
58
59 pub fn frame(self) -> GeopResult<ViewFrame> {
61 let unit = |index: usize, negative: bool| ViewAxis::Unit { index, negative };
62 let (direction, right, up) = match self {
63 ViewKind::Front => (unit(1, false), unit(0, false), unit(2, false)),
64 ViewKind::Back => (unit(1, true), unit(0, true), unit(2, false)),
65 ViewKind::Top => (unit(2, true), unit(0, false), unit(1, false)),
66 ViewKind::Bottom => (unit(2, false), unit(0, false), unit(1, true)),
67 ViewKind::Right => (unit(0, true), unit(1, false), unit(2, false)),
68 ViewKind::Left => (unit(0, false), unit(1, true), unit(2, false)),
69 ViewKind::Iso => {
70 let v = |x: f64, y: f64, z: f64| {
71 Vector3::from_array([
72 Scalar::from_f64(x),
73 Scalar::from_f64(y),
74 Scalar::from_f64(z),
75 ])
76 };
77 (
78 ViewAxis::General(v(-1.0, 1.0, -1.0).normalize()?),
79 ViewAxis::General(v(1.0, 1.0, 0.0).normalize()?),
80 ViewAxis::General(v(-1.0, 1.0, 2.0).normalize()?),
81 )
82 }
83 };
84 Ok(ViewFrame {
85 direction,
86 right,
87 up,
88 })
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
95#[serde(into = "String", try_from = "String")]
96pub enum DrawnView {
97 View(ViewKind),
98 Section,
99}
100
101impl DrawnView {
102 pub fn name(self) -> &'static str {
103 match self {
104 DrawnView::View(kind) => kind.name(),
105 DrawnView::Section => "section",
106 }
107 }
108
109 pub fn orthographic(self) -> bool {
112 self != DrawnView::View(ViewKind::Iso)
113 }
114}
115
116impl From<DrawnView> for String {
117 fn from(view: DrawnView) -> String {
118 view.name().to_string()
119 }
120}
121
122impl TryFrom<String> for DrawnView {
123 type Error = String;
124
125 fn try_from(name: String) -> Result<Self, String> {
126 match name.as_str() {
127 "section" => Ok(DrawnView::Section),
128 _ => ViewKind::from_name(&name)
129 .map(DrawnView::View)
130 .ok_or_else(|| format!("{name:?} names no view of a drawing")),
131 }
132 }
133}
134
135impl std::fmt::Display for DrawnView {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 f.write_str(self.name())
138 }
139}
140
141#[derive(Clone, Copy, Debug)]
146pub enum ViewAxis {
147 Unit {
149 index: usize,
150 negative: bool,
151 },
152 General(Vector3),
153}
154
155impl ViewAxis {
156 pub fn dot(&self, v: &Vector3) -> Scalar {
158 match *self {
159 ViewAxis::Unit { index, negative } => {
160 if negative {
161 v[index].neg()
162 } else {
163 v[index]
164 }
165 }
166 ViewAxis::General(axis) => axis.prod_dot(v),
167 }
168 }
169
170 pub fn vector(&self) -> Vector3 {
171 match *self {
172 ViewAxis::Unit { index, negative } => {
173 let mut v = Vector3::zero();
174 v[index] = if negative {
175 Scalar::ONE.neg()
176 } else {
177 Scalar::ONE
178 };
179 v
180 }
181 ViewAxis::General(axis) => axis,
182 }
183 }
184}
185
186#[derive(Clone, Copy, Debug)]
189pub struct ViewFrame {
190 pub direction: ViewAxis,
191 pub right: ViewAxis,
192 pub up: ViewAxis,
193}
194
195impl ViewFrame {
196 pub fn looking(direction: Vector3, up: Vector3) -> GeopResult<Self> {
200 let d = direction.normalize()?;
201 let right = d.prod_cross(&up).normalize()?;
202 let up = right.prod_cross(&d).normalize()?;
203 Ok(ViewFrame {
204 direction: ViewAxis::General(d),
205 right: ViewAxis::General(right),
206 up: ViewAxis::General(up),
207 })
208 }
209
210 pub fn project_point(&self, p: &Vector3) -> Vector2 {
212 Vector2::from_array([self.right.dot(p), self.up.dot(p)])
213 }
214
215 pub fn project_curve(&self, curve: &NurbCurve3D) -> GeopResult<NurbCurve2D> {
219 let points = curve
220 .control_points
221 .iter()
222 .map(|q| {
223 let h = q.head::<3>();
224 Vector3::from_array([self.right.dot(&h), self.up.dot(&h), q[3]])
225 })
226 .collect();
227 NurbCurve2D::try_new(curve.degree, points, curve.knot_vector.clone())
228 }
229
230 pub fn toward_eye(&self) -> Vector3 {
232 self.direction.vector().neg()
233 }
234}