Skip to content

Commit 9ecbfb7

Browse files
authored
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
1 parent f1cbc4b commit 9ecbfb7

File tree

12 files changed

+153
-42
lines changed

12 files changed

+153
-42
lines changed

editor/src/messages/portfolio/document/overlays/utility_types_native.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,10 @@ impl OverlayContextInternal {
784784

785785
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
786786
let sign = scale.signum();
787-
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
787+
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
788+
.unwrap()
789+
.with_alpha(0.05)
790+
.to_rgba_hex_srgb();
788791
fill_color.insert(0, '#');
789792
let fill_color = Some(fill_color.as_str());
790793
self.line(start + DVec2::X * radius * sign, start + DVec2::X * radius * scale.abs(), None, None);
@@ -817,7 +820,10 @@ impl OverlayContextInternal {
817820

818821
// Hover ring
819822
if show_hover_ring {
820-
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
823+
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
824+
.unwrap()
825+
.with_alpha(0.5)
826+
.to_rgba_hex_srgb();
821827
fill_color.insert(0, '#');
822828

823829
let circle = kurbo::Circle::new((center.x, center.y), hover_ring_centerline_radius);

editor/src/messages/portfolio/document/overlays/utility_types_web.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,10 @@ impl OverlayContext {
698698

699699
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
700700
let sign = scale.signum();
701-
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
701+
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
702+
.unwrap()
703+
.with_alpha(0.05)
704+
.to_rgba_hex_srgb();
702705
fill_color.insert(0, '#');
703706
let fill_color = Some(fill_color.as_str());
704707
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None);
@@ -735,7 +738,10 @@ impl OverlayContext {
735738

736739
// Hover ring
737740
if show_hover_ring {
738-
let mut fill_color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).to_rgba_hex_srgb();
741+
let mut fill_color = Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
742+
.unwrap()
743+
.with_alpha(0.5)
744+
.to_rgba_hex_srgb();
739745
fill_color.insert(0, '#');
740746

741747
self.render_context.set_line_width(HOVER_RING_STROKE_WIDTH);

editor/src/messages/portfolio/document/utility_types/misc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ impl Default for GridSnapping {
229229
isometric_y_spacing: 1.,
230230
isometric_angle_a: 30.,
231231
isometric_angle_b: 30.,
232-
grid_color: Color::from_rgb_str(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
232+
grid_color: Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
233233
dot_display: false,
234234
}
235235
}

editor/src/messages/tool/tool_messages/path_tool.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,7 +1888,7 @@ impl Fsm for PathToolFsmState {
18881888
}
18891889
}
18901890
Self::Drawing { selection_shape } => {
1891-
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
1891+
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
18921892
.unwrap()
18931893
.with_alpha(0.05)
18941894
.to_rgba_hex_srgb();
@@ -1978,7 +1978,10 @@ impl Fsm for PathToolFsmState {
19781978
let viewport_diagonal = viewport.size().into_dvec2().length();
19791979

19801980
let faded = |color: &str| {
1981-
let mut color = graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
1981+
let mut color = graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap())
1982+
.unwrap()
1983+
.with_alpha(0.25)
1984+
.to_rgba_hex_srgb();
19821985
color.insert(0, '#');
19831986
color
19841987
};

editor/src/messages/tool/tool_messages/pen_tool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1783,7 +1783,7 @@ impl Fsm for PenToolFsmState {
17831783
})
17841784
.collect();
17851785

1786-
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
1786+
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
17871787
.unwrap()
17881788
.with_alpha(0.05)
17891789
.to_rgba_hex_srgb();

editor/src/messages/tool/tool_messages/select_tool.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ impl Fsm for SelectToolFsmState {
689689
.parent(document.metadata())
690690
.is_some_and(|parent| selected.selected_layers_contains(parent, document.metadata()))
691691
}) {
692-
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
692+
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
693693
.unwrap()
694694
.with_alpha(0.5)
695695
.to_rgba_hex_srgb();
@@ -903,7 +903,10 @@ impl Fsm for SelectToolFsmState {
903903
let color = if !hover {
904904
color
905905
} else {
906-
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
906+
let color_string = &graphene_std::Color::from_rgb_hex_for_overlays(color.strip_prefix('#').unwrap())
907+
.unwrap()
908+
.with_alpha(0.25)
909+
.to_rgba_hex_srgb();
907910
&format!("#{color_string}")
908911
};
909912
let line_center = tool_data.line_center;
@@ -927,7 +930,10 @@ impl Fsm for SelectToolFsmState {
927930
} else {
928931
(COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED)
929932
};
930-
let mut perp_color = graphene_std::Color::from_rgb_str(perp_color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
933+
let mut perp_color = graphene_std::Color::from_rgb_hex_for_overlays(perp_color.strip_prefix('#').unwrap())
934+
.unwrap()
935+
.with_alpha(0.25)
936+
.to_rgba_hex_srgb();
931937
perp_color.insert(0, '#');
932938
let perp_color = perp_color.as_str();
933939
overlay_context.line(origin - edge * viewport_diagonal, origin + edge * viewport_diagonal, Some(edge_color), None);
@@ -972,7 +978,7 @@ impl Fsm for SelectToolFsmState {
972978
}
973979

974980
// Update the selection box
975-
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
981+
let mut fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
976982
.unwrap()
977983
.with_alpha(0.05)
978984
.to_rgba_hex_srgb();

editor/src/messages/tool/tool_messages/text_tool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ impl Fsm for TextToolFsmState {
570570
..
571571
} = transition_data;
572572
let font_cache = &persistent_data.font_cache;
573-
let fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
573+
let fill_color = graphene_std::Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
574574
.unwrap()
575575
.with_alpha(0.05)
576576
.to_rgba_hex_srgb();

node-graph/graph-craft/src/document/value.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,15 +297,12 @@ impl TaggedValue {
297297
fn to_color(input: &str) -> Option<Color> {
298298
// String syntax (e.g. "000000ff")
299299
if input.starts_with('"') && input.ends_with('"') {
300-
let color = input.trim().trim_matches('"').trim().trim_start_matches('#');
301-
match color.len() {
302-
6 => return Color::from_rgb_str(color),
303-
8 => return Color::from_rgba_str(color),
304-
_ => {
305-
log::error!("Invalid default value color string: {input}");
306-
return None;
307-
}
300+
let hex = input.trim().trim_matches('"').trim().trim_start_matches('#');
301+
let color = Color::from_hex_str(hex);
302+
if color.is_none() {
303+
log::error!("Invalid default value color string: {input}");
308304
}
305+
return color;
309306
}
310307

311308
// Color constant syntax (e.g. Color::BLACK)

node-graph/libraries/no-std-types/src/color/color_types.rs

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ impl Color {
435435
Color { red, green, blue, alpha }.to_linear_srgb().map_rgb(|channel| channel * alpha)
436436
}
437437

438-
/// Create a [Color] from a hue, saturation, lightness and alpha (all between 0 and 1)
438+
/// Create a [Color] from a hue, saturation, lightness, and alpha (all between 0 and 1)
439439
///
440440
/// # Examples
441441
/// ```
@@ -477,6 +477,25 @@ impl Color {
477477
Color { red, green, blue, alpha }
478478
}
479479

480+
/// Create a [Color] from hue, saturation, value, and alpha (all between 0 and 1).
481+
pub fn from_hsva(hue: f32, saturation: f32, value: f32, alpha: f32) -> Color {
482+
let h_prime = (hue * 6.) % 6.;
483+
let i = h_prime as i32;
484+
let f = h_prime - i as f32;
485+
let p = value * (1. - saturation);
486+
let q = value * (1. - f * saturation);
487+
let t = value * (1. - (1. - f) * saturation);
488+
let (red, green, blue) = match i % 6 {
489+
0 => (value, t, p),
490+
1 => (q, value, p),
491+
2 => (p, value, t),
492+
3 => (p, q, value),
493+
4 => (t, p, value),
494+
_ => (value, p, q),
495+
};
496+
Color { red, green, blue, alpha }
497+
}
498+
480499
/// Return the `red` component.
481500
///
482501
/// # Examples
@@ -922,11 +941,27 @@ impl Color {
922941
[hue, saturation, lightness, self.alpha]
923942
}
924943

925-
// TODO: Readd formatting
944+
// TODO: This incorrectly handles gamma/linear and premultiplied alpha. For now, this can only be used for overlay drawing, not artwork.
945+
// TODO: Remove this function and have overlays directly use the hex colors and not use the `Color` struct at all.
946+
/// Creates a color from a 6-character RGB hex string (without a # prefix).
947+
///
948+
/// ```
949+
/// use core_types::color::Color;
950+
/// let color = Color::from_rgb_hex_for_overlays("7C67FA").unwrap();
951+
/// ```
952+
pub fn from_rgb_hex_for_overlays(color_str: &str) -> Option<Color> {
953+
if color_str.len() != 6 {
954+
return None;
955+
}
956+
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
957+
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
958+
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
959+
960+
Some(Color::from_rgb8_srgb(r, g, b))
961+
}
926962

927-
/// Creates a color from a 8-character RGBA hex string (without a # prefix).
963+
/// Creates a color from an 8-character RGBA hex string (without a # prefix).
928964
///
929-
/// # Examples
930965
/// ```
931966
/// use core_types::color::Color;
932967
/// let color = Color::from_rgba_str("7C67FA61").unwrap();
@@ -935,12 +970,12 @@ impl Color {
935970
if color_str.len() != 8 {
936971
return None;
937972
}
938-
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
939-
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
940-
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
941-
let a = u8::from_str_radix(&color_str[6..8], 16).ok()?;
973+
let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.;
974+
let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.;
975+
let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.;
976+
let alpha = u8::from_str_radix(&color_str[6..8], 16).ok()? as f32 / 255.;
942977

943-
Some(Color::from_rgba8_srgb(r, g, b, a))
978+
Some(Color { red, green, blue, alpha })
944979
}
945980

946981
/// Creates a color from a 6-character RGB hex string (without a # prefix).
@@ -953,11 +988,23 @@ impl Color {
953988
if color_str.len() != 6 {
954989
return None;
955990
}
956-
let r = u8::from_str_radix(&color_str[0..2], 16).ok()?;
957-
let g = u8::from_str_radix(&color_str[2..4], 16).ok()?;
958-
let b = u8::from_str_radix(&color_str[4..6], 16).ok()?;
991+
let red = u8::from_str_radix(&color_str[0..2], 16).ok()? as f32 / 255.;
992+
let green = u8::from_str_radix(&color_str[2..4], 16).ok()? as f32 / 255.;
993+
let blue = u8::from_str_radix(&color_str[4..6], 16).ok()? as f32 / 255.;
959994

960-
Some(Color::from_rgb8_srgb(r, g, b))
995+
Some(Color { red, green, blue, alpha: 1. })
996+
}
997+
998+
/// Creates a color from a hex color code string with an optional `#` prefix, such as `#RRGGBB`, `RRGGBB`, `#RRGGBBAA`, or `RRGGBBAA`.
999+
/// Returns `None` for invalid or unrecognized strings.
1000+
#[cfg(feature = "std")]
1001+
pub fn from_hex_str(hex: &str) -> Option<Color> {
1002+
let hex = hex.trim().trim_start_matches('#');
1003+
match hex.len() {
1004+
6 => Color::from_rgb_str(hex),
1005+
8 => Color::from_rgba_str(hex),
1006+
_ => None,
1007+
}
9611008
}
9621009

9631010
/// Linearly interpolates between two colors based on t.

node-graph/nodes/gcore/src/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use core_types::table::Table;
22
use core_types::{Color, ExtractVarArgs};
33
use core_types::{Ctx, ExtractIndex, ExtractPosition};
44
use glam::DVec2;
5+
use graphic_types::vector_types::GradientStops;
56
use graphic_types::{Graphic, Vector};
67
use raster_types::{CPU, Raster};
78

@@ -37,6 +38,14 @@ fn read_color(ctx: impl Ctx + ExtractVarArgs) -> Table<Color> {
3738
var_arg.downcast_ref().cloned().unwrap_or_default()
3839
}
3940

41+
#[node_macro::node(category("Context"), path(graphene_core::vector))]
42+
fn read_gradient(ctx: impl Ctx + ExtractVarArgs) -> Table<GradientStops> {
43+
let Ok(var_arg) = ctx.vararg(0) else { return Default::default() };
44+
let var_arg = var_arg as &dyn std::any::Any;
45+
46+
var_arg.downcast_ref().cloned().unwrap_or_default()
47+
}
48+
4049
#[node_macro::node(category("Context"), path(core_types::vector))]
4150
async fn read_position(
4251
ctx: impl Ctx + ExtractPosition,

0 commit comments

Comments
 (0)