Restore functionality of GPU infrastructure (#1797)

* Update gpu nodes to compile again

Restructure `gpu-executor` and `wgpu-executor`

And libssl to nix shell

Fix graphene-cli and add half percision color format

Fix texture scaling

Remove vulkan executor

Fix compile errors

Improve execution request deduplication

* Fix warnings

* Fix graph compile issues

* Code review

* Remove test file

* Fix lint

* Wip make node futures send

* Make futures Send on non wasm targets

* Fix warnings

* Fix nested use of block_on

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert
2024-07-15 15:14:48 +02:00
committed by GitHub
parent 59a943f42f
commit 212f08c6c8
66 changed files with 1572 additions and 1577 deletions

View File

@@ -16,6 +16,12 @@ use glam::DAffine2;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SurfaceId(pub u64);
impl core::fmt::Display for SurfaceId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SurfaceFrame {
@@ -30,6 +36,17 @@ impl Hash for SurfaceFrame {
}
}
impl Transform for SurfaceFrame {
fn transform(&self) -> DAffine2 {
self.transform
}
}
impl TransformMut for SurfaceFrame {
fn transform_mut(&mut self) -> &mut DAffine2 {
&mut self.transform
}
}
unsafe impl StaticType for SurfaceFrame {
type Static = SurfaceFrame;
}
@@ -48,6 +65,10 @@ pub struct SurfaceHandle<Surface> {
pub surface_id: SurfaceId,
pub surface: Surface,
}
// #[cfg(target_arch = "wasm32")]
// unsafe impl<T: dyn_any::WasmNotSend> Send for SurfaceHandle<T> {}
// #[cfg(target_arch = "wasm32")]
// unsafe impl<T: dyn_any::WasmNotSync> Sync for SurfaceHandle<T> {}
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
type Static = SurfaceHandle<T>;
@@ -83,7 +104,10 @@ impl<'a, Surface> Drop for SurfaceHandle<'a, Surface> {
}
}*/
#[cfg(target_arch = "wasm32")]
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>;
#[cfg(not(target_arch = "wasm32"))]
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>> + Send>>;
pub trait ApplicationIo {
type Surface;

View File

@@ -1,15 +1,16 @@
use crate::application_io::SurfaceHandleFrame;
use crate::raster::{BlendMode, ImageFrame};
use crate::renderer::GraphicElementRendered;
use crate::transform::Footprint;
use crate::vector::VectorData;
use crate::{Color, Node};
use crate::{Color, Node, SurfaceFrame};
use bezier_rs::BezierHandles;
use dyn_any::{DynAny, StaticType};
use node_macro::node_fn;
use core::future::Future;
use core::ops::{Deref, DerefMut};
use glam::{DAffine2, DVec2, IVec2, UVec2};
use web_sys::HtmlCanvasElement;
pub mod renderer;
@@ -67,6 +68,8 @@ pub enum GraphicElement {
VectorData(Box<VectorData>),
/// A bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
ImageFrame(ImageFrame<Color>),
/// A Canvas evement
Surface(SurfaceFrame),
}
// TODO: Can this be removed? It doesn't necessarily make that much sense to have a default when, instead, the entire GraphicElement just shouldn't exist if there's no specific content to assign it.
@@ -127,10 +130,10 @@ pub struct ConstructLayerNode<Stack, GraphicElement> {
}
#[node_fn(ConstructLayerNode)]
async fn construct_layer<Data: Into<GraphicElement>, Fut1: Future<Output = GraphicGroup>, Fut2: Future<Output = Data>>(
async fn construct_layer<Data: Into<GraphicElement> + Send>(
footprint: crate::transform::Footprint,
mut stack: impl Node<crate::transform::Footprint, Output = Fut1>,
graphic_element: impl Node<crate::transform::Footprint, Output = Fut2>,
mut stack: impl Node<crate::transform::Footprint, Output = GraphicGroup>,
graphic_element: impl Node<crate::transform::Footprint, Output = Data>,
) -> GraphicGroup {
let graphic_element = self.graphic_element.eval(footprint).await;
let mut stack = self.stack.eval(footprint).await;
@@ -162,9 +165,9 @@ pub struct ConstructArtboardNode<Contents, Label, Location, Dimensions, Backgrou
}
#[node_fn(ConstructArtboardNode)]
async fn construct_artboard<Fut: Future<Output = GraphicGroup>>(
async fn construct_artboard(
mut footprint: Footprint,
contents: impl Node<Footprint, Output = Fut>,
contents: impl Node<Footprint, Output = GraphicGroup>,
label: String,
location: IVec2,
dimensions: IVec2,
@@ -189,11 +192,7 @@ pub struct AddArtboardNode<ArtboardGroup, Artboard> {
}
#[node_fn(AddArtboardNode)]
async fn add_artboard<Data: Into<Artboard>, Fut1: Future<Output = ArtboardGroup>, Fut2: Future<Output = Data>>(
footprint: Footprint,
artboards: impl Node<Footprint, Output = Fut1>,
artboard: impl Node<Footprint, Output = Fut2>,
) -> ArtboardGroup {
async fn add_artboard<Data: Into<Artboard> + Send>(footprint: Footprint, artboards: impl Node<Footprint, Output = ArtboardGroup>, artboard: impl Node<Footprint, Output = Data>) -> ArtboardGroup {
let artboard = self.artboard.eval(footprint).await;
let mut artboards = self.artboards.eval(footprint).await;
@@ -229,6 +228,25 @@ impl From<GraphicGroup> for GraphicElement {
GraphicElement::GraphicGroup(graphic_group)
}
}
impl From<SurfaceFrame> for GraphicElement {
fn from(surface: SurfaceFrame) -> Self {
GraphicElement::Surface(surface)
}
}
impl From<alloc::sync::Arc<SurfaceHandleFrame<HtmlCanvasElement>>> for GraphicElement {
fn from(surface: alloc::sync::Arc<SurfaceHandleFrame<HtmlCanvasElement>>) -> Self {
let surface_id = surface.surface_handle.surface_id;
let transform = surface.transform;
GraphicElement::Surface(SurfaceFrame { surface_id, transform })
}
}
impl From<SurfaceHandleFrame<HtmlCanvasElement>> for GraphicElement {
fn from(surface: SurfaceHandleFrame<HtmlCanvasElement>) -> Self {
let surface_id = surface.surface_handle.surface_id;
let transform = surface.transform;
GraphicElement::Surface(SurfaceFrame { surface_id, transform })
}
}
impl Deref for GraphicGroup {
type Target = Vec<GraphicElement>;
@@ -287,72 +305,3 @@ impl GraphicGroup {
tree
}
}
impl GraphicElement {
fn to_usvg_node(&self) -> usvg::Node {
fn to_transform(transform: DAffine2) -> usvg::Transform {
let cols = transform.to_cols_array();
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
}
match self {
GraphicElement::VectorData(vector_data) => {
use usvg::tiny_skia_path::PathBuilder;
let mut builder = PathBuilder::new();
let transform = to_transform(vector_data.transform);
for subpath in vector_data.stroke_bezier_paths() {
let start = vector_data.transform.transform_point2(subpath[0].anchor);
builder.move_to(start.x as f32, start.y as f32);
for bezier in subpath.iter() {
bezier.apply_transformation(|pos| vector_data.transform.transform_point2(pos));
let end = bezier.end;
match bezier.handles {
BezierHandles::Linear => builder.line_to(end.x as f32, end.y as f32),
BezierHandles::Quadratic { handle } => builder.quad_to(handle.x as f32, handle.y as f32, end.x as f32, end.y as f32),
BezierHandles::Cubic { handle_start, handle_end } => {
builder.cubic_to(handle_start.x as f32, handle_start.y as f32, handle_end.x as f32, handle_end.y as f32, end.x as f32, end.y as f32)
}
}
}
if subpath.closed {
builder.close()
}
}
let path = builder.finish().unwrap();
let mut path = usvg::Path::new(path.into());
path.abs_transform = transform;
// TODO: use proper style
path.fill = None;
path.stroke = Some(usvg::Stroke::default());
usvg::Node::Path(Box::new(path))
}
GraphicElement::ImageFrame(image_frame) => {
if image_frame.image.width * image_frame.image.height == 0 {
return usvg::Node::Group(Box::default());
}
let png = image_frame.image.to_png();
usvg::Node::Image(Box::new(usvg::Image {
id: String::new(),
abs_transform: to_transform(image_frame.transform),
visibility: usvg::Visibility::Visible,
view_box: usvg::ViewBox {
rect: usvg::NonZeroRect::from_xywh(0., 0., 1., 1.).unwrap(),
aspect: usvg::AspectRatio::default(),
},
rendering_mode: usvg::ImageRendering::OptimizeSpeed,
kind: usvg::ImageKind::PNG(png.into()),
bounding_box: None,
}))
}
GraphicElement::GraphicGroup(group) => {
let mut group_element = usvg::Group::default();
for element in group.iter() {
group_element.children.push(element.to_usvg_node());
}
usvg::Node::Group(Box::new(group_element))
}
}
}
}

View File

@@ -1,9 +1,11 @@
mod quad;
use crate::raster::bbox::Bbox;
use crate::raster::{BlendMode, Image, ImageFrame};
use crate::transform::Transform;
use crate::uuid::generate_uuid;
use crate::vector::PointId;
use crate::SurfaceFrame;
use crate::{vector::VectorData, Artboard, Color, GraphicElement, GraphicGroup};
pub use quad::Quad;
@@ -489,6 +491,39 @@ impl GraphicElementRendered for crate::ArtboardGroup {
}
}
impl GraphicElementRendered for SurfaceFrame {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
let transform = self.transform;
let (width, height) = (transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
let matrix = (transform * DAffine2::from_scale((width, height).into()).inverse())
.to_cols_array()
.iter()
.enumerate()
.fold(String::new(), |val, (i, entry)| val + &(entry.to_string() + if i == 5 { "" } else { "," }));
let canvas = format!(
r#"<foreignObject width="{}" height="{}" transform="matrix({})"><div data-canvas-placeholder="canvas{}"></div></foreignObject>"#,
width.abs(),
height.abs(),
matrix,
self.surface_id
);
render.svg.push(canvas.into())
}
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
let bbox = Bbox::from_transform(transform);
let aabb = bbox.to_axis_aligned_bbox();
Some([aabb.start, aabb.end])
}
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
fn contains_artboard(&self) -> bool {
false
}
}
impl GraphicElementRendered for ImageFrame<Color> {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
let transform: String = format_transform_matrix(self.transform * render.transform);
@@ -559,6 +594,7 @@ impl GraphicElementRendered for GraphicElement {
GraphicElement::VectorData(vector_data) => vector_data.render_svg(render, render_params),
GraphicElement::ImageFrame(image_frame) => image_frame.render_svg(render, render_params),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params),
GraphicElement::Surface(surface) => surface.render_svg(render, render_params),
}
}
@@ -567,6 +603,7 @@ impl GraphicElementRendered for GraphicElement {
GraphicElement::VectorData(vector_data) => GraphicElementRendered::bounding_box(&**vector_data, transform),
GraphicElement::ImageFrame(image_frame) => image_frame.bounding_box(transform),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform),
GraphicElement::Surface(surface) => surface.bounding_box(transform),
}
}
@@ -575,6 +612,7 @@ impl GraphicElementRendered for GraphicElement {
GraphicElement::VectorData(vector_data) => vector_data.add_click_targets(click_targets),
GraphicElement::ImageFrame(image_frame) => image_frame.add_click_targets(click_targets),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.add_click_targets(click_targets),
GraphicElement::Surface(surface) => surface.add_click_targets(click_targets),
}
}
@@ -583,6 +621,7 @@ impl GraphicElementRendered for GraphicElement {
GraphicElement::VectorData(vector_data) => vector_data.to_usvg_node(),
GraphicElement::ImageFrame(image_frame) => image_frame.to_usvg_node(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.to_usvg_node(),
GraphicElement::Surface(surface) => surface.to_usvg_node(),
}
}
@@ -591,6 +630,7 @@ impl GraphicElementRendered for GraphicElement {
GraphicElement::VectorData(vector_data) => vector_data.contains_artboard(),
GraphicElement::ImageFrame(image_frame) => image_frame.contains_artboard(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.contains_artboard(),
GraphicElement::Surface(surface) => surface.contains_artboard(),
}
}
}

View File

@@ -6,6 +6,7 @@ extern crate alloc;
#[cfg_attr(feature = "log", macro_use)]
#[cfg(feature = "log")]
extern crate log;
pub use crate as graphene_core;
pub mod consts;
pub mod generic;
@@ -176,3 +177,5 @@ pub use crate::application_io::{SurfaceFrame, SurfaceId};
pub type WasmSurfaceHandle = application_io::SurfaceHandle<web_sys::HtmlCanvasElement>;
#[cfg(feature = "wasm")]
pub type WasmSurfaceHandleFrame = application_io::SurfaceHandleFrame<web_sys::HtmlCanvasElement>;
pub use dyn_any::{WasmNotSend, WasmNotSync};

View File

@@ -1,46 +1,49 @@
use crate::Node;
use crate::{Node, WasmNotSend};
use core::future::Future;
use core::ops::Deref;
use std::sync::Mutex;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
use core::cell::Cell;
use core::pin::Pin;
use dyn_any::DynFuture;
/// Caches the output of a given Node and acts as a proxy
#[derive(Default)]
pub struct MemoNode<T, CachedNode> {
cache: Cell<Option<T>>,
cache: Arc<Mutex<Option<T>>>,
node: CachedNode,
}
impl<'i, 'o: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, ()> for MemoNode<T, CachedNode>
impl<'i, 'o: 'i, T: 'i + Clone + 'o + WasmNotSend, CachedNode: 'i> Node<'i, ()> for MemoNode<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, ()>,
for<'a> <CachedNode as Node<'a, ()>>::Output: core::future::Future<Output = T> + 'a,
for<'a> <CachedNode as Node<'a, ()>>::Output: core::future::Future<Output = T> + WasmNotSend,
{
// TODO: This should return a reference to the cached cached_value
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
fn eval(&'i self, input: ()) -> Pin<Box<dyn Future<Output = T> + 'i>> {
Box::pin(async move {
if let Some(cached_value) = self.cache.take() {
self.cache.set(Some(cached_value.clone()));
cached_value
} else {
let value = self.node.eval(input).await;
self.cache.set(Some(value.clone()));
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: ()) -> Self::Output {
if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() {
let data = cached_value.clone();
Box::pin(async move { data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some(value.clone());
value
}
})
})
}
}
fn reset(&self) {
self.cache.set(None);
self.cache.lock().unwrap().take();
}
}
impl<T, CachedNode> MemoNode<T, CachedNode> {
pub const fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
MemoNode { cache: Cell::new(None), node }
pub fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
MemoNode { cache: Default::default(), node }
}
}
@@ -50,41 +53,43 @@ impl<T, CachedNode> MemoNode<T, CachedNode> {
/// use with caution.
#[derive(Default)]
pub struct ImpureMemoNode<I, T, CachedNode> {
cache: Cell<Option<T>>,
cache: Arc<Mutex<Option<T>>>,
node: CachedNode,
_phantom: std::marker::PhantomData<I>,
}
impl<'i, 'o: 'i, I: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, I> for ImpureMemoNode<I, T, CachedNode>
impl<'i, 'o: 'i, I: 'i, T: 'i + Clone + 'o + WasmNotSend, CachedNode: 'i> Node<'i, I> for ImpureMemoNode<I, T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + 'a,
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + WasmNotSend,
{
// TODO: This should return a reference to the cached cached_value
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
fn eval(&'i self, input: I) -> Pin<Box<dyn Future<Output = T> + 'i>> {
Box::pin(async move {
if let Some(cached_value) = self.cache.take() {
self.cache.set(Some(cached_value.clone()));
cached_value
} else {
let value = self.node.eval(input).await;
self.cache.set(Some(value.clone()));
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() {
let data = cached_value.clone();
Box::pin(async move { data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some(value.clone());
value
}
})
})
}
}
fn reset(&self) {
self.cache.set(None);
self.cache.lock().unwrap().take();
}
}
impl<T, I, CachedNode> ImpureMemoNode<I, T, CachedNode> {
pub const fn new(node: CachedNode) -> ImpureMemoNode<I, T, CachedNode> {
pub fn new(node: CachedNode) -> ImpureMemoNode<I, T, CachedNode> {
ImpureMemoNode {
cache: Cell::new(None),
cache: Default::default(),
node,
_phantom: core::marker::PhantomData,
}
@@ -102,37 +107,38 @@ pub struct IORecord<I, O> {
/// Caches the output of the last graph evaluation for introspection
#[derive(Default)]
pub struct MonitorNode<I, T, N> {
io: Cell<Option<Arc<IORecord<I, T>>>>,
#[allow(clippy::type_complexity)]
io: Arc<Mutex<Option<Arc<IORecord<I, T>>>>>,
node: N,
}
#[cfg(feature = "alloc")]
impl<'i, 'a: 'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
impl<'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
where
I: Clone + 'static,
<N as Node<'i, I>>::Output: Future<Output = T>,
T: Clone + 'static,
N: Node<'i, I>,
I: Clone + 'static + Send + Sync,
T: Clone + 'static + Send + Sync,
for<'a> N: Node<'a, I, Output: Future<Output = T> + WasmNotSend> + 'i,
{
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
let io = self.io.clone();
let output_fut = self.node.eval(input.clone());
Box::pin(async move {
let output = self.node.eval(input.clone()).await;
self.io.set(Some(Arc::new(IORecord { input, output: output.clone() })));
let output = output_fut.await;
*io.lock().unwrap() = Some(Arc::new(IORecord { input, output: output.clone() }));
output
})
}
fn serialize(&self) -> Option<Arc<dyn core::any::Any>> {
let io = self.io.take();
self.io.set(io.clone());
let io = self.io.lock().unwrap();
(io).as_ref().map(|output| output.clone() as Arc<dyn core::any::Any>)
}
}
#[cfg(feature = "alloc")]
impl<I, T, N> MonitorNode<I, T, N> {
pub const fn new(node: N) -> MonitorNode<I, T, N> {
MonitorNode { io: Cell::new(None), node }
pub fn new(node: N) -> MonitorNode<I, T, N> {
MonitorNode { io: Arc::new(Mutex::new(None)), node }
}
}

View File

@@ -379,7 +379,7 @@ pub struct IntoNode<I, O> {
#[node_macro::node_fn(IntoNode<_I, _O>)]
async fn into<_I, _O>(input: _I) -> _O
where
_I: Into<_O>,
_I: Into<_O> + Sync + Send,
{
input.into()
}

View File

@@ -1,4 +1,5 @@
use core::hash::Hash;
use half::f16;
use dyn_any::{DynAny, StaticType};
#[cfg(feature = "serde")]
@@ -14,6 +15,78 @@ use super::{
discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float},
Alpha, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGBMut, Rec709Primaries, RGB, SRGB,
};
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable)]
pub struct RGBA16F {
red: f16,
green: f16,
blue: f16,
alpha: f16,
}
impl From<Color> for RGBA16F {
#[inline(always)]
fn from(c: Color) -> Self {
Self {
red: f16::from_f32(c.r()),
green: f16::from_f32(c.g()),
blue: f16::from_f32(c.b()),
alpha: f16::from_f32(c.a()),
}
}
}
impl Luminance for RGBA16F {
type LuminanceChannel = f32;
#[inline(always)]
fn luminance(&self) -> f32 {
// TODO: verify this is correct for sRGB
0.2126 * self.red() + 0.7152 * self.green() + 0.0722 * self.blue()
}
}
impl RGB for RGBA16F {
type ColorChannel = f32;
#[inline(always)]
fn red(&self) -> f32 {
self.red.to_f32()
}
#[inline(always)]
fn green(&self) -> f32 {
self.green.to_f32()
}
#[inline(always)]
fn blue(&self) -> f32 {
self.blue.to_f32()
}
}
impl Rec709Primaries for RGBA16F {}
impl Alpha for RGBA16F {
type AlphaChannel = f32;
#[inline(always)]
fn alpha(&self) -> f32 {
self.alpha.to_f32() / 255.
}
const TRANSPARENT: Self = RGBA16F {
red: f16::from_f32_const(0.),
green: f16::from_f32_const(0.),
blue: f16::from_f32_const(0.),
alpha: f16::from_f32_const(0.),
};
fn multiplied_alpha(&self, alpha: Self::AlphaChannel) -> Self {
let alpha = alpha * 255.;
let mut result = *self;
result.alpha = f16::from_f32(alpha * self.alpha());
result
}
}
impl Pixel for RGBA16F {}
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -33,7 +106,7 @@ impl From<Color> for SRGBA8 {
red: float_to_srgb_u8(c.r()),
green: float_to_srgb_u8(c.g()),
blue: float_to_srgb_u8(c.b()),
alpha: (c.a() * 255.0) as u8,
alpha: (c.a() * 255.) as u8,
}
}
}
@@ -45,7 +118,7 @@ impl From<SRGBA8> for Color {
red: srgb_u8_to_float(color.red),
green: srgb_u8_to_float(color.green),
blue: srgb_u8_to_float(color.blue),
alpha: color.alpha as f32 / 255.0,
alpha: color.alpha as f32 / 255.,
}
}
}
@@ -63,15 +136,15 @@ impl RGB for SRGBA8 {
type ColorChannel = f32;
#[inline(always)]
fn red(&self) -> f32 {
self.red as f32 / 255.0
self.red as f32 / 255.
}
#[inline(always)]
fn green(&self) -> f32 {
self.green as f32 / 255.0
self.green as f32 / 255.
}
#[inline(always)]
fn blue(&self) -> f32 {
self.blue as f32 / 255.0
self.blue as f32 / 255.
}
}
@@ -82,13 +155,13 @@ impl Alpha for SRGBA8 {
type AlphaChannel = f32;
#[inline(always)]
fn alpha(&self) -> f32 {
self.alpha as f32 / 255.0
self.alpha as f32 / 255.
}
const TRANSPARENT: Self = SRGBA8 { red: 0, green: 0, blue: 0, alpha: 0 };
fn multiplied_alpha(&self, alpha: Self::AlphaChannel) -> Self {
let alpha = alpha * 255.0;
let alpha = alpha * 255.;
let mut result = *self;
result.alpha = (alpha * self.alpha()) as u8;
result
@@ -338,7 +411,7 @@ impl Color {
#[inline(always)]
pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
let alpha = alpha as f32 / 255.;
let map_range = |int_color| int_color as f32 / 255.0;
let map_range = |int_color| int_color as f32 / 255.;
Color {
red: map_range(red),
green: map_range(green),

View File

@@ -1,5 +1,3 @@
use core::future::Future;
use dyn_any::StaticType;
use glam::DAffine2;
@@ -27,6 +25,12 @@ pub trait Transform {
}
}
impl<T: Transform> Transform for &T {
fn transform(&self) -> DAffine2 {
(*self).transform()
}
}
pub trait TransformMut: Transform {
fn transform_mut(&mut self) -> &mut DAffine2;
fn translate(&mut self, offset: DVec2) {
@@ -42,14 +46,6 @@ impl<P: Pixel> Transform for ImageFrame<P> {
self.local_pivot(pivot)
}
}
impl<P: Pixel> Transform for &ImageFrame<P> {
fn transform(&self) -> DAffine2 {
self.transform
}
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
(*self).local_pivot(pivot)
}
}
impl<P: Pixel> TransformMut for ImageFrame<P> {
fn transform_mut(&mut self) -> &mut DAffine2 {
&mut self.transform
@@ -60,11 +56,6 @@ impl Transform for GraphicGroup {
self.transform
}
}
impl Transform for &GraphicGroup {
fn transform(&self) -> DAffine2 {
self.transform
}
}
impl TransformMut for GraphicGroup {
fn transform_mut(&mut self) -> &mut DAffine2 {
&mut self.transform
@@ -76,6 +67,7 @@ impl Transform for GraphicElement {
GraphicElement::VectorData(vector_shape) => vector_shape.transform(),
GraphicElement::ImageFrame(image_frame) => image_frame.transform(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform(),
GraphicElement::Surface(surface) => surface.transform(),
}
}
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
@@ -83,13 +75,7 @@ impl Transform for GraphicElement {
GraphicElement::VectorData(vector_shape) => vector_shape.local_pivot(pivot),
GraphicElement::ImageFrame(image_frame) => image_frame.local_pivot(pivot),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.local_pivot(pivot),
}
}
fn decompose_scale(&self) -> DVec2 {
match self {
GraphicElement::VectorData(vector_shape) => vector_shape.decompose_scale(),
GraphicElement::ImageFrame(image_frame) => image_frame.decompose_scale(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.decompose_scale(),
GraphicElement::Surface(surface) => surface.local_pivot(pivot),
}
}
}
@@ -99,6 +85,7 @@ impl TransformMut for GraphicElement {
GraphicElement::VectorData(vector_shape) => vector_shape.transform_mut(),
GraphicElement::ImageFrame(image_frame) => image_frame.transform_mut(),
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform_mut(),
GraphicElement::Surface(surface) => surface.transform_mut(),
}
}
}
@@ -137,16 +124,6 @@ impl TransformMut for DAffine2 {
}
}
#[derive(Debug, Clone, Copy)]
pub struct TransformNode<TransformTarget, Translation, Rotation, Scale, Shear, Pivot> {
pub(crate) transform_target: TransformTarget,
pub(crate) translate: Translation,
pub(crate) rotate: Rotation,
pub(crate) scale: Scale,
pub(crate) shear: Shear,
pub(crate) _pivot: Pivot,
}
#[derive(Debug, Clone, Copy, dyn_any::DynAny, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RenderQuality {
@@ -233,19 +210,26 @@ impl TransformMut for Footprint {
}
}
#[derive(Debug, Clone, Copy)]
pub struct TransformNode<TransformTarget, Translation, Rotation, Scale, Shear, Pivot> {
pub(crate) transform_target: TransformTarget,
pub(crate) translate: Translation,
pub(crate) rotate: Rotation,
pub(crate) scale: Scale,
pub(crate) shear: Shear,
pub(crate) _pivot: Pivot,
}
#[node_macro::node_fn(TransformNode)]
pub(crate) async fn transform_vector_data<Fut: Future>(
pub(crate) async fn transform_vector_data<T: TransformMut>(
mut footprint: Footprint,
transform_target: impl Node<Footprint, Output = Fut>,
transform_target: impl Node<Footprint, Output = T>,
translate: DVec2,
rotate: f64,
scale: DVec2,
shear: DVec2,
_pivot: DVec2,
) -> Fut::Output
where
Fut::Output: TransformMut,
{
) -> T {
let modification = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]);
if !footprint.ignore_modifications {
*footprint.transform_mut() = footprint.transform() * modification;

View File

@@ -337,7 +337,7 @@ impl HandleId {
}
#[cfg(test)]
fn assert_subpath_eq(generated: &Vec<bezier_rs::Subpath<PointId>>, expected: &[bezier_rs::Subpath<PointId>]) {
fn assert_subpath_eq(generated: &[bezier_rs::Subpath<PointId>], expected: &[bezier_rs::Subpath<PointId>]) {
assert_eq!(generated.len(), expected.len());
for (generated, expected) in generated.iter().zip(expected) {
assert_eq!(generated.manipulator_groups().len(), expected.manipulator_groups().len());

View File

@@ -443,7 +443,7 @@ fn modify_existing() {
false,
),
];
let mut vector_data = VectorData::from_subpaths(&subpaths, false);
let mut vector_data = VectorData::from_subpaths(subpaths, false);
let mut modify_new = VectorModification::create_from_vector(&vector_data);
let mut modify_original = VectorModification::default();

View File

@@ -4,7 +4,6 @@ use super::{PointId, SegmentId, StrokeId, VectorData};
use crate::renderer::GraphicElementRendered;
use crate::transform::{Footprint, Transform, TransformMut};
use crate::{Color, GraphicGroup, Node};
use core::future::Future;
use bezier_rs::{Cap, Join, Subpath, SubpathTValue, TValue};
use glam::{DAffine2, DVec2};
@@ -212,10 +211,10 @@ pub struct CopyToPoints<Points, Instance, RandomScaleMin, RandomScaleMax, Random
}
#[node_macro::node_fn(CopyToPoints)]
async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + TransformMut, FP: Future<Output = VectorData>, FI: Future<Output = I>>(
async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + TransformMut + Send>(
footprint: Footprint,
points: impl Node<Footprint, Output = FP>,
instance: impl Node<Footprint, Output = FI>,
points: impl Node<Footprint, Output = VectorData>,
instance: impl Node<Footprint, Output = I>,
random_scale_min: f64,
random_scale_max: f64,
random_scale_bias: f64,
@@ -280,14 +279,14 @@ pub struct SamplePoints<VectorData, Spacing, StartOffset, StopOffset, AdaptiveSp
}
#[node_macro::node_fn(SamplePoints)]
async fn sample_points<FV: Future<Output = VectorData>, FL: Future<Output = Vec<f64>>>(
async fn sample_points(
footprint: Footprint,
mut vector_data: impl Node<Footprint, Output = FV>,
mut vector_data: impl Node<Footprint, Output = VectorData>,
spacing: f64,
start_offset: f64,
stop_offset: f64,
adaptive_spacing: bool,
lengths_of_segments_of_subpaths: impl Node<Footprint, Output = FL>,
lengths_of_segments_of_subpaths: impl Node<Footprint, Output = Vec<f64>>,
) -> VectorData {
let vector_data = self.vector_data.eval(footprint).await;
let lengths_of_segments_of_subpaths = self.lengths_of_segments_of_subpaths.eval(footprint).await;
@@ -422,13 +421,7 @@ pub struct MorphNode<Source, Target, StartIndex, Time> {
}
#[node_macro::node_fn(MorphNode)]
async fn morph<SourceFuture: Future<Output = VectorData>, TargetFuture: Future<Output = VectorData>>(
footprint: Footprint,
source: impl Node<Footprint, Output = SourceFuture>,
target: impl Node<Footprint, Output = TargetFuture>,
start_index: u32,
time: f64,
) -> VectorData {
async fn morph(footprint: Footprint, source: impl Node<Footprint, Output = VectorData>, target: impl Node<Footprint, Output = VectorData>, start_index: u32, time: f64) -> VectorData {
let source = self.source.eval(footprint).await;
let target = self.target.eval(footprint).await;
let mut result = VectorData::empty();
@@ -516,7 +509,7 @@ pub struct AreaNode<VectorData> {
}
#[node_macro::node_fn(AreaNode)]
async fn area_node<Fut: Future<Output = VectorData>>(empty: (), vector_data: impl Node<Footprint, Output = Fut>) -> f64 {
async fn area_node(empty: (), vector_data: impl Node<Footprint, Output = VectorData>) -> f64 {
let vector_data = self.vector_data.eval(Footprint::default()).await;
let mut area = 0.;
@@ -534,7 +527,7 @@ pub struct CentroidNode<VectorData, CentroidType> {
}
#[node_macro::node_fn(CentroidNode)]
async fn centroid_node<Fut: Future<Output = VectorData>>(empty: (), vector_data: impl Node<Footprint, Output = Fut>, centroid_type: CentroidType) -> DVec2 {
async fn centroid_node(empty: (), vector_data: impl Node<Footprint, Output = VectorData>, centroid_type: CentroidType) -> DVec2 {
let vector_data = self.vector_data.eval(Footprint::default()).await;
if centroid_type == CentroidType::Area {
@@ -594,11 +587,12 @@ mod test {
impl<'i, T: 'i, N: Node<'i, T> + Clone> Node<'i, T> for FutureWrapperNode<N>
where
N: Node<'i, T>,
N: Node<'i, T, Output: Send>,
{
type Output = Pin<Box<dyn core::future::Future<Output = N::Output> + 'i>>;
type Output = Pin<Box<dyn core::future::Future<Output = N::Output> + 'i + Send>>;
fn eval(&'i self, input: T) -> Self::Output {
Box::pin(async move { self.0.eval(input) })
let result = self.0.eval(input);
Box::pin(async move { result })
}
}