UI gradients long hue paths fix (#20010)
# Objective The false and true arguments for the select statement in `lerp_hue_long` are misordered, resulting in it taking the wrong hue path:  ## Solution Swap the arguments around. Also fixed another case I found during testing. The hue was interpolated even when it is undefined for one of the endpoints (for example in a gradient from black to yellow). In those cases it shouldn't interpolate, instead it should return the hue of the other end point. ## Testing I added a `linear_gradient` module to the testbed `ui` example, run with: ``` cargo run --example testbed_ui ``` In the linear gradients screen (press space to switch) it shows a column of red to yellow linear gradients. The last gradient in the column uses the OKLCH long path, which should look like this:  matching the same gradient in CSS: https://jsfiddle.net/fevshkdy/14/ if the correct hue path is chosen.
This commit is contained in:
parent
3aed85a88b
commit
5e3927ba48
@ -154,29 +154,28 @@ fn mix_linear_rgba_in_oklaba_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn linear_rgba_to_hsla(c: vec4<f32>) -> vec4<f32> {
|
fn linear_rgba_to_hsla(c: vec4<f32>) -> vec4<f32> {
|
||||||
let maxc = max(max(c.r, c.g), c.b);
|
let max = max(max(c.r, c.g), c.b);
|
||||||
let minc = min(min(c.r, c.g), c.b);
|
let min = min(min(c.r, c.g), c.b);
|
||||||
let delta = maxc - minc;
|
let l = (max + min) * 0.5;
|
||||||
let l = (maxc + minc) * 0.5;
|
if max == min {
|
||||||
var h: f32 = 0.0;
|
return vec4(0., 0., l, c.a);
|
||||||
var s: f32 = 0.0;
|
} else {
|
||||||
if delta != 0.0 {
|
let delta = max - min;
|
||||||
s = delta / (1.0 - abs(2.0 * l - 1.0));
|
let s = delta / (1. - abs(2. * l - 1.));
|
||||||
if maxc == c.r {
|
var h = 0.;
|
||||||
h = ((c.g - c.b) / delta) % 6.0;
|
if max == c.r {
|
||||||
} else if maxc == c.g {
|
h = ((c.g - c.b) / delta) % 6.;
|
||||||
h = ((c.b - c.r) / delta) + 2.0;
|
} else if max == c.g {
|
||||||
|
h = ((c.b - c.r) / delta) + 2.;
|
||||||
} else {
|
} else {
|
||||||
h = ((c.r - c.g) / delta) + 4.0;
|
h = ((c.r - c.g) / delta) + 4.;
|
||||||
}
|
|
||||||
h = h / 6.0;
|
|
||||||
if h < 0.0 {
|
|
||||||
h = h + 1.0;
|
|
||||||
}
|
}
|
||||||
|
h = h / 6.;
|
||||||
|
return vec4<f32>(h, s, l, c.a);
|
||||||
}
|
}
|
||||||
return vec4<f32>(h, s, l, c.a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
||||||
let h = hsl.x;
|
let h = hsl.x;
|
||||||
let s = hsl.y;
|
let s = hsl.y;
|
||||||
@ -259,7 +258,7 @@ fn linear_rgba_to_oklcha(c: vec4<f32>) -> vec4<f32> {
|
|||||||
let o = linear_rgba_to_oklaba(c);
|
let o = linear_rgba_to_oklaba(c);
|
||||||
let chroma = sqrt(o.y * o.y + o.z * o.z);
|
let chroma = sqrt(o.y * o.y + o.z * o.z);
|
||||||
let hue = atan2(o.z, o.y);
|
let hue = atan2(o.z, o.y);
|
||||||
return vec4(o.x, chroma, select(hue + TAU, hue, hue < 0.0), o.a);
|
return vec4(o.x, chroma, rem_euclid(hue, TAU), o.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oklcha_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
fn oklcha_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
||||||
@ -279,22 +278,26 @@ fn lerp_hue(a: f32, b: f32, t: f32) -> f32 {
|
|||||||
|
|
||||||
fn lerp_hue_long(a: f32, b: f32, t: f32) -> f32 {
|
fn lerp_hue_long(a: f32, b: f32, t: f32) -> f32 {
|
||||||
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
||||||
return rem_euclid(a + select(diff - TAU, diff + TAU, 0. < diff) * t, TAU);
|
return rem_euclid(a + (diff + select(TAU, -TAU, 0. < diff)) * t, TAU);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_oklcha(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_oklcha(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ah = select(a.z, b.z, a.y == 0.);
|
||||||
|
let bh = select(b.z, a.z, b.y == 0.);
|
||||||
return vec4(
|
return vec4(
|
||||||
mix(a.xy, b.xy, t),
|
mix(a.xy, b.xy, t),
|
||||||
lerp_hue(a.z, b.z, t),
|
lerp_hue(ah, bh, t),
|
||||||
mix(a.a, b.a, t)
|
mix(a.a, b.a, t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_oklcha_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
fn mix_oklcha_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let ah = select(a.z, b.z, a.y == 0.);
|
||||||
|
let bh = select(b.z, a.z, b.y == 0.);
|
||||||
return vec4(
|
return vec4(
|
||||||
mix(a.xy, b.xy, t),
|
mix(a.xy, b.xy, t),
|
||||||
lerp_hue_long(a.z, b.z, t),
|
lerp_hue_long(ah, bh, t),
|
||||||
mix(a.a, b.a, t)
|
mix(a.w, b.w, t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ fn main() {
|
|||||||
.add_systems(OnEnter(Scene::Overflow), overflow::setup)
|
.add_systems(OnEnter(Scene::Overflow), overflow::setup)
|
||||||
.add_systems(OnEnter(Scene::Slice), slice::setup)
|
.add_systems(OnEnter(Scene::Slice), slice::setup)
|
||||||
.add_systems(OnEnter(Scene::LayoutRounding), layout_rounding::setup)
|
.add_systems(OnEnter(Scene::LayoutRounding), layout_rounding::setup)
|
||||||
|
.add_systems(OnEnter(Scene::LinearGradient), linear_gradient::setup)
|
||||||
.add_systems(OnEnter(Scene::RadialGradient), radial_gradient::setup)
|
.add_systems(OnEnter(Scene::RadialGradient), radial_gradient::setup)
|
||||||
.add_systems(Update, switch_scene);
|
.add_systems(Update, switch_scene);
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ enum Scene {
|
|||||||
Overflow,
|
Overflow,
|
||||||
Slice,
|
Slice,
|
||||||
LayoutRounding,
|
LayoutRounding,
|
||||||
|
LinearGradient,
|
||||||
RadialGradient,
|
RadialGradient,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +58,8 @@ impl Next for Scene {
|
|||||||
Scene::TextWrap => Scene::Overflow,
|
Scene::TextWrap => Scene::Overflow,
|
||||||
Scene::Overflow => Scene::Slice,
|
Scene::Overflow => Scene::Slice,
|
||||||
Scene::Slice => Scene::LayoutRounding,
|
Scene::Slice => Scene::LayoutRounding,
|
||||||
Scene::LayoutRounding => Scene::RadialGradient,
|
Scene::LayoutRounding => Scene::LinearGradient,
|
||||||
|
Scene::LinearGradient => Scene::RadialGradient,
|
||||||
Scene::RadialGradient => Scene::Image,
|
Scene::RadialGradient => Scene::Image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -551,6 +554,90 @@ mod layout_rounding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod linear_gradient {
|
||||||
|
use bevy::color::palettes::css::RED;
|
||||||
|
use bevy::color::palettes::css::YELLOW;
|
||||||
|
use bevy::color::Color;
|
||||||
|
use bevy::ecs::prelude::*;
|
||||||
|
use bevy::render::camera::Camera2d;
|
||||||
|
use bevy::state::state_scoped::DespawnOnExitState;
|
||||||
|
use bevy::ui::AlignItems;
|
||||||
|
use bevy::ui::BackgroundGradient;
|
||||||
|
use bevy::ui::ColorStop;
|
||||||
|
use bevy::ui::InterpolationColorSpace;
|
||||||
|
use bevy::ui::JustifyContent;
|
||||||
|
use bevy::ui::LinearGradient;
|
||||||
|
use bevy::ui::Node;
|
||||||
|
use bevy::ui::PositionType;
|
||||||
|
use bevy::ui::Val;
|
||||||
|
use bevy::utils::default;
|
||||||
|
|
||||||
|
pub fn setup(mut commands: Commands) {
|
||||||
|
commands.spawn((Camera2d, DespawnOnExitState(super::Scene::LinearGradient)));
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
Node {
|
||||||
|
flex_direction: bevy::ui::FlexDirection::Column,
|
||||||
|
width: Val::Percent(100.),
|
||||||
|
height: Val::Percent(100.),
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
row_gap: Val::Px(5.),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
DespawnOnExitState(super::Scene::LinearGradient),
|
||||||
|
))
|
||||||
|
.with_children(|commands| {
|
||||||
|
for stops in [
|
||||||
|
vec![ColorStop::auto(RED), ColorStop::auto(YELLOW)],
|
||||||
|
vec![
|
||||||
|
ColorStop::auto(Color::BLACK),
|
||||||
|
ColorStop::auto(RED),
|
||||||
|
ColorStop::auto(Color::WHITE),
|
||||||
|
],
|
||||||
|
] {
|
||||||
|
for color_space in [
|
||||||
|
InterpolationColorSpace::LinearRgb,
|
||||||
|
InterpolationColorSpace::Srgb,
|
||||||
|
InterpolationColorSpace::OkLab,
|
||||||
|
InterpolationColorSpace::OkLch,
|
||||||
|
InterpolationColorSpace::OkLchLong,
|
||||||
|
InterpolationColorSpace::Hsl,
|
||||||
|
InterpolationColorSpace::HslLong,
|
||||||
|
InterpolationColorSpace::Hsv,
|
||||||
|
InterpolationColorSpace::HsvLong,
|
||||||
|
] {
|
||||||
|
commands.spawn((
|
||||||
|
Node {
|
||||||
|
justify_content: JustifyContent::SpaceEvenly,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
children![(
|
||||||
|
Node {
|
||||||
|
height: Val::Px(30.),
|
||||||
|
width: Val::Px(300.),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
BackgroundGradient::from(LinearGradient {
|
||||||
|
color_space,
|
||||||
|
angle: LinearGradient::TO_RIGHT,
|
||||||
|
stops: stops.clone(),
|
||||||
|
}),
|
||||||
|
children![
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
bevy::ui::widget::Text(format!("{color_space:?}")),
|
||||||
|
]
|
||||||
|
)],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod radial_gradient {
|
mod radial_gradient {
|
||||||
use bevy::color::palettes::css::RED;
|
use bevy::color::palettes::css::RED;
|
||||||
use bevy::color::palettes::tailwind::GRAY_700;
|
use bevy::color::palettes::tailwind::GRAY_700;
|
||||||
|
Loading…
Reference in New Issue
Block a user