New nodes: RGBA to Color, HSVA to Color, Hex to Color, and Read Gradient (#3838)
* New nodes: RGBA to Color, HSVA to Color, Hex to Color, and Read Gradient * Simplify
This commit is contained in:
parent
f1cbc4b396
commit
9ecbfb7110
|
|
@ -784,7 +784,10 @@ impl OverlayContextInternal {
|
|||
|
||||
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
|
||||
let sign = scale.signum();
|
||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
|
||||
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
let fill_color = Some(fill_color.as_str());
|
||||
self.line(start + DVec2::X * radius * sign, start + DVec2::X * radius * scale.abs(), None, None);
|
||||
|
|
@ -817,7 +820,10 @@ impl OverlayContextInternal {
|
|||
|
||||
// Hover ring
|
||||
if show_hover_ring {
|
||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
|
||||
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.5)
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
|
||||
let circle = kurbo::Circle::new((center.x, center.y), hover_ring_centerline_radius);
|
||||
|
|
|
|||
|
|
@ -698,7 +698,10 @@ impl OverlayContext {
|
|||
|
||||
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
|
||||
let sign = scale.signum();
|
||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
|
||||
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
let fill_color = Some(fill_color.as_str());
|
||||
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None);
|
||||
|
|
@ -735,7 +738,10 @@ impl OverlayContext {
|
|||
|
||||
// Hover ring
|
||||
if show_hover_ring {
|
||||
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
|
||||
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.5)
|
||||
.to_rgba_hex_srgb();
|
||||
fill_color.insert(0, '#');
|
||||
|
||||
self.render_context.set_line_width(HOVER_RING_STROKE_WIDTH);
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ impl Default for GridSnapping {
|
|||
isometric_y_spacing: 1.,
|
||||
isometric_angle_a: 30.,
|
||||
isometric_angle_b: 30.,
|
||||
grid_color: Color::from_rgb_str(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
|
||||
grid_color: Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
|
||||
dot_display: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1888,7 +1888,7 @@ impl Fsm for PathToolFsmState {
|
|||
}
|
||||
}
|
||||
Self::Drawing { selection_shape } => {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
|
|
@ -1978,7 +1978,10 @@ impl Fsm for PathToolFsmState {
|
|||
let viewport_diagonal = viewport.size().into_dvec2().length();
|
||||
|
||||
let faded = |color: &str| {
|
||||
let mut color = graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
||||
let mut color = graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.25)
|
||||
.to_rgba_hex_srgb();
|
||||
color.insert(0, '#');
|
||||
color
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1783,7 +1783,7 @@ impl Fsm for PenToolFsmState {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
|
|
|
|||
|
|
@ -689,7 +689,7 @@ impl Fsm for SelectToolFsmState {
|
|||
.parent(document.metadata())
|
||||
.is_some_and(|parent| selected.selected_layers_contains(parent, document.metadata()))
|
||||
}) {
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.5)
|
||||
.to_rgba_hex_srgb();
|
||||
|
|
@ -903,7 +903,10 @@ impl Fsm for SelectToolFsmState {
|
|||
let color = if !hover {
|
||||
color
|
||||
} else {
|
||||
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
||||
let color_string = &graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.25)
|
||||
.to_rgba_hex_srgb();
|
||||
&format!("#{color_string}")
|
||||
};
|
||||
let line_center = tool_data.line_center;
|
||||
|
|
@ -927,7 +930,10 @@ impl Fsm for SelectToolFsmState {
|
|||
} else {
|
||||
(COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED)
|
||||
};
|
||||
let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
|
||||
let mut perp_color = graphene_std::Color::from_rgb_hex_for_overlays(perp_color.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.25)
|
||||
.to_rgba_hex_srgb();
|
||||
perp_color.insert(0, '#');
|
||||
let perp_color = perp_color.as_str();
|
||||
overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None);
|
||||
|
|
@ -972,7 +978,7 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
|
||||
// Update the selection box
|
||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
|
|
|
|||
|
|
@ -570,7 +570,7 @@ impl Fsm for TextToolFsmState {
|
|||
..
|
||||
} = transition_data;
|
||||
let font_cache = &persistent_data.font_cache;
|
||||
let fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
let fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.to_rgba_hex_srgb();
|
||||
|
|
|
|||
|
|
@ -297,15 +297,12 @@ impl TaggedValue {
|
|||
fn to_color(input: &str) -> Option<Color> {
|
||||
// String syntax (e.g. "000000ff")
|
||||
if input.starts_with('"') && input.ends_with('"') {
|
||||
let color = input.trim().trim_matches('"').trim().trim_start_matches('#');
|
||||
match color.len() {
|
||||
6 => return Color::from_rgb_str(color),
|
||||
8 => return Color::from_rgba_str(color),
|
||||
_ => {
|
||||
log::error!("Invalid default value color string: {input}");
|
||||
return None;
|
||||
}
|
||||
let hex = input.trim().trim_matches('"').trim().trim_start_matches('#');
|
||||
let color = Color::from_hex_str(hex);
|
||||
if color.is_none() {
|
||||
log::error!("Invalid default value color string: {input}");
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// Color constant syntax (e.g. Color::BLACK)
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ impl Color {
|
|||
Color { red, green, blue, alpha }.to_linear_srgb().map_rgb(|channel| channel * alpha)
|
||||
}
|
||||
|
||||
/// Create a [Color] from a hue, saturation, lightness and alpha (all between 0 and 1)
|
||||
/// Create a [Color] from a hue, saturation, lightness, and alpha (all between 0 and 1)
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
|
|
@ -477,6 +477,25 @@ impl Color {
|
|||
Color { red, green, blue, alpha }
|
||||
}
|
||||
|
||||
/// Create a [Color] from hue, saturation, value, and alpha (all between 0 and 1).
|
||||
pub fn from_hsva(hue: f32, saturation: f32, value: f32, alpha: f32) -> Color {
|
||||
let h_prime = (hue * 6.) % 6.;
|
||||
let i = h_prime as i32;
|
||||
let f = h_prime - i as f32;
|
||||
let p = value * (1. - saturation);
|
||||
let q = value * (1. - f * saturation);
|
||||
let t = value * (1. - (1. - f) * saturation);
|
||||
let (red, green, blue) = match i % 6 {
|
||||
0 => (value, t, p),
|
||||
1 => (q, value, p),
|
||||
2 => (p, value, t),
|
||||
3 => (p, q, value),
|
||||
4 => (t, p, value),
|
||||
_ => (value, p, q),
|
||||
};
|
||||
Color { red, green, blue, alpha }
|
||||
}
|
||||
|
||||
/// Return the `red` component.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -922,11 +941,27 @@ impl Color {
|
|||
[hue, saturation, lightness, self.alpha]
|
||||
}
|
||||
|
||||
// TODO: Readd formatting
|
||||
|
||||
/// Creates a color from a 8-character RGBA hex string (without a # prefix).
|
||||
// TODO: This incorrectly handles gamma/linear and premultiplied alpha. For now, this can only be used for overlay drawing, not artwork.
|
||||
// TODO: Remove this function and have overlays directly use the hex colors and not use the `Color` struct at all.
|
||||
/// Creates a color from a 6-character RGB hex string (without a # prefix).
|
||||
///
|
||||
/// ```
|
||||
/// use core_types::color::Color;
|
||||
/// let color = Color::from_rgb_hex_for_overlays("7C67FA").unwrap();
|
||||
/// ```
|
||||
pub fn from_rgb_hex_for_overlays(color_str: &str) -> Option<Color> {
|
||||
if color_str.len() != 6 {
|
||||
return None;
|
||||
}
|
||||
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
|
||||
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
|
||||
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||
|
||||
Some(Color::from_rgb8_srgb(r, g, b))
|
||||
}
|
||||
|
||||
/// Creates a color from an 8-character RGBA hex string (without a # prefix).
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use core_types::color::Color;
|
||||
/// let color = Color::from_rgba_str("7C67FA61").unwrap();
|
||||
|
|
@ -935,12 +970,12 @@ impl Color {
|
|||
if color_str.len() != 8 {
|
||||
return None;
|
||||
}
|
||||
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
|
||||
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
|
||||
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||
let a = u8::from_str_radix(&color_str[6..8], 16).ok()?;
|
||||
let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.;
|
||||
let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.;
|
||||
let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.;
|
||||
let alpha = u8::from_str_radix(&color_str[6..8], 16).ok()? as f32 / 255.;
|
||||
|
||||
Some(Color::from_rgba8_srgb(r, g, b, a))
|
||||
Some(Color { red, green, blue, alpha })
|
||||
}
|
||||
|
||||
/// Creates a color from a 6-character RGB hex string (without a # prefix).
|
||||
|
|
@ -953,11 +988,23 @@ impl Color {
|
|||
if color_str.len() != 6 {
|
||||
return None;
|
||||
}
|
||||
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
|
||||
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
|
||||
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
|
||||
let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.;
|
||||
let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.;
|
||||
let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.;
|
||||
|
||||
Some(Color::from_rgb8_srgb(r, g, b))
|
||||
Some(Color { red, green, blue, alpha: 1. })
|
||||
}
|
||||
|
||||
/// Creates a color from a hex color code string with an optional `#` prefix, such as `#RRGGBB`, `RRGGBB`, `#RRGGBBAA`, or `RRGGBBAA`.
|
||||
/// Returns `None` for invalid or unrecognized strings.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from_hex_str(hex: &str) -> Option<Color> {
|
||||
let hex = hex.trim().trim_start_matches('#');
|
||||
match hex.len() {
|
||||
6 => Color::from_rgb_str(hex),
|
||||
8 => Color::from_rgba_str(hex),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two colors based on t.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use core_types::table::Table;
|
|||
use core_types::{Color, ExtractVarArgs};
|
||||
use core_types::{Ctx, ExtractIndex, ExtractPosition};
|
||||
use glam::DVec2;
|
||||
use graphic_types::vector_types::GradientStops;
|
||||
use graphic_types::{Graphic, Vector};
|
||||
use raster_types::{CPU, Raster};
|
||||
|
||||
|
|
@ -37,6 +38,14 @@ fn read_color(ctx: impl Ctx + ExtractVarArgs) -> Table<Color> {
|
|||
var_arg.downcast_ref().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(graphene_core::vector))]
|
||||
fn read_gradient(ctx: impl Ctx + ExtractVarArgs) -> Table<GradientStops> {
|
||||
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
|
||||
let var_arg = var_arg as &dyn std::any::Any;
|
||||
|
||||
var_arg.downcast_ref().cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Context"), path(core_types::vector))]
|
||||
async fn read_position(
|
||||
ctx: impl Ctx + ExtractPosition,
|
||||
|
|
|
|||
|
|
@ -765,6 +765,48 @@ fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Table<
|
|||
color
|
||||
}
|
||||
|
||||
/// Constructs a color value from red, green, blue, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("RGBA to Color"))]
|
||||
fn rgba_to_color(_: impl Ctx, _primary: (), red: Fraction, green: Fraction, blue: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
let red = (red as f32).clamp(0., 1.);
|
||||
let green = (green as f32).clamp(0., 1.);
|
||||
let blue = (blue as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_rgbaf32_unchecked(red, green, blue, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from hue, saturation, value, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("HSVA to Color"))]
|
||||
fn hsva_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(1.)] value: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
let hue = (hue as f32) - (hue as f32).floor();
|
||||
let saturation = (saturation as f32).clamp(0., 1.);
|
||||
let value = (value as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_hsva(hue, saturation, value, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from hue, saturation, lightness, and alpha components given as numbers from 0 to 1.
|
||||
#[node_macro::node(category("Color"), name("HSLA to Color"))]
|
||||
fn hsla_to_color(_: impl Ctx, _primary: (), hue: Fraction, #[default(1.)] saturation: Fraction, #[default(0.5)] lightness: Fraction, #[default(1.)] alpha: Fraction) -> Table<Color> {
|
||||
let hue = (hue as f32) - (hue as f32).floor();
|
||||
let saturation = (saturation as f32).clamp(0., 1.);
|
||||
let lightness = (lightness as f32).clamp(0., 1.);
|
||||
let alpha = (alpha as f32).clamp(0., 1.);
|
||||
|
||||
Table::new_from_element(Color::from_hsla(hue, saturation, lightness, alpha))
|
||||
}
|
||||
|
||||
/// Constructs a color value from an sRGB color code string, such as `#RRGGBB` or `#RRGGBBAA`. Invalid hex code strings produce no color.
|
||||
#[node_macro::node(category("Color"), name("Hex to Color"))]
|
||||
fn hex_to_color(_: impl Ctx, hex_code: String) -> Table<Color> {
|
||||
match Color::from_hex_str(&hex_code) {
|
||||
Some(c) => Table::new_from_element(c),
|
||||
None => Table::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors.
|
||||
#[node_macro::node(category("Value"))]
|
||||
fn gradient_value(_: impl Ctx, _primary: (), gradient: Table<GradientStops>) -> Table<GradientStops> {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
use core_types::color::Color;
|
||||
use core_types::context::Ctx;
|
||||
use core_types::registry::types::IntegerCount;
|
||||
use core_types::table::{Table, TableRow};
|
||||
use raster_types::{CPU, Raster};
|
||||
|
||||
#[node_macro::node(category("Color"))]
|
||||
async fn image_color_palette(
|
||||
_: impl Ctx,
|
||||
image: Table<Raster<CPU>>,
|
||||
#[hard_min(1.)]
|
||||
#[soft_max(28.)]
|
||||
max_size: u32,
|
||||
) -> Table<Color> {
|
||||
async fn image_color_palette(_: impl Ctx, image: Table<Raster<CPU>>, #[default(4)] count: IntegerCount) -> Table<Color> {
|
||||
const GRID: f32 = 3.;
|
||||
|
||||
let bins = GRID * GRID * GRID;
|
||||
|
|
@ -35,7 +30,7 @@ async fn image_color_palette(
|
|||
|
||||
shorted
|
||||
.iter()
|
||||
.take(max_size as usize)
|
||||
.take(count as usize)
|
||||
.flat_map(|&i| {
|
||||
let list = &color_bins[i];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue