fj_core/operations/transform/
mod.rs

1//! API for transforming objects
2
3mod curve;
4mod cycle;
5mod edge;
6mod face;
7mod region;
8mod shell;
9mod solid;
10mod surface;
11mod vertex;
12
13use std::collections::{btree_map, BTreeMap};
14
15use fj_math::{Transform, Vector};
16use type_map::TypeMap;
17
18use crate::{
19    objects::{AnyObject, Stored},
20    operations::insert::Insert,
21    storage::{Handle, ObjectId},
22    Core,
23};
24
25use super::derive::DeriveFrom;
26
27/// Transform an object
28///
29/// # Implementation Note
30///
31/// So far, a general `transform` method is available, along some convenience
32/// methods for more specific transformations.
33///
34/// More convenience methods can be added as required. The only reason this
35/// hasn't been done so far, is that no one has put in the work yet.
36pub trait TransformObject: Sized {
37    /// Transform the object
38    fn transform(&self, transform: &Transform, core: &mut Core) -> Self {
39        let mut cache = TransformCache::default();
40        self.transform_with_cache(transform, core, &mut cache)
41    }
42
43    /// Transform the object using the provided cache
44    fn transform_with_cache(
45        &self,
46        transform: &Transform,
47        core: &mut Core,
48        cache: &mut TransformCache,
49    ) -> Self;
50
51    /// Translate the object
52    ///
53    /// Convenience wrapper around [`TransformObject::transform`].
54    fn translate(&self, offset: impl Into<Vector<3>>, core: &mut Core) -> Self {
55        self.transform(&Transform::translation(offset), core)
56    }
57
58    /// Rotate the object
59    ///
60    /// Convenience wrapper around [`TransformObject::transform`].
61    fn rotate(
62        &self,
63        axis_angle: impl Into<Vector<3>>,
64        core: &mut Core,
65    ) -> Self {
66        self.transform(&Transform::rotation(axis_angle), core)
67    }
68}
69
70impl<T> TransformObject for Handle<T>
71where
72    T: Clone + Insert<Inserted = Handle<T>> + TransformObject + 'static,
73    Handle<T>: Into<AnyObject<Stored>>,
74{
75    fn transform_with_cache(
76        &self,
77        transform: &Transform,
78        core: &mut Core,
79        cache: &mut TransformCache,
80    ) -> Self {
81        if let Some(object) = cache.get(self) {
82            return object.clone();
83        }
84
85        let transformed = self
86            .clone_object()
87            .transform_with_cache(transform, core, cache)
88            .insert(core)
89            .derive_from(self, core);
90
91        cache.insert(self.clone(), transformed.clone());
92
93        transformed
94    }
95}
96
97/// A cache for transformed objects
98///
99/// See [`TransformObject`].
100#[derive(Default)]
101pub struct TransformCache(TypeMap);
102
103impl TransformCache {
104    fn entry<T: 'static>(
105        &mut self,
106        key: &Handle<T>,
107    ) -> btree_map::Entry<ObjectId, Handle<T>> {
108        let map = self
109            .0
110            .entry::<BTreeMap<ObjectId, Handle<T>>>()
111            .or_insert_with(BTreeMap::new);
112
113        map.entry(key.id())
114    }
115
116    fn get<T: 'static>(&mut self, key: &Handle<T>) -> Option<&Handle<T>> {
117        let map = self
118            .0
119            .entry::<BTreeMap<ObjectId, Handle<T>>>()
120            .or_insert_with(BTreeMap::new);
121
122        map.get(&key.id())
123    }
124
125    fn insert<T: 'static>(&mut self, key: Handle<T>, value: Handle<T>) {
126        let map = self
127            .0
128            .entry::<BTreeMap<ObjectId, Handle<T>>>()
129            .or_insert_with(BTreeMap::new);
130
131        map.insert(key.id(), value);
132    }
133}