Skip to content

Add origin to each layer #2730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8b614f1
add origin
mTvare6 Jun 18, 2025
5394417
cleanup pivot
mTvare6 Jun 19, 2025
90e8859
a lot of stuff
mTvare6 Jun 24, 2025
02c1548
Merge branch 'master' into origin-graph-data
mTvare6 Jun 24, 2025
4a035e1
reset pivot
mTvare6 Jun 25, 2025
eacd277
fix transform with pivot issues
mTvare6 Jun 25, 2025
23cbea2
fixes
mTvare6 Jun 26, 2025
a7847ec
some more cleanup
mTvare6 Jun 26, 2025
519d84a
fixes
mTvare6 Jun 26, 2025
bb7a847
finally works
mTvare6 Jun 26, 2025
8e1ba29
origin fixes
mTvare6 Jun 27, 2025
6e5d69c
Merge branch 'master' into origin-graph-data
mTvare6 Jun 27, 2025
d4d1aa6
fix spaces
mTvare6 Jun 27, 2025
c42d4da
fix using dragged_layers
mTvare6 Jun 27, 2025
7139f34
simplify pivot logic
mTvare6 Jun 27, 2025
58383ad
fix bugs
mTvare6 Jun 27, 2025
dad772e
fix the final bug
mTvare6 Jun 27, 2025
6a5c1d2
fix in select_tool
mTvare6 Jun 27, 2025
7e8fe03
fix updates
mTvare6 Jun 27, 2025
c6d320e
some more refactors to fix misunderstanding and refactor
mTvare6 Jun 28, 2025
d428016
add checkboxes
mTvare6 Jun 28, 2025
5156d4f
fix labels
mTvare6 Jun 28, 2025
4df1240
Merge branch 'master' into origin-graph-data
mTvare6 Jun 28, 2025
12060ba
fix stuff which broke at merge
mTvare6 Jun 28, 2025
8d494d8
update
mTvare6 Jun 28, 2025
b1ccbc9
cargo fmt
mTvare6 Jun 28, 2025
a59552d
fix serde crash
mTvare6 Jun 29, 2025
ea575a8
fix pivot not updating on move
mTvare6 Jun 29, 2025
3578ac5
fix pivot not becoming last active refernce
mTvare6 Jun 29, 2025
63d01fc
fix redraw issues
mTvare6 Jun 29, 2025
14e921a
add: active pivot
mTvare6 Jun 30, 2025
7fd448b
Merge branch 'master' into origin-graph-data
mTvare6 Jun 30, 2025
eab6bbd
cargo fmt
mTvare6 Jun 30, 2025
b47c263
fix pivot showing up in default mode
mTvare6 Jun 30, 2025
b601d9b
Merge branch 'master' into origin-graph-data
mTvare6 Jun 30, 2025
d07eb5e
add: pivot pin
mTvare6 Jul 2, 2025
44e1bda
fix: use pin icons
mTvare6 Jul 2, 2025
dcc428d
cargo: cargo lock update?
mTvare6 Jul 2, 2025
dc23f09
merge
mTvare6 Jul 2, 2025
20cfccb
fix: use checkbox instead of Overlays
mTvare6 Jul 2, 2025
117b24f
refactor: add dot to path_tool
mTvare6 Jul 4, 2025
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
1,733 changes: 973 additions & 760 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub const SELECTION_DRAG_ANGLE: f64 = 90.;
pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.;
pub const PIVOT_CROSSHAIR_LENGTH: f64 = 9.;
pub const PIVOT_DIAMETER: f64 = 5.;
pub const DOWEL_PIN_RADIUS: f64 = 5.;

// COMPASS ROSE
pub const COMPASS_ROSE_RING_INNER_DIAMETER: f64 = 13.;
Expand Down Expand Up @@ -137,6 +138,7 @@ pub const COLOR_OVERLAY_BLUE_50: &str = "rgba(0, 168, 255, 0.5)";
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
pub const COLOR_OVERLAY_GREEN: &str = "#63ce63";
pub const COLOR_OVERLAY_RED: &str = "#ef5454";
pub const COLOR_OVERLAY_ORANGE: &str = "#e27a44";
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
pub const COLOR_OVERLAY_LABEL_BACKGROUND: &str = "#000000cc";
Expand Down
27 changes: 27 additions & 0 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
OverlaysType::HoverOutline => visibility_settings.hover_outline = visible,
OverlaysType::SelectionOutline => visibility_settings.selection_outline = visible,
OverlaysType::Pivot => visibility_settings.pivot = visible,
OverlaysType::Origin => visibility_settings.origin = visible,
OverlaysType::Path => visibility_settings.path = visible,
OverlaysType::Anchors => {
visibility_settings.anchors = visible;
Expand Down Expand Up @@ -1708,6 +1709,14 @@ impl DocumentMessageHandler {
.reduce(graphene_std::renderer::Quad::combine_bounds)
}

pub fn selected_visible_and_unlock_layers_bounding_box_document(&self) -> Option<[DVec2; 2]> {
self.network_interface
.selected_nodes()
.selected_visible_and_unlocked_layers(&self.network_interface)
.map(|layer| self.metadata().nonzero_bounding_box(layer))
.reduce(graphene_std::renderer::Quad::combine_bounds)
}

pub fn document_network(&self) -> &NodeNetwork {
self.network_interface.document_network()
}
Expand Down Expand Up @@ -2259,6 +2268,24 @@ impl DocumentMessageHandler {
]
},
},
LayoutGroup::Row {
widgets: {
let mut checkbox_id = CheckboxId::default();
vec![
CheckboxInput::new(self.overlays_visibility_settings.pivot)
.on_update(|optional_input: &CheckboxInput| {
DocumentMessage::SetOverlaysVisibility {
visible: optional_input.checked,
overlays_type: Some(OverlaysType::Origin),
}
.into()
})
.for_label(checkbox_id.clone())
.widget_holder(),
TextLabel::new("Transform Origin".to_string()).for_checkbox(&mut checkbox_id).widget_holder(),
]
},
},
LayoutGroup::Row {
widgets: {
let mut checkbox_id = CheckboxId::default();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use glam::{DAffine2, IVec2};
use graph_craft::document::NodeId;
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
Expand Down Expand Up @@ -52,10 +52,6 @@ pub enum GraphOperationMessage {
transform_in: TransformIn,
skip_rerender: bool,
},
TransformSetPivot {
layer: LayerNodeIdentifier,
pivot: DVec2,
},
Vector {
layer: LayerNodeIdentifier,
modification_type: VectorModificationType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,6 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
modify_inputs.transform_set(transform, transform_in, skip_rerender);
}
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
if layer == LayerNodeIdentifier::ROOT_PARENT {
log::error!("Cannot run TransformSetPivot on ROOT_PARENT");
return;
}
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
modify_inputs.pivot_set(pivot);
}
}
GraphOperationMessage::Vector { layer, modification_type } => {
if layer == LayerNodeIdentifier::ROOT_PARENT {
log::error!("Cannot run Vector on ROOT_PARENT");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use glam::{DAffine2, IVec2};
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
Expand Down Expand Up @@ -451,12 +451,6 @@ impl<'a> ModifyInputsContext<'a> {
}
}

pub fn pivot_set(&mut self, new_pivot: DVec2) {
let Some(transform_node_id) = self.existing_node_id("Transform", true) else { return };

self.set_input_with_refresh(InputConnector::node(transform_node_id, 5), NodeInput::value(TaggedValue::DVec2(new_pivot), false), false);
}

pub fn vector_modify(&mut self, modification_type: VectorModificationType) {
let Some(path_node_id) = self.existing_node_id("Path", true) else { return };
self.network_interface.vector_modify(&path_node_id, modification_type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::F64(0.), false),
NodeInput::value(TaggedValue::DVec2(DVec2::ONE), false),
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
NodeInput::value(TaggedValue::DVec2(DVec2::splat(0.5)), false),
],
implementation: DocumentNodeImplementation::Network(NodeNetwork {
exports: vec![NodeInput::node(NodeId(1), 0)],
Expand All @@ -1664,7 +1663,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(f64), 2),
NodeInput::network(concrete!(DVec2), 3),
NodeInput::network(concrete!(DVec2), 4),
NodeInput::network(concrete!(DVec2), 5),
],
manual_composition: Some(concrete!(Context)),
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::transform_nodes::TransformNode")),
Expand Down Expand Up @@ -1732,7 +1730,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}),
),
PropertiesRow::with_override("Skew", "TODO", WidgetOverride::Custom("transform_skew".to_string())),
PropertiesRow::with_override("Pivot", "TODO", WidgetOverride::Hidden),
],
output_names: vec!["Data".to_string()],
..Default::default()
Expand Down
42 changes: 40 additions & 2 deletions editor/src/messages/portfolio/document/overlays/utility_types.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::utility_functions::overlay_canvas_context;
use crate::consts::{
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER,
COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
};
use crate::messages::prelude::Message;
use bezier_rs::{Bezier, Subpath};
use core::borrow::Borrow;
use core::f64::consts::{FRAC_PI_2, TAU};
use core::f64::consts::{FRAC_PI_2, PI, TAU};
use glam::{DAffine2, DVec2};
use graphene_std::Color;
use graphene_std::math::quad::Quad;
Expand All @@ -33,6 +33,7 @@ pub enum OverlaysType {
HoverOutline,
SelectionOutline,
Pivot,
Origin,
Path,
Anchors,
Handles,
Expand All @@ -49,6 +50,8 @@ pub struct OverlaysVisibilitySettings {
pub hover_outline: bool,
pub selection_outline: bool,
pub pivot: bool,
#[serde(default)]
pub origin: bool,
pub path: bool,
pub anchors: bool,
pub handles: bool,
Expand All @@ -66,6 +69,7 @@ impl Default for OverlaysVisibilitySettings {
hover_outline: true,
selection_outline: true,
pivot: true,
origin: true,
path: true,
anchors: true,
handles: true,
Expand Down Expand Up @@ -110,6 +114,10 @@ impl OverlaysVisibilitySettings {
self.all && self.pivot
}

pub fn origin(&self) -> bool {
self.all && self.origin
}

pub fn path(&self) -> bool {
self.all && self.path
}
Expand Down Expand Up @@ -550,6 +558,36 @@ impl OverlayContext {
self.end_dpi_aware_transform();
}

pub fn dowel_pin(&mut self, position: DVec2, color: Option<&str>) {
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
let color = color.unwrap_or(COLOR_OVERLAY_YELLOW);

self.start_dpi_aware_transform();

// Draw the background circle with a white fill and blue outline
self.render_context.begin_path();
self.render_context.arc(x, y, DOWEL_PIN_RADIUS, 0., TAU).expect("Failed to draw the circle");
self.render_context.set_fill_style_str(COLOR_OVERLAY_WHITE);
self.render_context.fill();
self.render_context.set_stroke_style_str(color);
self.render_context.stroke();

// Draw the two blue filled sectors
self.render_context.begin_path();
// Top-left sector
self.render_context.move_to(x, y);
self.render_context.arc(x, y, DOWEL_PIN_RADIUS, FRAC_PI_2, PI).expect("Failed to draw arc");
self.render_context.close_path();
// Bottom-right sector
self.render_context.move_to(x, y);
self.render_context.arc(x, y, DOWEL_PIN_RADIUS, PI + FRAC_PI_2, TAU).expect("Failed to draw arc");
self.render_context.close_path();
self.render_context.set_fill_style_str(color);
self.render_context.fill();

self.end_dpi_aware_transform();
}

/// Used by the Pen and Path tools to outline the path of the shape.
pub fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
self.start_dpi_aware_transform();
Expand Down
18 changes: 18 additions & 0 deletions editor/src/messages/portfolio/document/utility_types/nodes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use super::network_interface::NodeNetworkInterface;
use crate::messages::tool::common_functionality::graph_modification_utils;
use glam::DVec2;
use graph_craft::document::{NodeId, NodeNetwork};
use serde::ser::SerializeStruct;

Expand Down Expand Up @@ -98,6 +100,22 @@ impl SelectedNodes {
.filter(move |&layer| self.layer_visible(layer, network_interface) && !self.layer_locked(layer, network_interface))
}

pub fn selected_visible_and_unlocked_layers_mean_average_origin<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> DVec2 {
let (sum, count) = self
.selected_visible_and_unlocked_layers(network_interface)
.map(|layer| graph_modification_utils::get_viewport_origin(layer, network_interface))
.fold((glam::DVec2::ZERO, 0), |(sum, count), item| (sum + item, count + 1));
if count == 0 { DVec2::ZERO } else { sum / count as f64 }
}

pub fn selected_visible_and_unlocked_median_points<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> DVec2 {
let (sum, count) = self
.selected_visible_and_unlocked_layers(network_interface)
.map(|layer| graph_modification_utils::get_viewport_center(layer, network_interface))
.fold((glam::DVec2::ZERO, 0), |(sum, count), item| (sum + item, count + 1));
if count == 0 { DVec2::ZERO } else { sum / count as f64 }
}

pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
metadata.all_layers().filter(|layer| self.0.contains(&layer.to_node()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::messages::portfolio::document::graph_operation::transform_utils;
use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn};
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::utility_types::ToolType;
use glam::{DAffine2, DMat2, DVec2};
Expand Down Expand Up @@ -537,17 +536,6 @@ impl<'a> Selected<'a> {
}
}

pub fn mean_average_of_pivots(&mut self) -> DVec2 {
let xy_summation = self
.selected
.iter()
.map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.network_interface))
.reduce(|a, b| a + b)
.unwrap_or_default();

xy_summation / self.selected.len() as f64
}

pub fn center_of_aabb(&mut self) -> DVec2 {
let [min, max] = self
.selected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,20 +243,25 @@ pub fn new_custom(id: NodeId, nodes: Vec<(NodeId, NodeTemplate)>, parent: LayerN
LayerNodeIdentifier::new_unchecked(id)
}

/// Locate the final pivot from the transform (TODO: decide how the pivot should actually work)
pub fn get_pivot(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> {
let pivot_node_input_index = 5;
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, network_interface).find_input("Transform", pivot_node_input_index)? {
Some(*pivot)
/// Locate the origin of the transform node
pub fn get_origin(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> {
let origin_node_input_index = 1;
if let TaggedValue::DVec2(origin) = NodeGraphLayer::new(layer, network_interface).find_input("Transform", origin_node_input_index)? {
Some(*origin)
} else {
None
}
}

pub fn get_viewport_pivot(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DVec2 {
pub fn get_viewport_origin(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DVec2 {
let origin = get_origin(layer, network_interface).unwrap_or_default();
network_interface.document_metadata().document_to_viewport.transform_point2(origin)
}

pub fn get_viewport_center(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DVec2 {
let [min, max] = network_interface.document_metadata().nonzero_bounding_box(layer);
let pivot = get_pivot(layer, network_interface).unwrap_or(DVec2::splat(0.5));
network_interface.document_metadata().transform_to_viewport(layer).transform_point2(min + (max - min) * pivot)
let center = DVec2::splat(0.5);
network_interface.document_metadata().transform_to_viewport(layer).transform_point2(min + (max - min) * center)
}

/// Get the current gradient of a layer from the closest "Fill" node.
Expand Down
Loading
Loading