Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,189 changes: 9,188 additions & 1 deletion demo-artwork/procedural-string-lights.graphite

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions demo-artwork/red-dress.graphite

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use crate::messages::prelude::*;
pub struct DemoArtworkDialog;

/// `(name, thumbnail, filename)`
const ARTWORK: [(&str, &str, &str); 3] = [
const ARTWORK: [(&str, &str, &str); 4] = [
("Valley of Spires", "ThumbnailValleyOfSpires", "valley-of-spires.graphite"),
("Red Dress", "ThumbnailRedDress", "red-dress.graphite"),
("Just a Potted Cactus", "ThumbnailJustAPottedCactus", "just-a-potted-cactus.graphite"),
("Procedural String Lights", "ThumbnailProceduralStringLights", "procedural-string-lights.graphite"),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
}
Undo => {
self.undo_in_progress = true;

responses.add(ToolMessage::PreUndo);
responses.add(DocumentMessage::DocumentHistoryBackward);
responses.add(OverlaysMessage::Draw);
responses.add(DocumentMessage::UndoFinished);
Expand Down
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
1 change: 1 addition & 0 deletions editor/src/messages/tool/tool_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ pub enum ToolMessage {
},
DeactivateTools,
InitTools,
PreUndo,
Redo,
RefreshToolOptions,
ResetColors,
Expand Down
13 changes: 10 additions & 3 deletions editor/src/messages/tool/tool_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, DocumentId, &InputPre
tool_data.active_tool_mut().process_message(ToolMessage::UpdateHints, responses, &mut data);
tool_data.active_tool_mut().process_message(ToolMessage::UpdateCursor, responses, &mut data);
}
ToolMessage::PreUndo => {
let tool_data = &mut self.tool_state.tool_data;
match tool_data.active_tool_type {
ToolType::Pen => {}
_ => {
responses.add(BroadcastEvent::ToolAbort);
}
}
}
ToolMessage::Redo => {
let tool_data = &mut self.tool_state.tool_data;
match tool_data.active_tool_type {
Expand Down Expand Up @@ -236,9 +245,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, DocumentId, &InputPre
ToolType::Pen => {
responses.add(PenToolMessage::Undo);
}
_ => {
responses.add(BroadcastEvent::ToolAbort);
}
_ => {}
}
}

Expand Down
18 changes: 11 additions & 7 deletions editor/src/messages/tool/tool_messages/freehand_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,33 +216,37 @@ impl Fsm for FreehandToolFsmState {
(FreehandToolFsmState::Ready, FreehandToolMessage::DragStart) => {
responses.add(DocumentMessage::StartTransaction);

let parent = document.new_layer_parent();
let transform = document.metadata().transform_to_viewport(parent);
let pos = transform.inverse().transform_point2(input.mouse.position);

tool_data.dragged = false;
tool_data.extend_from_start = false;
tool_data.last_point = pos;

tool_data.weight = tool_options.line_weight;

if let Some((layer, subpath_index, from_start)) = should_extend(document, input.mouse.position, crate::consts::SNAP_POINT_TOLERANCE) {
let transform = document.metadata().transform_to_viewport(layer);
let pos = transform.inverse().transform_point2(input.mouse.position);
let manipulator_group = ManipulatorGroup::new_anchor(pos);
let modification = if from_start {
tool_data.extend_from_start = true;
VectorDataModification::AddStartManipulatorGroup { subpath_index, manipulator_group }
} else {
VectorDataModification::AddEndManipulatorGroup { subpath_index, manipulator_group }
};
responses.add(GraphOperationMessage::Vector { layer, modification });

tool_data.dragged = true;
tool_data.last_point = pos;
tool_data.layer = Some(layer);

responses.add(GraphOperationMessage::Vector { layer, modification });
} else {
responses.add(DocumentMessage::DeselectAllLayers);

let parent = document.new_layer_parent();
let transform = document.metadata().transform_to_viewport(parent);
let pos = transform.inverse().transform_point2(input.mouse.position);
let subpath = bezier_rs::Subpath::from_anchors([pos], false);

let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), parent, responses);

tool_data.last_point = pos;
tool_data.layer = Some(layer);

responses.add(GraphOperationMessage::FillSet {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/src/utility-functions/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
// Demo artwork
import ThumbnailJustAPottedCactus from "@graphite-frontend/assets/images/demo-artwork/thumbnail-just-a-potted-cactus.png";
import ThumbnailProceduralStringLights from "@graphite-frontend/assets/images/demo-artwork/thumbnail-procedural-string-lights.png";
import ThumbnailRedDress from "@graphite-frontend/assets/images/demo-artwork/thumbnail-red-dress.png";
import ThumbnailValleyOfSpires from "@graphite-frontend/assets/images/demo-artwork/thumbnail-valley-of-spires.png";

const DEMO_ARTWORK = {
ThumbnailJustAPottedCactus,
ThumbnailProceduralStringLights,
ThumbnailValleyOfSpires,
ThumbnailRedDress,
} as const;

// All images
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