Desktop: Render overlays with vello (#2965)

* Render overlays with vello

* Fix nix flake comments

* Rendering refactor with better names and code location

* Remove unnecessary overlay renders

* Post rebase fix
This commit is contained in:
Timon
2025-08-02 14:27:24 +00:00
committed by GitHub
parent 037bcb6b26
commit 34a8b9b6f1
14 changed files with 728 additions and 401 deletions
@@ -13,8 +13,12 @@ use graphene_std::raster::Image;
use graphene_std::raster::color::Color;
use graphene_std::text::{Font, TextAlign};
#[cfg(not(target_arch = "wasm32"))]
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
#[impl_message(Message, Frontend)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(derivative::Derivative, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
#[derivative(Debug, PartialEq)]
pub enum FrontendMessage {
// Display prefix: make the frontend show something, like a dialog
DisplayDialog {
@@ -318,4 +322,10 @@ pub enum FrontendMessage {
UpdateViewportHolePunch {
active: bool,
},
#[cfg(not(target_arch = "wasm32"))]
RenderOverlays(
#[serde(skip, default = "OverlayContext::default")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
OverlayContext,
),
}
@@ -2,12 +2,8 @@ pub mod grid_overlays;
mod overlays_message;
mod overlays_message_handler;
pub mod utility_functions;
#[cfg(target_arch = "wasm32")]
#[cfg_attr(not(target_arch = "wasm32"), path = "utility_types_vello.rs")]
pub mod utility_types;
#[cfg(not(target_arch = "wasm32"))]
pub mod utility_types_vello;
#[cfg(not(target_arch = "wasm32"))]
pub use utility_types_vello as utility_types;
#[doc(inline)]
pub use overlays_message::{OverlaysMessage, OverlaysMessageDiscriminant};
@@ -73,34 +73,18 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
OverlaysMessage::Draw => {
use super::utility_types::OverlayContext;
use vello::Scene;
let size = ipp.viewport_bounds.size();
let size = ipp.viewport_bounds.size().as_uvec2();
let scene = Scene::new();
let overlay_context = OverlayContext::new(size, device_pixel_ratio, visibility_settings);
if visibility_settings.all() {
let overlay_context = OverlayContext {
scene,
size: size.as_dvec2(),
device_pixel_ratio,
visibility_settings,
};
responses.add(DocumentMessage::GridOverlays(overlay_context.clone()));
for provider in &self.overlay_providers {
let overlay_context = OverlayContext {
scene: Scene::new(),
size: size.as_dvec2(),
device_pixel_ratio,
visibility_settings,
};
responses.add(provider(overlay_context));
responses.add(provider(overlay_context.clone()));
}
}
// TODO: Render the Vello scene to a texture and display it
responses.add(FrontendMessage::RenderOverlays(overlay_context));
}
OverlaysMessage::AddProvider(message) => {
self.overlay_providers.insert(message);
@@ -13,6 +13,7 @@ use graphene_std::math::quad::Quad;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::{PointId, SegmentId, VectorData};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use vello::Scene;
use vello::kurbo::{self, BezPath};
use vello::peniko;
@@ -132,12 +133,12 @@ impl OverlaysVisibilitySettings {
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(serde::Serialize, serde::Deserialize, specta::Type)]
pub struct OverlayContext {
// Serde functionality isn't used but is required by the message system macros
#[serde(skip)]
#[specta(skip)]
pub scene: Scene,
internal: Arc<Mutex<OverlayContextInternal>>,
pub size: DVec2,
// The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size.
// It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed.
@@ -145,6 +146,22 @@ pub struct OverlayContext {
pub visibility_settings: OverlaysVisibilitySettings,
}
impl Clone for OverlayContext {
fn clone(&self) -> Self {
let internal = self.internal.lock().expect("Failed to lock internal overlay context");
let size = internal.size;
let device_pixel_ratio = internal.device_pixel_ratio;
let visibility_settings = internal.visibility_settings;
drop(internal); // Explicitly release the lock before cloning the Arc<Mutex<_>>
Self {
internal: self.internal.clone(),
size,
device_pixel_ratio,
visibility_settings,
}
}
}
// Manual implementations since Scene doesn't implement PartialEq or Debug
impl PartialEq for OverlayContext {
fn eq(&self, other: &Self) -> bool {
@@ -167,7 +184,7 @@ impl std::fmt::Debug for OverlayContext {
impl Default for OverlayContext {
fn default() -> Self {
Self {
scene: Scene::new(),
internal: Mutex::new(OverlayContextInternal::default()).into(),
size: DVec2::ZERO,
device_pixel_ratio: 1.0,
visibility_settings: OverlaysVisibilitySettings::default(),
@@ -181,6 +198,235 @@ impl core::hash::Hash for OverlayContext {
}
impl OverlayContext {
pub(super) fn new(size: DVec2, device_pixel_ratio: f64, visibility_settings: OverlaysVisibilitySettings) -> Self {
Self {
internal: Arc::new(Mutex::new(OverlayContextInternal::new(size, device_pixel_ratio, visibility_settings))),
size,
device_pixel_ratio,
visibility_settings,
}
}
pub fn take_scene(self) -> Scene {
let mut internal = self.internal.lock().expect("Failed to lock internal overlay context");
std::mem::take(&mut *internal).scene
}
fn internal(&'_ self) -> MutexGuard<'_, OverlayContextInternal> {
self.internal.lock().expect("Failed to lock internal overlay context")
}
pub fn quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>) {
self.internal().quad(quad, stroke_color, color_fill);
}
pub fn draw_triangle(&mut self, base: DVec2, direction: DVec2, size: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
self.internal().draw_triangle(base, direction, size, color_fill, color_stroke);
}
pub fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
self.internal().dashed_quad(quad, stroke_color, color_fill, dash_width, dash_gap_width, dash_offset);
}
pub fn polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>) {
self.internal().polygon(polygon, stroke_color, color_fill);
}
pub fn dashed_polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
self.internal().dashed_polygon(polygon, stroke_color, color_fill, dash_width, dash_gap_width, dash_offset);
}
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>) {
self.internal().line(start, end, color, thickness);
}
#[allow(clippy::too_many_arguments)]
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
self.internal().dashed_line(start, end, color, thickness, dash_width, dash_gap_width, dash_offset);
}
pub fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
self.internal().hover_manipulator_handle(position, selected);
}
pub fn hover_manipulator_anchor(&mut self, position: DVec2, selected: bool) {
self.internal().hover_manipulator_anchor(position, selected);
}
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
self.internal().manipulator_handle(position, selected, color);
}
pub fn manipulator_anchor(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
self.internal().manipulator_anchor(position, selected, color);
}
pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
self.internal().square(position, size, color_fill, color_stroke);
}
pub fn pixel(&mut self, position: DVec2, color: Option<&str>) {
self.internal().pixel(position, color);
}
pub fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
self.internal().circle(position, radius, color_fill, color_stroke);
}
pub fn dashed_ellipse(
&mut self,
center: DVec2,
radius_x: f64,
radius_y: f64,
rotation: Option<f64>,
start_angle: Option<f64>,
end_angle: Option<f64>,
counterclockwise: Option<bool>,
color_fill: Option<&str>,
color_stroke: Option<&str>,
dash_width: Option<f64>,
dash_gap_width: Option<f64>,
dash_offset: Option<f64>,
) {
self.internal().dashed_ellipse(
center,
radius_x,
radius_y,
rotation,
start_angle,
end_angle,
counterclockwise,
color_fill,
color_stroke,
dash_width,
dash_gap_width,
dash_offset,
);
}
pub fn draw_arc(&mut self, center: DVec2, radius: f64, start_from: f64, end_at: f64) {
self.internal().draw_arc(center, radius, start_from, end_at);
}
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
self.internal().draw_arc_gizmo_angle(pivot, bold_radius, arc_radius, offset_angle, angle);
}
pub fn draw_angle(&mut self, pivot: DVec2, radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
self.internal().draw_angle(pivot, radius, arc_radius, offset_angle, angle);
}
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
self.internal().draw_scale(start, scale, radius, text);
}
pub fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
self.internal().compass_rose(compass_center, angle, show_compass_with_hover_ring);
}
pub fn pivot(&mut self, position: DVec2, angle: f64) {
self.internal().pivot(position, angle);
}
pub fn dowel_pin(&mut self, position: DVec2, angle: f64, color: Option<&str>) {
self.internal().dowel_pin(position, angle, color);
}
#[allow(clippy::too_many_arguments)]
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
self.internal().arc_sweep_angle(offset_angle, angle, end_point_position, bold_radius, pivot, text, transform);
}
/// Used by the Pen and Path tools to outline the path of the shape.
pub fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
self.internal().outline_vector(vector_data, transform);
}
/// Used by the Pen tool in order to show how the bezier curve would look like.
pub fn outline_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
self.internal().outline_bezier(bezier, transform);
}
/// Used by the path tool segment mode in order to show the selected segments.
pub fn outline_select_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
self.internal().outline_select_bezier(bezier, transform);
}
pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
self.internal().outline_overlay_bezier(bezier, transform);
}
/// Used by the Select tool to outline a path or a free point when selected or hovered.
pub fn outline(&mut self, target_types: impl Iterator<Item = impl Borrow<ClickTargetType>>, transform: DAffine2, color: Option<&str>) {
self.internal().outline(target_types, transform, color);
}
/// Fills the area inside the path. Assumes `color` is in gamma space.
/// Used by the Pen tool to show the path being closed.
pub fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
self.internal().fill_path(subpaths, transform, color);
}
/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
/// 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) {
self.internal().fill_path_pattern(subpaths, transform, color);
}
pub fn get_width(&self, text: &str) -> f64 {
self.internal().get_width(text)
}
pub fn text(&self, text: &str, font_color: &str, background_color: Option<&str>, transform: DAffine2, padding: f64, pivot: [Pivot; 2]) {
self.internal().text(text, font_color, background_color, transform, padding, pivot);
}
pub fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {
self.internal().translation_box(translation, quad, typed_string);
}
}
pub enum Pivot {
Start,
Middle,
End,
}
pub enum DrawHandles {
All,
SelectedAnchors(Vec<SegmentId>),
FrontierHandles(HashMap<SegmentId, Vec<PointId>>),
None,
}
pub(super) struct OverlayContextInternal {
scene: Scene,
size: DVec2,
device_pixel_ratio: f64,
visibility_settings: OverlaysVisibilitySettings,
}
impl Default for OverlayContextInternal {
fn default() -> Self {
Self {
scene: Scene::new(),
size: DVec2::ZERO,
device_pixel_ratio: 1.0,
visibility_settings: OverlaysVisibilitySettings::default(),
}
}
}
impl OverlayContextInternal {
pub(super) fn new(size: DVec2, device_pixel_ratio: f64, visibility_settings: OverlaysVisibilitySettings) -> Self {
Self {
scene: Scene::new(),
size,
device_pixel_ratio,
visibility_settings,
}
}
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);
@@ -190,11 +436,11 @@ impl OverlayContext {
peniko::Color::from_rgba8(r, g, b, a)
}
pub fn quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>) {
fn quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>) {
self.dashed_polygon(&quad.0, stroke_color, color_fill, None, None, None);
}
pub fn draw_triangle(&mut self, base: DVec2, direction: DVec2, size: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
fn draw_triangle(&mut self, base: DVec2, direction: DVec2, size: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
let normal = direction.perp();
@@ -215,15 +461,15 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &path);
}
pub fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
fn dashed_quad(&mut self, quad: Quad, stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
self.dashed_polygon(&quad.0, stroke_color, color_fill, dash_width, dash_gap_width, dash_offset);
}
pub fn polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>) {
fn polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>) {
self.dashed_polygon(polygon, stroke_color, color_fill, None, None, None);
}
pub fn dashed_polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
fn dashed_polygon(&mut self, polygon: &[DVec2], stroke_color: Option<&str>, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
if polygon.len() < 2 {
return;
}
@@ -255,12 +501,12 @@ impl OverlayContext {
self.scene.stroke(&stroke, transform, Self::parse_color(stroke_color), None, &path);
}
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>) {
fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>) {
self.dashed_line(start, end, color, thickness, None, None, None)
}
#[allow(clippy::too_many_arguments)]
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, thickness: Option<f64>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
let transform = self.get_transform();
let start = start.round() - DVec2::splat(0.5);
@@ -280,7 +526,7 @@ impl OverlayContext {
self.scene.stroke(&stroke, transform, Self::parse_color(color.unwrap_or(COLOR_OVERLAY_BLUE)), None, &path);
}
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
let transform = self.get_transform();
let position = position.round() - DVec2::splat(0.5);
@@ -293,7 +539,7 @@ impl OverlayContext {
.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color.unwrap_or(COLOR_OVERLAY_BLUE)), None, &circle);
}
pub fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
fn hover_manipulator_handle(&mut self, position: DVec2, selected: bool) {
let transform = self.get_transform();
let position = position.round() - DVec2::splat(0.5);
@@ -311,13 +557,13 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &inner_circle);
}
pub fn manipulator_anchor(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
fn manipulator_anchor(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
let color_stroke = color.unwrap_or(COLOR_OVERLAY_BLUE);
let color_fill = if selected { color_stroke } else { COLOR_OVERLAY_WHITE };
self.square(position, None, Some(color_fill), Some(color_stroke));
}
pub fn hover_manipulator_anchor(&mut self, position: DVec2, selected: bool) {
fn hover_manipulator_anchor(&mut self, position: DVec2, selected: bool) {
self.square(position, Some(MANIPULATOR_GROUP_MARKER_SIZE + 2.), Some(COLOR_OVERLAY_BLUE_50), Some(COLOR_OVERLAY_BLUE_50));
let color_fill = if selected { COLOR_OVERLAY_BLUE } else { COLOR_OVERLAY_WHITE };
self.square(position, None, Some(color_fill), Some(COLOR_OVERLAY_BLUE));
@@ -327,7 +573,7 @@ impl OverlayContext {
kurbo::Affine::scale(self.device_pixel_ratio)
}
pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
let size = size.unwrap_or(MANIPULATOR_GROUP_MARKER_SIZE);
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
@@ -343,7 +589,7 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &rect);
}
pub fn pixel(&mut self, position: DVec2, color: Option<&str>) {
fn pixel(&mut self, position: DVec2, color: Option<&str>) {
let size = 1.;
let color_fill = color.unwrap_or(COLOR_OVERLAY_WHITE);
@@ -356,7 +602,7 @@ impl OverlayContext {
self.scene.fill(peniko::Fill::NonZero, transform, Self::parse_color(color_fill), None, &rect);
}
pub fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
let position = position.round();
@@ -369,7 +615,7 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &circle);
}
pub fn dashed_ellipse(
fn dashed_ellipse(
&mut self,
_center: DVec2,
_radius_x: f64,
@@ -386,7 +632,7 @@ impl OverlayContext {
) {
}
pub fn draw_arc(&mut self, center: DVec2, radius: f64, start_from: f64, end_at: f64) {
fn draw_arc(&mut self, center: DVec2, radius: f64, start_from: f64, end_at: f64) {
let segments = ((end_at - start_from).abs() / (std::f64::consts::PI / 4.)).ceil() as usize;
let step = (end_at - start_from) / segments as f64;
let half_step = step / 2.;
@@ -420,13 +666,13 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(1.0), self.get_transform(), Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
}
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle);
self.line(pivot, end_point1, None, None);
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
}
pub fn draw_angle(&mut self, pivot: DVec2, radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
fn draw_angle(&mut self, pivot: DVec2, radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
let end_point1 = pivot + radius * DVec2::from_angle(angle + offset_angle);
let end_point2 = pivot + radius * DVec2::from_angle(offset_angle);
self.line(pivot, end_point1, None, None);
@@ -434,7 +680,7 @@ impl OverlayContext {
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
}
pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
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();
fill_color.insert(0, '#');
@@ -452,7 +698,7 @@ impl OverlayContext {
)
}
pub fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
const HOVER_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
const MAIN_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
const MAIN_RING_INNER_RADIUS: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
@@ -508,7 +754,7 @@ impl OverlayContext {
.stroke(&kurbo::Stroke::new(MAIN_RING_STROKE_WIDTH), transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &circle);
}
pub fn pivot(&mut self, position: DVec2, angle: f64) {
fn pivot(&mut self, position: DVec2, angle: f64) {
let uv = DVec2::from_angle(angle);
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
@@ -539,7 +785,7 @@ impl OverlayContext {
self.scene.stroke(&stroke, transform, Self::parse_color(COLOR_OVERLAY_YELLOW), None, &path);
}
pub fn dowel_pin(&mut self, position: DVec2, angle: f64, color: Option<&str>) {
fn dowel_pin(&mut self, position: DVec2, angle: f64, color: Option<&str>) {
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
let color = color.unwrap_or(COLOR_OVERLAY_YELLOW_DULL);
@@ -581,14 +827,14 @@ impl OverlayContext {
}
#[allow(clippy::too_many_arguments)]
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
self.manipulator_handle(end_point_position, true, None);
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
self.text(text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
}
/// Used by the Pen and Path tools to outline the path of the shape.
pub fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
@@ -604,7 +850,7 @@ impl OverlayContext {
}
/// Used by the Pen tool in order to show how the bezier curve would look like.
pub fn outline_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
fn outline_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
self.bezier_to_path(bezier, transform, true, &mut path);
@@ -613,7 +859,7 @@ impl OverlayContext {
}
/// Used by the path tool segment mode in order to show the selected segments.
pub fn outline_select_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
fn outline_select_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
self.bezier_to_path(bezier, transform, true, &mut path);
@@ -621,7 +867,7 @@ impl OverlayContext {
self.scene.stroke(&kurbo::Stroke::new(4.0), vello_transform, Self::parse_color(COLOR_OVERLAY_BLUE), None, &path);
}
pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
self.bezier_to_path(bezier, transform, true, &mut path);
@@ -695,7 +941,7 @@ impl OverlayContext {
}
/// Used by the Select tool to outline a path or a free point when selected or hovered.
pub fn outline(&mut self, target_types: impl Iterator<Item = impl Borrow<ClickTargetType>>, transform: DAffine2, color: Option<&str>) {
fn outline(&mut self, target_types: impl Iterator<Item = impl Borrow<ClickTargetType>>, transform: DAffine2, color: Option<&str>) {
let mut subpaths: Vec<bezier_rs::Subpath<PointId>> = vec![];
for target_type in target_types {
@@ -717,7 +963,7 @@ impl OverlayContext {
/// Fills the area inside the path. Assumes `color` is in gamma space.
/// Used by the Pen tool to show the path being closed.
pub fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
fn fill_path(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &str) {
let path = self.push_path(subpaths, transform);
self.scene.fill(peniko::Fill::NonZero, self.get_transform(), Self::parse_color(color), None, &path);
@@ -725,7 +971,7 @@ impl OverlayContext {
/// Fills the area inside the path with a pattern. Assumes `color` is in gamma space.
/// 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) {
fn fill_path_pattern(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2, color: &Color) {
// TODO: Implement pattern fill in Vello
// For now, just fill with a semi-transparent version of the color
let path = self.push_path(subpaths, transform);
@@ -745,16 +991,16 @@ impl OverlayContext {
);
}
pub fn get_width(&self, _text: &str) -> f64 {
fn get_width(&self, _text: &str) -> f64 {
// TODO: Implement proper text measurement in Vello
0.
}
pub fn text(&self, _text: &str, _font_color: &str, _background_color: Option<&str>, _transform: DAffine2, _padding: f64, _pivot: [Pivot; 2]) {
fn text(&self, _text: &str, _font_color: &str, _background_color: Option<&str>, _transform: DAffine2, _padding: f64, _pivot: [Pivot; 2]) {
// TODO: Implement text rendering in Vello
}
pub fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {
fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {
if translation.x.abs() > 1e-3 {
self.dashed_line(quad.top_left(), quad.top_right(), None, None, Some(2.), Some(2.), Some(0.5));
@@ -784,16 +1030,3 @@ impl OverlayContext {
}
}
}
pub enum Pivot {
Start,
Middle,
End,
}
pub enum DrawHandles {
All,
SelectedAnchors(Vec<SegmentId>),
FrontierHandles(HashMap<SegmentId, Vec<PointId>>),
None,
}