mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 14:58:05 +08:00
Add Table<Color> as a graphical type (#3033)
* Reduce code duplication in bounding box impls on Table * Working Table<Color> rendering in the graph * Implement color and fix other rendering with Vello and polish
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::table::{Table, TableRowRef};
|
||||
use crate::vector::Vector;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, Graphic, OwnedContextImpl};
|
||||
use glam::DVec2;
|
||||
use graphene_core_shaders::color::Color;
|
||||
|
||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
@@ -11,7 +12,8 @@ async fn instance_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
reverse: bool,
|
||||
@@ -52,7 +54,8 @@ async fn instance_repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Color>,
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
#[default(1)] count: u64,
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::algorithms::spline::{solve_spline_first_handle_closed, solve_spline_f
|
||||
use super::misc::{CentroidType, bezpath_from_manipulator_groups, bezpath_to_manipulator_groups, point_to_dvec2};
|
||||
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
||||
use super::{PointId, SegmentDomain, SegmentId, StrokeId, Vector, VectorExt};
|
||||
use crate::bounds::BoundingBox;
|
||||
use crate::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
|
||||
use crate::table::{Table, TableRow, TableRowMut};
|
||||
@@ -106,7 +106,7 @@ where
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
||||
async fn fill<F: Into<Fill> + 'n + Send, V>(
|
||||
async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Vector>,
|
||||
@@ -116,7 +116,7 @@ async fn fill<F: Into<Fill> + 'n + Send, V>(
|
||||
Table<Graphic>,
|
||||
Table<Graphic>,
|
||||
Table<Graphic>,
|
||||
Table<Graphic>
|
||||
Table<Graphic>,
|
||||
)]
|
||||
/// The content with vector paths to apply the fill style to.
|
||||
mut content: V,
|
||||
@@ -135,10 +135,7 @@ async fn fill<F: Into<Fill> + 'n + Send, V>(
|
||||
fill: F,
|
||||
_backup_color: Option<Color>,
|
||||
_backup_gradient: Gradient,
|
||||
) -> V
|
||||
where
|
||||
V: VectorTableIterMut + 'n + Send,
|
||||
{
|
||||
) -> V {
|
||||
let fill: Fill = fill.into();
|
||||
for vector in content.vector_iter_mut() {
|
||||
let mut fill = fill.clone();
|
||||
@@ -219,7 +216,7 @@ where
|
||||
async fn repeat<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
// TODO: Implement other graphical types.
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)] instance: Table<I>,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)] instance: Table<I>,
|
||||
#[default(100., 100.)]
|
||||
// TODO: When using a custom Properties panel layout in document_node_definitions.rs and this default is set, the widget weirdly doesn't show up in the Properties panel. Investigation is needed.
|
||||
direction: PixelSize,
|
||||
@@ -255,7 +252,7 @@ async fn repeat<I: 'n + Send + Clone>(
|
||||
async fn circular_repeat<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
// TODO: Implement other graphical types.
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)] instance: Table<I>,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)] instance: Table<I>,
|
||||
angle_offset: Angle,
|
||||
#[unit(" px")]
|
||||
#[default(5)]
|
||||
@@ -291,7 +288,7 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
points: Table<Vector>,
|
||||
#[expose]
|
||||
/// Artwork to be copied and placed at each point.
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)]
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)]
|
||||
instance: Table<I>,
|
||||
/// Minimum range of randomized sizes given to each instance.
|
||||
#[default(1)]
|
||||
@@ -366,7 +363,7 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn mirror<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)] instance: Table<I>,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)] content: Table<I>,
|
||||
#[default(ReferencePoint::Center)] relative_to_bounds: ReferencePoint,
|
||||
#[unit(" px")] offset: f64,
|
||||
#[range((-90., 90.))] angle: Angle,
|
||||
@@ -375,14 +372,12 @@ async fn mirror<I: 'n + Send + Clone>(
|
||||
where
|
||||
Table<I>: BoundingBox,
|
||||
{
|
||||
let mut result_table = Table::new();
|
||||
|
||||
// Normalize the direction vector
|
||||
let normal = DVec2::from_angle(angle.to_radians());
|
||||
|
||||
// The mirror reference is based on the bounding box (at least for now, until we have proper local layer origins)
|
||||
let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY, false) else {
|
||||
return result_table;
|
||||
// The mirror reference may be based on the bounding box if an explicit reference point is chosen
|
||||
let RenderBoundingBox::Rectangle(bounding_box) = content.bounding_box(DAffine2::IDENTITY, false) else {
|
||||
return content;
|
||||
};
|
||||
|
||||
let reference_point_location = relative_to_bounds.point_in_bounding_box((bounding_box[0], bounding_box[1]).into());
|
||||
@@ -404,15 +399,17 @@ where
|
||||
reflection * DAffine2::from_translation(DVec2::from_angle(angle.to_radians()) * DVec2::splat(-offset))
|
||||
};
|
||||
|
||||
let mut result_table = Table::new();
|
||||
|
||||
// Add original instance depending on the keep_original flag
|
||||
if keep_original {
|
||||
for instance in instance.clone().into_iter() {
|
||||
for instance in content.clone().into_iter() {
|
||||
result_table.push(instance);
|
||||
}
|
||||
}
|
||||
|
||||
// Create and add mirrored instance
|
||||
for mut row in instance.into_iter() {
|
||||
for mut row in content.into_iter() {
|
||||
row.transform = reflected_transform * row.transform;
|
||||
result_table.push(row);
|
||||
}
|
||||
@@ -1901,7 +1898,7 @@ fn point_inside(_: impl Ctx, source: Table<Vector>, point: DVec2) -> bool {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("General"), path(graphene_core::vector))]
|
||||
async fn count_elements<I>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] source: Table<I>) -> u64 {
|
||||
async fn count_elements<I>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>)] source: Table<I>) -> u64 {
|
||||
source.len() as u64
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::misc::dvec2_to_point;
|
||||
use super::style::{PathStyle, Stroke};
|
||||
pub use super::vector_attributes::*;
|
||||
pub use super::vector_modification::*;
|
||||
use crate::bounds::BoundingBox;
|
||||
use crate::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use crate::math::quad::Quad;
|
||||
use crate::table::Table;
|
||||
use crate::transform::Transform;
|
||||
@@ -437,8 +437,9 @@ impl Vector {
|
||||
}
|
||||
|
||||
impl BoundingBox for Table<Vector> {
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> {
|
||||
self.iter()
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
||||
let bounds = self
|
||||
.iter()
|
||||
.flat_map(|row| {
|
||||
if !include_stroke {
|
||||
return row.element.bounding_box_with_transform(transform * *row.transform);
|
||||
@@ -455,7 +456,12 @@ impl BoundingBox for Table<Vector> {
|
||||
|
||||
row.element.bounding_box_with_transform(transform * *row.transform).map(|[a, b]| [a - offset, b + offset])
|
||||
})
|
||||
.reduce(Quad::combine_bounds)
|
||||
.reduce(Quad::combine_bounds);
|
||||
|
||||
match bounds {
|
||||
Some(bounds) => RenderBoundingBox::Rectangle(bounds),
|
||||
None => RenderBoundingBox::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user