Skip to content

Commit 471ef87

Browse files
committed
Add reference point input to the Mirror node
1 parent d39308c commit 471ef87

File tree

18 files changed

+387
-258
lines changed

18 files changed

+387
-258
lines changed

editor/src/messages/layout/layout_message_handler.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,13 @@ impl LayoutMessageHandler {
276276

277277
responses.add(callback_message);
278278
}
279-
Widget::PivotInput(pivot_input) => {
279+
Widget::ReferencePointInput(reference_point_input) => {
280280
let callback_message = match action {
281-
WidgetValueAction::Commit => (pivot_input.on_commit.callback)(&()),
281+
WidgetValueAction::Commit => (reference_point_input.on_commit.callback)(&()),
282282
WidgetValueAction::Update => {
283-
let update_value = value.as_str().expect("PivotInput update was not of type: u64");
284-
pivot_input.position = update_value.into();
285-
(pivot_input.on_update.callback)(pivot_input)
283+
let update_value = value.as_str().expect("ReferencePointInput update was not of type: u64");
284+
reference_point_input.value = update_value.into();
285+
(reference_point_input.on_update.callback)(reference_point_input)
286286
}
287287
};
288288

editor/src/messages/layout/utility_types/layout_widget.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ impl LayoutGroup {
373373
Widget::TextInput(x) => &mut x.tooltip,
374374
Widget::TextLabel(x) => &mut x.tooltip,
375375
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip,
376-
Widget::InvisibleStandinInput(_) | Widget::PivotInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
376+
Widget::InvisibleStandinInput(_) | Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
377377
};
378378
if val.is_empty() {
379379
val.clone_from(&tooltip);
@@ -546,7 +546,7 @@ pub enum Widget {
546546
NodeCatalog(NodeCatalog),
547547
NumberInput(NumberInput),
548548
ParameterExposeButton(ParameterExposeButton),
549-
PivotInput(PivotInput),
549+
ReferencePointInput(ReferencePointInput),
550550
PopoverButton(PopoverButton),
551551
RadioInput(RadioInput),
552552
Separator(Separator),
@@ -621,7 +621,7 @@ impl DiffUpdate {
621621
| Widget::CurveInput(_)
622622
| Widget::InvisibleStandinInput(_)
623623
| Widget::NodeCatalog(_)
624-
| Widget::PivotInput(_)
624+
| Widget::ReferencePointInput(_)
625625
| Widget::RadioInput(_)
626626
| Widget::Separator(_)
627627
| Widget::TextAreaInput(_)

editor/src/messages/layout/utility_types/widgets/input_widgets.rs

+4-86
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
22
use crate::messages::layout::utility_types::widget_prelude::*;
33
use derivative::*;
4-
use glam::DVec2;
54
use graphene_core::Color;
65
use graphene_core::raster::curve::Curve;
6+
use graphene_std::transform::ReferencePoint;
77
use graphite_proc_macros::WidgetBuilder;
88

99
#[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
@@ -411,100 +411,18 @@ pub struct CurveInput {
411411

412412
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)]
413413
#[derivative(Debug, PartialEq)]
414-
pub struct PivotInput {
414+
pub struct ReferencePointInput {
415415
#[widget_builder(constructor)]
416-
pub position: PivotPosition,
416+
pub value: ReferencePoint,
417417

418418
pub disabled: bool,
419419

420420
// Callbacks
421421
#[serde(skip)]
422422
#[derivative(Debug = "ignore", PartialEq = "ignore")]
423-
pub on_update: WidgetCallback<PivotInput>,
423+
pub on_update: WidgetCallback<ReferencePointInput>,
424424

425425
#[serde(skip)]
426426
#[derivative(Debug = "ignore", PartialEq = "ignore")]
427427
pub on_commit: WidgetCallback<()>,
428428
}
429-
430-
#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
431-
pub enum PivotPosition {
432-
#[default]
433-
None,
434-
TopLeft,
435-
TopCenter,
436-
TopRight,
437-
CenterLeft,
438-
Center,
439-
CenterRight,
440-
BottomLeft,
441-
BottomCenter,
442-
BottomRight,
443-
}
444-
445-
impl From<&str> for PivotPosition {
446-
fn from(input: &str) -> Self {
447-
match input {
448-
"None" => PivotPosition::None,
449-
"TopLeft" => PivotPosition::TopLeft,
450-
"TopCenter" => PivotPosition::TopCenter,
451-
"TopRight" => PivotPosition::TopRight,
452-
"CenterLeft" => PivotPosition::CenterLeft,
453-
"Center" => PivotPosition::Center,
454-
"CenterRight" => PivotPosition::CenterRight,
455-
"BottomLeft" => PivotPosition::BottomLeft,
456-
"BottomCenter" => PivotPosition::BottomCenter,
457-
"BottomRight" => PivotPosition::BottomRight,
458-
_ => panic!("Failed parsing unrecognized PivotPosition enum value '{input}'"),
459-
}
460-
}
461-
}
462-
463-
impl From<PivotPosition> for Option<DVec2> {
464-
fn from(input: PivotPosition) -> Self {
465-
match input {
466-
PivotPosition::None => None,
467-
PivotPosition::TopLeft => Some(DVec2::new(0., 0.)),
468-
PivotPosition::TopCenter => Some(DVec2::new(0.5, 0.)),
469-
PivotPosition::TopRight => Some(DVec2::new(1., 0.)),
470-
PivotPosition::CenterLeft => Some(DVec2::new(0., 0.5)),
471-
PivotPosition::Center => Some(DVec2::new(0.5, 0.5)),
472-
PivotPosition::CenterRight => Some(DVec2::new(1., 0.5)),
473-
PivotPosition::BottomLeft => Some(DVec2::new(0., 1.)),
474-
PivotPosition::BottomCenter => Some(DVec2::new(0.5, 1.)),
475-
PivotPosition::BottomRight => Some(DVec2::new(1., 1.)),
476-
}
477-
}
478-
}
479-
480-
impl From<DVec2> for PivotPosition {
481-
fn from(input: DVec2) -> Self {
482-
const TOLERANCE: f64 = 1e-5_f64;
483-
if input.y.abs() < TOLERANCE {
484-
if input.x.abs() < TOLERANCE {
485-
return PivotPosition::TopLeft;
486-
} else if (input.x - 0.5).abs() < TOLERANCE {
487-
return PivotPosition::TopCenter;
488-
} else if (input.x - 1.).abs() < TOLERANCE {
489-
return PivotPosition::TopRight;
490-
}
491-
} else if (input.y - 0.5).abs() < TOLERANCE {
492-
if input.x.abs() < TOLERANCE {
493-
return PivotPosition::CenterLeft;
494-
} else if (input.x - 0.5).abs() < TOLERANCE {
495-
return PivotPosition::Center;
496-
} else if (input.x - 1.).abs() < TOLERANCE {
497-
return PivotPosition::CenterRight;
498-
}
499-
} else if (input.y - 1.).abs() < TOLERANCE {
500-
if input.x.abs() < TOLERANCE {
501-
return PivotPosition::BottomLeft;
502-
} else if (input.x - 0.5).abs() < TOLERANCE {
503-
return PivotPosition::BottomCenter;
504-
} else if (input.x - 1.).abs() < TOLERANCE {
505-
return PivotPosition::BottomRight;
506-
}
507-
}
508-
PivotPosition::None
509-
}
510-
}

editor/src/messages/portfolio/document/node_graph/node_properties.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use graphene_core::vector::style::{GradientType, LineCap, LineJoin};
2323
use graphene_std::animation::RealTimeMode;
2424
use graphene_std::application_io::TextureFrameTable;
2525
use graphene_std::ops::XY;
26-
use graphene_std::transform::Footprint;
26+
use graphene_std::transform::{Footprint, ReferencePoint};
2727
use graphene_std::vector::VectorDataTable;
2828
use graphene_std::vector::misc::ArcType;
2929
use graphene_std::vector::misc::{BooleanOperation, GridType};
@@ -178,6 +178,7 @@ pub(crate) fn property_from_type(
178178
Some(x) if x == TypeId::of::<VectorDataTable>() => vector_data_widget(default_info).into(),
179179
Some(x) if x == TypeId::of::<RasterFrame>() || x == TypeId::of::<ImageFrameTable<Color>>() || x == TypeId::of::<TextureFrameTable>() => raster_widget(default_info).into(),
180180
Some(x) if x == TypeId::of::<GraphicGroupTable>() => group_widget(default_info).into(),
181+
Some(x) if x == TypeId::of::<ReferencePoint>() => reference_point_widget(default_info, false).into(),
181182
Some(x) if x == TypeId::of::<Footprint>() => footprint_widget(default_info, &mut extra_widgets),
182183
Some(x) if x == TypeId::of::<BlendMode>() => blend_mode_widget(default_info),
183184
Some(x) if x == TypeId::of::<RealTimeMode>() => real_time_mode_widget(default_info),
@@ -291,6 +292,27 @@ pub fn bool_widget(parameter_widgets_info: ParameterWidgetsInfo, checkbox_input:
291292
widgets
292293
}
293294

295+
pub fn reference_point_widget(parameter_widgets_info: ParameterWidgetsInfo, disabled: bool) -> Vec<WidgetHolder> {
296+
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
297+
298+
let mut widgets = start_widgets(parameter_widgets_info, FrontendGraphDataType::General);
299+
300+
let Some(input) = document_node.inputs.get(index) else {
301+
log::warn!("A widget failed to be built because its node's input index is invalid.");
302+
return vec![];
303+
};
304+
if let Some(&TaggedValue::ReferencePoint(reference_point)) = input.as_non_exposed_value() {
305+
widgets.extend_from_slice(&[
306+
Separator::new(SeparatorType::Unrelated).widget_holder(),
307+
ReferencePointInput::new(reference_point)
308+
.on_update(update_value(move |x: &ReferencePointInput| TaggedValue::ReferencePoint(x.value), node_id, index))
309+
.disabled(disabled)
310+
.widget_holder(),
311+
])
312+
}
313+
widgets
314+
}
315+
294316
pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widgets: &mut Vec<LayoutGroup>) -> LayoutGroup {
295317
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
296318

editor/src/messages/portfolio/portfolio_message_handler.rs

+28
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,34 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
828828
.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Bool(true), false), network_path);
829829
}
830830

831+
// Upgrade the Mirror node to add the `reference_point` input and change `offset` from `DVec2` to `f64`
832+
if reference == "Mirror" && inputs_count == 4 {
833+
let node_definition = resolve_document_node_type(reference).unwrap();
834+
let new_node_template = node_definition.default_node_template();
835+
let document_node = new_node_template.document_node;
836+
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
837+
document
838+
.network_interface
839+
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
840+
841+
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
842+
843+
let Some(&TaggedValue::DVec2(old_offset)) = old_inputs[1].as_value() else { return };
844+
let old_offset = if old_offset.x.abs() > old_offset.y.abs() { old_offset.x } else { old_offset.y };
845+
846+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
847+
document.network_interface.set_input(
848+
&InputConnector::node(*node_id, 1),
849+
NodeInput::value(TaggedValue::ReferencePoint(graphene_std::transform::ReferencePoint::Center), false),
850+
network_path,
851+
);
852+
document
853+
.network_interface
854+
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::F64(old_offset), false), network_path);
855+
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[2].clone(), network_path);
856+
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[3].clone(), network_path);
857+
}
858+
831859
// Upgrade artboard name being passed as hidden value input to "To Artboard"
832860
if reference == "Artboard" && upgrade_from_before_returning_nested_click_targets {
833861
let label = document.network_interface.display_name(node_id, network_path);

editor/src/messages/tool/common_functionality/pivot.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
33
use super::graph_modification_utils;
44
use crate::consts::PIVOT_DIAMETER;
5-
use crate::messages::layout::utility_types::widget_prelude::*;
65
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
76
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
87
use crate::messages::prelude::*;
98
use glam::{DAffine2, DVec2};
9+
use graphene_std::transform::ReferencePoint;
1010
use std::collections::VecDeque;
1111

1212
#[derive(Clone, Debug)]
@@ -18,7 +18,7 @@ pub struct Pivot {
1818
/// The viewspace pivot position (if applicable)
1919
pivot: Option<DVec2>,
2020
/// The old pivot position in the GUI, used to reduce refreshes of the document bar
21-
old_pivot_position: PivotPosition,
21+
old_pivot_position: ReferencePoint,
2222
}
2323

2424
impl Default for Pivot {
@@ -27,7 +27,7 @@ impl Default for Pivot {
2727
normalized_pivot: DVec2::splat(0.5),
2828
transform_from_normalized: Default::default(),
2929
pivot: Default::default(),
30-
old_pivot_position: PivotPosition::Center,
30+
old_pivot_position: ReferencePoint::Center,
3131
}
3232
}
3333
}
@@ -96,7 +96,7 @@ impl Pivot {
9696
should_refresh
9797
}
9898

99-
pub fn to_pivot_position(&self) -> PivotPosition {
99+
pub fn to_pivot_position(&self) -> ReferencePoint {
100100
self.normalized_pivot.into()
101101
}
102102

editor/src/messages/tool/tool_messages/select_tool.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use glam::DMat2;
2828
use graph_craft::document::NodeId;
2929
use graphene_core::renderer::Quad;
3030
use graphene_std::renderer::Rect;
31+
use graphene_std::transform::ReferencePoint;
3132
use graphene_std::vector::misc::BooleanOperation;
3233
use std::fmt;
3334

@@ -96,7 +97,7 @@ pub enum SelectToolMessage {
9697
PointerOutsideViewport(SelectToolPointerKeys),
9798
SelectOptions(SelectOptionsUpdate),
9899
SetPivot {
99-
position: PivotPosition,
100+
position: ReferencePoint,
100101
},
101102
}
102103

@@ -129,9 +130,9 @@ impl SelectTool {
129130
.widget_holder()
130131
}
131132

132-
fn pivot_widget(&self, disabled: bool) -> WidgetHolder {
133-
PivotInput::new(self.tool_data.pivot.to_pivot_position())
134-
.on_update(|pivot_input: &PivotInput| SelectToolMessage::SetPivot { position: pivot_input.position }.into())
133+
fn pivot_reference_point_widget(&self, disabled: bool) -> WidgetHolder {
134+
ReferencePointInput::new(self.tool_data.pivot.to_pivot_position())
135+
.on_update(|pivot_input: &ReferencePointInput| SelectToolMessage::SetPivot { position: pivot_input.value }.into())
135136
.disabled(disabled)
136137
.widget_holder()
137138
}
@@ -204,7 +205,7 @@ impl LayoutHolder for SelectTool {
204205

205206
// Pivot
206207
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
207-
widgets.push(self.pivot_widget(self.tool_data.selected_layers_count == 0));
208+
widgets.push(self.pivot_reference_point_widget(self.tool_data.selected_layers_count == 0));
208209

209210
// Align
210211
let disabled = self.tool_data.selected_layers_count < 2;

editor/src/node_graph_executor/runtime.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ impl NodeRuntime {
315315
return;
316316
}
317317

318-
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY);
318+
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true);
319319

320320
// Render the thumbnail from a `GraphicElement` into an SVG string
321321
let render_params = RenderParams::new(ViewMode::Normal, bounds, true, false, false);

frontend/src/components/widgets/WidgetSpan.svelte

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import DropdownInput from "@graphite/components/widgets/inputs/DropdownInput.svelte";
2020
import FontInput from "@graphite/components/widgets/inputs/FontInput.svelte";
2121
import NumberInput from "@graphite/components/widgets/inputs/NumberInput.svelte";
22-
import PivotInput from "@graphite/components/widgets/inputs/PivotInput.svelte";
2322
import RadioInput from "@graphite/components/widgets/inputs/RadioInput.svelte";
23+
import ReferencePointInput from "@graphite/components/widgets/inputs/ReferencePointInput.svelte";
2424
import TextAreaInput from "@graphite/components/widgets/inputs/TextAreaInput.svelte";
2525
import TextInput from "@graphite/components/widgets/inputs/TextInput.svelte";
2626
import WorkingColorsInput from "@graphite/components/widgets/inputs/WorkingColorsInput.svelte";
@@ -142,9 +142,9 @@
142142
incrementCallbackDecrease={() => widgetValueCommitAndUpdate(index, "Decrement")}
143143
/>
144144
{/if}
145-
{@const pivotInput = narrowWidgetProps(component.props, "PivotInput")}
146-
{#if pivotInput}
147-
<PivotInput {...exclude(pivotInput)} on:position={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
145+
{@const referencePointInput = narrowWidgetProps(component.props, "ReferencePointInput")}
146+
{#if referencePointInput}
147+
<ReferencePointInput {...exclude(referencePointInput)} on:value={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
148148
{/if}
149149
{@const popoverButton = narrowWidgetProps(component.props, "PopoverButton")}
150150
{#if popoverButton}

0 commit comments

Comments
 (0)