mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 04:38:11 +08:00
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:
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user