mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Update #[min/max] node macro attributes to #[soft/hard]_[min/max] and make them clamp their input data (#2464)
* Fix min and max macro not enforcing limits when data flows * Use trait based clamping * Remove min/max from testing * cargo fmt * Resolve into min, and hard_min * cargo fmt * fix traits * cargo fmt * fix tests * rename as soft_x * Add validation code * Clean up (not compiling because of DVec2 clamping) * Avoid needing to add trait bounds to node definitions * Code review --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -9,6 +9,7 @@ use core::future::Future;
|
||||
#[cfg(feature = "log")]
|
||||
extern crate log;
|
||||
pub use crate as graphene_core;
|
||||
pub use num_traits;
|
||||
|
||||
#[cfg(feature = "reflections")]
|
||||
pub use ctor;
|
||||
@@ -19,6 +20,7 @@ pub mod context;
|
||||
pub mod generic;
|
||||
pub mod instances;
|
||||
pub mod logic;
|
||||
pub mod misc;
|
||||
pub mod ops;
|
||||
pub mod structural;
|
||||
#[cfg(feature = "std")]
|
||||
|
||||
62
node-graph/gcore/src/misc.rs
Normal file
62
node-graph/gcore/src/misc.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
// TODO(TrueDoctor): Replace this with the more idiomatic approach instead of using `trait Clampable`.
|
||||
|
||||
/// A trait for types that can be clamped within a min/max range defined by f64.
|
||||
pub trait Clampable: Sized {
|
||||
/// Clamps the value to be no less than `min`.
|
||||
fn clamp_hard_min(self, min: f64) -> Self;
|
||||
/// Clamps the value to be no more than `max`.
|
||||
fn clamp_hard_max(self, max: f64) -> Self;
|
||||
}
|
||||
|
||||
// Implement for common numeric types
|
||||
macro_rules! impl_clampable_float {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
impl Clampable for $ty {
|
||||
#[inline(always)]
|
||||
fn clamp_hard_min(self, min: f64) -> Self {
|
||||
self.max(min as $ty)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clamp_hard_max(self, max: f64) -> Self {
|
||||
self.min(max as $ty)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
impl_clampable_float!(f32, f64);
|
||||
|
||||
macro_rules! impl_clampable_int {
|
||||
($($ty:ty),*) => {
|
||||
$(
|
||||
impl Clampable for $ty {
|
||||
#[inline(always)]
|
||||
fn clamp_hard_min(self, min: f64) -> Self {
|
||||
// Using try_from to handle potential range issues safely, though min should ideally be valid.
|
||||
// Consider using a different approach if f64 precision vs integer range is a concern.
|
||||
<$ty>::try_from(min.ceil() as i64).ok().map_or(self, |min_val| self.max(min_val))
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clamp_hard_max(self, max: f64) -> Self {
|
||||
<$ty>::try_from(max.floor() as i64).ok().map_or(self, |max_val| self.min(max_val))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
// Add relevant integer types (adjust as needed)
|
||||
impl_clampable_int!(u32, u64, i32, i64);
|
||||
|
||||
// Implement for DVec2 (component-wise clamping)
|
||||
use glam::DVec2;
|
||||
impl Clampable for DVec2 {
|
||||
#[inline(always)]
|
||||
fn clamp_hard_min(self, min: f64) -> Self {
|
||||
self.max(DVec2::splat(min))
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clamp_hard_max(self, max: f64) -> Self {
|
||||
self.min(DVec2::splat(max))
|
||||
}
|
||||
}
|
||||
@@ -1399,7 +1399,7 @@ async fn posterize<T: Adjust<Color>>(
|
||||
)]
|
||||
mut input: T,
|
||||
#[default(4)]
|
||||
#[min(2.)]
|
||||
#[hard_min(2.)]
|
||||
levels: u32,
|
||||
) -> T {
|
||||
input.adjust(|color| {
|
||||
@@ -1435,6 +1435,7 @@ async fn exposure<T: Adjust<Color>>(
|
||||
offset: f64,
|
||||
#[default(1.)]
|
||||
#[range((0.01, 10.))]
|
||||
#[hard_min(0.0001)]
|
||||
gamma_correction: f64,
|
||||
) -> T {
|
||||
input.adjust(|color| {
|
||||
|
||||
@@ -930,6 +930,8 @@ impl Color {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn gamma(&self, gamma: f32) -> Color {
|
||||
let gamma = gamma.max(0.0001);
|
||||
|
||||
// From https://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-6-gamma-correction/
|
||||
let inverse_gamma = 1. / gamma;
|
||||
self.map_rgb(|c: f32| c.powf(inverse_gamma))
|
||||
|
||||
@@ -101,7 +101,7 @@ fn regular_polygon<T: AsU64>(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[default(6)]
|
||||
#[min(3.)]
|
||||
#[hard_min(3.)]
|
||||
#[implementations(u32, u64, f64)]
|
||||
sides: T,
|
||||
#[default(50)] radius: f64,
|
||||
@@ -116,7 +116,7 @@ fn star<T: AsU64>(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[default(5)]
|
||||
#[min(2.)]
|
||||
#[hard_min(2.)]
|
||||
#[implementations(u32, u64, f64)]
|
||||
sides: T,
|
||||
#[default(50)] radius: f64,
|
||||
@@ -153,7 +153,7 @@ fn grid<T: GridSpacing>(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
grid_type: GridType,
|
||||
#[min(0.)]
|
||||
#[hard_min(0.)]
|
||||
#[default(10)]
|
||||
#[implementations(f64, DVec2)]
|
||||
spacing: T,
|
||||
|
||||
@@ -429,14 +429,18 @@ where
|
||||
async fn round_corners(
|
||||
_: impl Ctx,
|
||||
source: VectorDataTable,
|
||||
#[min(0.)]
|
||||
#[hard_min(0.)]
|
||||
#[default(10.)]
|
||||
radius: PixelLength,
|
||||
#[range((0., 1.))]
|
||||
#[hard_min(0.)]
|
||||
#[hard_max(1.)]
|
||||
#[default(0.5)]
|
||||
roundness: f64,
|
||||
#[default(100.)] edge_length_limit: Percentage,
|
||||
#[range((0., 180.))]
|
||||
#[hard_min(0.)]
|
||||
#[hard_max(180.)]
|
||||
#[default(5.)]
|
||||
min_angle_threshold: Angle,
|
||||
) -> VectorDataTable {
|
||||
@@ -538,7 +542,7 @@ async fn spatial_merge_by_distance(
|
||||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
#[default(0.1)]
|
||||
#[min(0.0001)]
|
||||
#[hard_min(0.0001)]
|
||||
distance: f64,
|
||||
) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
@@ -748,7 +752,7 @@ async fn remove_handles(
|
||||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
#[default(10.)]
|
||||
#[min(0.)]
|
||||
#[soft_min(0.)]
|
||||
max_handle_distance: f64,
|
||||
) -> VectorDataTable {
|
||||
let vector_data_transform = vector_data.transform();
|
||||
@@ -879,8 +883,8 @@ async fn generate_handles(
|
||||
// _: impl Ctx,
|
||||
// source: VectorDataTable,
|
||||
// #[default(1.)]
|
||||
// #[min(1.)]
|
||||
// #[max(8.)]
|
||||
// #[hard_min(1.)]
|
||||
// #[soft_max(8.)]
|
||||
// subdivisions: f64,
|
||||
// ) -> VectorDataTable {
|
||||
// let source_transform = source.transform();
|
||||
@@ -1367,7 +1371,7 @@ async fn poisson_disk_points(
|
||||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
#[default(10.)]
|
||||
#[min(0.01)]
|
||||
#[hard_min(0.01)]
|
||||
separation_disk_diameter: f64,
|
||||
seed: SeedValue,
|
||||
) -> VectorDataTable {
|
||||
|
||||
Reference in New Issue
Block a user