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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy