Animated thumbnails

This commit is contained in:
Adam
2025-07-17 00:31:09 -07:00
parent c2815ea0e3
commit 08c2d0543c
12 changed files with 239 additions and 60 deletions
-2
View File
@@ -6,9 +6,7 @@ use crate::messages::prelude::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Dispatcher { pub struct Dispatcher {
evaluation_queue: Vec<Message>, evaluation_queue: Vec<Message>,
introspection_queue: Vec<Message>,
queueing_evaluation_messages: bool, queueing_evaluation_messages: bool,
queueing_introspection_messages: bool,
message_queues: Vec<VecDeque<Message>>, message_queues: Vec<VecDeque<Message>>,
pub responses: Vec<FrontendMessage>, pub responses: Vec<FrontendMessage>,
pub message_handlers: DispatcherMessageHandlers, pub message_handlers: DispatcherMessageHandlers,
@@ -17,7 +17,6 @@ use crate::messages::portfolio::document::utility_types::document_metadata::{Doc
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ}; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeTemplate};
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer; use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity};
use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys; use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
@@ -1096,7 +1096,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
implementation: DocumentNodeImplementation::ProtoNode(text::text::IDENTIFIER), implementation: DocumentNodeImplementation::ProtoNode(text::text::IDENTIFIER),
manual_composition: Some(concrete!(Context)), manual_composition: Some(concrete!(Context)),
inputs: vec![ inputs: vec![
NodeInput::scope("editor-api"), NodeInput::scope("font-cache"),
NodeInput::value(TaggedValue::String("Lorem ipsum".to_string()), false), NodeInput::value(TaggedValue::String("Lorem ipsum".to_string()), false),
NodeInput::value( NodeInput::value(
TaggedValue::Font(Font::new(graphene_std::consts::DEFAULT_FONT_FAMILY.into(), graphene_std::consts::DEFAULT_FONT_STYLE.into())), TaggedValue::Font(Font::new(graphene_std::consts::DEFAULT_FONT_FAMILY.into(), graphene_std::consts::DEFAULT_FONT_STYLE.into())),
@@ -796,7 +796,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// Remove all thumbnails // Remove all thumbnails
cleared_thumbnails.push(sni); cleared_thumbnails.push(sni);
} }
document.node_graph_handler.node_graph_errors = Vec::new();
self.thumbnails_to_clear.extend(cleared_thumbnails); self.thumbnails_to_clear.extend(cleared_thumbnails);
} }
PortfolioMessage::EvaluateActiveDocumentWithThumbnails => { PortfolioMessage::EvaluateActiveDocumentWithThumbnails => {
@@ -869,7 +869,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let evaluated_data = match monitor_result { let evaluated_data = match monitor_result {
MonitorIntrospectResult::Error => continue, MonitorIntrospectResult::Error => continue,
MonitorIntrospectResult::Disabled => continue, MonitorIntrospectResult::Disabled => continue,
MonitorIntrospectResult::NotEvaluated => continue, MonitorIntrospectResult::NotEvaluated => {
continue;
}
MonitorIntrospectResult::Evaluated((data, changed)) => { MonitorIntrospectResult::Evaluated((data, changed)) => {
// If the evaluated value is the same as the previous, then just remap the ID // If the evaluated value is the same as the previous, then just remap the ID
if !changed { if !changed {
@@ -1253,30 +1255,30 @@ impl PortfolioMessageHandler {
.network_interface .network_interface
.viewport_loaded_thumbnail_position(&input_connector, graph_wire_style, &document.breadcrumb_network_path) .viewport_loaded_thumbnail_position(&input_connector, graph_wire_style, &document.breadcrumb_network_path)
{ {
let in_view = viewport_position.x > 0.0 && viewport_position.y > 0.0 && viewport_position.x < ipp.viewport_bounds()[1].x && viewport_position.y < ipp.viewport_bounds()[1].y; // let in_view = viewport_position.x > 0.0 && viewport_position.y > 0.0 && viewport_position.x < ipp.viewport_bounds()[1].x && viewport_position.y < ipp.viewport_bounds()[1].y;
if in_view { // if in_view {
let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else { let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else {
// The input is not connected to the export, which occurs if inside a disconnected node // The input is not connected to the export, which occurs if inside a disconnected node
wire_stack = Vec::new(); wire_stack = Vec::new();
nodes_to_render.clear(); nodes_to_render.clear();
continue; continue;
}; };
nodes_to_render.insert(protonode); nodes_to_render.insert(protonode);
} // }
} }
} }
}; };
// Get thumbnails for all visible layer // Get thumbnails for all visible layer ouputs
for visible_node in &document.node_graph_handler.visible_nodes(&mut document.network_interface, &document.breadcrumb_network_path, ipp) { // for visible_node in &document.node_graph_handler.visible_nodes(&mut document.network_interface, &document.breadcrumb_network_path, ipp) {
if document.network_interface.is_layer(&visible_node, &document.breadcrumb_network_path) { for visible_node in document.network_interface.nested_network(&document.breadcrumb_network_path).unwrap().nodes.keys() {
let Some(protonode) = document if document.network_interface.is_layer(visible_node, &document.breadcrumb_network_path) {
if let Some(protonode) = document
.network_interface .network_interface
.protonode_from_output(&OutputConnector::node(*visible_node, 0), &document.breadcrumb_network_path) .protonode_from_output(&OutputConnector::node(*visible_node, 0), &document.breadcrumb_network_path)
else { {
continue; nodes_to_render.insert(protonode);
}; };
nodes_to_render.insert(protonode);
} }
} }
+77 -7
View File
@@ -1,5 +1,5 @@
use crate::Color; use crate::Color;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2, IVec2, UVec2};
pub trait BoundingBox { pub trait BoundingBox {
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]>; fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]>;
@@ -15,10 +15,80 @@ macro_rules! none_impl {
}; };
} }
none_impl!(String);
none_impl!(bool);
none_impl!(f32);
none_impl!(f64);
none_impl!(DVec2);
none_impl!(Option<Color>);
none_impl!(Vec<Color>); none_impl!(Vec<Color>);
impl BoundingBox for u32 {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
text_bbox(i32_width(*self as i32))
}
}
impl BoundingBox for f64 {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
text_bbox(f64_width(*self))
}
}
impl BoundingBox for DVec2 {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
let width_x = f64_width(self.x);
let width_y = f64_width(self.y);
let total_width = width_x + width_y + 50.;
text_bbox(total_width)
}
}
impl BoundingBox for IVec2 {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
let width_x = i32_width(self.x);
let width_y = i32_width(self.y);
let total_width = width_x + width_y + 50.;
text_bbox(total_width)
}
}
impl BoundingBox for UVec2 {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
let width_x = i32_width(self.x as i32);
let width_y = i32_width(self.y as i32);
let total_width = width_x + width_y + 50.;
text_bbox(total_width)
}
}
impl BoundingBox for bool {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
text_bbox(60.)
}
}
impl BoundingBox for String {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
let width = self.len() * 16;
text_bbox(width as f64)
}
}
impl BoundingBox for Option<Color> {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
Some([(0., -5.).into(), (150., 110.).into()])
}
}
fn f64_width(f64: f64) -> f64 {
let left_of_decimal_width = i32_width(f64 as i32);
left_of_decimal_width + 5. + 2. * 16.
}
fn i32_width(i32: i32) -> f64 {
let number_of_digits = (i32.abs()).checked_ilog10().unwrap_or(0) + 1;
let mut width = number_of_digits * 16;
if i32 < 0 {
width += 20;
}
width.into()
}
fn text_bbox(width: f64) -> Option<[DVec2; 2]> {
Some([(-width / 2., 0.).into(), (width / 2., 30.).into()])
}
+2 -2
View File
@@ -630,8 +630,8 @@ fn get_animation_time(ctx: impl Ctx + ExtractAnimationTime) -> Option<f64> {
} }
#[node_macro::node(category("Context Getter"))] #[node_macro::node(category("Context Getter"))]
fn get_index(ctx: impl Ctx + ExtractIndex) -> Option<usize> { fn get_index(ctx: impl Ctx + ExtractIndex) -> Option<u32> {
ctx.try_index() ctx.try_index().map(|index| index as u32)
} }
// #[node_macro::node(category("Loop"))] // #[node_macro::node(category("Loop"))]
+25 -1
View File
@@ -1,4 +1,4 @@
use crate::Color; use crate::{Color, bounds::BoundingBox, vector::VectorDataTable};
use dyn_any::DynAny; use dyn_any::DynAny;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
@@ -32,6 +32,12 @@ impl Default for GradientStops {
} }
} }
impl BoundingBox for GradientStops {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
Into::<VectorDataTable>::into(Into::<Gradient>::into(self.clone())).bounding_box(DAffine2::default(), false)
}
}
impl IntoIterator for GradientStops { impl IntoIterator for GradientStops {
type Item = (f64, Color); type Item = (f64, Color);
type IntoIter = std::vec::IntoIter<(f64, Color)>; type IntoIter = std::vec::IntoIter<(f64, Color)>;
@@ -169,6 +175,24 @@ impl std::fmt::Display for Gradient {
} }
} }
impl From<GradientStops> for Gradient {
fn from(gradient_stops: GradientStops) -> Gradient {
Gradient {
stops: gradient_stops.clone(),
gradient_type: GradientType::Linear,
start: (0., 0.).into(),
end: (1., 0.).into(),
transform: DAffine2::IDENTITY,
}
}
}
impl BoundingBox for Gradient {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
Into::<VectorDataTable>::into(self.clone()).bounding_box(DAffine2::default(), false)
}
}
impl Gradient { impl Gradient {
/// Constructs a new gradient with the colors at 0 and 1 specified. /// Constructs a new gradient with the colors at 0 and 1 specified.
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, gradient_type: GradientType) -> Self { pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, gradient_type: GradientType) -> Self {
+4 -1
View File
@@ -1,3 +1,4 @@
use crate::gradient::{Gradient, GradientStops};
use crate::instances::Instances; use crate::instances::Instances;
use crate::raster_types::{CPU, GPU, Raster}; use crate::raster_types::{CPU, GPU, Raster};
use crate::vector::VectorData; use crate::vector::VectorData;
@@ -54,8 +55,10 @@ impl RenderComplexity for Raster<GPU> {
impl RenderComplexity for String {} impl RenderComplexity for String {}
impl RenderComplexity for bool {} impl RenderComplexity for bool {}
impl RenderComplexity for f32 {} impl RenderComplexity for u32 {}
impl RenderComplexity for f64 {} impl RenderComplexity for f64 {}
impl RenderComplexity for DVec2 {} impl RenderComplexity for DVec2 {}
impl RenderComplexity for Option<Color> {} impl RenderComplexity for Option<Color> {}
impl RenderComplexity for Vec<Color> {} impl RenderComplexity for Vec<Color> {}
impl RenderComplexity for GradientStops {}
impl RenderComplexity for Gradient {}
+33 -1
View File
@@ -5,13 +5,15 @@ mod modification;
use super::misc::{dvec2_to_point, point_to_dvec2}; use super::misc::{dvec2_to_point, point_to_dvec2};
use super::style::{PathStyle, Stroke}; use super::style::{PathStyle, Stroke};
use crate::bounds::BoundingBox; use crate::bounds::BoundingBox;
use crate::gradient::{Gradient, GradientType};
use crate::instances::Instances; use crate::instances::Instances;
use crate::math::quad::Quad; use crate::math::quad::Quad;
use crate::transform::Transform; use crate::transform::Transform;
use crate::vector::click_target::{ClickTargetType, FreePoint}; use crate::vector::click_target::{ClickTargetType, FreePoint};
use crate::vector::style::Fill;
use crate::{AlphaBlending, Color, GraphicGroupTable}; use crate::{AlphaBlending, Color, GraphicGroupTable};
pub use attributes::*; pub use attributes::*;
use bezier_rs::{BezierHandles, ManipulatorGroup}; use bezier_rs::{BezierHandles, ManipulatorGroup, Subpath};
use core::borrow::Borrow; use core::borrow::Borrow;
use core::hash::Hash; use core::hash::Hash;
use dyn_any::DynAny; use dyn_any::DynAny;
@@ -511,6 +513,36 @@ impl BoundingBox for VectorDataTable {
} }
} }
/// Convert a Gradient/GradientStops into VectorDataTable for rendering thumbnails
impl From<Gradient> for VectorDataTable {
fn from(mut gradient: Gradient) -> VectorDataTable {
match gradient.gradient_type {
GradientType::Linear => {
let mut rectangle = VectorData::from_subpath(Subpath::new_rect((0., 0.).into(), (150., 100.).into()));
// Handle vertical gradients
let intersection = if gradient.start.x == gradient.end.x {
DVec2::new(0., 100.)
} else {
let slope = (gradient.start.y - gradient.end.y) / (gradient.start.x - gradient.end.x);
if slope > 100. / 150. { DVec2::new(100. / slope, 100.) } else { DVec2::new(150., slope * 150.) }
};
gradient.start = (0., 0.).into();
gradient.end = intersection;
rectangle.style.fill = Fill::Gradient(gradient);
Instances::new(rectangle)
}
GradientType::Radial => {
let mut circle = VectorData::from_subpath(Subpath::new_ellipse((-100., -100.).into(), (100., 100.).into()));
gradient.start = (0., 0.).into();
gradient.end = (100., 0.).into();
gradient.transform = DAffine2::IDENTITY;
circle.style.fill = Fill::Gradient(gradient);
Instances::new(circle)
}
}
}
}
/// A selectable part of a curve, either an anchor (start or end of a bézier) or a handle (doesn't necessarily go through the bézier but influences curvature). /// A selectable part of a curve, either an anchor (start or end of a bézier) or a handle (doesn't necessarily go through the bézier but influences curvature).
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
pub enum ManipulatorPointId { pub enum ManipulatorPointId {
@@ -7,6 +7,7 @@ pub use glam::{DAffine2, DVec2, IVec2, UVec2};
use graphene_application_io::SurfaceFrame; use graphene_application_io::SurfaceFrame;
use graphene_brush::brush_cache::BrushCache; use graphene_brush::brush_cache::BrushCache;
use graphene_brush::brush_stroke::BrushStroke; use graphene_brush::brush_stroke::BrushStroke;
use graphene_core::gradient::GradientStops;
use graphene_core::raster_types::{CPU, GPU}; use graphene_core::raster_types::{CPU, GPU};
use graphene_core::transform::ReferencePoint; use graphene_core::transform::ReferencePoint;
use graphene_core::uuid::NodeId; use graphene_core::uuid::NodeId;
@@ -562,8 +563,13 @@ thumbnail_render! {
graphene_core::raster_types::RasterDataTable<GPU>, graphene_core::raster_types::RasterDataTable<GPU>,
graphene_core::GraphicElement, graphene_core::GraphicElement,
Option<Color>, Option<Color>,
GradientStops,
Vec<Color>, Vec<Color>,
u32,
f64, f64,
DVec2,
bool,
String,
} }
pub enum ThumbnailRenderResult { pub enum ThumbnailRenderResult {
+4 -1
View File
@@ -1,7 +1,9 @@
use glam::DVec2;
pub use graph_craft::document::value::RenderOutputType; pub use graph_craft::document::value::RenderOutputType;
use graph_craft::document::value::{EditorMetadata, RenderOutput}; use graph_craft::document::value::{EditorMetadata, RenderOutput};
pub use graph_craft::wasm_application_io::*; pub use graph_craft::wasm_application_io::*;
use graphene_application_io::ApplicationIo; use graphene_application_io::ApplicationIo;
use graphene_core::gradient::GradientStops;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use graphene_core::instances::Instances; use graphene_core::instances::Instances;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@@ -240,10 +242,11 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
graphene_core::Artboard, graphene_core::Artboard,
graphene_core::ArtboardGroupTable, graphene_core::ArtboardGroupTable,
Option<Color>, Option<Color>,
GradientStops,
Vec<Color>, Vec<Color>,
bool, bool,
f32,
f64, f64,
DVec2,
String, String,
)] )]
data: T, data: T,
+66 -24
View File
@@ -6,6 +6,7 @@ use glam::{DAffine2, DVec2};
use graphene_core::blending::BlendMode; use graphene_core::blending::BlendMode;
use graphene_core::bounds::BoundingBox; use graphene_core::bounds::BoundingBox;
use graphene_core::color::Color; use graphene_core::color::Color;
use graphene_core::gradient::{Gradient, GradientStops};
use graphene_core::instances::Instance; use graphene_core::instances::Instance;
use graphene_core::math::quad::Quad; use graphene_core::math::quad::Quad;
use graphene_core::raster::Image; use graphene_core::raster::Image;
@@ -1185,23 +1186,36 @@ impl GraphicElementRendered for GraphicElement {
} }
} }
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate. trait Primitive: std::fmt::Display + BoundingBox + RenderComplexity {
trait Primitive: std::fmt::Display + BoundingBox + RenderComplexity {} fn precision() -> bool {
impl Primitive for String {} false
}
}
impl Primitive for u32 {}
impl Primitive for f64 {
fn precision() -> bool {
true
}
}
impl Primitive for DVec2 {
fn precision() -> bool {
true
}
}
impl Primitive for bool {} impl Primitive for bool {}
impl Primitive for f32 {} impl Primitive for String {}
impl Primitive for f64 {}
impl Primitive for DVec2 {}
fn text_attributes(attributes: &mut SvgRenderAttrs) { fn text_attributes(attributes: &mut SvgRenderAttrs) {
attributes.push("fill", "black"); attributes.push("fill", "black");
attributes.push("font-size", "30"); attributes.push("font-size", "30");
attributes.push("dominant-baseline", "hanging");
attributes.push("text-anchor", "middle");
} }
impl<P: Primitive> GraphicElementRendered for P { impl<P: Primitive> GraphicElementRendered for P {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
log::debug!("Rendering svg for primative: {}", self); let text = if P::precision() { format!("{:.2}", self) } else { format!("{self}") };
render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}"))); render.parent_tag("text", text_attributes, |render| render.leaf_node(text));
} }
#[cfg(feature = "vello")] #[cfg(feature = "vello")]
@@ -1210,22 +1224,33 @@ impl<P: Primitive> GraphicElementRendered for P {
impl GraphicElementRendered for Option<Color> { impl GraphicElementRendered for Option<Color> {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
let Some(color) = self else { match self {
render.parent_tag("text", |_| {}, |render| render.leaf_node("Empty color")); Some(color) => {
return; render.leaf_tag("rect", |attributes| {
}; attributes.push("width", "150");
let color_info = format!("{:?} #{} {:?}", color, color.to_rgba_hex_srgb(), color.to_rgba8_srgb()); attributes.push("height", "100");
attributes.push("fill", format!("#{}", color.to_rgb_hex_srgb_from_gamma()));
render.leaf_tag("rect", |attributes| { if color.a() < 1. {
attributes.push("width", "100"); attributes.push("fill-opacity", ((color.a() * 1000.).round() / 1000.).to_string());
attributes.push("height", "100"); }
attributes.push("y", "40"); });
attributes.push("fill", format!("#{}", color.to_rgb_hex_srgb_from_gamma()));
if color.a() < 1. {
attributes.push("fill-opacity", ((color.a() * 1000.).round() / 1000.).to_string());
} }
}); None => {
render.parent_tag("text", text_attributes, |render| render.leaf_node(color_info)) render.leaf_tag("rect", |attributes| {
attributes.push("width", "150");
attributes.push("height", "100");
attributes.push("fill", format!("#ffffff"));
});
render.leaf_tag("line", |attributes| {
attributes.push("x1", "0");
attributes.push("y1", "100");
attributes.push("x2", "150");
attributes.push("y2", "0");
attributes.push("stroke", "red");
attributes.push("stroke-width", "5");
});
}
}
} }
#[cfg(feature = "vello")] #[cfg(feature = "vello")]
@@ -1236,7 +1261,7 @@ impl GraphicElementRendered for Vec<Color> {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
for (index, &color) in self.iter().enumerate() { for (index, &color) in self.iter().enumerate() {
render.leaf_tag("rect", |attributes| { render.leaf_tag("rect", |attributes| {
attributes.push("width", "100"); attributes.push("width", "150");
attributes.push("height", "100"); attributes.push("height", "100");
attributes.push("x", (index * 120).to_string()); attributes.push("x", (index * 120).to_string());
attributes.push("y", "40"); attributes.push("y", "40");
@@ -1252,6 +1277,23 @@ impl GraphicElementRendered for Vec<Color> {
fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2, _context: &mut RenderContext, _render_params: &RenderParams) {} fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2, _context: &mut RenderContext, _render_params: &RenderParams) {}
} }
impl GraphicElementRendered for GradientStops {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
// Gradient stops -> Gradient -> Vector data table
Into::<VectorDataTable>::into(Into::<Gradient>::into(self.clone())).render_svg(render, render_params);
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2, _context: &mut RenderContext, _render_params: &RenderParams) {}
}
impl GraphicElementRendered for Gradient {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
Into::<VectorDataTable>::into(self.clone()).render_svg(render, render_params);
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, _scene: &mut Scene, _transform: DAffine2, _context: &mut RenderContext, _render_params: &RenderParams) {}
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum SvgSegment { pub enum SvgSegment {
Slice(&'static str), Slice(&'static str),