mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Convert the node catalog to rank-polymorphic Item and List kernels, materialize stored values as ranked wires, and display wire rank in the graph
This commit is contained in:
committed by
Dennis Kobert
parent
ead622b969
commit
8bddd5680b
@@ -16,7 +16,7 @@ wgpu = ["dep:raster-types", "raster-types/wgpu"]
|
||||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
core-types = { workspace = true }
|
||||
graphene-hash = { workspace = true }
|
||||
graphene-hash = { workspace = true, features = ["derive"] }
|
||||
vector-types = { workspace = true }
|
||||
text-nodes = { workspace = true }
|
||||
graphene-resource = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use core_types::transform::Footprint;
|
||||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||
use glam::DVec2;
|
||||
use graphene_hash::CacheHash;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ptr::addr_of;
|
||||
@@ -61,7 +62,7 @@ pub trait GetEditorPreferences {
|
||||
fn max_render_region_area(&self) -> u32;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, graphene_hash::CacheHash)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, CacheHash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum ExportFormat {
|
||||
#[default]
|
||||
@@ -69,14 +70,14 @@ pub enum ExportFormat {
|
||||
Raster,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, graphene_hash::CacheHash)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, CacheHash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct TimingInformation {
|
||||
pub time: f64,
|
||||
pub animation_time: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, graphene_hash::CacheHash)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, CacheHash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct RenderConfig {
|
||||
pub viewport: Footprint,
|
||||
@@ -136,7 +137,7 @@ impl<Io> Hash for EditorApi<Io> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Io> core_types::graphene_hash::CacheHash for EditorApi<Io> {
|
||||
impl<Io> CacheHash for EditorApi<Io> {
|
||||
fn cache_hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(self, state);
|
||||
}
|
||||
|
||||
@@ -601,6 +601,11 @@ impl ItemAttributeValues {
|
||||
self.0.iter().map(|(key, value)| (key.as_str(), &**value))
|
||||
}
|
||||
|
||||
/// Returns a type-erased reference to the value of the attribute with the given key, if it exists.
|
||||
pub fn get_any(&self, key: &str) -> Option<&dyn std::any::Any> {
|
||||
self.0.iter().find_map(|(existing_key, value)| if existing_key == key { Some((**value).as_any()) } else { None })
|
||||
}
|
||||
|
||||
/// Returns a debug-formatted string representation of the attribute value for the given key, if it exists.
|
||||
/// The `overrides` function can provide custom formatting for specific type.
|
||||
pub fn display_value(&self, key: &str, overrides: fn(&dyn std::any::Any) -> Option<String>) -> Option<String> {
|
||||
@@ -1326,6 +1331,10 @@ impl<T> Item<T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: StaticTypeSized> StaticType for Item<T> {
|
||||
type Static = Item<T::Static>;
|
||||
}
|
||||
|
||||
// ===========
|
||||
// ItemIter<T>
|
||||
// ===========
|
||||
|
||||
@@ -61,6 +61,27 @@ impl Clampable for DVec2 {
|
||||
}
|
||||
}
|
||||
|
||||
// Implement for ranked wires (element-wise clamping across the frame)
|
||||
use crate::list::{Item, List};
|
||||
impl<T: Clampable> Clampable for Item<T> {
|
||||
fn clamp_hard_min(self, min: f64) -> Self {
|
||||
let (element, attributes) = self.into_parts();
|
||||
Item::from_parts(element.clamp_hard_min(min), attributes)
|
||||
}
|
||||
fn clamp_hard_max(self, max: f64) -> Self {
|
||||
let (element, attributes) = self.into_parts();
|
||||
Item::from_parts(element.clamp_hard_max(max), attributes)
|
||||
}
|
||||
}
|
||||
impl<T: Clampable> Clampable for List<T> {
|
||||
fn clamp_hard_min(self, min: f64) -> Self {
|
||||
self.into_iter().map(|item| item.clamp_hard_min(min)).collect()
|
||||
}
|
||||
fn clamp_hard_max(self, max: f64) -> Self {
|
||||
self.into_iter().map(|item| item.clamp_hard_max(max)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[derive(serde::Deserialize)]
|
||||
struct LegacyTable<T> {
|
||||
|
||||
@@ -77,8 +77,7 @@ impl Convert<DVec2, ()> for DVec2 {
|
||||
}
|
||||
|
||||
/// Constructs `Self` from a single anchor point at the given position. Implemented by the vector crate's
|
||||
/// path type so the `Convert` impl below can build a single-point path without core-types depending on
|
||||
/// that crate (mirroring how [`ListConvert`] bridges per-item list conversions).
|
||||
/// path type so a position wire can convert to a single-point path without core-types depending on that crate.
|
||||
pub trait FromAnchorPosition {
|
||||
fn from_anchor_position(position: DVec2) -> Self;
|
||||
}
|
||||
|
||||
@@ -217,6 +217,23 @@ impl From<()> for Footprint {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes an item's `transform` attribute by baking it into the underlying value itself.
|
||||
pub trait BakeTransform {
|
||||
fn bake_transform(&mut self, transform: &DAffine2);
|
||||
}
|
||||
|
||||
impl BakeTransform for DAffine2 {
|
||||
fn bake_transform(&mut self, transform: &DAffine2) {
|
||||
*self = *transform * *self;
|
||||
}
|
||||
}
|
||||
|
||||
impl BakeTransform for DVec2 {
|
||||
fn bake_transform(&mut self, transform: &DAffine2) {
|
||||
*self = transform.transform_point2(*self);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ApplyTransform {
|
||||
fn apply_transform(&mut self, modification: &DAffine2);
|
||||
fn left_apply_transform(&mut self, modification: &DAffine2);
|
||||
|
||||
@@ -1767,7 +1767,7 @@ fn render_vector_vello<S: LaneSource<Element = Vector>>(source: &S, scene: &mut
|
||||
}
|
||||
|
||||
fn collect_vector_metadata<S: LaneSource<Element = Vector>>(source: &S, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||
// Aggregate all items' targets per element_id so multi-item lists (e.g. 'Text' node with "Separate Glyphs" active) produce hit areas for every glyph.
|
||||
// Aggregate all items' targets per element_id so multi-item lists (e.g. the "Text to Vector Glyphs" node) produce hit areas for every glyph.
|
||||
// Targets are baked relative to item 0's transform since `Graphic::collect_metadata` records that as `local_transforms[element_id]`.
|
||||
let item_zero_transform: DAffine2 = if source.lane_count() > 0 { source.attr::<Transform>(0) } else { DAffine2::IDENTITY };
|
||||
let item_zero_inverse = if transform_is_invertible(item_zero_transform) {
|
||||
|
||||
@@ -63,6 +63,13 @@ impl core_types::ops::FromAnchorPosition for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
// Lets a position wire feed a ranked vector connector through the input adapter's element conversion
|
||||
impl From<DVec2> for Vector {
|
||||
fn from(position: DVec2) -> Self {
|
||||
<Self as core_types::ops::FromAnchorPosition>::from_anchor_position(position)
|
||||
}
|
||||
}
|
||||
|
||||
// Identity item conversion so `List<Vector>` satisfies the blanket `Convert<List<U>, ()> for List<T>`, letting its
|
||||
// auto-inserted input wrapper be a `ConvertNode` (which also accepts a `DVec2` anchor position) rather than an `IntoNode`.
|
||||
impl core_types::ops::ListConvert<Vector> for Vector {
|
||||
@@ -71,6 +78,15 @@ impl core_types::ops::ListConvert<Vector> for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
impl core_types::transform::BakeTransform for Vector {
|
||||
fn bake_transform(&mut self, transform: &glam::DAffine2) {
|
||||
for (_, point) in self.point_domain.positions_mut() {
|
||||
*point = transform.transform_point2(*point);
|
||||
}
|
||||
self.segment_domain.transform(*transform);
|
||||
}
|
||||
}
|
||||
|
||||
impl Vector {
|
||||
/// Add a subpath to this vector path.
|
||||
pub fn append_subpath(&mut self, subpath: impl Borrow<Subpath<PointId>>, preserve_id: bool) {
|
||||
|
||||
@@ -20,7 +20,7 @@ fn upload_to_texture(device: &wgpu::Device, queue: &wgpu::Queue, image: &Raster<
|
||||
device.create_texture_with_data(
|
||||
queue,
|
||||
&TextureDescriptor {
|
||||
label: Some("upload_texture node texture"),
|
||||
label: Some("upload_to_texture staging texture"),
|
||||
size: Extent3d {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
|
||||
Reference in New Issue
Block a user