session_rust/
arrow.rs

1use crate::{Line, Mesh, Point, Vector, Xform};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5/// An arrow geometry defined by a line and radius, the head is uniformly scaled.
6///
7/// The arrow is generated as a 10-sided cylinder body and an 8-sided cone head
8/// that is oriented along the line direction and scaled to match the line length and specified radius.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(tag = "type", rename = "Arrow")]
11pub struct Arrow {
12    pub line: Line,
13    pub mesh: Mesh,
14    pub radius: f32,
15    pub guid: String,
16    pub name: String,
17    #[serde(default = "Xform::identity")]
18    pub xform: Xform,
19}
20
21impl Arrow {
22    /// Creates a new `Arrow` from a line and radius.
23    ///
24    /// # Arguments
25    ///
26    /// * `line` - The centerline of the arrow
27    /// * `radius` - The radius of the arrow body
28    ///
29    /// # Returns
30    ///
31    /// A new `Arrow` with a cylinder body and cone head mesh
32    pub fn new(line: Line, radius: f32) -> Self {
33        let mesh = Self::create_arrow_mesh(&line, radius);
34        Self {
35            line,
36            mesh,
37            radius,
38            guid: Uuid::new_v4().to_string(),
39            name: "my_arrow".to_string(),
40            xform: Xform::identity(),
41        }
42    }
43
44    fn create_arrow_mesh(line: &Line, radius: f32) -> Mesh {
45        let start = line.start();
46        let line_vec = line.to_vector();
47        let length = line.length();
48
49        let z_axis = line_vec.normalize();
50        let x_axis = if z_axis.z().abs() < 0.9 {
51            Vector::new(0.0, 0.0, 1.0).cross(&z_axis).normalize()
52        } else {
53            Vector::new(1.0, 0.0, 0.0).cross(&z_axis).normalize()
54        };
55        let y_axis = z_axis.cross(&x_axis).normalize();
56
57        let cone_length = length * 0.2;
58        let body_length = length * 0.8;
59
60        let body_center = Point::new(
61            start.x() + line_vec.x() * 0.4,
62            start.y() + line_vec.y() * 0.4,
63            start.z() + line_vec.z() * 0.4,
64        );
65
66        let cone_base_center = Point::new(
67            start.x() + line_vec.x() * 0.9,
68            start.y() + line_vec.y() * 0.9,
69            start.z() + line_vec.z() * 0.9,
70        );
71
72        let body_scale = Xform::scale_xyz(radius * 2.0, radius * 2.0, body_length);
73        let rotation = Xform::from_cols(x_axis, y_axis, z_axis);
74        let body_translation =
75            Xform::translation(body_center.x(), body_center.y(), body_center.z());
76        let body_xform = &body_translation * &(&rotation * &body_scale);
77
78        let cone_scale = Xform::scale_xyz(radius * 3.0, radius * 3.0, cone_length);
79        let cone_translation = Xform::translation(
80            cone_base_center.x(),
81            cone_base_center.y(),
82            cone_base_center.z(),
83        );
84        let cone_xform = &cone_translation * &(&rotation * &cone_scale);
85
86        let body_geometry = Self::unit_cylinder_geometry();
87        let cone_geometry = Self::unit_cone_geometry();
88
89        let mut mesh = Mesh::new();
90
91        let mut body_vertex_map = Vec::new();
92        for v in &body_geometry.0 {
93            let transformed = body_xform.transformed_point(v);
94            let key = mesh.add_vertex(transformed, None);
95            body_vertex_map.push(key);
96        }
97
98        for tri in &body_geometry.1 {
99            let face_vertices = vec![
100                body_vertex_map[tri[0]],
101                body_vertex_map[tri[1]],
102                body_vertex_map[tri[2]],
103            ];
104            mesh.add_face(face_vertices, None);
105        }
106
107        let mut cone_vertex_map = Vec::new();
108        for v in &cone_geometry.0 {
109            let transformed = cone_xform.transformed_point(v);
110            let key = mesh.add_vertex(transformed, None);
111            cone_vertex_map.push(key);
112        }
113
114        for tri in &cone_geometry.1 {
115            let face_vertices = vec![
116                cone_vertex_map[tri[0]],
117                cone_vertex_map[tri[1]],
118                cone_vertex_map[tri[2]],
119            ];
120            mesh.add_face(face_vertices, None);
121        }
122
123        mesh
124    }
125
126    fn unit_cylinder_geometry() -> (Vec<Point>, Vec<[usize; 3]>) {
127        let vertices = vec![
128            Point::new(0.5, 0.0, -0.5),
129            Point::new(0.404508, 0.293893, -0.5),
130            Point::new(0.154508, 0.475528, -0.5),
131            Point::new(-0.154508, 0.475528, -0.5),
132            Point::new(-0.404508, 0.293893, -0.5),
133            Point::new(-0.5, 0.0, -0.5),
134            Point::new(-0.404508, -0.293893, -0.5),
135            Point::new(-0.154508, -0.475528, -0.5),
136            Point::new(0.154508, -0.475528, -0.5),
137            Point::new(0.404508, -0.293893, -0.5),
138            Point::new(0.5, 0.0, 0.5),
139            Point::new(0.404508, 0.293893, 0.5),
140            Point::new(0.154508, 0.475528, 0.5),
141            Point::new(-0.154508, 0.475528, 0.5),
142            Point::new(-0.404508, 0.293893, 0.5),
143            Point::new(-0.5, 0.0, 0.5),
144            Point::new(-0.404508, -0.293893, 0.5),
145            Point::new(-0.154508, -0.475528, 0.5),
146            Point::new(0.154508, -0.475528, 0.5),
147            Point::new(0.404508, -0.293893, 0.5),
148        ];
149
150        let triangles = vec![
151            [0, 1, 11],
152            [0, 11, 10],
153            [1, 2, 12],
154            [1, 12, 11],
155            [2, 3, 13],
156            [2, 13, 12],
157            [3, 4, 14],
158            [3, 14, 13],
159            [4, 5, 15],
160            [4, 15, 14],
161            [5, 6, 16],
162            [5, 16, 15],
163            [6, 7, 17],
164            [6, 17, 16],
165            [7, 8, 18],
166            [7, 18, 17],
167            [8, 9, 19],
168            [8, 19, 18],
169            [9, 0, 10],
170            [9, 10, 19],
171        ];
172
173        (vertices, triangles)
174    }
175
176    fn unit_cone_geometry() -> (Vec<Point>, Vec<[usize; 3]>) {
177        let vertices = vec![
178            Point::new(0.0, 0.0, 0.5),
179            Point::new(0.5, 0.0, -0.5),
180            Point::new(0.353553, -0.353553, -0.5),
181            Point::new(0.0, -0.5, -0.5),
182            Point::new(-0.353553, -0.353553, -0.5),
183            Point::new(-0.5, 0.0, -0.5),
184            Point::new(-0.353553, 0.353553, -0.5),
185            Point::new(0.0, 0.5, -0.5),
186            Point::new(0.353553, 0.353553, -0.5),
187        ];
188
189        let triangles = vec![
190            [0, 2, 1],
191            [0, 3, 2],
192            [0, 4, 3],
193            [0, 5, 4],
194            [0, 6, 5],
195            [0, 7, 6],
196            [0, 8, 7],
197            [0, 1, 8],
198        ];
199
200        (vertices, triangles)
201    }
202
203    ///////////////////////////////////////////////////////////////////////////////////////////
204    // JSON
205    ///////////////////////////////////////////////////////////////////////////////////////////
206
207    /// Serializes the Arrow to a JSON string.
208    pub fn jsondump(&self) -> Result<String, Box<dyn std::error::Error>> {
209        let data = serde_json::json!({
210            "type": "Arrow",
211            "guid": self.guid,
212            "name": self.name,
213            "radius": self.radius,
214            "line": self.line,
215            "mesh": self.mesh.jsondump()
216        });
217        Ok(serde_json::to_string_pretty(&data)?)
218    }
219
220    /// Deserializes an Arrow from a JSON string.
221    pub fn jsonload(json_data: &str) -> Result<Self, Box<dyn std::error::Error>> {
222        Ok(serde_json::from_str(json_data)?)
223    }
224
225    /// Serializes the Arrow to a JSON file.
226    pub fn to_json(&self, filepath: &str) -> Result<(), Box<dyn std::error::Error>> {
227        let json = self.jsondump()?;
228        std::fs::write(filepath, json)?;
229        Ok(())
230    }
231
232    /// Deserializes an Arrow from a JSON file.
233    pub fn from_json(filepath: &str) -> Result<Self, Box<dyn std::error::Error>> {
234        let json = std::fs::read_to_string(filepath)?;
235        Self::jsonload(&json)
236    }
237}
238
239#[cfg(test)]
240#[path = "arrow_test.rs"]
241mod arrow_test;