mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Replace the IntoPaint trait with direct Graphic-typed node inputs (#4442)
* Let the Fill and Stroke paint inputs take Item<Graphic>, replacing the IntoPaint trait * Rename the FIll node's "fill" input to "paint" * Let the graphic-consuming nodes take List<Graphic> directly, relying on the embedding adapters * Remove outdated todo comments * Re-save the demo art
This commit is contained in:
@@ -3,12 +3,9 @@
|
||||
//! while cover-specific data rides the inner `Item<Cover>`, reusing `ATTR_TRANSFORM` for the stroke-authoring space.
|
||||
|
||||
use crate::graphic::Graphic;
|
||||
use core_types::Color;
|
||||
use core_types::graphene_hash::CacheHash;
|
||||
use core_types::list::{ATTR_ALIGN, ATTR_APPEARANCE, ATTR_CAP, ATTR_DASH_OFFSET, ATTR_DASH_PATTERN, ATTR_JOIN, ATTR_JOIN_MITER_LIMIT, ATTR_PAINT, ATTR_TRANSFORM, ATTR_WEIGHT, Item, List};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::vector::style::{DashPattern, Stroke};
|
||||
use vector_types::{Gradient, Vector};
|
||||
|
||||
/// The geometry-to-region operator a coverage applies before painting:
|
||||
/// the interior of the geometry (fill) or the region swept along its outline (stroke).
|
||||
@@ -275,95 +272,10 @@ pub fn stamp_coverage<T>(item: &mut Item<T>, coverage: Coverage, paint: Graphic,
|
||||
item.attribute_mut_or_insert_default::<Appearance>(ATTR_APPEARANCE).replace_or_insert(coverage, paint, placement);
|
||||
}
|
||||
|
||||
// ================
|
||||
// TRAIT: IntoPaint
|
||||
// ================
|
||||
|
||||
/// Converts the types accepted by a paint input into the canonical `Graphic` stored in the `ATTR_PAINT` attribute.
|
||||
/// `List<Graphic>` deliberately has no impl: a multi-element paint is a type error.
|
||||
pub trait IntoPaint: Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static {
|
||||
fn into_paint(self) -> Graphic;
|
||||
}
|
||||
|
||||
impl IntoPaint for Item<Graphic> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
// Wrapping to keep the record's attributes would nest the paint as a group, changing how it renders
|
||||
self.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for Item<Vector> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::VectorList(List::new_from_item(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for Item<Raster<CPU>> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::RasterCPUList(List::new_from_item(self))
|
||||
}
|
||||
}
|
||||
|
||||
// No Item<Raster<GPU>> impl: GPU rasters have no Default, which the trait bounds require of the element
|
||||
|
||||
impl IntoPaint for Item<Color> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::ColorList(List::new_from_item(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for Item<Gradient> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::GradientList(List::new_from_item(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for Item<String> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::TextList(List::new_from_item(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<Vector> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::VectorList(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<Raster<CPU>> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::RasterCPUList(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<Raster<GPU>> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::RasterGPUList(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<Color> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::ColorList(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<Gradient> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::GradientList(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPaint for List<String> {
|
||||
fn into_paint(self) -> Graphic {
|
||||
Graphic::TextList(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core_types::list::ATTR_POSITION;
|
||||
use core_types::Color;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use vector_types::vector::style::{StrokeAlign, StrokeCap, StrokeJoin};
|
||||
|
||||
@@ -470,24 +382,4 @@ mod tests {
|
||||
appearance.replace_or_insert(Coverage::new_fill(), solid_paint(Color::RED), CoverPlacement::Above);
|
||||
assert!(appearance.has_painted_cover(Cover::Fill));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_paint_becomes_one_graphic_holding_every_element() {
|
||||
let mut colors = List::new_from_element(Color::RED);
|
||||
colors.push(Item::new_from_element(Color::BLUE));
|
||||
|
||||
let paint = colors.into_paint();
|
||||
let Graphic::ColorList(inner) = &paint else { panic!("expected a color graphic") };
|
||||
assert_eq!(inner.len(), 2, "a list paint is one graphic holding all its elements");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_paint_keeps_its_attributes_on_the_inner_row() {
|
||||
let color = Item::new_from_element(Color::RED).with_attribute(ATTR_POSITION, 0.25_f64);
|
||||
|
||||
let paint = color.into_paint();
|
||||
let Graphic::ColorList(inner) = &paint else { panic!("expected a color graphic") };
|
||||
assert_eq!(inner.len(), 1);
|
||||
assert_eq!(inner.attribute::<f64>(ATTR_POSITION, 0), Some(&0.25));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,12 +950,8 @@ mod tests {
|
||||
assert!(!graphic_list.attribute_keys().any(|key| key == ATTR_EDITOR_LAYER_PATH));
|
||||
}
|
||||
|
||||
// Round-tripping through that wrapper must not collapse the items' distinct stamps onto item 0's
|
||||
#[test]
|
||||
fn round_trip_through_the_wrapper_preserves_per_item_layer_paths() {
|
||||
let flattened: List<Vector> = vector_list_stamped_with_layers([7, 9]).into_flattened_list();
|
||||
|
||||
let layers = (0..flattened.len())
|
||||
fn layer_stamps(flattened: &List<Vector>) -> Vec<Option<NodeId>> {
|
||||
(0..flattened.len())
|
||||
.map(|index| {
|
||||
flattened
|
||||
.attribute_cloned_or_default::<NodeIdPath>(ATTR_EDITOR_LAYER_PATH, index)
|
||||
@@ -964,9 +960,25 @@ mod tests {
|
||||
.next_back()
|
||||
.copied()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect()
|
||||
}
|
||||
|
||||
assert_eq!(layers, [Some(NodeId(7)), Some(NodeId(9))]);
|
||||
// Round-tripping through that wrapper must not collapse the items' distinct stamps onto item 0's
|
||||
#[test]
|
||||
fn round_trip_through_the_wrapper_preserves_per_item_layer_paths() {
|
||||
let flattened: List<Vector> = vector_list_stamped_with_layers([7, 9]).into_flattened_list();
|
||||
|
||||
assert_eq!(layer_stamps(&flattened), [Some(NodeId(7)), Some(NodeId(9))]);
|
||||
}
|
||||
|
||||
// The embedding adapter reaches the same flattened stamps as the wrapper, each item carrying its own inside its variant
|
||||
#[test]
|
||||
fn embedding_each_item_preserves_per_item_layer_paths() {
|
||||
let embedded: List<Graphic> = vector_list_stamped_with_layers([7, 9]).into_iter().map(|item| Item::new_from_element(Graphic::from(item))).collect();
|
||||
|
||||
let flattened: List<Vector> = embedded.into_flattened_list();
|
||||
|
||||
assert_eq!(layer_stamps(&flattened), [Some(NodeId(7)), Some(NodeId(9))]);
|
||||
}
|
||||
|
||||
// Flattening must not invent attributes that neither the parent graphic nor the child carried
|
||||
|
||||
@@ -8,7 +8,7 @@ pub use raster_types;
|
||||
pub use vector_types;
|
||||
|
||||
// Re-export commonly used types at the crate root
|
||||
pub use appearance::{Appearance, Cover, CoverPlacement, Coverage, FillAndStroke, IntoPaint, stamp_coverage};
|
||||
pub use appearance::{Appearance, Cover, CoverPlacement, Coverage, FillAndStroke, stamp_coverage};
|
||||
pub use artboard::Artboard;
|
||||
pub use graphic::{Graphic, IntoGraphicList, TryFromGraphic, Vector};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user