Skip to content

Commit

Permalink
Copy to Points node: Add "Random Scale Bias" parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Feb 5, 2024
1 parent 9530e55 commit 7e5069f
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 13 deletions.
2 changes: 1 addition & 1 deletion demo-artwork/procedural-string-lights.graphite

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2653,13 +2653,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
name: "Copy to Points",
category: "Vector",
// TODO: Wrap this implementation with a document node that has a cache node so the output is cached?
implementation: NodeImplementation::proto("graphene_core::vector::CopyToPoints<_, _, _, _, _>"),
implementation: NodeImplementation::proto("graphene_core::vector::CopyToPoints<_, _, _, _, _, _>"),
manual_composition: Some(concrete!(Footprint)),
inputs: vec![
DocumentInputType::value("Points", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Instance", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Random Scale Min", TaggedValue::F32(1.), false),
DocumentInputType::value("Random Scale Max", TaggedValue::F32(1.), false),
DocumentInputType::value("Random Scale Bias", TaggedValue::F32(1.), false),
DocumentInputType::value("Random Rotation", TaggedValue::F32(0.), false),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2151,13 +2151,23 @@ pub fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId,
NumberInput::default().min(0.).mode_range().range_min(Some(0.)).range_max(Some(2.)).unit("x"),
true,
);
let random_scale_bias = number_widget(
document_node,
node_id,
4,
"Random Scale Bias",
NumberInput::default().mode_range().range_min(Some(-50.)).range_max(Some(50.)),
true,
);

let random_rotation = number_widget(document_node, node_id, 4, "Random Rotation", NumberInput::default().min(0.).max(360.).mode_range().unit("°"), true);
let random_rotation = number_widget(document_node, node_id, 5, "Random Rotation", NumberInput::default().min(0.).max(360.).mode_range().unit("°"), true);

vec![
LayoutGroup::Row { widgets: instance }.with_tooltip("Artwork to be copied and placed at each point"),
LayoutGroup::Row { widgets: random_scale_min }.with_tooltip("Minimum range of randomized sizes given to each instance"),
LayoutGroup::Row { widgets: random_scale_max }.with_tooltip("Maximum range of randomized sizes given to each instance"),
LayoutGroup::Row { widgets: random_scale_bias }
.with_tooltip("Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes)"),
LayoutGroup::Row { widgets: random_rotation }.with_tooltip("Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise"),
]
}
Expand Down
39 changes: 31 additions & 8 deletions node-graph/gcore/src/vector/vector_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ impl ConcatElement for GraphicGroup {
}

#[derive(Debug, Clone, Copy)]
pub struct CopyToPoints<Points, Instance, RandomScaleMin, RandomScaleMax, RandomRotation> {
pub struct CopyToPoints<Points, Instance, RandomScaleMin, RandomScaleMax, RandomScaleBias, RandomRotation> {
points: Points,
instance: Instance,
random_scale_min: RandomScaleMin,
random_scale_max: RandomScaleMax,
random_scale_bias: RandomScaleBias,
random_rotation: RandomRotation,
}

Expand All @@ -190,34 +191,56 @@ async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + Tr
instance: impl Node<Footprint, Output = FI>,
random_scale_min: f32,
random_scale_max: f32,
random_scale_bias: f32,
random_rotation: f32,
) -> I {
let points = self.points.eval(footprint).await;
let instance = self.instance.eval(footprint).await;
let random_scale_min = random_scale_min as f64;
let random_scale_max = random_scale_max as f64;
let random_scale_difference = random_scale_max - random_scale_min;
let random_scale_bias = random_scale_bias as f64;
let random_rotation = random_rotation as f64;

let points_list = points.subpaths.iter().flat_map(|s| s.anchors());

let instance_bounding_box = instance.bounding_box(DAffine2::IDENTITY).unwrap_or_default();
let instance_center = -0.5 * (instance_bounding_box[0] + instance_bounding_box[1]);

let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let mut scale_rng = rand::rngs::StdRng::seed_from_u64(0);
let mut rotation_rng = rand::rngs::StdRng::seed_from_u64(0);

let do_scale = random_scale_difference.abs() > 1e-6;
let do_rotation = random_rotation.abs() > 1e-6;

let mut result = I::default();
for point in points_list {
let center_transform = DAffine2::from_translation(instance_center);

let translation = points.transform.transform_point2(point);

let rotation = (rng.gen::<f64>() - 0.5) * random_rotation;
let rotation = rotation / 360. * std::f64::consts::TAU;

let scale = random_scale_min + rng.gen::<f64>() * (random_scale_max - random_scale_min);
let scale = DVec2::splat(scale);
let rotation = if do_rotation {
let degrees = (rotation_rng.gen::<f64>() - 0.5) * random_rotation;
degrees / 360. * std::f64::consts::TAU
} else {
0.
};

let scale = if do_scale {
if random_scale_bias.abs() < 1e-6 {
// Linear
random_scale_min + scale_rng.gen::<f64>() * random_scale_difference
} else {
// Weighted (see <https://www.desmos.com/calculator/gmavd3m9bd>)
let horizontal_scale_factor = 1. - 2_f64.powf(random_scale_bias);
let scale_factor = (1. - scale_rng.gen::<f64>() * horizontal_scale_factor).log2() / random_scale_bias;
random_scale_min + scale_factor * random_scale_difference
}
} else {
random_scale_min
};

result.concat(&instance, DAffine2::from_scale_angle_translation(scale, rotation, translation) * center_transform);
result.concat(&instance, DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation) * center_transform);
}

result
Expand Down
4 changes: 2 additions & 2 deletions node-graph/interpreted-executor/src/node_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,8 +734,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
)],
register_node!(graphene_std::raster::SampleNode<_>, input: Footprint, params: [ImageFrame<Color>]),
register_node!(graphene_std::raster::MandelbrotNode, input: Footprint, params: []),
async_node!(graphene_core::vector::CopyToPoints<_, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, Footprint => VectorData, () => f32, () => f32, () => f32]),
async_node!(graphene_core::vector::CopyToPoints<_, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => VectorData, Footprint => GraphicGroup, () => f32, () => f32, () => f32]),
async_node!(graphene_core::vector::CopyToPoints<_, _, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, Footprint => VectorData, () => f32, () => f32, () => f32, () => f32]),
async_node!(graphene_core::vector::CopyToPoints<_, _, _, _, _, _>, input: Footprint, output: GraphicGroup, fn_params: [Footprint => VectorData, Footprint => GraphicGroup, () => f32, () => f32, () => f32, () => f32]),
async_node!(graphene_core::vector::SamplePoints<_, _, _, _, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData, () => f32, () => f32, () => f32, () => bool, Footprint => Vec<Vec<f64>>]),
register_node!(graphene_core::vector::PoissonDiskPoints<_>, input: VectorData, params: [f32]),
register_node!(graphene_core::vector::LengthsOfSegmentsOfSubpaths, input: VectorData, params: []),
Expand Down

0 comments on commit 7e5069f

Please sign in to comment.