mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Clean up node catalog by adding missing units, more tooltips; fix 'Line' node missing parameters (#2813)
* Fix unit usages * Add node and parameter doc comments * Fix the parameters panel for the 'Line' node when added from the graph * Clean up nodes * Fix tests * Update the demo artwork
This commit is contained in:
parent
0febfaf142
commit
8c5accc069
21 changed files with 366 additions and 89 deletions
2
demo-artwork/changing-seasons.graphite
generated
2
demo-artwork/changing-seasons.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/isometric-fountain.graphite
generated
2
demo-artwork/isometric-fountain.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/marbled-mandelbrot.graphite
generated
2
demo-artwork/marbled-mandelbrot.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/painted-dreams.graphite
generated
2
demo-artwork/painted-dreams.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/parametric-dunescape.graphite
generated
2
demo-artwork/parametric-dunescape.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/procedural-string-lights.graphite
generated
2
demo-artwork/procedural-string-lights.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/red-dress.graphite
generated
2
demo-artwork/red-dress.graphite
generated
File diff suppressed because one or more lines are too long
2
demo-artwork/valley-of-spires.graphite
generated
2
demo-artwork/valley-of-spires.graphite
generated
File diff suppressed because one or more lines are too long
|
@ -1016,7 +1016,9 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
description: Cow::Borrowed(
|
||||
"Decomposes the X and Y components of a 2D coordinate.\n\nThe inverse of this node is \"Coordinate Value\", which can have either or both its X and Y exposed as graph inputs.",
|
||||
),
|
||||
properties: None,
|
||||
},
|
||||
// TODO: Remove this and just use the proto node definition directly
|
||||
|
@ -1856,7 +1858,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
NodeInput::network(concrete!(graphene_std::vector::VectorDataTable), 0),
|
||||
NodeInput::network(concrete!(vector::misc::PointSpacingType), 1),
|
||||
NodeInput::network(concrete!(f64), 2),
|
||||
NodeInput::network(concrete!(f64), 3),
|
||||
NodeInput::network(concrete!(u32), 3),
|
||||
NodeInput::network(concrete!(f64), 4),
|
||||
NodeInput::network(concrete!(f64), 5),
|
||||
NodeInput::network(concrete!(bool), 6),
|
||||
|
@ -1895,7 +1897,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorDataTable::default()), true),
|
||||
NodeInput::value(TaggedValue::PointSpacingType(Default::default()), false),
|
||||
NodeInput::value(TaggedValue::F64(100.), false),
|
||||
NodeInput::value(TaggedValue::F64(100.), false),
|
||||
NodeInput::value(TaggedValue::U32(100), false),
|
||||
NodeInput::value(TaggedValue::F64(0.), false),
|
||||
NodeInput::value(TaggedValue::F64(0.), false),
|
||||
NodeInput::value(TaggedValue::Bool(false), false),
|
||||
|
|
|
@ -1427,6 +1427,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||
} else {
|
||||
NumberInput::default()
|
||||
.value(Some(uniform_val))
|
||||
.unit(" px")
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, CornerRadiusInput::<f64>::INDEX))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder()
|
||||
|
|
|
@ -744,17 +744,62 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
|||
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
|
||||
let new_spacing_value = NodeInput::value(TaggedValue::PointSpacingType(graphene_std::vector::misc::PointSpacingType::Separation), false);
|
||||
let new_quantity_value = NodeInput::value(TaggedValue::U32(100), false);
|
||||
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), new_spacing_value, network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[1].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[1].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[2].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 6), old_inputs[4].clone(), network_path);
|
||||
|
||||
document.network_interface.replace_reference_name(node_id, network_path, "Sample Polyline".to_string());
|
||||
}
|
||||
|
||||
// Make the "Quantity" parameter a u32 instead of f64
|
||||
if reference == "Sample Polyline" {
|
||||
let node_definition = resolve_document_node_type("Sample Polyline").unwrap();
|
||||
let new_node_template = node_definition.default_node_template();
|
||||
let document_node = new_node_template.document_node;
|
||||
|
||||
// Get the inputs, obtain the quantity value, and put the inputs back
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
|
||||
let quantity_value = old_inputs.get(3).cloned();
|
||||
let _ = document.network_interface.replace_inputs(node_id, old_inputs, network_path);
|
||||
|
||||
if let Some(NodeInput::Value { tagged_value, exposed }) = quantity_value {
|
||||
if let TaggedValue::F64(value) = *tagged_value {
|
||||
let new_quantity_value = NodeInput::value(TaggedValue::U32(value as u32), exposed);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make the "Grid" node, if its input of index 3 is a DVec2 for "angles" instead of a u32 for the "columns" input that now succeeds "angles", move the angle to index 5 (after "columns" and "rows")
|
||||
if reference == "Grid" && inputs_count == 6 {
|
||||
let node_definition = resolve_document_node_type(reference).unwrap();
|
||||
let new_node_template = node_definition.default_node_template();
|
||||
let document_node = new_node_template.document_node;
|
||||
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
|
||||
let index_3_value = old_inputs.get(3).cloned();
|
||||
|
||||
if let Some(NodeInput::Value { tagged_value, exposed: _ }) = index_3_value {
|
||||
if matches!(*tagged_value, TaggedValue::DVec2(_)) {
|
||||
// Move index 3 to the end
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
|
||||
} else {
|
||||
// Swap it back if we're not changing anything
|
||||
let _ = document.network_interface.replace_inputs(node_id, old_inputs, network_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
use crate::raster_types::{CPU, RasterDataTable};
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{Color, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[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, VectorDataTable, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
log::debug!("{:#?}", value);
|
||||
value
|
||||
}
|
||||
|
||||
/// Meant for debugging purposes, not general use. Returns the size of the input type in bytes.
|
||||
#[node_macro::node(category("Debug"))]
|
||||
|
|
|
@ -2,7 +2,9 @@ use crate::Ctx;
|
|||
use dyn_any::DynAny;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
|
||||
/// Obtain the X or Y component of a coordinate.
|
||||
/// Obtains the X or Y component of a coordinate point.
|
||||
///
|
||||
/// The inverse of this node is "Coordinate Value", which can have either or both its X and Y exposed as graph inputs.
|
||||
#[node_macro::node(name("Extract XY"), category("Math: Vector"))]
|
||||
fn extract_xy<T: Into<DVec2>>(_: impl Ctx, #[implementations(DVec2, IVec2, UVec2)] vector: T, axis: XY) -> f64 {
|
||||
match axis {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use crate::ArtboardGroupTable;
|
||||
use crate::Color;
|
||||
use crate::GraphicElement;
|
||||
use crate::GraphicGroupTable;
|
||||
use crate::gradient::GradientStops;
|
||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{Color, Context, Ctx};
|
||||
use crate::{Context, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[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, VectorDataTable, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
log::debug!("{:#?}", value);
|
||||
value
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2)] value: T) -> String {
|
||||
format!("{:?}", value)
|
||||
|
@ -45,24 +44,42 @@ async fn switch<T, C: Send + 'n + Clone>(
|
|||
#[implementations(
|
||||
Context -> String,
|
||||
Context -> bool,
|
||||
Context -> f32,
|
||||
Context -> f64,
|
||||
Context -> u32,
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> VectorDataTable,
|
||||
Context -> DAffine2,
|
||||
Context -> ArtboardGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> GraphicElement,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
)]
|
||||
if_true: impl Node<C, Output = T>,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
Context -> String,
|
||||
Context -> bool,
|
||||
Context -> f32,
|
||||
Context -> f64,
|
||||
Context -> u32,
|
||||
Context -> u64,
|
||||
Context -> DVec2,
|
||||
Context -> VectorDataTable,
|
||||
Context -> DAffine2,
|
||||
Context -> ArtboardGroupTable,
|
||||
Context -> VectorDataTable,
|
||||
Context -> GraphicGroupTable,
|
||||
Context -> RasterDataTable<CPU>,
|
||||
Context -> RasterDataTable<GPU>,
|
||||
Context -> GraphicElement,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
)]
|
||||
if_false: impl Node<C, Output = T>,
|
||||
) -> T {
|
||||
|
|
|
@ -73,6 +73,14 @@ pub trait Convert<T>: Sized {
|
|||
fn convert(self) -> T;
|
||||
}
|
||||
|
||||
impl<T: ToString> Convert<String> for T {
|
||||
/// Converts this type into a `String` using its `ToString` implementation.
|
||||
#[inline]
|
||||
fn convert(self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements the [`Convert`] trait for conversion between the cartesian product of Rust's primitive numeric types.
|
||||
macro_rules! impl_convert {
|
||||
($from:ty, $to:ty) => {
|
||||
|
|
|
@ -70,6 +70,7 @@ async fn boundless_footprint<T: 'n + 'static>(
|
|||
|
||||
transform_target.eval(ctx.into_context()).await
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn freeze_real_time<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
|
|
|
@ -86,6 +86,7 @@ async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 {
|
|||
Default::default()
|
||||
}
|
||||
|
||||
// TODO: Make this return a u32 instead of an f64, but we ned to improve math-related compatibility with integer types first.
|
||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_index(ctx: impl Ctx + ExtractIndex) -> f64 {
|
||||
match ctx.try_index() {
|
||||
|
|
|
@ -37,7 +37,13 @@ impl CornerRadius for [f64; 4] {
|
|||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn circle(_: impl Ctx, _primary: (), #[default(50.)] radius: f64) -> VectorDataTable {
|
||||
fn circle(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[unit(" px")]
|
||||
#[default(50.)]
|
||||
radius: f64,
|
||||
) -> VectorDataTable {
|
||||
let radius = radius.abs();
|
||||
VectorDataTable::new(VectorData::from_subpath(Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
|
||||
}
|
||||
|
@ -46,7 +52,9 @@ fn circle(_: impl Ctx, _primary: (), #[default(50.)] radius: f64) -> VectorDataT
|
|||
fn arc(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[default(50.)] radius: f64,
|
||||
#[unit(" px")]
|
||||
#[default(50.)]
|
||||
radius: f64,
|
||||
start_angle: Angle,
|
||||
#[default(270.)]
|
||||
#[range((0., 360.))]
|
||||
|
@ -66,7 +74,16 @@ fn arc(
|
|||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn ellipse(_: impl Ctx, _primary: (), #[default(50)] radius_x: f64, #[default(25)] radius_y: f64) -> VectorDataTable {
|
||||
fn ellipse(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[unit(" px")]
|
||||
#[default(50)]
|
||||
radius_x: f64,
|
||||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_y: f64,
|
||||
) -> VectorDataTable {
|
||||
let radius = DVec2::new(radius_x, radius_y);
|
||||
let corner1 = -radius;
|
||||
let corner2 = radius;
|
||||
|
@ -87,8 +104,12 @@ fn ellipse(_: impl Ctx, _primary: (), #[default(50)] radius_x: f64, #[default(25
|
|||
fn rectangle<T: CornerRadius>(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[default(100)] width: f64,
|
||||
#[default(100)] height: f64,
|
||||
#[unit(" px")]
|
||||
#[default(100)]
|
||||
width: f64,
|
||||
#[unit(" px")]
|
||||
#[default(100)]
|
||||
height: f64,
|
||||
_individual_corner_radii: bool, // TODO: Move this to the bottom once we have a migration capability
|
||||
#[implementations(f64, [f64; 4])] corner_radius: T,
|
||||
#[default(true)] clamped: bool,
|
||||
|
@ -104,7 +125,9 @@ fn regular_polygon<T: AsU64>(
|
|||
#[hard_min(3.)]
|
||||
#[implementations(u32, u64, f64)]
|
||||
sides: T,
|
||||
#[default(50)] radius: f64,
|
||||
#[unit(" px")]
|
||||
#[default(50)]
|
||||
radius: f64,
|
||||
) -> VectorDataTable {
|
||||
let points = sides.as_u64();
|
||||
let radius: f64 = radius * 2.;
|
||||
|
@ -119,8 +142,12 @@ fn star<T: AsU64>(
|
|||
#[hard_min(2.)]
|
||||
#[implementations(u32, u64, f64)]
|
||||
sides: T,
|
||||
#[default(50)] radius_1: f64,
|
||||
#[default(25)] radius_2: f64,
|
||||
#[unit(" px")]
|
||||
#[default(50)]
|
||||
radius_1: f64,
|
||||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_2: f64,
|
||||
) -> VectorDataTable {
|
||||
let points = sides.as_u64();
|
||||
let diameter: f64 = radius_1 * 2.;
|
||||
|
@ -130,7 +157,7 @@ fn star<T: AsU64>(
|
|||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn line(_: impl Ctx, _primary: (), #[default((0., -50.))] start: PixelSize, #[default((0., 50.))] end: PixelSize) -> VectorDataTable {
|
||||
fn line(_: impl Ctx, _primary: (), #[default(0., 0.)] start: PixelSize, #[default(100., 100.)] end: PixelSize) -> VectorDataTable {
|
||||
VectorDataTable::new(VectorData::from_subpath(Subpath::new_line(start, end)))
|
||||
}
|
||||
|
||||
|
@ -153,13 +180,14 @@ fn grid<T: GridSpacing>(
|
|||
_: impl Ctx,
|
||||
_primary: (),
|
||||
grid_type: GridType,
|
||||
#[unit(" px")]
|
||||
#[hard_min(0.)]
|
||||
#[default(10)]
|
||||
#[implementations(f64, DVec2)]
|
||||
spacing: T,
|
||||
#[default(30., 30.)] angles: DVec2,
|
||||
#[default(10)] columns: u32,
|
||||
#[default(10)] rows: u32,
|
||||
#[default(30., 30.)] angles: DVec2,
|
||||
) -> VectorDataTable {
|
||||
let (x_spacing, y_spacing) = spacing.as_dvec2().into();
|
||||
let (angle_a, angle_b) = angles.into();
|
||||
|
@ -251,11 +279,11 @@ mod tests {
|
|||
#[test]
|
||||
fn isometric_grid_test() {
|
||||
// Doesn't crash with weird angles
|
||||
grid((), (), GridType::Isometric, 0., (0., 0.).into(), 5, 5);
|
||||
grid((), (), GridType::Isometric, 90., (90., 90.).into(), 5, 5);
|
||||
grid((), (), GridType::Isometric, 0., 5, 5, (0., 0.).into());
|
||||
grid((), (), GridType::Isometric, 90., 5, 5, (90., 90.).into());
|
||||
|
||||
// Works properly
|
||||
let grid = grid((), (), GridType::Isometric, 10., (30., 30.).into(), 5, 5);
|
||||
let grid = grid((), (), GridType::Isometric, 10., 5, 5, (30., 30.).into());
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter() {
|
||||
|
@ -270,7 +298,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn skew_isometric_grid_test() {
|
||||
let grid = grid((), (), GridType::Isometric, 10., (40., 30.).into(), 5, 5);
|
||||
let grid = grid((), (), GridType::Isometric, 10., 5, 5, (40., 30.).into());
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter() {
|
||||
|
|
|
@ -65,6 +65,7 @@ async fn assign_colors<T>(
|
|||
randomize: bool,
|
||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
|
||||
/// The seed used for randomization.
|
||||
/// Seed to determine unique variations on the randomized color selection.
|
||||
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.
|
||||
|
@ -165,6 +166,7 @@ async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
|
|||
#[default(Color::BLACK)]
|
||||
/// The stroke color.
|
||||
color: C,
|
||||
#[unit(" px")]
|
||||
#[default(2.)]
|
||||
/// The stroke weight.
|
||||
weight: f64,
|
||||
|
@ -183,6 +185,7 @@ async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
|
|||
/// The stroke dash lengths. Each length forms a distance in a pattern where the first length is a dash, the second is a gap, and so on. If the list is an odd length, the pattern repeats with solid-gap roles reversed.
|
||||
dash_lengths: Vec<f64>,
|
||||
/// The phase offset distance from the starting point of the dash pattern.
|
||||
#[unit(" px")]
|
||||
dash_offset: f64,
|
||||
) -> Instances<V>
|
||||
where
|
||||
|
@ -253,7 +256,9 @@ async fn circular_repeat<I: 'n + Send + Clone>(
|
|||
// TODO: Implement other GraphicElementRendered types.
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
||||
angle_offset: Angle,
|
||||
#[default(5)] radius: f64,
|
||||
#[unit(" px")]
|
||||
#[default(5)]
|
||||
radius: f64,
|
||||
#[default(5)] instances: IntegerCount,
|
||||
) -> Instances<I> {
|
||||
let count = instances.max(1);
|
||||
|
@ -363,7 +368,7 @@ async fn mirror<I: 'n + Send + Clone>(
|
|||
_: impl Ctx,
|
||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
||||
#[default(ReferencePoint::Center)] relative_to_bounds: ReferencePoint,
|
||||
offset: f64,
|
||||
#[unit(" px")] offset: f64,
|
||||
#[range((-90., 90.))] angle: Angle,
|
||||
#[default(true)] keep_original: bool,
|
||||
) -> Instances<I>
|
||||
|
@ -1139,10 +1144,10 @@ async fn sample_polyline(
|
|||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
spacing: PointSpacingType,
|
||||
separation: f64,
|
||||
quantity: f64,
|
||||
start_offset: f64,
|
||||
stop_offset: f64,
|
||||
#[unit(" px")] separation: f64,
|
||||
quantity: u32,
|
||||
#[unit(" px")] start_offset: f64,
|
||||
#[unit(" px")] stop_offset: f64,
|
||||
adaptive_spacing: bool,
|
||||
subpath_segment_lengths: Vec<f64>,
|
||||
) -> VectorDataTable {
|
||||
|
@ -1182,7 +1187,7 @@ async fn sample_polyline(
|
|||
|
||||
let amount = match spacing {
|
||||
PointSpacingType::Separation => separation,
|
||||
PointSpacingType::Quantity => quantity,
|
||||
PointSpacingType::Quantity => quantity as f64,
|
||||
};
|
||||
let Some(mut sample_bezpath) = sample_polyline_on_bezpath(bezpath, spacing, amount, start_offset, stop_offset, adaptive_spacing, current_bezpath_segments_length) else {
|
||||
continue;
|
||||
|
@ -1388,6 +1393,7 @@ async fn tangent_on_path(
|
|||
async fn poisson_disk_points(
|
||||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
#[unit(" px")]
|
||||
#[default(10.)]
|
||||
#[hard_min(0.01)]
|
||||
separation_disk_diameter: f64,
|
||||
|
@ -1498,7 +1504,14 @@ async fn spline(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
|
|||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
||||
async fn jitter_points(_: impl Ctx, vector_data: VectorDataTable, #[default(5.)] amount: f64, seed: SeedValue) -> VectorDataTable {
|
||||
async fn jitter_points(
|
||||
_: impl Ctx,
|
||||
vector_data: VectorDataTable,
|
||||
#[unit(" px")]
|
||||
#[default(5.)]
|
||||
amount: f64,
|
||||
seed: SeedValue,
|
||||
) -> VectorDataTable {
|
||||
let mut result_table = VectorDataTable::default();
|
||||
|
||||
for mut vector_data_instance in vector_data.instance_iter() {
|
||||
|
@ -2080,7 +2093,7 @@ mod test {
|
|||
#[tokio::test]
|
||||
async fn sample_polyline() {
|
||||
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 30., 0., 0., 0., false, vec![100.]).await;
|
||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 30., 0, 0., 0., false, vec![100.]).await;
|
||||
let sample_polyline = sample_polyline.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 0., DVec2::X * 30., DVec2::X * 60., DVec2::X * 90.]) {
|
||||
|
@ -2090,7 +2103,7 @@ mod test {
|
|||
#[tokio::test]
|
||||
async fn sample_polyline_adaptive_spacing() {
|
||||
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 18., 0., 45., 10., true, vec![100.]).await;
|
||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 18., 0, 45., 10., true, vec![100.]).await;
|
||||
let sample_polyline = sample_polyline.instance_ref_iter().next().unwrap().instance;
|
||||
assert_eq!(sample_polyline.point_domain.positions().len(), 4);
|
||||
for (pos, expected) in sample_polyline.point_domain.positions().iter().zip([DVec2::X * 45., DVec2::X * 60., DVec2::X * 75., DVec2::X * 90.]) {
|
||||
|
|
|
@ -78,8 +78,12 @@ fn math<U: num_traits::float::Float>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn add<U: Add<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32, DVec2, f64, DVec2)] augend: U,
|
||||
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32, DVec2, DVec2, f64)] addend: T,
|
||||
/// The left-hand side of the addition operation.
|
||||
#[implementations(f64, f32, u32, DVec2, f64, DVec2)]
|
||||
augend: U,
|
||||
/// The right-hand side of the addition operation.
|
||||
#[implementations(f64, f32, u32, DVec2, DVec2, f64)]
|
||||
addend: T,
|
||||
) -> <U as Add<T>>::Output {
|
||||
augend + addend
|
||||
}
|
||||
|
@ -88,8 +92,12 @@ fn add<U: Add<T>, T>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn subtract<U: Sub<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32, DVec2, f64, DVec2)] minuend: U,
|
||||
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32, DVec2, DVec2, f64)] subtrahend: T,
|
||||
/// The left-hand side of the subtraction operation.
|
||||
#[implementations(f64, f32, u32, DVec2, f64, DVec2)]
|
||||
minuend: U,
|
||||
/// The right-hand side of the subtraction operation.
|
||||
#[implementations(f64, f32, u32, DVec2, DVec2, f64)]
|
||||
subtrahend: T,
|
||||
) -> <U as Sub<T>>::Output {
|
||||
minuend - subtrahend
|
||||
}
|
||||
|
@ -98,9 +106,12 @@ fn subtract<U: Sub<T>, T>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn multiply<U: Mul<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32, DVec2, f64, DVec2)] multiplier: U,
|
||||
/// The left-hand side of the multiplication operation.
|
||||
#[implementations(f64, f32, u32, DVec2, f64, DVec2)]
|
||||
multiplier: U,
|
||||
/// The right-hand side of the multiplication operation.
|
||||
#[default(1.)]
|
||||
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32, DVec2, DVec2, f64)]
|
||||
#[implementations(f64, f32, u32, DVec2, DVec2, f64)]
|
||||
multiplicand: T,
|
||||
) -> <U as Mul<T>>::Output {
|
||||
multiplier * multiplicand
|
||||
|
@ -112,7 +123,10 @@ fn multiply<U: Mul<T>, T>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn divide<U: Div<T> + Default + PartialEq, T: Default + PartialEq>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, DVec2, f64)] numerator: U,
|
||||
/// The left-hand side of the division operation.
|
||||
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, DVec2, f64)]
|
||||
numerator: U,
|
||||
/// The right-hand side of the division operation.
|
||||
#[default(1.)]
|
||||
#[implementations(f64, f64, f32, f32, u32, u32, DVec2, f64, DVec2)]
|
||||
denominator: T,
|
||||
|
@ -130,10 +144,15 @@ where
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn modulo<U: Rem<T, Output: Add<T, Output: Rem<T, Output = U::Output>>>, T: Copy>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32, DVec2, DVec2, f64)] numerator: U,
|
||||
/// The left-hand side of the modulo operation.
|
||||
#[implementations(f64, f32, u32, DVec2, DVec2, f64)]
|
||||
numerator: U,
|
||||
/// The right-hand side of the modulo operation.
|
||||
#[default(2.)]
|
||||
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32, DVec2, f64, DVec2)]
|
||||
#[implementations(f64, f32, u32, DVec2, f64, DVec2)]
|
||||
modulus: T,
|
||||
/// Ensures the result will always be positive, even if the numerator is negative.
|
||||
#[default(true)]
|
||||
always_positive: bool,
|
||||
) -> <U as Rem<T>>::Output {
|
||||
if always_positive { (numerator % modulus + modulus) % modulus } else { numerator % modulus }
|
||||
|
@ -143,9 +162,12 @@ fn modulo<U: Rem<T, Output: Add<T, Output: Rem<T, Output = U::Output>>>, T: Copy
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn exponent<U: Pow<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f64, &f64, f32, &f32, f32, &f32, u32, &u32, u32, &u32)] base: U,
|
||||
/// The base number that will be raised to the power.
|
||||
#[implementations(f64, f32, u32)]
|
||||
base: U,
|
||||
/// The power to which the base number will be raised.
|
||||
#[default(2.)]
|
||||
#[implementations(f64, f64, &f64, &f64, f32, f32, &f32, &f32, u32, u32, &u32, &u32)]
|
||||
#[implementations(f64, f32, u32)]
|
||||
power: T,
|
||||
) -> <U as num_traits::Pow<T>>::Output {
|
||||
base.pow(power)
|
||||
|
@ -155,9 +177,11 @@ fn exponent<U: Pow<T>, T>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn root<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The number for which the nth root will be calculated.
|
||||
#[default(2.)]
|
||||
#[implementations(f64, f32)]
|
||||
radicand: U,
|
||||
/// The degree of the root to be calculated. Square root is 2, cube root is 3, and so on.
|
||||
#[default(2.)]
|
||||
#[implementations(f64, f32)]
|
||||
degree: U,
|
||||
|
@ -175,7 +199,10 @@ fn root<U: num_traits::float::Float>(
|
|||
#[node_macro::node(category("Math: Arithmetic"))]
|
||||
fn logarithm<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, f32)] value: U,
|
||||
/// The number for which the logarithm will be calculated.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
/// The base of the logarithm, such as 2 (binary), 10 (decimal), and e (natural logarithm).
|
||||
#[default(2.)]
|
||||
#[implementations(f64, f32)]
|
||||
base: U,
|
||||
|
@ -193,39 +220,83 @@ fn logarithm<U: num_traits::float::Float>(
|
|||
|
||||
/// The sine trigonometric function (sin) calculates the ratio of the angle's opposite side length to its hypotenuse length.
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn sine<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] theta: U, radians: bool) -> U {
|
||||
fn sine<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The given angle.
|
||||
#[implementations(f64, f32)]
|
||||
theta: U,
|
||||
/// Whether the given angle should be interpreted as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U {
|
||||
if radians { theta.sin() } else { theta.to_radians().sin() }
|
||||
}
|
||||
|
||||
/// The cosine trigonometric function (cos) calculates the ratio of the angle's adjacent side length to its hypotenuse length.
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn cosine<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] theta: U, radians: bool) -> U {
|
||||
fn cosine<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The given angle.
|
||||
#[implementations(f64, f32)]
|
||||
theta: U,
|
||||
/// Whether the given angle should be interpreted as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U {
|
||||
if radians { theta.cos() } else { theta.to_radians().cos() }
|
||||
}
|
||||
|
||||
/// The tangent trigonometric function (tan) calculates the ratio of the angle's opposite side length to its adjacent side length.
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn tangent<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] theta: U, radians: bool) -> U {
|
||||
fn tangent<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The given angle.
|
||||
#[implementations(f64, f32)]
|
||||
theta: U,
|
||||
/// Whether the given angle should be interpreted as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U {
|
||||
if radians { theta.tan() } else { theta.to_radians().tan() }
|
||||
}
|
||||
|
||||
/// The inverse sine trigonometric function (asin) calculates the angle whose sine is the specified value.
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn sine_inverse<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U, radians: bool) -> U {
|
||||
fn sine_inverse<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
/// Whether the resulting angle should be given in as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U {
|
||||
if radians { value.asin() } else { value.asin().to_degrees() }
|
||||
}
|
||||
|
||||
/// The inverse cosine trigonometric function (acos) calculates the angle whose cosine is the specified value.
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn cosine_inverse<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U, radians: bool) -> U {
|
||||
fn cosine_inverse<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The given value for which the angle will be calculated. Must be in the range [-1, 1] or else the result will be NaN.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
/// Whether the resulting angle should be given in as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U {
|
||||
if radians { value.acos() } else { value.acos().to_degrees() }
|
||||
}
|
||||
|
||||
/// The inverse tangent trigonometric function (atan or atan2, depending on input type) calculates:
|
||||
/// atan: the angle whose tangent is the specified scalar number.
|
||||
/// atan2: the angle of a ray from the origin to the specified coordinate.
|
||||
///
|
||||
/// The resulting angle is always in the range [0°, 180°] or, in radians, [-π/2, π/2].
|
||||
#[node_macro::node(category("Math: Trig"))]
|
||||
fn tangent_inverse<U: TangentInverse>(_: impl Ctx, #[implementations(f64, f32, DVec2)] value: U, radians: bool) -> U::Output {
|
||||
fn tangent_inverse<U: TangentInverse>(
|
||||
_: impl Ctx,
|
||||
/// The given value for which the angle will be calculated.
|
||||
#[implementations(f64, f32, DVec2)]
|
||||
value: U,
|
||||
/// Whether the resulting angle should be given in as radians instead of degrees.
|
||||
radians: bool,
|
||||
) -> U::Output {
|
||||
value.atan(radians)
|
||||
}
|
||||
|
||||
|
@ -257,10 +328,13 @@ impl TangentInverse for DVec2 {
|
|||
fn random<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
/// Seed to determine the unique variation of which number will be generated.
|
||||
seed: u64,
|
||||
/// The smaller end of the range within which the random number will be generated.
|
||||
#[implementations(f64, f32)]
|
||||
#[default(0.)]
|
||||
min: U,
|
||||
/// The larger end of the range within which the random number will be generated.
|
||||
#[implementations(f64, f32)]
|
||||
#[default(1.)]
|
||||
max: U,
|
||||
|
@ -294,37 +368,73 @@ fn to_f64<U: num_traits::int::PrimInt>(_: impl Ctx, #[implementations(u32, u64)]
|
|||
|
||||
/// The rounding function (round) maps an input value to its nearest whole number. Halfway values are rounded away from zero.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn round<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> U {
|
||||
fn round<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The number which will be rounded.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
) -> U {
|
||||
value.round()
|
||||
}
|
||||
|
||||
/// The floor function (floor) reduces an input value to its nearest larger whole number, unless the input number is already whole.
|
||||
/// The floor function (floor) rounds down an input value to the nearest whole number, unless the input number is already whole.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn floor<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> U {
|
||||
fn floor<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The number which will be rounded down.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
) -> U {
|
||||
value.floor()
|
||||
}
|
||||
|
||||
/// The ceiling function (ceil) increases an input value to its nearest smaller whole number, unless the input number is already whole.
|
||||
/// The ceiling function (ceil) rounds up an input value to the nearest whole number, unless the input number is already whole.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn ceiling<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> U {
|
||||
fn ceiling<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The number which will be rounded up.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
) -> U {
|
||||
value.ceil()
|
||||
}
|
||||
|
||||
/// The absolute value function (abs) removes the negative sign from an input value, if present.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn absolute_value<U: num_traits::float::Float>(_: impl Ctx, #[implementations(f64, f32)] value: U) -> U {
|
||||
fn absolute_value<U: num_traits::float::Float>(
|
||||
_: impl Ctx,
|
||||
/// The number which will be made positive.
|
||||
#[implementations(f64, f32)]
|
||||
value: U,
|
||||
) -> U {
|
||||
value.abs()
|
||||
}
|
||||
|
||||
/// The minimum function (min) picks the smaller of two numbers.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn min<T: std::cmp::PartialOrd>(_: impl Ctx, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] value: T, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] other_value: T) -> T {
|
||||
fn min<T: std::cmp::PartialOrd>(
|
||||
_: impl Ctx,
|
||||
/// One of the two numbers, of which the lesser will be returned.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
value: T,
|
||||
/// The other of the two numbers, of which the lesser will be returned.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
other_value: T,
|
||||
) -> T {
|
||||
if value < other_value { value } else { other_value }
|
||||
}
|
||||
|
||||
/// The maximum function (max) picks the larger of two numbers.
|
||||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn max<T: std::cmp::PartialOrd>(_: impl Ctx, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] value: T, #[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] other_value: T) -> T {
|
||||
fn max<T: std::cmp::PartialOrd>(
|
||||
_: impl Ctx,
|
||||
/// One of the two numbers, of which the greater will be returned.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
value: T,
|
||||
/// The other of the two numbers, of which the greater will be returned.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
other_value: T,
|
||||
) -> T {
|
||||
if value > other_value { value } else { other_value }
|
||||
}
|
||||
|
||||
|
@ -332,9 +442,15 @@ fn max<T: std::cmp::PartialOrd>(_: impl Ctx, #[implementations(f64, &f64, f32, &
|
|||
#[node_macro::node(category("Math: Numeric"))]
|
||||
fn clamp<T: std::cmp::PartialOrd>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] min: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, &str)] max: T,
|
||||
/// The number to be clamped, which will be restricted to the range between the minimum and maximum values.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
value: T,
|
||||
/// The left (smaller) side of the range. The output will never be less than this number.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
min: T,
|
||||
/// The right (greater) side of the range. The output will never be greater than this number.
|
||||
#[implementations(f64, f32, u32, &str)]
|
||||
max: T,
|
||||
) -> T {
|
||||
let (min, max) = if min < max { (min, max) } else { (max, min) };
|
||||
if value < min {
|
||||
|
@ -350,8 +466,12 @@ fn clamp<T: std::cmp::PartialOrd>(
|
|||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn equals<U: std::cmp::PartialEq<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, DVec2, &DVec2, &str)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, DVec2, &DVec2, &str)] other_value: U,
|
||||
/// One of the two numbers to compare for equality.
|
||||
#[implementations(f64, f32, u32, DVec2, &str)]
|
||||
value: T,
|
||||
/// The other of the two numbers to compare for equality.
|
||||
#[implementations(f64, f32, u32, DVec2, &str)]
|
||||
other_value: U,
|
||||
) -> bool {
|
||||
other_value == value
|
||||
}
|
||||
|
@ -360,8 +480,12 @@ fn equals<U: std::cmp::PartialEq<T>, T>(
|
|||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn not_equals<U: std::cmp::PartialEq<T>, T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, DVec2, &DVec2, &str)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32, DVec2, &DVec2, &str)] other_value: U,
|
||||
/// One of the two numbers to compare for inequality.
|
||||
#[implementations(f64, f32, u32, DVec2, &str)]
|
||||
value: T,
|
||||
/// The other of the two numbers to compare for inequality.
|
||||
#[implementations(f64, f32, u32, DVec2, &str)]
|
||||
other_value: U,
|
||||
) -> bool {
|
||||
other_value != value
|
||||
}
|
||||
|
@ -371,8 +495,13 @@ fn not_equals<U: std::cmp::PartialEq<T>, T>(
|
|||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn less_than<T: std::cmp::PartialOrd<T>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] other_value: T,
|
||||
/// The number on the left-hand side of the comparison.
|
||||
#[implementations(f64, f32, u32)]
|
||||
value: T,
|
||||
/// The number on the right-hand side of the comparison.
|
||||
#[implementations(f64, f32, u32)]
|
||||
other_value: T,
|
||||
/// Uses the less-than-or-equal operation (<=) instead of the less-than operation (<).
|
||||
or_equal: bool,
|
||||
) -> bool {
|
||||
if or_equal { value <= other_value } else { value < other_value }
|
||||
|
@ -383,8 +512,13 @@ fn less_than<T: std::cmp::PartialOrd<T>>(
|
|||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn greater_than<T: std::cmp::PartialOrd<T>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] value: T,
|
||||
#[implementations(f64, &f64, f32, &f32, u32, &u32)] other_value: T,
|
||||
/// The number on the left-hand side of the comparison.
|
||||
#[implementations(f64, f32, u32)]
|
||||
value: T,
|
||||
/// The number on the right-hand side of the comparison.
|
||||
#[implementations(f64, f32, u32)]
|
||||
other_value: T,
|
||||
/// Uses the greater-than-or-equal operation (>=) instead of the greater-than operation (>).
|
||||
or_equal: bool,
|
||||
) -> bool {
|
||||
if or_equal { value >= other_value } else { value > other_value }
|
||||
|
@ -392,19 +526,35 @@ fn greater_than<T: std::cmp::PartialOrd<T>>(
|
|||
|
||||
/// The logical or operation (||) returns true if either of the two inputs are true, or false if both are false.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn logical_or(_: impl Ctx, value: bool, other_value: bool) -> bool {
|
||||
fn logical_or(
|
||||
_: impl Ctx,
|
||||
/// One of the two boolean values, either of which may be true for the node to output true.
|
||||
value: bool,
|
||||
/// The other of the two boolean values, either of which may be true for the node to output true.
|
||||
other_value: bool,
|
||||
) -> bool {
|
||||
value || other_value
|
||||
}
|
||||
|
||||
/// The logical and operation (&&) returns true if both of the two inputs are true, or false if any are false.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn logical_and(_: impl Ctx, value: bool, other_value: bool) -> bool {
|
||||
fn logical_and(
|
||||
_: impl Ctx,
|
||||
/// One of the two boolean values, both of which must be true for the node to output true.
|
||||
value: bool,
|
||||
/// The other of the two boolean values, both of which must be true for the node to output true.
|
||||
other_value: bool,
|
||||
) -> bool {
|
||||
value && other_value
|
||||
}
|
||||
|
||||
/// The logical not operation (!) reverses true and false value of the input.
|
||||
#[node_macro::node(category("Math: Logic"))]
|
||||
fn logical_not(_: impl Ctx, input: bool) -> bool {
|
||||
fn logical_not(
|
||||
_: impl Ctx,
|
||||
/// The boolean value to be reversed.
|
||||
input: bool,
|
||||
) -> bool {
|
||||
!input
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ impl<P: Pixel + Alpha> Sample for BrushStampGenerator<P> {
|
|||
}
|
||||
|
||||
#[node_macro::node(skip_impl)]
|
||||
fn brush_stamp_generator(diameter: f64, color: Color, hardness: f64, flow: f64) -> BrushStampGenerator<Color> {
|
||||
fn brush_stamp_generator(#[unit(" px")] diameter: f64, color: Color, hardness: f64, flow: f64) -> BrushStampGenerator<Color> {
|
||||
// Diameter
|
||||
let radius = diameter / 2.;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue