Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,18 @@ pub const SCALE_EFFECT: f64 = 0.5;
// COLORS
pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff";
pub const COLOR_OVERLAY_BLUE_50: &str = "#00a8ff80";
pub const COLOR_OVERLAY_BLUE_25: &str = "#00a8ff40";
pub const COLOR_OVERLAY_BLUE_05: &str = "#00a8ff0d";
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
pub const COLOR_OVERLAY_YELLOW_DULL: &str = "#d7ba8b";
pub const COLOR_OVERLAY_GREEN: &str = "#63ce63";
pub const COLOR_OVERLAY_GREEN_25: &str = "#63ce6340";
pub const COLOR_OVERLAY_RED: &str = "#ef5454";
pub const COLOR_OVERLAY_RED_25: &str = "#ef545440";
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
pub const COLOR_OVERLAY_GRAY_25: &str = "#cccccc40";
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
pub const COLOR_OVERLAY_WHITE_05: &str = "#ffffff0d";
pub const COLOR_OVERLAY_BLACK: &str = "#000000";
pub const COLOR_OVERLAY_BLACK_75: &str = "#000000bf";

Expand Down
33 changes: 15 additions & 18 deletions editor/src/messages/portfolio/document/overlays/grid_overlays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use graphene_std::vector::style::FillChoice;

fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
let origin = document.snapping_state.grid.origin;
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
let grid_color = document.snapping_state.grid.color.as_str();
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
return;
};
Expand Down Expand Up @@ -38,7 +38,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
} else {
DVec2::new(secondary_pos, primary_end)
};
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None);
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None);
}
}
}
Expand All @@ -50,7 +50,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
// TODO: Implement this with a dashed line (`set_line_dash`), with integer spacing which is continuously adjusted to correct the accumulated error.
fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
let origin = document.snapping_state.grid.origin;
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
let grid_color = document.snapping_state.grid.color.as_str();
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
return;
};
Expand Down Expand Up @@ -80,13 +80,13 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte
let x_per_dot = (end.x - start.x) / total_dots;
for dot_index in 0..=total_dots as usize {
let exact_x = x_per_dot * dot_index as f64;
overlay_context.pixel(document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(), Some(&grid_color))
overlay_context.pixel(document_to_viewport.transform_point2(DVec2::new(start.x + exact_x, start.y)).round(), Some(grid_color))
}
}
}

fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
let grid_color = document.snapping_state.grid.color.as_str();
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
let origin = document.snapping_state.grid.origin;
let document_to_viewport = document
Expand All @@ -111,7 +111,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
let x_pos = (((min_x - origin.x) / spacing).ceil() + line_index as f64) * spacing + origin.x;
let start = DVec2::new(x_pos, min_y);
let end = DVec2::new(x_pos, max_y);
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None);
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None);
}

for (tan, multiply) in [(tan_a, -1.), (tan_b, 1.)] {
Expand All @@ -125,13 +125,13 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
let y_pos = (((inverse_project(&min_y) - origin.y) / spacing).ceil() + line_index as f64) * spacing + origin.y;
let start = DVec2::new(min_x, project(&DVec2::new(min_x, y_pos)));
let end = DVec2::new(max_x, project(&DVec2::new(max_x, y_pos)));
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(&grid_color), None);
overlay_context.line(document_to_viewport.transform_point2(start), document_to_viewport.transform_point2(end), Some(grid_color), None);
}
}
}

fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, y_axis_spacing: f64, angle_a: f64, angle_b: f64) {
let grid_color = "#".to_string() + &document.snapping_state.grid.grid_color.to_rgba_hex_srgb();
let grid_color = document.snapping_state.grid.color.as_str();
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
let origin = document.snapping_state.grid.origin;
let document_to_viewport = document
Expand Down Expand Up @@ -173,7 +173,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context
overlay_context.dashed_line(
document_to_viewport.transform_point2(start),
document_to_viewport.transform_point2(end),
Some(&grid_color),
Some(grid_color),
None,
Some(1.),
Some((spacing_x / cos_a) * document_to_viewport.matrix2.x_axis.length() - 1.),
Expand Down Expand Up @@ -220,13 +220,6 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
}
})
};
let update_color = |grid, update: fn(&mut GridSnapping) -> Option<&mut Color>| {
update_val::<ColorInput, _>(grid, move |grid, color| {
if let (Some(color), Some(update_color)) = (color.value.as_solid(), update(grid)) {
*update_color = color.to_linear_srgb();
}
})
};
let update_display = |grid, update: fn(&mut GridSnapping) -> Option<&mut bool>| {
update_val::<CheckboxInput, _>(grid, move |grid, checkbox| {
if let Some(update) = update(grid) {
Expand Down Expand Up @@ -285,10 +278,14 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
Separator::new(SeparatorStyle::Related).widget_instance(),
]);
color_widgets.push(
ColorInput::new(FillChoice::Solid(grid.grid_color.to_gamma_srgb()))
ColorInput::new(FillChoice::Solid(Color::from_hex_str(&grid.color).unwrap_or(Color::BLACK)))
.tooltip_label("Grid Display Color")
.allow_none(false)
.on_update(update_color(grid, |grid| Some(&mut grid.grid_color)))
.on_update(update_val::<ColorInput, _>(grid, |grid, color| {
if let Some(color) = color.value.as_solid() {
grid.color = format!("#{}", color.to_rgba_hex_srgb_from_gamma());
}
}))
.widget_instance(),
);
widgets.push(LayoutGroup::Row { widgets: color_widgets });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,15 @@ pub fn text_width(text: &str, font_size: f64) -> f64 {
let bounds = text_context.bounding_box(text, &font, &GLOBAL_FONT_CACHE, typesetting, false);
bounds.x
}

pub fn hex_to_rgba_u8(hex: &str) -> [u8; 4] {
let hex = hex.trim().trim_start_matches('#');
if hex.len() != 6 && hex.len() != 8 {
Comment thread
Keavon marked this conversation as resolved.
return [0, 0, 0, 255];
}
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
let a = if hex.len() >= 8 { u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) } else { 255 };
[r, g, b, a]
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::consts::{
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW,
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_WHITE_05, COLOR_OVERLAY_YELLOW,
COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS,
GRADIENT_MIDPOINT_DIAMOND_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
};
use crate::messages::portfolio::document::overlays::utility_functions::{GLOBAL_FONT_CACHE, GLOBAL_TEXT_CONTEXT};
use crate::messages::portfolio::document::overlays::utility_functions::{GLOBAL_FONT_CACHE, GLOBAL_TEXT_CONTEXT, hex_to_rgba_u8};
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::prelude::Message;
use crate::messages::prelude::ViewportMessageHandler;
use core::borrow::Borrow;
use core::f64::consts::{FRAC_PI_2, PI, TAU};
use glam::{DAffine2, DVec2};
use graphene_std::Color;
use graphene_std::math::quad::Quad;
use graphene_std::subpath::{self, Subpath};
use graphene_std::table::Table;
Expand Down Expand Up @@ -404,9 +403,9 @@ impl OverlayContext {
self.internal().fill_path(subpaths, transform, color);
}

/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
/// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string.
/// Used by the fill tool to show the area to be filled.
pub fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &Color) {
pub fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
self.internal().fill_path_pattern(subpaths, transform, color);
}

Expand Down Expand Up @@ -457,11 +456,7 @@ impl OverlayContextInternal {
}

fn parse_color(color: &str) -> peniko::Color {
let hex = color.trim_start_matches('#');
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
let a = if hex.len() >= 8 { u8::from_str_radix(&hex[6..8], 16).unwrap_or(255) } else { 255 };
let [r, g, b, a] = hex_to_rgba_u8(color);
peniko::Color::from_rgba8(r, g, b, a)
}

Expand Down Expand Up @@ -784,12 +779,7 @@ 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_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());
let fill_color = Some(COLOR_OVERLAY_WHITE_05);
self.line(start + DVec2::X * radius * sign, start + DVec2::X * radius * scale.abs(), None, None);
self.circle(start, radius, fill_color, None);
self.circle(start, radius * scale.abs(), fill_color, None);
Expand Down Expand Up @@ -820,15 +810,9 @@ impl OverlayContextInternal {

// Hover ring
if show_hover_ring {
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);
self.scene
.stroke(&kurbo::Stroke::new(hover_ring_stroke_width), transform, Self::parse_color(&fill_color), None, &circle);
.stroke(&kurbo::Stroke::new(hover_ring_stroke_width), transform, Self::parse_color(COLOR_OVERLAY_BLUE_50), None, &circle);
}

// Arrows
Expand Down Expand Up @@ -1067,16 +1051,16 @@ impl OverlayContextInternal {
self.scene.fill(peniko::Fill::NonZero, self.get_transform(), Self::parse_color(color), None, &path);
}

/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
/// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string.
/// Used by the fill tool to show the area to be filled.
fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &Color) {
fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
const PATTERN_WIDTH: u32 = 4;
const PATTERN_HEIGHT: u32 = 4;

// Create a 4x4 pixel pattern with colored pixels at (0,0) and (2,2)
// This matches the Canvas2D checkerboard pattern
let mut data = vec![0u8; (PATTERN_WIDTH * PATTERN_HEIGHT * 4) as usize];
let rgba = color.to_rgba8_srgb();
let rgba = hex_to_rgba_u8(color);

// ┌▄▄┬──┬──┬──┐
// ├▀▀┼──┼──┼──┤
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::utility_functions::overlay_canvas_context;
use super::utility_functions::{hex_to_rgba_u8, overlay_canvas_context};
use crate::consts::{
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW,
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLACK, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_WHITE_05, COLOR_OVERLAY_YELLOW,
COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS,
GRADIENT_MIDPOINT_DIAMOND_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, RESIZE_HANDLE_SIZE, SEGMENT_SELECTED_THICKNESS,
SKEW_TRIANGLE_OFFSET, SKEW_TRIANGLE_SIZE,
Expand All @@ -11,7 +11,6 @@ use crate::messages::viewport::ViewportMessageHandler;
use core::borrow::Borrow;
use core::f64::consts::{FRAC_PI_2, PI, TAU};
use glam::{DAffine2, DVec2};
use graphene_std::Color;
use graphene_std::math::quad::Quad;
use graphene_std::subpath::Subpath;
use graphene_std::vector::click_target::ClickTargetType;
Expand Down Expand Up @@ -698,12 +697,7 @@ 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_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());
let fill_color = Some(COLOR_OVERLAY_WHITE_05);
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None, None);
self.circle(start, radius, fill_color, None);
self.circle(start, radius * scale.abs(), fill_color, None);
Expand Down Expand Up @@ -738,16 +732,10 @@ impl OverlayContext {

// Hover ring
if show_hover_ring {
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);
self.render_context.begin_path();
self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect("Failed to draw hover ring");
self.render_context.set_stroke_style_str(&fill_color);
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50);
self.render_context.stroke();
}

Expand Down Expand Up @@ -1017,9 +1005,9 @@ impl OverlayContext {
self.render_context.fill();
}

/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
/// Fills the area inside the path with a pattern. Assumes `color` is an sRGB hex string.
/// Used by the fill tool to show the area to be filled.
pub fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &Color) {
pub fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
const PATTERN_WIDTH: usize = 4;
const PATTERN_HEIGHT: usize = 4;

Expand All @@ -1035,6 +1023,8 @@ impl OverlayContext {
// 4x4 pixels, 4 components (RGBA) per pixel
let mut data = [0_u8; 4 * PATTERN_WIDTH * PATTERN_HEIGHT];

let rgba = hex_to_rgba_u8(color);

// ┌▄▄┬──┬──┬──┐
// ├▀▀┼──┼──┼──┤
// ├──┼──┼▄▄┼──┤
Expand All @@ -1043,7 +1033,7 @@ impl OverlayContext {
let pixels = [(0, 0), (2, 2)];
for &(x, y) in &pixels {
let index = (x + y * PATTERN_WIDTH) * 4;
data[index..index + 4].copy_from_slice(&color.to_rgba8_srgb());
data[index..index + 4].copy_from_slice(&rgba);
}

let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(wasm_bindgen::Clamped(&data), PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
Expand Down
5 changes: 2 additions & 3 deletions editor/src/messages/portfolio/document/utility_types/misc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::consts::COLOR_OVERLAY_GRAY;
use glam::DVec2;
use graphene_std::raster::Color;
use std::fmt;

#[repr(transparent)]
Expand Down Expand Up @@ -216,7 +215,7 @@ pub struct GridSnapping {
pub isometric_y_spacing: f64,
pub isometric_angle_a: f64,
pub isometric_angle_b: f64,
pub grid_color: Color,
pub color: String,
Comment thread
Keavon marked this conversation as resolved.
pub dot_display: bool,
}

Expand All @@ -229,7 +228,7 @@ impl Default for GridSnapping {
isometric_y_spacing: 1.,
isometric_angle_a: 30.,
isometric_angle_b: 30.,
grid_color: Color::from_rgb_hex_for_overlays(COLOR_OVERLAY_GRAY.strip_prefix('#').unwrap()).unwrap(),
color: COLOR_OVERLAY_GRAY.to_string(),
dot_display: false,
}
}
Expand Down
3 changes: 2 additions & 1 deletion editor/src/messages/tool/tool_messages/fill_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ impl Fsm for FillToolFsmState {

// Get the layer the user is hovering over
if let Some(layer) = document.click(input, viewport) {
overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &preview_color);
let color_hex = format!("#{}", preview_color.to_rgba_hex_srgb());
overlay_context.fill_path_pattern(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer), &color_hex);
}

self
Expand Down
2 changes: 1 addition & 1 deletion editor/src/messages/tool/tool_messages/gradient_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ impl Fsm for GradientToolFsmState {
let (start, end) = (transform.transform_point2(*start), transform.transform_point2(*end));

fn color_to_hex(color: graphene_std::Color) -> String {
format!("#{}", color.with_alpha(1.).to_rgba_hex_srgb())
format!("#{}", color.to_rgb_hex_srgb_from_gamma())
Comment thread
Keavon marked this conversation as resolved.
}

let start_hex = stops.color.first().map(|&c| color_to_hex(c)).unwrap_or(String::from(COLOR_OVERLAY_BLUE));
Expand Down
Loading