lightwalk/combinators/binary/smooth/smooth_exponential/
union.rs

1use std::{fmt::Display, marker::PhantomData};
2
3use num::Float;
4
5use crate::{math::smooth_functions::smooth_min_exponential, prelude::*};
6
7/// Smooth union of two SDFs using the
8/// [`smooth_min_exponential`](crate::math::smooth_functions::smooth_min_exponential) function.
9///
10/// See [`crate::combinators::SdfCombinationOperations`] for more information on how to use this.
11#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct SUnionExponential<
13    Lhs,
14    Rhs,
15    Scalar: Float,
16    State: SdfState<Scalar, DIM>,
17    const DIM: usize,
18> where
19    Lhs: Sdf<Scalar, DIM, State = State>,
20    Rhs: Sdf<Scalar, DIM, State = State>,
21{
22    lhs: Lhs,
23    rhs: Rhs,
24    factor: Scalar,
25    marker: PhantomData<State>,
26}
27
28impl<Lhs, Rhs, Scalar: Display + Float, State: SdfState<Scalar, DIM>, const DIM: usize>
29    Sdf<Scalar, DIM> for SUnionExponential<Lhs, Rhs, Scalar, State, DIM>
30where
31    Lhs: Sdf<Scalar, DIM, State = State>,
32    Rhs: Sdf<Scalar, DIM, State = State>,
33{
34    type State = State;
35
36    #[inline]
37    fn distance(&self, position: [Scalar; DIM]) -> Scalar {
38        let lhs = self.lhs.distance(position);
39        let rhs = self.rhs.distance(position);
40        smooth_min_exponential(lhs, rhs, self.factor)
41    }
42
43    #[inline]
44    fn state(&self, position: [Scalar; DIM]) -> Self::State {
45        let (lhs, lhs_state) = self.lhs.distance_and_state(position);
46        let (rhs, rhs_state) = self.rhs.distance_and_state(position);
47
48        let lhs_weight = (-lhs / self.factor).exp2();
49        let rhs_weight = (-rhs / self.factor).exp2();
50        let fac = rhs_weight / (lhs_weight + rhs_weight);
51
52        lhs_state.mix(&rhs_state, fac)
53    }
54
55    #[inline]
56    fn distance_and_state(&self, position: [Scalar; DIM]) -> (Scalar, Self::State) {
57        let (lhs_distance, lhs_state) = self.lhs.distance_and_state(position);
58        let (rhs_distance, rhs_state) = self.rhs.distance_and_state(position);
59
60        let distance = smooth_min_exponential(lhs_distance, rhs_distance, self.factor);
61
62        let state = {
63            let lhs_weight = (-lhs_distance / self.factor).exp2();
64            let rhs_weight = (-rhs_distance / self.factor).exp2();
65            let fac = rhs_weight / (lhs_weight + rhs_weight);
66
67            lhs_state.mix(&rhs_state, fac)
68        };
69
70        (distance, state)
71    }
72}
73
74impl<Lhs, Rhs, Scalar: Float, State: SdfState<Scalar, DIM>, const DIM: usize>
75    SUnionExponential<Lhs, Rhs, Scalar, State, DIM>
76where
77    Lhs: Sdf<Scalar, DIM, State = State>,
78    Rhs: Sdf<Scalar, DIM, State = State>,
79{
80    #[inline]
81    pub const fn new(lhs: Lhs, rhs: Rhs, factor: Scalar) -> Self {
82        Self {
83            lhs,
84            rhs,
85            factor,
86            marker: PhantomData,
87        }
88    }
89}