Skip to content

Commit 1a64e29

Browse files
committed
Refactor push_path and add scaling to stroke overlay
1 parent 602122e commit 1a64e29

File tree

3 files changed

+73
-56
lines changed

3 files changed

+73
-56
lines changed

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

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ impl OverlayContext {
597597
self.end_dpi_aware_transform();
598598
}
599599

600-
pub fn push_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2) {
600+
pub fn draw_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2) {
601601
self.start_dpi_aware_transform();
602602

603603
self.render_context.begin_path();
@@ -611,28 +611,29 @@ impl OverlayContext {
611611

612612
self.render_context.move_to(transform.transform_point2(first.start()).x, transform.transform_point2(first.start()).y);
613613
for curve in curves {
614+
let splat_value = 0.5;
614615
match curve.handles {
615616
bezier_rs::BezierHandles::Linear => {
616617
let a = transform.transform_point2(curve.end());
617-
let a = a.round() - DVec2::splat(0.5);
618+
let a = a.round() - DVec2::splat(splat_value);
618619

619620
self.render_context.line_to(a.x, a.y)
620621
}
621622
bezier_rs::BezierHandles::Quadratic { handle } => {
622623
let a = transform.transform_point2(handle);
623624
let b = transform.transform_point2(curve.end());
624-
let a = a.round() - DVec2::splat(0.5);
625-
let b = b.round() - DVec2::splat(0.5);
625+
let a = a.round() - DVec2::splat(splat_value);
626+
let b = b.round() - DVec2::splat(splat_value);
626627

627628
self.render_context.quadratic_curve_to(a.x, a.y, b.x, b.y)
628629
}
629630
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
630631
let a = transform.transform_point2(handle_start);
631632
let b = transform.transform_point2(handle_end);
632633
let c = transform.transform_point2(curve.end());
633-
let a = a.round() - DVec2::splat(0.5);
634-
let b = b.round() - DVec2::splat(0.5);
635-
let c = c.round() - DVec2::splat(0.5);
634+
let a = a.round() - DVec2::splat(splat_value);
635+
let b = b.round() - DVec2::splat(splat_value);
636+
let c = c.round() - DVec2::splat(splat_value);
636637

637638
self.render_context.bezier_curve_to(a.x, a.y, b.x, b.y, c.x, c.y)
638639
}
@@ -649,60 +650,64 @@ impl OverlayContext {
649650

650651
/// Used by the Select tool to outline a path selected or hovered.
651652
pub fn outline(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: Option<&str>) {
652-
self.push_path(subpaths, transform);
653+
self.draw_path(subpaths, transform);
653654

654655
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
655656
self.render_context.set_stroke_style_str(color);
656657
self.render_context.stroke();
657658
}
658659

659-
/// Fills the area inside the path. Assumes `color` is in gamma space.
660-
/// Used by the Pen tool to show the path being closed.
661-
pub fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
662-
self.push_path(subpaths, transform);
663-
664-
self.render_context.set_fill_style_str(color);
665-
self.render_context.fill();
666-
}
667-
668-
/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
669-
/// Used by the fill tool to show the area to be filled.
670-
pub fn fill_path_pattern(&mut self, color: &Color) {
671-
const PATTERN_WIDTH: usize = 4;
672-
const PATTERN_HEIGHT: usize = 4;
673-
674-
let pattern_canvas = OffscreenCanvas::new(PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
675-
let pattern_context: OffscreenCanvasRenderingContext2d = pattern_canvas
676-
.get_context("2d")
677-
.ok()
678-
.flatten()
679-
.expect("Failed to get canvas context")
680-
.dyn_into()
681-
.expect("Context should be a canvas 2d context");
660+
/// Fills the area inside the path (with an optional pattern). Assumes `color` is in gamma space.
661+
/// Used by the Pen tool to show the path being closed and by the Fill tool to show the area to be filled with a pattern.
662+
pub fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str, with_pattern: bool, width_for_inner_boundary: Option<f64>) {
663+
self.render_context.set_line_width(width_for_inner_boundary.unwrap_or(1.) * self.device_pixel_ratio);
664+
self.draw_path(subpaths, transform);
665+
666+
if with_pattern {
667+
const PATTERN_WIDTH: usize = 4;
668+
const PATTERN_HEIGHT: usize = 4;
669+
670+
let pattern_canvas = OffscreenCanvas::new(PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
671+
let pattern_context: OffscreenCanvasRenderingContext2d = pattern_canvas
672+
.get_context("2d")
673+
.ok()
674+
.flatten()
675+
.expect("Failed to get canvas context")
676+
.dyn_into()
677+
.expect("Context should be a canvas 2d context");
678+
679+
// 4x4 pixels, 4 components (RGBA) per pixel
680+
let mut data = [0_u8; 4 * PATTERN_WIDTH * PATTERN_HEIGHT];
681+
682+
// ┌▄▄┬──┬──┬──┐
683+
// ├▀▀┼──┼──┼──┤
684+
// ├──┼──┼▄▄┼──┤
685+
// ├──┼──┼▀▀┼──┤
686+
// └──┴──┴──┴──┘
687+
let pixels = [(0, 0), (2, 2)];
688+
for &(x, y) in &pixels {
689+
let index = (x + y * PATTERN_WIDTH as usize) * 4;
690+
data[index..index + 4].copy_from_slice(&Color::from_rgba_str(color).unwrap().to_rgba8_srgb());
691+
}
682692

683-
// 4x4 pixels, 4 components (RGBA) per pixel
684-
let mut data = [0_u8; 4 * PATTERN_WIDTH * PATTERN_HEIGHT];
693+
let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(wasm_bindgen::Clamped(&mut data), PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
694+
pattern_context.put_image_data(&image_data, 0., 0.).unwrap();
695+
let pattern = self.render_context.create_pattern_with_offscreen_canvas(&pattern_canvas, "repeat").unwrap().unwrap();
685696

686-
// ┌▄▄┬──┬──┬──┐
687-
// ├▀▀┼──┼──┼──┤
688-
// ├──┼──┼▄▄┼──┤
689-
// ├──┼──┼▀▀┼──┤
690-
// └──┴──┴──┴──┘
691-
let pixels = [(0, 0), (2, 2)];
692-
for &(x, y) in &pixels {
693-
let index = (x + y * PATTERN_WIDTH as usize) * 4;
694-
data[index..index + 4].copy_from_slice(&color.to_rgba8_srgb());
697+
self.render_context.set_fill_style_canvas_pattern(&pattern);
698+
self.render_context.fill();
699+
} else {
700+
self.render_context.set_fill_style_str(color);
701+
self.render_context.fill();
695702
}
696703

697-
let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(wasm_bindgen::Clamped(&mut data), PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
698-
pattern_context.put_image_data(&image_data, 0., 0.).unwrap();
699-
let pattern = self.render_context.create_pattern_with_offscreen_canvas(&pattern_canvas, "repeat").unwrap().unwrap();
700-
701-
self.render_context.set_fill_style_canvas_pattern(&pattern);
702-
self.render_context.fill();
704+
self.render_context.set_line_width(1.);
703705
}
704706

705-
pub fn fill_stroke_pattern(&mut self, color: &Color) {
707+
pub fn fill_stroke(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &Color, width: Option<f64>) {
708+
self.render_context.set_line_width(width.unwrap_or(1.) * self.device_pixel_ratio);
709+
self.draw_path(subpaths, transform);
710+
706711
const PATTERN_WIDTH: usize = 4;
707712
const PATTERN_HEIGHT: usize = 4;
708713

@@ -735,6 +740,8 @@ impl OverlayContext {
735740

736741
self.render_context.set_stroke_style_canvas_pattern(&pattern);
737742
self.render_context.stroke();
743+
744+
self.render_context.set_line_width(1.);
738745
}
739746

740747
pub fn get_width(&self, text: &str) -> f64 {

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::tool_prelude::*;
22
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
3-
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
3+
use crate::messages::tool::common_functionality::graph_modification_utils::{NodeGraphLayer, get_stroke_width};
44
use graphene_core::vector::style::Fill;
55
use graphene_std::renderer::ClickTarget;
66

@@ -107,16 +107,26 @@ impl Fsm for FillToolFsmState {
107107

108108
// Get the layer the user is hovering over
109109
if let Some(layer) = document.click(input) {
110-
overlay_context.push_path(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
111-
let _ = document.metadata().click_targets(layer).is_some_and(|target| {
112-
target
110+
let _ = document.metadata().click_targets(layer).is_some_and(|targets| {
111+
targets
113112
.iter()
114113
.any(|click_target| close_to_stroke(input.mouse.position, click_target, document.metadata().transform_to_viewport(layer)))
115114
.then(|| {
116-
overlay_context.fill_stroke_pattern(&preview_color);
115+
overlay_context.fill_stroke(
116+
document.metadata().layer_outline(layer),
117+
document.metadata().transform_to_viewport(layer),
118+
&preview_color,
119+
get_stroke_width(layer, &document.network_interface),
120+
);
117121
})
118122
.or_else(|| {
119-
overlay_context.fill_path_pattern(&preview_color);
123+
overlay_context.fill_path(
124+
document.metadata().layer_outline(layer),
125+
document.metadata().transform_to_viewport(layer),
126+
&preview_color.to_rgba_hex_srgb(),
127+
true,
128+
get_stroke_width(layer, &document.network_interface),
129+
);
120130
Some(())
121131
});
122132
true

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1641,7 +1641,7 @@ impl Fsm for PenToolFsmState {
16411641
.with_alpha(0.05)
16421642
.to_rgba_hex_srgb();
16431643
fill_color.insert(0, '#');
1644-
overlay_context.fill_path(subpaths.iter(), transform, fill_color.as_str());
1644+
overlay_context.fill_path(subpaths.iter(), transform, fill_color.as_str(), false, None);
16451645
}
16461646
}
16471647
}

0 commit comments

Comments
 (0)