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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#[cfg(feature = "dim3")]
use crate::geometry::PolyhedronFace;
use crate::geometry::{Cuboid, CuboidFeature, CuboidFeatureFace};
use crate::math::{Point, Vector};
use crate::utils::WSign;

pub fn local_support_point(cube: &Cuboid, local_dir: Vector<f32>) -> Point<f32> {
    local_dir.copy_sign_to(cube.half_extents).into()
}

// #[cfg(feature = "dim2")]
// pub fn polygon_ref(
//     cuboid: Cuboid,
//     out_vertices: &mut [Point<f32>; 4],
//     out_normals: &mut [Vector<f32>; 4],
// ) -> PolygonRef {
//     *out_vertices = [
//         Point::new(cuboid.half_extents.x, -cuboid.half_extents.y),
//         Point::new(cuboid.half_extents.x, cuboid.half_extents.y),
//         Point::new(-cuboid.half_extents.x, cuboid.half_extents.y),
//         Point::new(-cuboid.half_extents.x, -cuboid.half_extents.y),
//     ];
//     *out_normals = [Vector::x(), Vector::y(), -Vector::x(), -Vector::y()];
//
//     PolygonRef {
//         vertices: &out_vertices[..],
//         normals: &out_normals[..],
//     }
// }

#[cfg(feature = "dim2")]
pub fn vertex_feature_id(vertex: Point<f32>) -> u8 {
    ((vertex.x.to_bits() >> 31) & 0b001 | (vertex.y.to_bits() >> 30) & 0b010) as u8
}

// #[cfg(feature = "dim3")]
// pub fn vertex_feature_id(vertex: Point<f32>) -> u8 {
//     ((vertex.x.to_bits() >> 31) & 0b001
//         | (vertex.y.to_bits() >> 30) & 0b010
//         | (vertex.z.to_bits() >> 29) & 0b100) as u8
// }

#[cfg(feature = "dim3")]
pub fn polyhedron_support_face(cube: &Cuboid, local_dir: Vector<f32>) -> PolyhedronFace {
    support_face(cube, local_dir).into()
}

#[cfg(feature = "dim2")]
pub(crate) fn support_feature(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeature {
    // In 2D, it is best for stability to always return a face.
    // It won't have any notable impact on performances anyway.
    CuboidFeature::Face(support_face(cube, local_dir))

    /*
    let amax = local_dir.amax();

    const MAX_DOT_THRESHOLD: f32 = 0.98480775301; // 10 degrees.

    if amax > MAX_DOT_THRESHOLD {
        // Support face.
        CuboidFeature::Face(cube.support_face(local_dir))
    } else {
        // Support vertex
        CuboidFeature::Vertex(cube.support_vertex(local_dir))
    }
    */
}

#[cfg(feature = "dim3")]
pub(crate) fn support_feature(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeature {
    CuboidFeature::Face(support_face(cube, local_dir))
    /*
    const MAX_DOT_THRESHOLD: f32 = crate::utils::COS_10_DEGREES;
    const MIN_DOT_THRESHOLD: f32 = 1.0 - MAX_DOT_THRESHOLD;

    let amax = local_dir.amax();
    let amin = local_dir.amin();

    if amax > MAX_DOT_THRESHOLD {
        // Support face.
        CuboidFeature::Face(support_face(cube, local_dir))
    } else if amin < MIN_DOT_THRESHOLD {
        // Support edge.
        CuboidFeature::Edge(support_edge(cube, local_dir))
    } else {
        // Support vertex.
        CuboidFeature::Vertex(support_vertex(cube, local_dir))
    }
    */
}

// #[cfg(feature = "dim3")]
// pub(crate) fn support_vertex(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeatureVertex {
//     let vertex = local_support_point(cube, local_dir);
//     let vid = vertex_feature_id(vertex);
//
//     CuboidFeatureVertex { vertex, vid }
// }

// #[cfg(feature = "dim3")]
// pub(crate) fn support_edge(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeatureEdge {
//     let he = cube.half_extents;
//     let i = local_dir.iamin();
//     let j = (i + 1) % 3;
//     let k = (i + 2) % 3;
//     let mut a = Point::origin();
//     a[i] = he[i];
//     a[j] = local_dir[j].copy_sign_to(he[j]);
//     a[k] = local_dir[k].copy_sign_to(he[k]);
//
//     let mut b = a;
//     b[i] = -he[i];
//
//     let vid1 = vertex_feature_id(a);
//     let vid2 = vertex_feature_id(b);
//     let eid = (vid1.max(vid2) << 3) | vid1.min(vid2) | 0b11_000_000;
//
//     CuboidFeatureEdge {
//         vertices: [a, b],
//         vids: [vid1, vid2],
//         eid,
//     }
// }

#[cfg(feature = "dim2")]
pub fn support_face(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeatureFace {
    let he = cube.half_extents;
    let i = local_dir.iamin();
    let j = (i + 1) % 2;
    let mut a = Point::origin();
    a[i] = he[i];
    a[j] = local_dir[j].copy_sign_to(he[j]);

    let mut b = a;
    b[i] = -he[i];

    let vid1 = vertex_feature_id(a);
    let vid2 = vertex_feature_id(b);
    let fid = (vid1.max(vid2) << 2) | vid1.min(vid2) | 0b11_00_00;

    CuboidFeatureFace {
        vertices: [a, b],
        vids: [vid1, vid2],
        fid,
    }
}

#[cfg(feature = "dim3")]
pub(crate) fn support_face(cube: &Cuboid, local_dir: Vector<f32>) -> CuboidFeatureFace {
    // NOTE: can we use the orthonormal basis of local_dir
    // to make this AoSoA friendly?
    let he = cube.half_extents;
    let iamax = local_dir.iamax();
    let sign = local_dir[iamax].copy_sign_to(1.0);

    let vertices = match iamax {
        0 => [
            Point::new(he.x * sign, he.y, he.z),
            Point::new(he.x * sign, -he.y, he.z),
            Point::new(he.x * sign, -he.y, -he.z),
            Point::new(he.x * sign, he.y, -he.z),
        ],
        1 => [
            Point::new(he.x, he.y * sign, he.z),
            Point::new(-he.x, he.y * sign, he.z),
            Point::new(-he.x, he.y * sign, -he.z),
            Point::new(he.x, he.y * sign, -he.z),
        ],
        2 => [
            Point::new(he.x, he.y, he.z * sign),
            Point::new(he.x, -he.y, he.z * sign),
            Point::new(-he.x, -he.y, he.z * sign),
            Point::new(-he.x, he.y, he.z * sign),
        ],
        _ => unreachable!(),
    };

    pub fn vid(i: u8) -> u8 {
        // Each vertex has an even feature id.
        i * 2
    }

    let sign_index = ((sign as i8 + 1) / 2) as usize;
    // The vertex id as numbered depending on the sign of the vertex
    // component. A + sign means the corresponding bit is 0 while a -
    // sign means the corresponding bit is 1.
    // For exampl the vertex [2.0, -1.0, -3.0] has the id 0b011
    let vids = match iamax {
        0 => [
            [vid(0b000), vid(0b010), vid(0b011), vid(0b001)],
            [vid(0b100), vid(0b110), vid(0b111), vid(0b101)],
        ][sign_index],
        1 => [
            [vid(0b000), vid(0b100), vid(0b101), vid(0b001)],
            [vid(0b010), vid(0b110), vid(0b111), vid(0b011)],
        ][sign_index],
        2 => [
            [vid(0b000), vid(0b010), vid(0b110), vid(0b100)],
            [vid(0b001), vid(0b011), vid(0b111), vid(0b101)],
        ][sign_index],
        _ => unreachable!(),
    };

    // The feature ids of edges is obtained from the vertex ids
    // of their endpoints.
    // Assuming vid1 > vid2, we do:   (vid1 << 3) | vid2 | 0b11000000
    //
    let eids = match iamax {
        0 => [
            [0b11_010_000, 0b11_011_010, 0b11_011_001, 0b11_001_000],
            [0b11_110_100, 0b11_111_110, 0b11_111_101, 0b11_101_100],
        ][sign_index],
        1 => [
            [0b11_100_000, 0b11_101_100, 0b11_101_001, 0b11_001_000],
            [0b11_110_010, 0b11_111_110, 0b11_111_011, 0b11_011_010],
        ][sign_index],
        2 => [
            [0b11_010_000, 0b11_110_010, 0b11_110_100, 0b11_100_000],
            [0b11_011_001, 0b11_111_011, 0b11_111_101, 0b11_101_001],
        ][sign_index],
        _ => unreachable!(),
    };

    // The face with normals [x, y, z] are numbered [10, 11, 12].
    // The face with negated normals are numbered [13, 14, 15].
    let fid = iamax + sign_index * 3 + 10;

    CuboidFeatureFace {
        vertices,
        vids,
        eids,
        fid: fid as u8,
    }
}