Skip to main content

geop_core_editor/view/
kept.rs

1//! How a [`PartView`] is kept between processes (see
2//! [`geop_core_program::kept`]): what a drawer drew of a part, and a placed
3//! part as drawn, read back instead of meshed again. Written exactly, so
4//! that what is picked in a view read back is what is picked in the view
5//! drawn.
6//!
7//! Each face's mesh is put in the pool by itself, so that two views of
8//! revisions of one model — and the same part drawn alone and placed —
9//! write a mesh they have alike once; so is the view of each part placed in
10//! it, by its component.
11//!
12//! A view with sketches is not kept: a sketch is drawn from what it is,
13//! cheaply, and is drawn again ([`write`] refuses it).
14
15use std::{
16    collections::{BTreeMap, HashMap},
17    rc::Rc,
18};
19
20use geop_core_math::{
21    geop_error::{GeopError, GeopResult},
22    primitives::{CoordinateSystem, DatumComponent, DatumKind, Pose},
23    scalars::Scalar,
24    vector::{Vector, Vector3},
25};
26use geop_core_program::{
27    kept::Pool,
28    workspace::{base64, from_base64},
29};
30use serde::{Deserialize, Serialize};
31
32use super::{
33    FaceMesh, PartView, ViewAnnotation, ViewDatum, ViewEdge, ViewFace, ViewInstance, ViewVertex,
34};
35use crate::editing::Extent;
36
37/// A [`PartView`] as it is written.
38#[derive(Serialize, Deserialize)]
39struct Written {
40    model: String,
41    vertices: Vec<Vertex>,
42    edges: Vec<Edge>,
43    faces: Vec<Face>,
44    datum_target: String,
45    datums: Vec<Datum>,
46    annotations: Vec<Annotation>,
47    solids: Vec<String>,
48    instances: Vec<Instance>,
49    #[serde(with = "geop_core_math::exact")]
50    center: Vector3,
51    #[serde(with = "geop_core_math::exact")]
52    size: Scalar,
53    color: Option<String>,
54    face_colors: BTreeMap<String, String>,
55}
56
57#[derive(Serialize, Deserialize)]
58struct Vertex {
59    name: String,
60    solid: Option<String>,
61    faces: Vec<String>,
62    #[serde(with = "geop_core_math::exact")]
63    at: Vector3,
64}
65
66#[derive(Serialize, Deserialize)]
67struct Edge {
68    name: String,
69    solid: Option<String>,
70    faces: Vec<String>,
71    #[serde(with = "geop_core_math::exact")]
72    polyline: Vec<Vector3>,
73    #[serde(default)]
74    within_a_surface: bool,
75    ends: [String; 2],
76}
77
78/// A face, its mesh by its key in the pool.
79#[derive(Serialize, Deserialize)]
80struct Face {
81    name: String,
82    #[serde(default)]
83    also: Vec<String>,
84    solid: Option<String>,
85    feature: Option<String>,
86    mesh: String,
87}
88
89/// A [`FaceMesh`] as it is put in the pool: its key, each corner its
90/// triangles share once — its point and its normal, six numbers, the bits
91/// of each in base64 — and each triangle by its corners' indices, in
92/// base64 too. A mesh's numbers are sharp ([`FaceMesh::new`]): the bits of
93/// one are all of it.
94#[derive(Serialize, Deserialize)]
95struct Mesh {
96    key: String,
97    corners: String,
98    triangles: String,
99}
100
101impl Mesh {
102    fn of(mesh: &FaceMesh) -> GeopResult<Self> {
103        let mut index: HashMap<[u64; 6], u32> = HashMap::new();
104        let (mut corners, mut triangles) = (Vec::new(), Vec::new());
105        for (points, normals) in mesh.triangles.iter().zip(&mesh.normals) {
106            for (p, n) in points.iter().zip(normals) {
107                let mut bits = [0; 6];
108                let numbers = p.to_array().into_iter().chain(n.to_array());
109                for (bit, x) in bits.iter_mut().zip(numbers) {
110                    if x.lo.to_bits() != x.hi.to_bits() {
111                        return Err(GeopError::new("a mesh with a corner not sharp is not kept"));
112                    }
113                    *bit = x.lo.to_bits();
114                }
115                let next = index.len() as u32;
116                let at = *index.entry(bits).or_insert_with(|| {
117                    corners.extend(bits.iter().flat_map(|b| b.to_le_bytes()));
118                    next
119                });
120                triangles.extend(at.to_le_bytes());
121            }
122        }
123        Ok(Self {
124            key: mesh.key.clone(),
125            corners: base64(&corners),
126            triangles: base64(&triangles),
127        })
128    }
129
130    fn mesh(self) -> GeopResult<FaceMesh> {
131        let corner_bytes = from_base64(&self.corners)?;
132        let corners: Vec<[Scalar; 6]> = (corner_bytes.as_chunks::<48>().0.iter())
133            .map(|corner| {
134                std::array::from_fn(|k| {
135                    let bytes = corner.get(8 * k..8 * k + 8).unwrap_or_default();
136                    let bits = u64::from_le_bytes(bytes.try_into().unwrap_or_default());
137                    Scalar::from_f64(f64::from_bits(bits))
138                })
139            })
140            .collect();
141        let corner = |bytes: &[u8]| -> GeopResult<&[Scalar; 6]> {
142            let at = u32::from_le_bytes(bytes.try_into().unwrap_or_default());
143            (corners.get(at as usize)).ok_or_else(|| broken("a mesh's corners"))
144        };
145        let (mut triangles, mut normals) = (Vec::new(), Vec::new());
146        let triangle_bytes = from_base64(&self.triangles)?;
147        for triangle in triangle_bytes.as_chunks::<12>().0 {
148            let [a, b, c] = [0, 4, 8].map(|k| corner(triangle.get(k..k + 4).unwrap_or_default()));
149            let [a, b, c] = [a?, b?, c?];
150            let point = |c: &[Scalar; 6]| Vector3::from_array([c[0], c[1], c[2]]);
151            let normal = |c: &[Scalar; 6]| Vector3::from_array([c[3], c[4], c[5]]);
152            triangles.push([point(a), point(b), point(c)]);
153            normals.push([normal(a), normal(b), normal(c)]);
154        }
155        Ok(FaceMesh {
156            key: self.key,
157            triangles,
158            normals,
159        })
160    }
161}
162
163#[derive(Serialize, Deserialize)]
164struct Datum {
165    name: String,
166    kind: DatumKind,
167    #[serde(with = "geop_core_math::exact")]
168    frame: CoordinateSystem,
169    components: Vec<(DatumComponent, String)>,
170}
171
172#[derive(Serialize, Deserialize)]
173struct Annotation {
174    name: String,
175    label: String,
176    #[serde(with = "geop_core_math::exact")]
177    polyline: Vec<Vector3>,
178}
179
180/// A part placed, its part's view by its key in the pool.
181#[derive(Serialize, Deserialize)]
182struct Instance {
183    name: String,
184    component: String,
185    #[serde(with = "geop_core_math::exact")]
186    pose: Pose,
187    view: String,
188    state: Option<String>,
189    fixed: bool,
190}
191
192/// `view` written, its meshes and the views of its placed parts put in
193/// `pool`. A view with sketches is refused.
194pub fn write(view: &PartView, pool: &mut Pool) -> GeopResult<String> {
195    text(&written(view, pool, &mut HashMap::new())?)
196}
197
198/// The view `text` writes, its parts taken from `pool`.
199pub fn read(text: &str, pool: &Pool) -> GeopResult<PartView> {
200    let written: Written = serde_json::from_str(text)
201        .map_err(|e| GeopError::new(format!("reading a kept view: {e}")))?;
202    view_of(written, pool, &mut HashMap::new())
203}
204
205/// Puts `view` in `pool`, as [`write`] writes it, and gives what it is
206/// taken by ([`get`]): a view kept as a part of another value.
207pub fn put(view: &PartView, pool: &mut Pool) -> GeopResult<String> {
208    let written = written(view, pool, &mut HashMap::new())?;
209    pool.put(&written)
210}
211
212/// The view [`put`] put as `key` in `pool`: read once, however many
213/// records read it — the drawing of a part placed a thousand times.
214pub fn get(key: &str, pool: &Pool) -> GeopResult<Rc<PartView>> {
215    pool.shared(key, || view_of(pool.get(key)?, pool, &mut HashMap::new()))
216}
217
218fn text<T: Serialize>(value: &T) -> GeopResult<String> {
219    serde_json::to_string(value).map_err(|e| GeopError::new(format!("writing a kept view: {e}")))
220}
221
222/// `view` as it is written, its parts put in `pool` — each placed part's
223/// view once, its key kept in `placed` by the view.
224fn written(
225    view: &PartView,
226    pool: &mut Pool,
227    placed: &mut HashMap<*const PartView, String>,
228) -> GeopResult<Written> {
229    if !view.sketches.is_empty() {
230        return Err(GeopError::new(
231            "a view with sketches is not kept: its sketches are drawn again",
232        ));
233    }
234    let faces = (view.faces.iter())
235        .map(|face| {
236            Ok(Face {
237                also: face.also.clone(),
238                name: face.name.clone(),
239                solid: face.solid.clone(),
240                feature: face.feature.clone(),
241                mesh: pool.put(&Mesh::of(&face.mesh)?)?,
242            })
243        })
244        .collect::<GeopResult<_>>()?;
245    let instances = (view.instances.iter())
246        .map(|instance| {
247            let key = match placed.get(&Rc::as_ptr(&instance.view)) {
248                Some(key) => key.clone(),
249                None => {
250                    let inner = written(&instance.view, pool, placed)?;
251                    let key = pool.put(&inner)?;
252                    placed.insert(Rc::as_ptr(&instance.view), key.clone());
253                    key
254                }
255            };
256            Ok(Instance {
257                name: instance.name.clone(),
258                component: instance.component.clone(),
259                pose: instance.pose,
260                view: key,
261                state: instance.state.clone(),
262                fixed: instance.fixed,
263            })
264        })
265        .collect::<GeopResult<_>>()?;
266    Ok(Written {
267        model: view.model.clone(),
268        vertices: (view.vertices.iter())
269            .map(|v| Vertex {
270                name: v.name.clone(),
271                solid: v.solid.clone(),
272                faces: v.faces.clone(),
273                at: v.at,
274            })
275            .collect(),
276        edges: (view.edges.iter())
277            .map(|e| Edge {
278                name: e.name.clone(),
279                solid: e.solid.clone(),
280                faces: e.faces.clone(),
281                polyline: e.polyline.clone(),
282                within_a_surface: e.within_a_surface,
283                ends: e.ends.clone(),
284            })
285            .collect(),
286        faces,
287        datum_target: view.datum_target.clone(),
288        datums: (view.datums.iter())
289            .map(|d| Datum {
290                name: d.name.clone(),
291                kind: d.kind,
292                frame: d.frame.clone(),
293                components: d.components.clone(),
294            })
295            .collect(),
296        annotations: (view.annotations.iter())
297            .map(|a| Annotation {
298                name: a.name.clone(),
299                label: a.label.clone(),
300                polyline: a.polyline.clone(),
301            })
302            .collect(),
303        solids: view.solids.clone(),
304        instances,
305        center: view.extent.center,
306        size: view.extent.size,
307        color: view.color.clone(),
308        face_colors: view.face_colors.clone(),
309    })
310}
311
312/// The view `written` writes, its parts taken from `pool` — each placed
313/// part's view once, by its key in `placed`, shared by every instance of it.
314fn view_of(
315    written: Written,
316    pool: &Pool,
317    placed: &mut HashMap<String, Rc<PartView>>,
318) -> GeopResult<PartView> {
319    let faces = (written.faces.into_iter())
320        .map(|face| {
321            let mesh: Mesh = pool.get(&face.mesh)?;
322            Ok(ViewFace {
323                name: face.name,
324                also: face.also,
325                solid: face.solid,
326                feature: face.feature,
327                mesh: Rc::new(mesh.mesh()?),
328            })
329        })
330        .collect::<GeopResult<_>>()?;
331    let mut instances = Vec::new();
332    for instance in written.instances {
333        let view = match placed.get(&instance.view) {
334            Some(view) => view.clone(),
335            None => {
336                let inner: Written = pool.get(&instance.view)?;
337                let view = Rc::new(view_of(inner, pool, placed)?);
338                placed.insert(instance.view.clone(), view.clone());
339                view
340            }
341        };
342        let origin = CoordinateSystem::world_at(Vector::zero());
343        instances.push(ViewInstance {
344            frame: instance.pose.motion().apply_frame(&origin)?,
345            name: instance.name,
346            component: instance.component,
347            pose: instance.pose,
348            view,
349            state: instance.state,
350            fixed: instance.fixed,
351            color: None,
352        });
353    }
354    Ok(PartView {
355        model: written.model,
356        vertices: (written.vertices.into_iter())
357            .map(|v| ViewVertex {
358                name: v.name,
359                solid: v.solid,
360                faces: v.faces,
361                at: v.at,
362            })
363            .collect(),
364        edges: (written.edges.into_iter())
365            .map(|e| ViewEdge {
366                name: e.name,
367                solid: e.solid,
368                faces: e.faces,
369                polyline: e.polyline,
370                within_a_surface: e.within_a_surface,
371                ends: e.ends,
372            })
373            .collect(),
374        faces,
375        sketches: Vec::new(),
376        datum_target: written.datum_target,
377        datums: (written.datums.into_iter())
378            .map(|d| ViewDatum {
379                name: d.name,
380                kind: d.kind,
381                frame: d.frame,
382                components: d.components,
383            })
384            .collect(),
385        annotations: (written.annotations.into_iter())
386            .map(|a| ViewAnnotation {
387                name: a.name,
388                label: a.label,
389                polyline: a.polyline,
390            })
391            .collect(),
392        solids: written.solids,
393        instances,
394        extent: Extent {
395            center: written.center,
396            size: written.size,
397        },
398        color: written.color,
399        face_colors: written.face_colors,
400        faces_tree: std::cell::RefCell::default(),
401    })
402}
403
404fn broken(what: &str) -> GeopError {
405    GeopError::new(format!("reading a kept view: {what} are not all there"))
406}