File size: 5,951 Bytes
2409829 |
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 |
use dyn_any::{DynAny, StaticType, StaticTypeSized};
use graphene_core::Node;
use graphene_core::color::{Channel, Linear, LuminanceMut};
use std::hash::{Hash, Hasher};
use std::ops::{Add, Mul, Sub};
#[derive(Debug, Clone, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)]
pub struct Curve {
#[serde(rename = "manipulatorGroups")]
pub manipulator_groups: Vec<CurveManipulatorGroup>,
#[serde(rename = "firstHandle")]
pub first_handle: [f32; 2],
#[serde(rename = "lastHandle")]
pub last_handle: [f32; 2],
}
impl Default for Curve {
fn default() -> Self {
Self {
manipulator_groups: vec![],
first_handle: [0.2; 2],
last_handle: [0.8; 2],
}
}
}
impl Hash for Curve {
fn hash<H: Hasher>(&self, state: &mut H) {
self.manipulator_groups.hash(state);
[self.first_handle, self.last_handle].iter().flatten().for_each(|f| f.to_bits().hash(state));
}
}
#[derive(Debug, Clone, Copy, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)]
pub struct CurveManipulatorGroup {
pub anchor: [f32; 2],
pub handles: [[f32; 2]; 2],
}
impl Hash for CurveManipulatorGroup {
fn hash<H: Hasher>(&self, state: &mut H) {
for c in self.handles.iter().chain([&self.anchor]).flatten() {
c.to_bits().hash(state);
}
}
}
#[derive(Debug)]
pub struct CubicSplines {
pub x: [f32; 4],
pub y: [f32; 4],
}
impl CubicSplines {
pub fn solve(&self) -> [f32; 4] {
let (x, y) = (&self.x, &self.y);
// Build an augmented matrix to solve the system of equations using Gaussian elimination
let mut augmented_matrix = [
[
2. / (x[1] - x[0]),
1. / (x[1] - x[0]),
0.,
0.,
// |
3. * (y[1] - y[0]) / ((x[1] - x[0]) * (x[1] - x[0])),
],
[
1. / (x[1] - x[0]),
2. * (1. / (x[1] - x[0]) + 1. / (x[2] - x[1])),
1. / (x[2] - x[1]),
0.,
// |
3. * ((y[1] - y[0]) / ((x[1] - x[0]) * (x[1] - x[0])) + (y[2] - y[1]) / ((x[2] - x[1]) * (x[2] - x[1]))),
],
[
0.,
1. / (x[2] - x[1]),
2. * (1. / (x[2] - x[1]) + 1. / (x[3] - x[2])),
1. / (x[3] - x[2]),
// |
3. * ((y[2] - y[1]) / ((x[2] - x[1]) * (x[2] - x[1])) + (y[3] - y[2]) / ((x[3] - x[2]) * (x[3] - x[2]))),
],
[
0.,
0.,
1. / (x[3] - x[2]),
2. / (x[3] - x[2]),
// |
3. * (y[3] - y[2]) / ((x[3] - x[2]) * (x[3] - x[2])),
],
];
// Gaussian elimination: forward elimination
for row in 0..4 {
let pivot_row_index = (row..4)
.max_by(|&a_row, &b_row| augmented_matrix[a_row][row].abs().partial_cmp(&augmented_matrix[b_row][row].abs()).unwrap_or(std::cmp::Ordering::Equal))
.unwrap();
// Swap the current row with the row that has the largest pivot element
augmented_matrix.swap(row, pivot_row_index);
// Eliminate the current column in all rows below the current one
for row_below_current in row + 1..4 {
assert!(augmented_matrix[row][row].abs() > f32::EPSILON);
let scale_factor = augmented_matrix[row_below_current][row] / augmented_matrix[row][row];
for col in row..5 {
augmented_matrix[row_below_current][col] -= augmented_matrix[row][col] * scale_factor
}
}
}
// Gaussian elimination: back substitution
let mut solutions = [0.; 4];
for col in (0..4).rev() {
assert!(augmented_matrix[col][col].abs() > f32::EPSILON);
solutions[col] = augmented_matrix[col][4] / augmented_matrix[col][col];
for row in (0..col).rev() {
augmented_matrix[row][4] -= augmented_matrix[row][col] * solutions[col];
augmented_matrix[row][col] = 0.;
}
}
solutions
}
pub fn interpolate(&self, input: f32, solutions: &[f32]) -> f32 {
if input <= self.x[0] {
return self.y[0];
}
if input >= self.x[self.x.len() - 1] {
return self.y[self.x.len() - 1];
}
// Find the segment that the input falls between
let mut segment = 1;
while self.x[segment] < input {
segment += 1;
}
let segment_start = segment - 1;
let segment_end = segment;
// Calculate the output value using quadratic interpolation
let input_value = self.x[segment_start];
let input_value_prev = self.x[segment_end];
let output_value = self.y[segment_start];
let output_value_prev = self.y[segment_end];
let solutions_value = solutions[segment_start];
let solutions_value_prev = solutions[segment_end];
let output_delta = solutions_value_prev * (input_value - input_value_prev) - (output_value - output_value_prev);
let solution_delta = (output_value - output_value_prev) - solutions_value * (input_value - input_value_prev);
let input_ratio = (input - input_value_prev) / (input_value - input_value_prev);
let prev_output_ratio = (1. - input_ratio) * output_value_prev;
let output_ratio = input_ratio * output_value;
let quadratic_ratio = input_ratio * (1. - input_ratio) * (output_delta * (1. - input_ratio) + solution_delta * input_ratio);
let result = prev_output_ratio + output_ratio + quadratic_ratio;
result.clamp(0., 1.)
}
}
pub struct ValueMapperNode<C> {
lut: Vec<C>,
}
unsafe impl<C: StaticTypeSized> StaticType for ValueMapperNode<C> {
type Static = ValueMapperNode<C::Static>;
}
impl<C> ValueMapperNode<C> {
pub const fn new(lut: Vec<C>) -> Self {
Self { lut }
}
}
impl<'i, L: LuminanceMut + 'i> Node<'i, L> for ValueMapperNode<L::LuminanceChannel>
where
L::LuminanceChannel: Linear + Copy,
L::LuminanceChannel: Add<Output = L::LuminanceChannel>,
L::LuminanceChannel: Sub<Output = L::LuminanceChannel>,
L::LuminanceChannel: Mul<Output = L::LuminanceChannel>,
{
type Output = L;
fn eval(&'i self, mut val: L) -> L {
let luminance: f32 = val.luminance().to_linear();
let floating_sample_index = luminance * (self.lut.len() - 1) as f32;
let index_in_lut = floating_sample_index.floor() as usize;
let a = self.lut[index_in_lut];
let b = self.lut[(index_in_lut + 1).clamp(0, self.lut.len() - 1)];
let result = a.lerp(b, L::LuminanceChannel::from_linear(floating_sample_index.fract()));
val.set_luminance(result);
val
}
}
|