Replace the Color type with Table<Color> everywhere (#3048)

This commit is contained in:
Keavon Chambers
2025-08-12 00:38:23 -07:00
committed by GitHub
parent 437fc70500
commit 1b351aca76
46 changed files with 306 additions and 386 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -163,8 +163,6 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Raster<GPU>>, Table<Raster<GPU>>,
Table<Color>, Table<Color>,
Color,
Option<Color>,
f64, f64,
u32, u32,
u64, u64,
@@ -487,28 +485,6 @@ impl TableRowLayout for Color {
} }
} }
impl TableRowLayout for Option<Color> {
fn type_name() -> &'static str {
"Option<Color>"
}
fn identifier(&self) -> String {
format!(
"Option<Color> (#{})",
if let Some(color) = self { color.to_linear_srgb().to_rgba_hex_srgb() } else { "None".to_string() }
)
}
fn element_widget(&self, _index: usize) -> WidgetHolder {
ColorInput::new(if let Some(color) = self { FillChoice::Solid(*color) } else { FillChoice::None })
.disabled(true)
.menu_direction(Some(MenuDirection::Top))
.widget_holder()
}
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
let widgets = vec![self.element_widget(0)];
vec![LayoutGroup::Row { widgets }]
}
}
impl TableRowLayout for f64 { impl TableRowLayout for f64 {
fn type_name() -> &'static str { fn type_name() -> &'static str {
"Number (f64)" "Number (f64)"
@@ -135,7 +135,7 @@ impl<'a> ModifyInputsContext<'a> {
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)), Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)), Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)),
Some(NodeInput::value(TaggedValue::DVec2(artboard.dimensions.into()), false)), Some(NodeInput::value(TaggedValue::DVec2(artboard.dimensions.into()), false)),
Some(NodeInput::value(TaggedValue::Color(artboard.background), false)), Some(NodeInput::value(TaggedValue::Color(Table::new_from_element(artboard.background)), false)),
Some(NodeInput::value(TaggedValue::Bool(artboard.clip), false)), Some(NodeInput::value(TaggedValue::Bool(artboard.clip), false)),
]); ]);
self.network_interface.insert_node(new_id, artboard_node_template, &[]); self.network_interface.insert_node(new_id, artboard_node_template, &[]);
@@ -329,11 +329,11 @@ impl<'a> ModifyInputsContext<'a> {
match &fill { match &fill {
Fill::None => { Fill::None => {
let input_connector = InputConnector::node(fill_node_id, backup_color_index); let input_connector = InputConnector::node(fill_node_id, backup_color_index);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::OptionalColor(None), false), true); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Color(Table::new()), false), true);
} }
Fill::Solid(color) => { Fill::Solid(color) => {
let input_connector = InputConnector::node(fill_node_id, backup_color_index); let input_connector = InputConnector::node(fill_node_id, backup_color_index);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::OptionalColor(Some(*color)), false), true); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Color(Table::new_from_element(*color)), false), true);
} }
Fill::Gradient(gradient) => { Fill::Gradient(gradient) => {
let input_connector = InputConnector::node(fill_node_id, backup_gradient_index); let input_connector = InputConnector::node(fill_node_id, backup_gradient_index);
@@ -372,8 +372,10 @@ impl<'a> ModifyInputsContext<'a> {
pub fn stroke_set(&mut self, stroke: Stroke) { pub fn stroke_set(&mut self, stroke: Stroke) {
let Some(stroke_node_id) = self.existing_node_id("Stroke", true) else { return }; let Some(stroke_node_id) = self.existing_node_id("Stroke", true) else { return };
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::ColorInput::<Option<graphene_std::Color>>::INDEX); let stroke_color = if let Some(color) = stroke.color { Table::new_from_element(color) } else { Table::new() };
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::OptionalColor(stroke.color), false), true);
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::ColorInput::INDEX);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::Color(stroke_color), false), true);
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::WeightInput::INDEX); let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::WeightInput::INDEX);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.weight), false), true); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.weight), false), true);
let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::AlignInput::INDEX); let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::AlignInput::INDEX);
@@ -399,7 +399,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::Graphic(Default::default()), true), NodeInput::value(TaggedValue::Graphic(Default::default()), true),
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false), NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false), NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false),
NodeInput::value(TaggedValue::Color(Color::WHITE), false), NodeInput::value(TaggedValue::Color(Table::new_from_element(Color::WHITE)), false),
NodeInput::value(TaggedValue::Bool(false), false), NodeInput::value(TaggedValue::Bool(false), false),
], ],
..Default::default() ..Default::default()
@@ -89,7 +89,7 @@ pub struct NodeGraphMessageHandler {
reordering_import: Option<usize>, reordering_import: Option<usize>,
/// The index of the export that is being moved /// The index of the export that is being moved
reordering_export: Option<usize>, reordering_export: Option<usize>,
/// The end index of the moved port /// The end index of the moved connector
end_index: Option<usize>, end_index: Option<usize>,
/// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend /// Used to keep track of what nodes are sent to the front end so that only visible ones are sent to the frontend
frontend_nodes: Vec<NodeId>, frontend_nodes: Vec<NodeId>,
@@ -20,6 +20,7 @@ use graphene_std::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute, BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute,
SelectiveColorChoice, SelectiveColorChoice,
}; };
use graphene_std::table::{Table, TableRow};
use graphene_std::text::{Font, TextAlign}; use graphene_std::text::{Font, TextAlign};
use graphene_std::transform::{Footprint, ReferencePoint, Transform}; use graphene_std::transform::{Footprint, ReferencePoint, Transform};
use graphene_std::vector::misc::{ArcType, CentroidType, GridType, MergeByDistanceAlgorithm, PointSpacingType}; use graphene_std::vector::misc::{ArcType, CentroidType, GridType, MergeByDistanceAlgorithm, PointSpacingType};
@@ -180,8 +181,7 @@ pub(crate) fn property_from_type(
// ============ // ============
// STRUCT TYPES // STRUCT TYPES
// ============ // ============
Some(x) if x == TypeId::of::<Color>() => color_widget(default_info, ColorInput::default().allow_none(false)), Some(x) if x == TypeId::of::<Table<Color>>() => color_widget(default_info, ColorInput::default().allow_none(true)),
Some(x) if x == TypeId::of::<Option<Color>>() => color_widget(default_info, ColorInput::default().allow_none(true)),
Some(x) if x == TypeId::of::<GradientStops>() => color_widget(default_info, ColorInput::default().allow_none(false)), Some(x) if x == TypeId::of::<GradientStops>() => color_widget(default_info, ColorInput::default().allow_none(false)),
Some(x) if x == TypeId::of::<Font>() => font_widget(default_info), Some(x) if x == TypeId::of::<Font>() => font_widget(default_info),
Some(x) if x == TypeId::of::<Curve>() => curve_widget(default_info), Some(x) if x == TypeId::of::<Curve>() => curve_widget(default_info),
@@ -908,28 +908,25 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
// Add the color input // Add the color input
match &**tagged_value { match &**tagged_value {
TaggedValue::Color(color) => widgets.push( TaggedValue::Color(color_table) => widgets.push(
color_button color_button
.value(FillChoice::Solid(*color)) .value(match color_table.iter().next() {
.on_update(update_value(|x: &ColorInput| TaggedValue::Color(x.value.as_solid().unwrap_or_default()), node_id, index)) Some(color) => FillChoice::Solid(*color.element),
.on_commit(commit_value)
.widget_holder(),
),
TaggedValue::OptionalColor(color) => widgets.push(
color_button
.value(match color {
Some(color) => FillChoice::Solid(*color),
None => FillChoice::None, None => FillChoice::None,
}) })
.on_update(update_value(|x: &ColorInput| TaggedValue::OptionalColor(x.value.as_solid()), node_id, index)) .on_update(update_value(
|input: &ColorInput| TaggedValue::Color(input.value.as_solid().iter().map(|&color| TableRow::new_from_element(color)).collect()),
node_id,
index,
))
.on_commit(commit_value) .on_commit(commit_value)
.widget_holder(), .widget_holder(),
), ),
TaggedValue::GradientStops(x) => widgets.push( TaggedValue::GradientStops(gradient_stops) => widgets.push(
color_button color_button
.value(FillChoice::Gradient(x.clone())) .value(FillChoice::Gradient(gradient_stops.clone()))
.on_update(update_value( .on_update(update_value(
|x: &ColorInput| TaggedValue::GradientStops(x.value.as_gradient().cloned().unwrap_or_default()), |input: &ColorInput| TaggedValue::GradientStops(input.value.as_gradient().cloned().unwrap_or_default()),
node_id, node_id,
index, index,
)) ))
@@ -1559,7 +1556,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
} }
}; };
let (fill, backup_color, backup_gradient) = if let (Some(TaggedValue::Fill(fill)), &Some(&TaggedValue::OptionalColor(backup_color)), Some(TaggedValue::Gradient(backup_gradient))) = ( let (fill, backup_color, backup_gradient) = if let (Some(TaggedValue::Fill(fill)), Some(TaggedValue::Color(backup_color)), Some(TaggedValue::Gradient(backup_gradient))) = (
&document_node.inputs[FillInput::<Color>::INDEX].as_value(), &document_node.inputs[FillInput::<Color>::INDEX].as_value(),
&document_node.inputs[BackupColorInput::INDEX].as_value(), &document_node.inputs[BackupColorInput::INDEX].as_value(),
&document_node.inputs[BackupGradientInput::INDEX].as_value(), &document_node.inputs[BackupGradientInput::INDEX].as_value(),
@@ -1569,7 +1566,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
return vec![LayoutGroup::Row { widgets: widgets_first_row }]; return vec![LayoutGroup::Row { widgets: widgets_first_row }];
}; };
let fill2 = fill.clone(); let fill2 = fill.clone();
let backup_color_fill: Fill = backup_color.into(); let backup_color_fill: Fill = backup_color.clone().into();
let backup_gradient_fill: Fill = backup_gradient.clone().into(); let backup_gradient_fill: Fill = backup_gradient.clone().into();
widgets_first_row.push(Separator::new(SeparatorType::Unrelated).widget_holder()); widgets_first_row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
@@ -1582,13 +1579,13 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
Fill::None => NodeGraphMessage::SetInputValue { Fill::None => NodeGraphMessage::SetInputValue {
node_id, node_id,
input_index: BackupColorInput::INDEX, input_index: BackupColorInput::INDEX,
value: TaggedValue::OptionalColor(None), value: TaggedValue::Color(Table::new()),
} }
.into(), .into(),
Fill::Solid(color) => NodeGraphMessage::SetInputValue { Fill::Solid(color) => NodeGraphMessage::SetInputValue {
node_id, node_id,
input_index: BackupColorInput::INDEX, input_index: BackupColorInput::INDEX,
value: TaggedValue::OptionalColor(Some(*color)), value: TaggedValue::Color(Table::new_from_element(*color)),
} }
.into(), .into(),
Fill::Gradient(gradient) => NodeGraphMessage::SetInputValue { Fill::Gradient(gradient) => NodeGraphMessage::SetInputValue {
@@ -1750,7 +1747,7 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
let miter_limit_disabled = join_value != &StrokeJoin::Miter; let miter_limit_disabled = join_value != &StrokeJoin::Miter;
let color = color_widget( let color = color_widget(
ParameterWidgetsInfo::new(node_id, ColorInput::<Option<Color>>::INDEX, true, context), ParameterWidgetsInfo::new(node_id, ColorInput::INDEX, true, context),
crate::messages::layout::utility_types::widgets::button_widgets::ColorInput::default(), crate::messages::layout::utility_types::widgets::button_widgets::ColorInput::default(),
); );
let weight = number_widget(ParameterWidgetsInfo::new(node_id, WeightInput::INDEX, true, context), NumberInput::default().unit(" px").min(0.)); let weight = number_widget(ParameterWidgetsInfo::new(node_id, WeightInput::INDEX, true, context), NumberInput::default().unit(" px").min(0.));
@@ -31,7 +31,7 @@ impl FrontendGraphDataType {
TaggedValue::Graphic(_) => Self::Graphic, TaggedValue::Graphic(_) => Self::Graphic,
TaggedValue::Raster(_) => Self::Raster, TaggedValue::Raster(_) => Self::Raster,
TaggedValue::Vector(_) => Self::Vector, TaggedValue::Vector(_) => Self::Vector,
TaggedValue::ColorTable(_) | TaggedValue::Color(_) | TaggedValue::OptionalColor(_) => Self::Color, TaggedValue::Color(_) => Self::Color,
_ => Self::General, _ => Self::General,
} }
} }
@@ -179,8 +179,8 @@ pub struct FrontendClickTargets {
pub node_click_targets: Vec<String>, pub node_click_targets: Vec<String>,
#[serde(rename = "layerClickTargets")] #[serde(rename = "layerClickTargets")]
pub layer_click_targets: Vec<String>, pub layer_click_targets: Vec<String>,
#[serde(rename = "portClickTargets")] #[serde(rename = "connectorClickTargets")]
pub port_click_targets: Vec<String>, pub connector_click_targets: Vec<String>,
#[serde(rename = "iconClickTargets")] #[serde(rename = "iconClickTargets")]
pub icon_click_targets: Vec<String>, pub icon_click_targets: Vec<String>,
#[serde(rename = "allNodesBoundingBox")] #[serde(rename = "allNodesBoundingBox")]
@@ -1287,7 +1287,7 @@ impl NodeNetworkInterface {
let artboard = self.document_node(&artboard_node_identifier.to_node(), &[]); let artboard = self.document_node(&artboard_node_identifier.to_node(), &[]);
let clip_input = artboard.unwrap().inputs.get(5).unwrap(); let clip_input = artboard.unwrap().inputs.get(5).unwrap();
if let NodeInput::Value { tagged_value, .. } = clip_input { if let NodeInput::Value { tagged_value, .. } = clip_input {
if tagged_value.to_primitive_string() == "true" { if tagged_value.clone().into_inner() == TaggedValue::Bool(true) {
return Some(Quad::clip( return Some(Quad::clip(
self.document_metadata.bounding_box_document(layer).unwrap_or_default(), self.document_metadata.bounding_box_document(layer).unwrap_or_default(),
self.document_metadata.bounding_box_document(artboard_node_identifier).unwrap_or_default(), self.document_metadata.bounding_box_document(artboard_node_identifier).unwrap_or_default(),
@@ -3048,7 +3048,7 @@ impl NodeNetworkInterface {
pub fn collect_frontend_click_targets(&mut self, network_path: &[NodeId]) -> FrontendClickTargets { pub fn collect_frontend_click_targets(&mut self, network_path: &[NodeId]) -> FrontendClickTargets {
let mut all_node_click_targets = Vec::new(); let mut all_node_click_targets = Vec::new();
let mut port_click_targets = Vec::new(); let mut connector_click_targets = Vec::new();
let mut icon_click_targets = Vec::new(); let mut icon_click_targets = Vec::new();
let Some(network_metadata) = self.network_metadata(network_path) else { let Some(network_metadata) = self.network_metadata(network_path) else {
log::error!("Could not get nested network_metadata in collect_frontend_click_targets"); log::error!("Could not get nested network_metadata in collect_frontend_click_targets");
@@ -3066,7 +3066,7 @@ impl NodeNetworkInterface {
if let ClickTargetType::Subpath(subpath) = port.target_type() { if let ClickTargetType::Subpath(subpath) = port.target_type() {
let mut port_path = String::new(); let mut port_path = String::new();
let _ = subpath.subpath_to_svg(&mut port_path, DAffine2::IDENTITY); let _ = subpath.subpath_to_svg(&mut port_path, DAffine2::IDENTITY);
port_click_targets.push(port_path); connector_click_targets.push(port_path);
} }
} }
if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_metadata { if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_metadata {
@@ -3145,7 +3145,7 @@ impl NodeNetworkInterface {
FrontendClickTargets { FrontendClickTargets {
node_click_targets, node_click_targets,
layer_click_targets, layer_click_targets,
port_click_targets, connector_click_targets,
icon_click_targets, icon_click_targets,
all_nodes_bounding_box, all_nodes_bounding_box,
import_exports_bounding_box, import_exports_bounding_box,
@@ -300,10 +300,6 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
"graphene_core::raster::BlendNode", "graphene_core::raster::BlendNode",
], ],
}, },
NodeReplacement {
node: graphene_std::raster_nodes::blending_nodes::blend_color_pair::IDENTIFIER,
aliases: &["graphene_raster_nodes::adjustments::BlendColorPairNode", "graphene_core::raster::BlendColorPairNode"],
},
NodeReplacement { NodeReplacement {
node: graphene_std::raster_nodes::blending_nodes::color_overlay::IDENTIFIER, node: graphene_std::raster_nodes::blending_nodes::color_overlay::IDENTIFIER,
aliases: &[ aliases: &[
@@ -637,9 +633,9 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}; };
let vector = Vector::from_subpath(Subpath::from_anchors_linear(points.to_vec(), false)); let vector = Vector::from_subpath(Subpath::from_anchors_linear(points.to_vec(), false));
// Retrieve the output connectors linked to the "Spline" node's output port // Retrieve the output connectors linked to the "Spline" node's output connector
let Some(spline_outputs) = document.network_interface.outward_wires(network_path)?.get(&OutputConnector::node(*node_id, 0)).cloned() else { let Some(spline_outputs) = document.network_interface.outward_wires(network_path)?.get(&OutputConnector::node(*node_id, 0)).cloned() else {
log::error!("Vec of InputConnector Spline node is connected to its output port 0."); log::error!("Vec of InputConnector Spline node is connected to its output connector 0.");
return None; return None;
}; };
@@ -684,7 +680,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
// Reposition the new "Path" node with an offset relative to the original "Spline" node's position // Reposition the new "Path" node with an offset relative to the original "Spline" node's position
document.network_interface.shift_node(&new_path_id, node_position + IVec2::new(-7, 0), network_path); document.network_interface.shift_node(&new_path_id, node_position + IVec2::new(-7, 0), network_path);
// Redirect each output connection from the old node to the new "Spline" node's output port // Redirect each output connection from the old node to the new "Spline" node's output connector
for input_connector in spline_outputs { for input_connector in spline_outputs {
document.network_interface.set_input(&input_connector, NodeInput::node(new_spline_id, 0), network_path); document.network_interface.set_input(&input_connector, NodeInput::node(new_spline_id, 0), network_path);
} }
@@ -15,7 +15,7 @@ use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::table::Table; use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::misc::ManipulatorPointId; use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::style::Gradient; use graphene_std::vector::style::{Fill, Gradient};
use graphene_std::vector::{PointId, SegmentId, VectorModificationType}; use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
use std::collections::VecDeque; use std::collections::VecDeque;
@@ -273,7 +273,7 @@ pub fn get_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkI
let fill_index = 1; let fill_index = 1;
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?;
let TaggedValue::Fill(graphene_std::vector::style::Fill::Gradient(gradient)) = inputs.get(fill_index)?.as_value()? else { let TaggedValue::Fill(Fill::Gradient(gradient)) = inputs.get(fill_index)?.as_value()? else {
return None; return None;
}; };
Some(gradient.clone()) Some(gradient.clone())
@@ -284,7 +284,7 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetwor
let fill_index = 1; let fill_index = 1;
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?;
let TaggedValue::Fill(graphene_std::vector::style::Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else { let TaggedValue::Fill(Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
return None; return None;
}; };
Some(color.to_linear_srgb()) Some(color.to_linear_srgb())
-2
View File
@@ -387,8 +387,6 @@ impl NodeGraphExecutor {
TaggedValue::F64(render_object) => Self::debug_render(render_object, footprint, responses), TaggedValue::F64(render_object) => Self::debug_render(render_object, footprint, responses),
TaggedValue::DVec2(render_object) => Self::debug_render(render_object, footprint, responses), TaggedValue::DVec2(render_object) => Self::debug_render(render_object, footprint, responses),
TaggedValue::String(render_object) => Self::debug_render(render_object, footprint, responses), TaggedValue::String(render_object) => Self::debug_render(render_object, footprint, responses),
TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, footprint, responses),
TaggedValue::Palette(render_object) => Self::debug_render(render_object, footprint, responses),
_ => return Err(format!("Invalid node graph output type: {node_graph_output:#?}")), _ => return Err(format!("Invalid node graph output type: {node_graph_output:#?}")),
}; };
+3 -3
View File
@@ -117,10 +117,10 @@
--color-data-number-dim: #886b60; --color-data-number-dim: #886b60;
--color-data-artboard: #fbf9eb; --color-data-artboard: #fbf9eb;
--color-data-artboard-dim: #b9b9a9; --color-data-artboard-dim: #b9b9a9;
--color-data-graphic: #66b195; --color-data-graphic: #68c587;
--color-data-graphic-dim: #3d725e; --color-data-graphic-dim: #37754c;
--color-data-raster: #e4bb72; --color-data-raster: #e4bb72;
--color-data-raster-dim: #8b7752; --color-data-raster-dim: #9a7b43;
--color-data-vector: #65bbe5; --color-data-vector: #65bbe5;
--color-data-vector-dim: #417892; --color-data-vector-dim: #417892;
--color-data-color: #af81eb; --color-data-color: #af81eb;
+3 -3
View File
@@ -730,6 +730,7 @@
width: 36px; width: 36px;
height: 24px; height: 24px;
border-radius: 2px; border-radius: 2px;
overflow: hidden;
flex: 0 0 auto; flex: 0 0 auto;
background-image: var(--color-transparent-checkered-background); background-image: var(--color-transparent-checkered-background);
background-size: var(--color-transparent-checkered-background-size-mini); background-size: var(--color-transparent-checkered-background-size-mini);
@@ -737,9 +738,8 @@
background-repeat: var(--color-transparent-checkered-background-repeat); background-repeat: var(--color-transparent-checkered-background-repeat);
svg { svg {
width: calc(100% - 4px); width: 100%;
height: calc(100% - 4px); height: 100%;
margin: 2px;
} }
} }
+54 -48
View File
@@ -32,7 +32,7 @@
// Imports/Export are stored at a key value of 0 // Imports/Export are stored at a key value of 0
$: gridSpacing = calculateGridSpacing($nodeGraph.transform.scale); $: gridSpacing = calculateGridSpacing($nodeGraph.transform.scale);
$: dotRadius = 1 + Math.floor($nodeGraph.transform.scale - 0.5 + 0.001) / 2; $: gridDotRadius = 1 + Math.floor($nodeGraph.transform.scale - 0.5 + 0.001) / 2;
let inputElement: HTMLInputElement; let inputElement: HTMLInputElement;
let hoveringImportIndex: number | undefined = undefined; let hoveringImportIndex: number | undefined = undefined;
@@ -192,7 +192,7 @@
return output.connectedTo return output.connectedTo
.map((inputConnector) => { .map((inputConnector) => {
if ((inputConnector as Node).nodeId === undefined) return `Connected to export index ${inputConnector.index}`; if ((inputConnector as Node).nodeId === undefined) return `Connected to export index ${inputConnector.index}`;
return `Connected to ${(inputConnector as Node).nodeId}, port index ${inputConnector.index}`; return `Connected to ${(inputConnector as Node).nodeId}, connector index ${inputConnector.index}`;
}) })
.join("\n"); .join("\n");
} }
@@ -200,7 +200,7 @@
function inputConnectedToText(input: FrontendGraphInput): string { function inputConnectedToText(input: FrontendGraphInput): string {
if (input.connectedTo === undefined) return "Connected to nothing"; if (input.connectedTo === undefined) return "Connected to nothing";
if ((input.connectedTo as Node).nodeId === undefined) return `Connected to import index ${input.connectedTo.index}`; if ((input.connectedTo as Node).nodeId === undefined) return `Connected to import index ${input.connectedTo.index}`;
return `Connected to ${(input.connectedTo as Node).nodeId}, port index ${input.connectedTo.index}`; return `Connected to ${(input.connectedTo as Node).nodeId}, connector index ${input.connectedTo.index}`;
} }
function primaryOutputConnectedToLayer(node: FrontendNode): boolean { function primaryOutputConnectedToLayer(node: FrontendNode): boolean {
@@ -238,7 +238,7 @@
style:--grid-spacing={`${gridSpacing}px`} style:--grid-spacing={`${gridSpacing}px`}
style:--grid-offset-x={`${$nodeGraph.transform.x}px`} style:--grid-offset-x={`${$nodeGraph.transform.x}px`}
style:--grid-offset-y={`${$nodeGraph.transform.y}px`} style:--grid-offset-y={`${$nodeGraph.transform.y}px`}
style:--dot-radius={`${dotRadius}px`} style:--grid-dot-radius={`${gridDotRadius}px`}
data-node-graph data-node-graph
> >
<!-- Right click menu for adding nodes --> <!-- Right click menu for adding nodes -->
@@ -298,8 +298,8 @@
{#each $nodeGraph.clickTargets.layerClickTargets as pathString} {#each $nodeGraph.clickTargets.layerClickTargets as pathString}
<path class="layer" d={pathString} /> <path class="layer" d={pathString} />
{/each} {/each}
{#each $nodeGraph.clickTargets.portClickTargets as pathString} {#each $nodeGraph.clickTargets.connectorClickTargets as pathString}
<path class="port" d={pathString} /> <path class="connector" d={pathString} />
{/each} {/each}
{#each $nodeGraph.clickTargets.iconClickTargets as pathString} {#each $nodeGraph.clickTargets.iconClickTargets as pathString}
<path class="visibility" d={pathString} /> <path class="visibility" d={pathString} />
@@ -332,14 +332,14 @@
</svg> </svg>
</div> </div>
<!-- Import and Export ports --> <!-- Import and Export connectors -->
<div class="imports-and-exports" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}> <div class="imports-and-exports" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
{#each $nodeGraph.imports as { outputMetadata, position }, index} {#each $nodeGraph.imports as { outputMetadata, position }, index}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="connector"
data-port="output" data-connector="output"
data-datatype={outputMetadata.dataType} data-datatype={outputMetadata.dataType}
style:--data-color={`var(--color-data-${outputMetadata.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${outputMetadata.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${outputMetadata.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${outputMetadata.dataType.toLowerCase()}-dim)`}
@@ -411,8 +411,8 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="connector"
data-port="input" data-connector="input"
data-datatype={inputMetadata.dataType} data-datatype={inputMetadata.dataType}
style:--data-color={`var(--color-data-${inputMetadata.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${inputMetadata.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${inputMetadata.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${inputMetadata.dataType.toLowerCase()}-dim)`}
@@ -521,8 +521,8 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 12" viewBox="0 0 8 12"
class="port top" class="connector top"
data-port="output" data-connector="output"
data-datatype={node.primaryOutput.dataType} data-datatype={node.primaryOutput.dataType}
style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`}
@@ -542,8 +542,8 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 12" viewBox="0 0 8 12"
class="port bottom" class="connector bottom"
data-port="input" data-connector="input"
data-datatype={node.primaryInput?.dataType} data-datatype={node.primaryInput?.dataType}
style:--data-color={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()})`} style:--data-color={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()}-dim)`}
@@ -561,14 +561,14 @@
{/if} {/if}
</svg> </svg>
</div> </div>
<!-- Layer input port (from left) --> <!-- Layer input connector (from left) -->
{#if node.exposedInputs.length > 0} {#if node.exposedInputs.length > 0}
<div class="input ports"> <div class="input connectors">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="connector"
data-port="input" data-connector="input"
data-datatype={stackDataInput.dataType} data-datatype={stackDataInput.dataType}
style:--data-color={`var(--color-data-${stackDataInput.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${stackDataInput.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${stackDataInput.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${stackDataInput.dataType.toLowerCase()}-dim)`}
@@ -679,14 +679,14 @@
{/each} {/each}
</div> </div>
{/if} {/if}
<!-- Input ports --> <!-- Input connectors -->
<div class="input ports"> <div class="input connectors">
{#if node.primaryInput?.dataType} {#if node.primaryInput?.dataType}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port primary-port" class="connector primary-connector"
data-port="input" data-connector="input"
data-datatype={node.primaryInput?.dataType} data-datatype={node.primaryInput?.dataType}
style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`}
@@ -704,8 +704,8 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="connector"
data-port="input" data-connector="input"
data-datatype={secondary.dataType} data-datatype={secondary.dataType}
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
@@ -720,14 +720,14 @@
{/if} {/if}
{/each} {/each}
</div> </div>
<!-- Output ports --> <!-- Output connectors -->
<div class="output ports"> <div class="output connectors">
{#if node.primaryOutput} {#if node.primaryOutput}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port primary-port" class="connector primary-connector"
data-port="output" data-connector="output"
data-datatype={node.primaryOutput.dataType} data-datatype={node.primaryOutput.dataType}
style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`}
@@ -744,8 +744,8 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="connector"
data-port="output" data-connector="output"
data-datatype={secondary.dataType} data-datatype={secondary.dataType}
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`} style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`} style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
@@ -801,8 +801,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
background-size: var(--grid-spacing) var(--grid-spacing); background-size: var(--grid-spacing) var(--grid-spacing);
background-position: calc(var(--grid-offset-x) - var(--dot-radius)) calc(var(--grid-offset-y) - var(--dot-radius)); background-position: calc(var(--grid-offset-x) - var(--grid-dot-radius)) calc(var(--grid-offset-y) - var(--grid-dot-radius));
background-image: radial-gradient(circle at var(--dot-radius) var(--dot-radius), var(--color-3-darkgray) var(--dot-radius), transparent 0); background-image: radial-gradient(circle at var(--grid-dot-radius) var(--grid-dot-radius), var(--color-3-darkgray) var(--grid-dot-radius), transparent 0);
background-repeat: repeat; background-repeat: repeat;
image-rendering: pixelated; image-rendering: pixelated;
mix-blend-mode: screen; mix-blend-mode: screen;
@@ -859,7 +859,7 @@
stroke: blue; stroke: blue;
} }
.port { .connector {
stroke: green; stroke: green;
} }
@@ -898,11 +898,13 @@
} }
.imports-and-exports { .imports-and-exports {
position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute;
// Keeps the connectors above the wires
z-index: 1;
.port { .connector {
position: absolute; position: absolute;
width: 8px; width: 8px;
height: 8px; height: 8px;
@@ -1088,8 +1090,10 @@
border: 1px dashed var(--data-color); border: 1px dashed var(--data-color);
} }
.ports { .connectors {
position: absolute; position: absolute;
// Keeps the connectors above the wires
z-index: 1;
&.input { &.input {
left: -3px; left: -3px;
@@ -1100,7 +1104,7 @@
} }
} }
.port { .connector {
// Double the intended value because of margin collapsing, but for the first and last we divide it by two as intended // Double the intended value because of margin collapsing, but for the first and last we divide it by two as intended
margin: calc(24px - 8px) 0; margin: calc(24px - 8px) 0;
width: 8px; width: 8px;
@@ -1117,7 +1121,7 @@
border-radius: 8px; border-radius: 8px;
--extra-width-to-reach-grid-multiple: 8px; --extra-width-to-reach-grid-multiple: 8px;
--node-chain-area-left-extension: 0; --node-chain-area-left-extension: 0;
// Keep this equation in sync with the equivalent one in the Svelte template `<clipPath><path d="layerBorderMask(...)" /></clipPath>` above, as well as the `left` port offset CSS rule above in `.ports.input` above. // Keep this equation in sync with the equivalent one in the Svelte template `<clipPath><path d="layerBorderMask(...)" /></clipPath>` above, as well as the `left` connector offset CSS rule above in `.connectors.input` above.
width: calc((var(--layer-area-width) - 0.5) * 24px); width: calc((var(--layer-area-width) - 0.5) * 24px);
padding-left: calc(var(--node-chain-area-left-extension) * 24px); padding-left: calc(var(--node-chain-area-left-extension) * 24px);
margin-left: calc((0.5 - var(--node-chain-area-left-extension)) * 24px); margin-left: calc((0.5 - var(--node-chain-area-left-extension)) * 24px);
@@ -1141,8 +1145,10 @@
border-radius: 2px; border-radius: 2px;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
width: 72px;
height: 48px; height: 48px;
// We shorten the width by 1px on the left and right so the inner thumbnail graphic maintains a perfect 3:2 aspect ratio
width: calc(72px - 2px);
margin: 0 1px;
&::before { &::before {
content: ""; content: "";
@@ -1153,7 +1159,7 @@
} }
&::before, &::before,
svg:not(.port) { svg:not(.connector) {
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
margin: auto; margin: auto;
@@ -1163,7 +1169,7 @@
height: calc(100% - 2px); height: calc(100% - 2px);
} }
.port { .connector {
position: absolute; position: absolute;
margin: 0 auto; margin: 0 auto;
left: 0; left: 0;
@@ -1211,21 +1217,21 @@
right: -12px; right: -12px;
} }
.input.ports { .input.connectors {
left: calc(-3px + var(--node-chain-area-left-extension) * 24px - 36px); left: calc(-3px + var(--node-chain-area-left-extension) * 24px - 36px);
} }
.solo-drag-grip, .solo-drag-grip,
.visibility, .visibility,
.input.ports, .input.connectors,
.input.ports .port { .input.connectors .connector {
position: absolute; position: absolute;
margin: auto 0; margin: auto 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
} }
.input.ports .port { .input.connectors .connector {
left: 24px; left: 24px;
} }
} }
@@ -1259,11 +1265,11 @@
} }
} }
.port { .connector {
&:first-of-type { &:first-of-type {
margin-top: calc((24px - 8px) / 2); margin-top: calc((24px - 8px) / 2);
&:not(.primary-port) { &:not(.primary-connector) {
margin-top: calc((24px - 8px) / 2 + 24px); margin-top: calc((24px - 8px) / 2 + 24px);
} }
} }
+1 -1
View File
@@ -180,7 +180,7 @@ export class Box {
export type FrontendClickTargets = { export type FrontendClickTargets = {
readonly nodeClickTargets: string[]; readonly nodeClickTargets: string[];
readonly layerClickTargets: string[]; readonly layerClickTargets: string[];
readonly portClickTargets: string[]; readonly connectorClickTargets: string[];
readonly iconClickTargets: string[]; readonly iconClickTargets: string[];
readonly allNodesBoundingBox: string; readonly allNodesBoundingBox: string;
readonly importExportsBoundingBox: string; readonly importExportsBoundingBox: string;
+2 -3
View File
@@ -130,7 +130,7 @@ where
pub async fn create_brush_texture(brush_style: &BrushStyle) -> Raster<CPU> { pub async fn create_brush_texture(brush_style: &BrushStyle) -> Raster<CPU> {
let stamp = brush_stamp_generator(brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow); let stamp = brush_stamp_generator(brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow);
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(brush_style.diameter), 0., -DVec2::splat(brush_style.diameter / 2.)); let transform = DAffine2::from_scale_angle_translation(DVec2::splat(brush_style.diameter), 0., -DVec2::splat(brush_style.diameter / 2.));
let blank_texture = empty_image((), transform, Color::TRANSPARENT).into_iter().next().unwrap_or_default(); let blank_texture = empty_image((), transform, Table::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap_or_default();
let image = blend_stamp_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 1.)); let image = blend_stamp_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 1.));
image.element image.element
@@ -230,7 +230,6 @@ async fn brush(_: impl Ctx, mut image_frame_table: Table<Raster<CPU>>, strokes:
let stroke_origin_in_layer = bbox.start - snap_offset - DVec2::splat(stroke.style.diameter / 2.); let stroke_origin_in_layer = bbox.start - snap_offset - DVec2::splat(stroke.style.diameter / 2.);
let stroke_to_layer = DAffine2::from_translation(stroke_origin_in_layer) * DAffine2::from_scale(stroke_size); let stroke_to_layer = DAffine2::from_translation(stroke_origin_in_layer) * DAffine2::from_scale(stroke_size);
// let normal_blend = BlendColorPairNode::new(ValueNode::new(CopiedNode::new(BlendMode::Normal)), ValueNode::new(CopiedNode::new(100.)));
let normal_blend = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::Normal, 1.)); let normal_blend = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::Normal, 1.));
let blit_node = BlitNode::new( let blit_node = BlitNode::new(
FutureWrapperNode::new(ClonedNode::new(brush_texture)), FutureWrapperNode::new(ClonedNode::new(brush_texture)),
@@ -241,7 +240,7 @@ async fn brush(_: impl Ctx, mut image_frame_table: Table<Raster<CPU>>, strokes:
let target = core::mem::take(&mut brush_plan.first_stroke_texture); let target = core::mem::take(&mut brush_plan.first_stroke_texture);
extend_image_to_bounds((), Table::new_from_row(target), stroke_to_layer) extend_image_to_bounds((), Table::new_from_row(target), stroke_to_layer)
} else { } else {
empty_image((), stroke_to_layer, Color::TRANSPARENT) empty_image((), stroke_to_layer, Table::new_from_element(Color::TRANSPARENT))
// EmptyImageNode::new(CopiedNode::new(stroke_to_layer), CopiedNode::new(Color::TRANSPARENT)).eval(()) // EmptyImageNode::new(CopiedNode::new(stroke_to_layer), CopiedNode::new(Color::TRANSPARENT)).eval(())
}; };
+4 -1
View File
@@ -104,7 +104,7 @@ async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
label: String, label: String,
location: DVec2, location: DVec2,
dimensions: DVec2, dimensions: DVec2,
background: Color, background: Table<Color>,
clip: bool, clip: bool,
) -> Table<Artboard> { ) -> Table<Artboard> {
let location = location.as_ivec2(); let location = location.as_ivec2();
@@ -123,6 +123,9 @@ async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
let dimensions = dimensions.abs(); let dimensions = dimensions.abs();
let background: Option<Color> = background.into();
let background = background.unwrap_or(Color::WHITE);
Table::new_from_element(Artboard { Table::new_from_element(Artboard {
content, content,
label, label,
+1 -4
View File
@@ -22,14 +22,11 @@ macro_rules! none_impl {
} }
}; };
} }
none_impl!(String);
none_impl!(bool); none_impl!(bool);
none_impl!(f32); none_impl!(f32);
none_impl!(f64); none_impl!(f64);
none_impl!(DVec2); none_impl!(DVec2);
none_impl!(Option<Color>); // TODO: Remove this? none_impl!(String);
none_impl!(Vec<Color>); // TODO: Remove this?
impl BoundingBox for Color { impl BoundingBox for Color {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> RenderBoundingBox { fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> RenderBoundingBox {
+4 -5
View File
@@ -1,11 +1,10 @@
use crate::Ctx;
use crate::raster_types::{CPU, Raster}; use crate::raster_types::{CPU, Raster};
use crate::table::Table; use crate::table::Table;
use crate::vector::Vector;
use crate::{Color, Ctx};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
#[node_macro::node(category("Debug"), name("Log to Console"))] #[node_macro::node(category("Debug"), name("Log to Console"))]
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, Table<Vector>, DAffine2, Color, Option<Color>)] value: T) -> T { fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(bool, f64, u32, u64, DVec2, DAffine2, String)] value: T) -> T {
// KEEP THIS `debug!()` - It acts as the output for the debug node itself // KEEP THIS `debug!()` - It acts as the output for the debug node itself
log::debug!("{value:#?}"); log::debug!("{value:#?}");
value value
@@ -19,13 +18,13 @@ fn size_of(_: impl Ctx, ty: crate::Type) -> Option<usize> {
/// Meant for debugging purposes, not general use. Wraps the input value in the Some variant of an Option. /// Meant for debugging purposes, not general use. Wraps the input value in the Some variant of an Option.
#[node_macro::node(category("Debug"))] #[node_macro::node(category("Debug"))]
fn some<T>(_: impl Ctx, #[implementations(f64, f32, u32, u64, String, Color)] input: T) -> Option<T> { fn some<T>(_: impl Ctx, #[implementations(f64, f32, u32, u64, String)] input: T) -> Option<T> {
Some(input) Some(input)
} }
/// Meant for debugging purposes, not general use. Unwraps the input value from an Option, returning the default value if the input is None. /// Meant for debugging purposes, not general use. Unwraps the input value from an Option, returning the default value if the input is None.
#[node_macro::node(category("Debug"))] #[node_macro::node(category("Debug"))]
fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<u32>, Option<u64>, Option<String>, Option<Color>)] input: Option<T>) -> T { fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<u32>, Option<u64>, Option<String>)] input: Option<T>) -> T {
input.unwrap_or_default() input.unwrap_or_default()
} }
+12 -8
View File
@@ -139,6 +139,11 @@ impl From<Option<Color>> for Table<Graphic> {
} }
} }
} }
impl From<Table<Color>> for Option<Color> {
fn from(color: Table<Color>) -> Self {
color.into_iter().next().map(|row| row.element)
}
}
// DAffine2 // DAffine2
impl From<DAffine2> for Graphic { impl From<DAffine2> for Graphic {
@@ -297,8 +302,6 @@ async fn wrap_graphic<T: Into<Graphic> + 'n>(
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Raster<GPU>>, Table<Raster<GPU>>,
Table<Color>, Table<Color>,
Color,
Option<Color>,
DAffine2, DAffine2,
)] )]
content: T, content: T,
@@ -317,8 +320,6 @@ async fn to_graphic<T: Into<Table<Graphic>> + 'n>(
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Raster<GPU>>, Table<Raster<GPU>>,
Table<Color>, Table<Color>,
Color,
Option<Color>,
)] )]
content: T, content: T,
) -> Table<Graphic> { ) -> Table<Graphic> {
@@ -416,13 +417,16 @@ fn index<T: AtIndex + Clone + Default>(
_: impl Ctx, _: impl Ctx,
/// The collection of data, such as a list or table. /// The collection of data, such as a list or table.
#[implementations( #[implementations(
Vec<Color>, Vec<f64>,
Vec<Option<Color>>, Vec<u32>,
Vec<f64>, Vec<u64>, Vec<u64>,
Vec<DVec2>, Vec<DVec2>,
Table<Artboard>,
Table<Graphic>,
Table<Vector>, Table<Vector>,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Graphic>, Table<Raster<GPU>>,
Table<Color>,
)] )]
collection: T, collection: T,
/// The index of the item to retrieve, starting from 0 for the first item. /// The index of the item to retrieve, starting from 0 for the first item.
+5 -7
View File
@@ -10,14 +10,14 @@ use crate::{Context, Ctx};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
#[node_macro::node(category("Type Conversion"))] #[node_macro::node(category("Type Conversion"))]
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<Vector>)] value: T) -> String { fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(bool, f64, u32, u64, DVec2, DAffine2, String)] value: T) -> String {
format!("{:?}", value) format!("{value:?}")
} }
#[node_macro::node(category("Text"))] #[node_macro::node(category("Text"))]
fn serialize<T: serde::Serialize>( fn serialize<T: serde::Serialize>(
_: impl Ctx, _: impl Ctx,
#[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Color, Option<Color>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)] value: T, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)] value: T,
) -> String { ) -> String {
serde_json::to_string(&value).unwrap_or_else(|_| "Serialization Error".to_string()) serde_json::to_string(&value).unwrap_or_else(|_| "Serialization Error".to_string())
} }
@@ -64,8 +64,7 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> Table<Vector>, Context -> Table<Vector>,
Context -> Table<Raster<CPU>>, Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>, Context -> Table<Raster<GPU>>,
Context -> Color, Context -> Table<Color>,
Context -> Option<Color>,
Context -> GradientStops, Context -> GradientStops,
)] )]
if_true: impl Node<C, Output = T>, if_true: impl Node<C, Output = T>,
@@ -84,8 +83,7 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> Table<Vector>, Context -> Table<Vector>,
Context -> Table<Raster<CPU>>, Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>, Context -> Table<Raster<GPU>>,
Context -> Color, Context -> Table<Color>,
Context -> Option<Color>,
Context -> GradientStops, Context -> GradientStops,
)] )]
if_false: impl Node<C, Output = T>, if_false: impl Node<C, Output = T>,
+27
View File
@@ -60,3 +60,30 @@ impl Clampable for DVec2 {
self.min(DVec2::splat(max)) self.min(DVec2::splat(max))
} }
} }
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_color<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<crate::table::Table<graphene_core_shaders::color::Color>, D::Error> {
use crate::table::Table;
use graphene_core_shaders::color::Color;
use serde::Deserialize;
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum EitherFormat {
Color(Color),
OptionalColor(Option<Color>),
ColorTable(Table<Color>),
}
Ok(match EitherFormat::deserialize(deserializer)? {
EitherFormat::Color(color) => Table::new_from_element(color),
EitherFormat::OptionalColor(color) => {
if let Some(color) = color {
Table::new_from_element(color)
} else {
Table::new()
}
}
EitherFormat::ColorTable(color_table) => color_table,
})
}
@@ -64,5 +64,3 @@ impl RenderComplexity for bool {}
impl RenderComplexity for f32 {} impl RenderComplexity for f32 {}
impl RenderComplexity for f64 {} impl RenderComplexity for f64 {}
impl RenderComplexity for DVec2 {} impl RenderComplexity for DVec2 {}
impl RenderComplexity for Option<Color> {}
impl RenderComplexity for Vec<Color> {}
+9
View File
@@ -252,6 +252,15 @@ pub struct TableRow<T> {
} }
impl<T> TableRow<T> { impl<T> TableRow<T> {
pub fn new_from_element(element: T) -> Self {
Self {
element,
transform: DAffine2::IDENTITY,
alpha_blending: AlphaBlending::default(),
source_node_id: None,
}
}
pub fn as_ref(&self) -> TableRowRef<'_, T> { pub fn as_ref(&self) -> TableRowRef<'_, T> {
TableRowRef { TableRowRef {
element: &self.element, element: &self.element,
+7 -11
View File
@@ -2,6 +2,7 @@
use crate::Color; use crate::Color;
pub use crate::gradient::*; pub use crate::gradient::*;
use crate::table::Table;
use dyn_any::DynAny; use dyn_any::DynAny;
use glam::DAffine2; use glam::DAffine2;
@@ -120,6 +121,12 @@ impl From<Option<Color>> for Fill {
} }
} }
impl From<Table<Color>> for Fill {
fn from(color: Table<Color>) -> Fill {
Fill::solid_or_none(color.into())
}
}
impl From<Gradient> for Fill { impl From<Gradient> for Fill {
fn from(gradient: Gradient) -> Fill { fn from(gradient: Gradient) -> Fill {
Fill::Gradient(gradient) Fill::Gradient(gradient)
@@ -309,17 +316,6 @@ impl std::hash::Hash for Stroke {
} }
} }
impl From<Color> for Stroke {
fn from(color: Color) -> Self {
Self::new(Some(color), 1.)
}
}
impl From<Option<Color>> for Stroke {
fn from(color: Option<Color>) -> Self {
Self::new(color, 1.)
}
}
impl Stroke { impl Stroke {
pub const fn new(color: Option<Color>, weight: f64) -> Self { pub const fn new(color: Option<Color>, weight: f64) -> Self {
Self { Self {
+17 -27
View File
@@ -48,28 +48,28 @@ impl VectorTableIterMut for Table<Vector> {
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector))] #[node_macro::node(category("Vector: Style"), path(graphene_core::vector))]
async fn assign_colors<T>( async fn assign_colors<T>(
_: impl Ctx, _: impl Ctx,
/// The content with vector paths to apply the fill and/or stroke style to.
#[implementations(Table<Graphic>, Table<Vector>)] #[implementations(Table<Graphic>, Table<Vector>)]
#[widget(ParsedWidgetOverride::Hidden)] #[widget(ParsedWidgetOverride::Hidden)]
/// The content with vector paths to apply the fill and/or stroke style to.
mut content: T, mut content: T,
#[default(true)]
/// Whether to style the fill. /// Whether to style the fill.
#[default(true)]
fill: bool, fill: bool,
/// Whether to style the stroke. /// Whether to style the stroke.
stroke: bool, stroke: bool,
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
/// The range of colors to select from. /// The range of colors to select from.
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
gradient: GradientStops, gradient: GradientStops,
/// Whether to reverse the gradient. /// Whether to reverse the gradient.
reverse: bool, reverse: bool,
/// Whether to randomize the color selection for each element from throughout the gradient. /// Whether to randomize the color selection for each element from throughout the gradient.
randomize: bool, randomize: bool,
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
/// The seed used for randomization. /// The seed used for randomization.
/// Seed to determine unique variations on the randomized color selection. /// Seed to determine unique variations on the randomized color selection.
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
seed: SeedValue, seed: SeedValue,
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
/// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once. /// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once.
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
repeat_every: u32, repeat_every: u32,
) -> T ) -> T
where where
@@ -108,32 +108,28 @@ where
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))] #[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>( async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>(
_: impl Ctx, _: impl Ctx,
/// The content with vector paths to apply the fill style to.
#[implementations( #[implementations(
Table<Vector>, Table<Vector>,
Table<Vector>, Table<Vector>,
Table<Vector>, Table<Vector>,
Table<Vector>,
Table<Graphic>,
Table<Graphic>, Table<Graphic>,
Table<Graphic>, Table<Graphic>,
Table<Graphic>, Table<Graphic>,
)] )]
/// The content with vector paths to apply the fill style to.
mut content: V, mut content: V,
/// The fill to paint the path with.
#[implementations( #[implementations(
Fill, Fill,
Option<Color>, Table<Color>,
Color,
Gradient, Gradient,
Fill, Fill,
Option<Color>, Table<Color>,
Color,
Gradient, Gradient,
)] )]
#[default(Color::BLACK)] #[default(Color::BLACK)]
/// The fill to paint the path with.
fill: F, fill: F,
_backup_color: Option<Color>, _backup_color: Table<Color>,
_backup_gradient: Gradient, _backup_gradient: Gradient,
) -> V { ) -> V {
let fill: Fill = fill.into(); let fill: Fill = fill.into();
@@ -150,23 +146,17 @@ async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>(
/// Applies a stroke style to the vector contained in the input. /// Applies a stroke style to the vector contained in the input.
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("stroke_properties"))] #[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("stroke_properties"))]
async fn stroke<C: Into<Option<Color>> + 'n + Send, V>( async fn stroke<V>(
_: impl Ctx, _: impl Ctx,
#[implementations(Table<Vector>, Table<Vector>, Table<Graphic>, Table<Graphic>)]
/// The content with vector paths to apply the stroke style to. /// The content with vector paths to apply the stroke style to.
#[implementations(Table<Vector>, Table<Graphic>)]
mut content: Table<V>, mut content: Table<V>,
#[implementations(
Option<Color>,
Color,
Option<Color>,
Color,
)]
#[default(Color::BLACK)]
/// The stroke color. /// The stroke color.
color: C, #[default(Color::BLACK)]
color: Table<Color>,
/// The stroke weight.
#[unit(" px")] #[unit(" px")]
#[default(2.)] #[default(2.)]
/// The stroke weight.
weight: f64, weight: f64,
/// The alignment of stroke to the path's centerline or (for closed shapes) the inside or outside of the shape. /// The alignment of stroke to the path's centerline or (for closed shapes) the inside or outside of the shape.
align: StrokeAlign, align: StrokeAlign,
@@ -174,8 +164,8 @@ async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
cap: StrokeCap, cap: StrokeCap,
/// The curvature of the bent stroke at sharp corners. /// The curvature of the bent stroke at sharp corners.
join: StrokeJoin, join: StrokeJoin,
#[default(4.)]
/// The threshold for when a miter-joined stroke is converted to a bevel-joined stroke when a sharp angle becomes pointier than this ratio. /// The threshold for when a miter-joined stroke is converted to a bevel-joined stroke when a sharp angle becomes pointier than this ratio.
#[default(4.)]
miter_limit: f64, miter_limit: f64,
/// The order to paint the stroke on top of the fill, or the fill on top of the stroke. /// The order to paint the stroke on top of the fill, or the fill on top of the stroke.
/// <https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty> /// <https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty>
@@ -286,8 +276,8 @@ async fn circular_repeat<I: 'n + Send + Clone>(
async fn copy_to_points<I: 'n + Send + Clone>( async fn copy_to_points<I: 'n + Send + Clone>(
_: impl Ctx, _: impl Ctx,
points: Table<Vector>, points: Table<Vector>,
#[expose]
/// Artwork to be copied and placed at each point. /// Artwork to be copied and placed at each point.
#[expose]
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)] #[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Color>)]
instance: Table<I>, instance: Table<I>,
/// Minimum range of randomized sizes given to each instance. /// Minimum range of randomized sizes given to each instance.
+5 -3
View File
@@ -1,6 +1,7 @@
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graphene_core::gradient::GradientStops; use graphene_core::gradient::GradientStops;
use graphene_core::registry::types::{Fraction, Percentage, PixelSize, TextArea}; use graphene_core::registry::types::{Fraction, Percentage, PixelSize, TextArea};
use graphene_core::table::Table;
use graphene_core::transform::Footprint; use graphene_core::transform::Footprint;
use graphene_core::{Color, Ctx, num_traits}; use graphene_core::{Color, Ctx, num_traits};
use log::warn; use log::warn;
@@ -659,15 +660,16 @@ fn vec2_value(_: impl Ctx, _primary: (), x: f64, y: f64) -> DVec2 {
/// Constructs a color value which may be set to any color, or no color. /// Constructs a color value which may be set to any color, or no color.
#[node_macro::node(category("Value"))] #[node_macro::node(category("Value"))]
fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Option<Color>) -> Option<Color> { fn color_value(_: impl Ctx, _primary: (), #[default(Color::RED)] color: Table<Color>) -> Table<Color> {
color color
} }
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right). /// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
#[node_macro::node(category("Color"))] #[node_macro::node(category("Color"))]
fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Color { fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Table<Color> {
let position = position.clamp(0., 1.); let position = position.clamp(0., 1.);
gradient.evaluate(position) let color = gradient.evaluate(position);
Table::new_from_element(color)
} }
/// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors. /// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors.
+5 -8
View File
@@ -193,16 +193,15 @@ tagged_value! {
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard"))] // TODO: Eventually remove this migration document upgrade code #[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ArtboardGroup")] #[serde(alias = "ArtboardGroup")]
Artboard(Table<Artboard>), Artboard(Table<Artboard>),
ColorTable(Table<Color>), // TODO: Rename to Color #[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::misc::migrate_color"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ColorTable", alias = "OptionalColor")]
Color(Table<Color>),
// ============ // ============
// STRUCT TYPES // STRUCT TYPES
// ============ // ============
#[serde(alias = "IVec2", alias = "UVec2")] #[serde(alias = "IVec2", alias = "UVec2")]
DVec2(DVec2), DVec2(DVec2),
DAffine2(DAffine2), DAffine2(DAffine2),
Color(Color),
OptionalColor(Option<Color>),
Palette(Vec<Color>),
Stroke(graphene_core::vector::style::Stroke), Stroke(graphene_core::vector::style::Stroke),
Gradient(graphene_core::vector::style::Gradient), Gradient(graphene_core::vector::style::Gradient),
#[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code #[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code
@@ -259,7 +258,6 @@ impl TaggedValue {
TaggedValue::F64(x) => x.to_string() + "_f64", TaggedValue::F64(x) => x.to_string() + "_f64",
TaggedValue::Bool(x) => x.to_string(), TaggedValue::Bool(x) => x.to_string(),
TaggedValue::BlendMode(x) => "BlendMode::".to_string() + &x.to_string(), TaggedValue::BlendMode(x) => "BlendMode::".to_string() + &x.to_string(),
TaggedValue::Color(x) => format!("Color {x:?}"),
_ => panic!("Cannot convert to primitive string"), _ => panic!("Cannot convert to primitive string"),
} }
} }
@@ -280,7 +278,7 @@ impl TaggedValue {
6 => return Color::from_rgb_str(color), 6 => return Color::from_rgb_str(color),
8 => return Color::from_rgba_str(color), 8 => return Color::from_rgba_str(color),
_ => { _ => {
log::error!("Invalid default value color string: {}", input); log::error!("Invalid default value color string: {input}");
return None; return None;
} }
} }
@@ -352,8 +350,7 @@ impl TaggedValue {
x if x == TypeId::of::<u32>() => FromStr::from_str(string).map(TaggedValue::U32).ok()?, x if x == TypeId::of::<u32>() => FromStr::from_str(string).map(TaggedValue::U32).ok()?,
x if x == TypeId::of::<DVec2>() => to_dvec2(string).map(TaggedValue::DVec2)?, x if x == TypeId::of::<DVec2>() => to_dvec2(string).map(TaggedValue::DVec2)?,
x if x == TypeId::of::<bool>() => FromStr::from_str(string).map(TaggedValue::Bool).ok()?, x if x == TypeId::of::<bool>() => FromStr::from_str(string).map(TaggedValue::Bool).ok()?,
x if x == TypeId::of::<Color>() => to_color(string).map(TaggedValue::Color)?, x if x == TypeId::of::<Table<Color>>() => to_color(string).map(|color| TaggedValue::Color(Table::new_from_element(color)))?,
x if x == TypeId::of::<Option<Color>>() => to_color(string).map(|color| TaggedValue::OptionalColor(Some(color)))?,
x if x == TypeId::of::<Fill>() => to_color(string).map(|color| TaggedValue::Fill(Fill::solid(color)))?, x if x == TypeId::of::<Fill>() => to_color(string).map(|color| TaggedValue::Fill(Fill::solid(color)))?,
x if x == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?, x if x == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?,
_ => return None, _ => return None,
-1
View File
@@ -119,7 +119,6 @@ impl Hash for ConstructionArgs {
} }
impl ConstructionArgs { impl ConstructionArgs {
// TODO: what? Used in the gpu_compiler crate for something.
pub fn new_function_args(&self) -> Vec<String> { pub fn new_function_args(&self) -> Vec<String> {
match self { match self {
ConstructionArgs::Nodes(nodes) => nodes.iter().map(|(n, _)| format!("n{:0x}", n.0)).collect(), ConstructionArgs::Nodes(nodes) => nodes.iter().map(|(n, _)| format!("n{:0x}", n.0)).collect(),
+14 -14
View File
@@ -8,13 +8,6 @@ impl Adjust<Color> for Color {
*self = map_fn(self); *self = map_fn(self);
} }
} }
impl Adjust<Color> for Option<Color> {
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
if let Some(color) = self {
*color = map_fn(color)
}
}
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod adjust_std { mod adjust_std {
@@ -23,13 +16,6 @@ mod adjust_std {
use graphene_core::raster_types::{CPU, Raster}; use graphene_core::raster_types::{CPU, Raster};
use graphene_core::table::Table; use graphene_core::table::Table;
impl Adjust<Color> for GradientStops {
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
for (_, color) in self.iter_mut() {
*color = map_fn(color);
}
}
}
impl Adjust<Color> for Table<Raster<CPU>> { impl Adjust<Color> for Table<Raster<CPU>> {
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) { fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
for row in self.iter_mut() { for row in self.iter_mut() {
@@ -39,4 +25,18 @@ mod adjust_std {
} }
} }
} }
impl Adjust<Color> for Table<Color> {
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
for color in self.iter_mut() {
*color.element = map_fn(color.element);
}
}
}
impl Adjust<Color> for GradientStops {
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
for (_, color) in self.iter_mut() {
*color = map_fn(color);
}
}
}
} }
+19 -16
View File
@@ -44,8 +44,8 @@ pub enum LuminanceCalculation {
fn luminance<T: Adjust<Color>>( fn luminance<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -68,8 +68,8 @@ fn luminance<T: Adjust<Color>>(
fn gamma_correction<T: Adjust<Color>>( fn gamma_correction<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -88,8 +88,8 @@ fn gamma_correction<T: Adjust<Color>>(
fn extract_channel<T: Adjust<Color>>( fn extract_channel<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -111,8 +111,8 @@ fn extract_channel<T: Adjust<Color>>(
fn make_opaque<T: Adjust<Color>>( fn make_opaque<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -136,8 +136,8 @@ fn make_opaque<T: Adjust<Color>>(
fn brightness_contrast<T: Adjust<Color>>( fn brightness_contrast<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -225,8 +225,8 @@ fn brightness_contrast<T: Adjust<Color>>(
fn levels<T: Adjust<Color>>( fn levels<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -292,12 +292,12 @@ fn levels<T: Adjust<Color>>(
async fn black_and_white<T: Adjust<Color>>( async fn black_and_white<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
#[default(Color::BLACK)] tint: Color, #[default(Color::BLACK)] tint: Table<Color>,
#[default(40.)] #[default(40.)]
#[range((-200., 300.))] #[range((-200., 300.))]
reds: Percentage, reds: Percentage,
@@ -317,6 +317,9 @@ async fn black_and_white<T: Adjust<Color>>(
#[range((-200., 300.))] #[range((-200., 300.))]
magentas: Percentage, magentas: Percentage,
) -> T { ) -> T {
let tint: Option<Color> = tint.into();
let tint = tint.unwrap_or(Color::BLACK);
image.adjust(|color| { image.adjust(|color| {
let color = color.to_gamma_srgb(); let color = color.to_gamma_srgb();
@@ -364,8 +367,8 @@ async fn black_and_white<T: Adjust<Color>>(
async fn hue_saturation<T: Adjust<Color>>( async fn hue_saturation<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -398,8 +401,8 @@ async fn hue_saturation<T: Adjust<Color>>(
async fn invert<T: Adjust<Color>>( async fn invert<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -420,8 +423,8 @@ async fn invert<T: Adjust<Color>>(
async fn threshold<T: Adjust<Color>>( async fn threshold<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -465,8 +468,8 @@ async fn threshold<T: Adjust<Color>>(
async fn vibrance<T: Adjust<Color>>( async fn vibrance<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -630,8 +633,8 @@ pub enum DomainWarpType {
async fn channel_mixer<T: Adjust<Color>>( async fn channel_mixer<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -758,8 +761,8 @@ pub enum SelectiveColorChoice {
async fn selective_color<T: Adjust<Color>>( async fn selective_color<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -900,8 +903,8 @@ async fn selective_color<T: Adjust<Color>>(
async fn posterize<T: Adjust<Color>>( async fn posterize<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
@@ -933,8 +936,8 @@ async fn posterize<T: Adjust<Color>>(
async fn exposure<T: Adjust<Color>>( async fn exposure<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut input: T, mut input: T,
+23 -26
View File
@@ -17,15 +17,6 @@ impl Blend<Color> for Color {
blend_fn(*self, *under) blend_fn(*self, *under)
} }
} }
impl Blend<Color> for Option<Color> {
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
match (self, under) {
(Some(a), Some(b)) => Some(blend_fn(*a, *b)),
(a, None) => *a,
(None, b) => *b,
}
}
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod blend_std { mod blend_std {
@@ -51,6 +42,15 @@ mod blend_std {
result_table result_table
} }
} }
impl Blend<Color> for Table<Color> {
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
let mut result_table = self.clone();
for (over, under) in result_table.iter_mut().zip(under.iter()) {
*over.element = blend_fn(*over.element, *under.element);
}
result_table
}
}
impl Blend<Color> for GradientStops { impl Blend<Color> for GradientStops {
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self { fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
let mut combined_stops = self.iter().map(|(position, _)| position).chain(under.iter().map(|(position, _)| position)).collect::<Vec<_>>(); let mut combined_stops = self.iter().map(|(position, _)| position).chain(under.iter().map(|(position, _)| position)).collect::<Vec<_>>();
@@ -126,15 +126,15 @@ pub fn apply_blend_mode(foreground: Color, background: Color, blend_mode: BlendM
async fn blend<T: Blend<Color> + Send>( async fn blend<T: Blend<Color> + Send>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
over: T, over: T,
#[expose] #[expose]
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
under: T, under: T,
@@ -148,17 +148,20 @@ async fn blend<T: Blend<Color> + Send>(
fn color_overlay<T: Adjust<Color>>( fn color_overlay<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
#[default(Color::BLACK)] color: Color, #[default(Color::BLACK)] color: Table<Color>,
blend_mode: BlendMode, blend_mode: BlendMode,
#[default(100.)] opacity: Percentage, #[default(100.)] opacity: Percentage,
) -> T { ) -> T {
let opacity = (opacity as f32 / 100.).clamp(0., 1.); let opacity = (opacity as f32 / 100.).clamp(0., 1.);
let color: Option<Color> = color.into();
let color = color.unwrap_or(Color::BLACK);
image.adjust(|pixel| { image.adjust(|pixel| {
let image = pixel.map_rgb(|channel| channel * (1. - opacity)); let image = pixel.map_rgb(|channel| channel * (1. - opacity));
@@ -171,18 +174,6 @@ fn color_overlay<T: Adjust<Color>>(
image image
} }
#[cfg(feature = "std")]
#[node_macro::node(category(""), skip_impl)]
fn blend_color_pair<BlendModeNode, OpacityNode>(input: (Color, Color), blend_mode: &'n BlendModeNode, opacity: &'n OpacityNode) -> Color
where
BlendModeNode: graphene_core::Node<'n, (), Output = BlendMode> + 'n,
OpacityNode: graphene_core::Node<'n, (), Output = Percentage> + 'n,
{
let blend_mode = blend_mode.eval(());
let opacity = opacity.eval(());
blend_colors(input.0, input.1, blend_mode, opacity / 100.)
}
#[cfg(all(feature = "std", test))] #[cfg(all(feature = "std", test))]
mod test { mod test {
use graphene_core::blending::BlendMode; use graphene_core::blending::BlendMode;
@@ -202,7 +193,13 @@ mod test {
// 100% of the output should come from the multiplied value // 100% of the output should come from the multiplied value
let opacity = 100_f64; let opacity = 100_f64;
let result = super::color_overlay((), Table::new_from_element(Raster::new_cpu(image.clone())), overlay_color, BlendMode::Multiply, opacity); let result = super::color_overlay(
(),
Table::new_from_element(Raster::new_cpu(image.clone())),
Table::new_from_element(overlay_color),
BlendMode::Multiply,
opacity,
);
let result = result.iter().next().unwrap().element; let result = result.iter().next().unwrap().element;
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0) // The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
+1 -1
View File
@@ -13,8 +13,8 @@ use graphene_core::{Color, Ctx};
async fn gradient_map<T: Adjust<Color>>( async fn gradient_map<T: Adjust<Color>>(
_: impl Ctx, _: impl Ctx,
#[implementations( #[implementations(
Color,
Table<Raster<CPU>>, Table<Raster<CPU>>,
Table<Color>,
GradientStops, GradientStops,
)] )]
mut image: T, mut image: T,
@@ -1,7 +1,7 @@
use graphene_core::color::Color; use graphene_core::color::Color;
use graphene_core::context::Ctx; use graphene_core::context::Ctx;
use graphene_core::raster_types::{CPU, Raster}; use graphene_core::raster_types::{CPU, Raster};
use graphene_core::table::Table; use graphene_core::table::{Table, TableRow};
#[node_macro::node(category("Color"))] #[node_macro::node(category("Color"))]
async fn image_color_palette( async fn image_color_palette(
@@ -10,13 +10,13 @@ async fn image_color_palette(
#[hard_min(1.)] #[hard_min(1.)]
#[soft_max(28.)] #[soft_max(28.)]
max_size: u32, max_size: u32,
) -> Vec<Color> { ) -> Table<Color> {
const GRID: f32 = 3.; const GRID: f32 = 3.;
let bins = GRID * GRID * GRID; let bins = GRID * GRID * GRID;
let mut histogram: Vec<usize> = vec![0; (bins + 1.) as usize]; let mut histogram = vec![0; (bins + 1.) as usize];
let mut colors: Vec<Vec<Color>> = vec![vec![]; (bins + 1.) as usize]; let mut color_bins = vec![Vec::new(); (bins + 1.) as usize];
for row in image.iter() { for row in image.iter() {
for pixel in row.element.data.iter() { for pixel in row.element.data.iter() {
@@ -27,40 +27,38 @@ async fn image_color_palette(
let bin = (r * GRID + g * GRID + b * GRID) as usize; let bin = (r * GRID + g * GRID + b * GRID) as usize;
histogram[bin] += 1; histogram[bin] += 1;
colors[bin].push(pixel.to_gamma_srgb()); color_bins[bin].push(pixel.to_gamma_srgb());
} }
} }
let shorted = histogram.iter().enumerate().filter(|&(_, &count)| count > 0).map(|(i, _)| i).collect::<Vec<usize>>(); let shorted = histogram.iter().enumerate().filter(|&(_, &count)| count > 0).map(|(i, _)| i).collect::<Vec<usize>>();
let mut palette = vec![]; shorted
.iter()
.take(max_size as usize)
.flat_map(|&i| {
let list = &color_bins[i];
for i in shorted.iter().take(max_size as usize) { let mut r = 0.;
let list = colors[*i].clone(); let mut g = 0.;
let mut b = 0.;
let mut a = 0.;
let mut r = 0.; for color in list.iter() {
let mut g = 0.; r += color.r();
let mut b = 0.; g += color.g();
let mut a = 0.; b += color.b();
a += color.a();
}
for color in list.iter() { r /= list.len() as f32;
r += color.r(); g /= list.len() as f32;
g += color.g(); b /= list.len() as f32;
b += color.b(); a /= list.len() as f32;
a += color.a();
}
r /= list.len() as f32; Color::from_rgbaf32(r, g, b, a).map(TableRow::new_from_element).into_iter()
g /= list.len() as f32; })
b /= list.len() as f32; .collect()
a /= list.len() as f32;
let color = Color::from_rgbaf32(r, g, b, a).unwrap();
palette.push(color);
}
palette
} }
#[cfg(test)] #[cfg(test)]
@@ -81,6 +79,6 @@ mod test {
})), })),
1, 1,
); );
assert_eq!(futures::executor::block_on(result), [Color::from_rgbaf32(0., 0., 0., 1.).unwrap()]); assert_eq!(futures::executor::block_on(result), Table::new_from_element(Color::from_rgbaf32(0., 0., 0., 1.).unwrap()));
} }
} }
+4 -3
View File
@@ -246,7 +246,7 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
let image_data = &row.element.data; let image_data = &row.element.data;
let (image_width, image_height) = (row.element.width, row.element.height); let (image_width, image_height) = (row.element.width, row.element.height);
if image_width == 0 || image_height == 0 { if image_width == 0 || image_height == 0 {
return empty_image((), bounds, Color::TRANSPARENT).into_iter().next().unwrap(); return empty_image((), bounds, Table::new_from_element(Color::TRANSPARENT)).into_iter().next().unwrap();
} }
let orig_image_scale = DVec2::new(image_width as f64, image_height as f64); let orig_image_scale = DVec2::new(image_width as f64, image_height as f64);
@@ -280,11 +280,12 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
} }
#[node_macro::node(category("Debug: Raster"))] #[node_macro::node(category("Debug: Raster"))]
pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> Table<Raster<CPU>> { pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Table<Color>) -> Table<Raster<CPU>> {
let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32; let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32;
let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32; let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32;
let image = Image::new(width, height, color); let color: Option<Color> = color.into();
let image = Image::new(width, height, color.unwrap_or(Color::WHITE));
let mut result_table = Table::new_from_element(Raster::new_cpu(image)); let mut result_table = Table::new_from_element(Raster::new_cpu(image));
let row = result_table.get_mut(0).unwrap(); let row = result_table.get_mut(0).unwrap();
@@ -291,8 +291,6 @@ async fn render<'a: 'n, T: 'n + Render + WasmNotSend>(
Context -> Table<Vector>, Context -> Table<Vector>,
Context -> Table<Raster<CPU>>, Context -> Table<Raster<CPU>>,
Context -> Table<Color>, Context -> Table<Color>,
Context -> Option<Color>,
Context -> Vec<Color>,
Context -> bool, Context -> bool,
Context -> f32, Context -> f32,
Context -> f64, Context -> f64,
+1 -69
View File
@@ -1309,13 +1309,12 @@ impl Render for Table<Color> {
} }
} }
/// 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 {}
impl Primitive for String {}
impl Primitive for bool {} impl Primitive for bool {}
impl Primitive for f32 {} impl Primitive for f32 {}
impl Primitive for f64 {} impl Primitive for f64 {}
impl Primitive for DVec2 {} impl Primitive for DVec2 {}
impl Primitive for String {}
fn text_attributes(attributes: &mut SvgRenderAttrs) { fn text_attributes(attributes: &mut SvgRenderAttrs) {
attributes.push("fill", "white"); attributes.push("fill", "white");
@@ -1332,73 +1331,6 @@ impl<P: Primitive> Render for P {
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 Render for Option<Color> {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
if let Some(color) = self {
color.render_svg(render, render_params);
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
if let Some(color) = self {
color.render_to_vello(scene, parent_transform, _context, render_params);
}
}
}
impl Render for Color {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
render.leaf_tag("rect", |attributes| {
attributes.push("width", render_params.footprint.resolution.x.to_string());
attributes.push("height", render_params.footprint.resolution.y.to_string());
let matrix = format_transform_matrix(render_params.footprint.transform.inverse());
if !matrix.is_empty() {
attributes.push("transform", matrix);
}
attributes.push("fill", format!("#{}", self.to_rgb_hex_srgb_from_gamma()));
if self.a() < 1. {
attributes.push("fill-opacity", ((self.a() * 1000.).round() / 1000.).to_string());
}
});
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
let transform = parent_transform * render_params.footprint.transform.inverse();
let vello_color = peniko::Color::new([self.r(), self.g(), self.b(), self.a()]);
let rect = kurbo::Rect::from_origin_size(
kurbo::Point::ZERO,
kurbo::Size::new(render_params.footprint.resolution.x as f64, render_params.footprint.resolution.y as f64),
);
scene.fill(peniko::Fill::NonZero, kurbo::Affine::new(transform.to_cols_array()), vello_color, None, &rect);
}
}
impl Render for Vec<Color> {
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
for (index, &color) in self.iter().enumerate() {
render.leaf_tag("rect", |attributes| {
attributes.push("width", "100");
attributes.push("height", "100");
attributes.push("x", (index * 120).to_string());
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());
}
});
}
}
#[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),
@@ -63,8 +63,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Color>]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Color]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => IVec2]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => IVec2]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DVec2]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DVec2]),
@@ -95,7 +93,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => [f64; 4]]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => [f64; 4]]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<NodeId>]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<NodeId>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::text::Font]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::text::Font]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<BrushStroke>]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<BrushStroke>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BrushCache]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BrushCache]),
+9 -8
View File
@@ -1271,15 +1271,16 @@ mod tests {
let tuples = quote_spanned!(problem_span=> () ()); let tuples = quote_spanned!(problem_span=> () ());
let input = quote! { let input = quote! {
fn test_node( fn test_node(
#[implementations((), #tuples, Footprint)] footprint: F, #[implementations((), #tuples, Footprint)]
footprint: F,
#[implementations( #[implementations(
() -> Color, () -> Table<Raster<CPU>>,
() -> Table<Raster<CPU>>, () -> Table<Color>,
() -> GradientStops, () -> GradientStops,
Footprint -> Color, Footprint -> Table<Raster<CPU>>,
Footprint -> Table<Raster<CPU>>, Footprint -> Table<Color>,
Footprint -> GradientStops, Footprint -> GradientStops,
)] )]
image: impl Node<F, Output = T>, image: impl Node<F, Output = T>,
) -> T { ) -> T {
// Implementation details... // Implementation details...