1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! API for transforming objects

mod curve;
mod cycle;
mod edge;
mod face;
mod region;
mod shell;
mod solid;
mod surface;
mod vertex;

use std::collections::{btree_map, BTreeMap};

use fj_math::{Transform, Vector};
use type_map::TypeMap;

use crate::{
    objects::{AnyObject, Stored},
    operations::insert::Insert,
    storage::{Handle, ObjectId},
    Core,
};

use super::derive::DeriveFrom;

/// Transform an object
///
/// # Implementation Note
///
/// So far, a general `transform` method is available, along some convenience
/// methods for more specific transformations.
///
/// More convenience methods can be added as required. The only reason this
/// hasn't been done so far, is that no one has put in the work yet.
pub trait TransformObject: Sized {
    /// Transform the object
    fn transform(&self, transform: &Transform, core: &mut Core) -> Self {
        let mut cache = TransformCache::default();
        self.transform_with_cache(transform, core, &mut cache)
    }

    /// Transform the object using the provided cache
    fn transform_with_cache(
        &self,
        transform: &Transform,
        core: &mut Core,
        cache: &mut TransformCache,
    ) -> Self;

    /// Translate the object
    ///
    /// Convenience wrapper around [`TransformObject::transform`].
    fn translate(&self, offset: impl Into<Vector<3>>, core: &mut Core) -> Self {
        self.transform(&Transform::translation(offset), core)
    }

    /// Rotate the object
    ///
    /// Convenience wrapper around [`TransformObject::transform`].
    fn rotate(
        &self,
        axis_angle: impl Into<Vector<3>>,
        core: &mut Core,
    ) -> Self {
        self.transform(&Transform::rotation(axis_angle), core)
    }
}

impl<T> TransformObject for Handle<T>
where
    T: Clone + Insert<Inserted = Handle<T>> + TransformObject + 'static,
    Handle<T>: Into<AnyObject<Stored>>,
{
    fn transform_with_cache(
        &self,
        transform: &Transform,
        core: &mut Core,
        cache: &mut TransformCache,
    ) -> Self {
        if let Some(object) = cache.get(self) {
            return object.clone();
        }

        let transformed = self
            .clone_object()
            .transform_with_cache(transform, core, cache)
            .insert(core)
            .derive_from(self, core);

        cache.insert(self.clone(), transformed.clone());

        transformed
    }
}

/// A cache for transformed objects
///
/// See [`TransformObject`].
#[derive(Default)]
pub struct TransformCache(TypeMap);

impl TransformCache {
    fn entry<T: 'static>(
        &mut self,
        key: &Handle<T>,
    ) -> btree_map::Entry<ObjectId, Handle<T>> {
        let map = self
            .0
            .entry::<BTreeMap<ObjectId, Handle<T>>>()
            .or_insert_with(BTreeMap::new);

        map.entry(key.id())
    }

    fn get<T: 'static>(&mut self, key: &Handle<T>) -> Option<&Handle<T>> {
        let map = self
            .0
            .entry::<BTreeMap<ObjectId, Handle<T>>>()
            .or_insert_with(BTreeMap::new);

        map.get(&key.id())
    }

    fn insert<T: 'static>(&mut self, key: Handle<T>, value: Handle<T>) {
        let map = self
            .0
            .entry::<BTreeMap<ObjectId, Handle<T>>>()
            .or_insert_with(BTreeMap::new);

        map.insert(key.id(), value);
    }
}