Skip to content

Commit fcc440e

Browse files
authored
Merge branch 'master' into segment_editing_mode
2 parents fd057a7 + 1090770 commit fcc440e

16 files changed

Lines changed: 824 additions & 158 deletions

File tree

demo-artwork/parametric-dunescape.graphite

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
951951
properties: None,
952952
},
953953
DocumentNodeDefinition {
954-
identifier: "Split Vector2",
954+
identifier: "Split Coordinate",
955955
category: "Math: Vector",
956956
node_template: NodeTemplate {
957957
document_node: DocumentNode {
@@ -982,7 +982,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
982982
..Default::default()
983983
},
984984
persistent_node_metadata: DocumentNodePersistentMetadata {
985-
input_properties: vec![("Vector2", "TODO").into()],
985+
input_properties: vec![("Coordinate", "TODO").into()],
986986
output_names: vec!["X".to_string(), "Y".to_string()],
987987
has_primary_output: false,
988988
network_metadata: Some(NodeNetworkMetadata {
@@ -2913,7 +2913,7 @@ fn static_input_properties() -> InputProperties {
29132913
.input_metadata(&node_id, index, "min", context.selection_network_path)
29142914
.and_then(|value| value.as_f64());
29152915

2916-
Ok(vec![node_properties::vector2_widget(
2916+
Ok(vec![node_properties::coordinate_widget(
29172917
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
29182918
x,
29192919
y,
@@ -3190,7 +3190,7 @@ fn static_input_properties() -> InputProperties {
31903190
Box::new(|node_id, index, context| {
31913191
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
31923192
Ok(vec![LayoutGroup::Row {
3193-
widgets: node_properties::array_of_vector2_widget(
3193+
widgets: node_properties::array_of_coordinates_widget(
31943194
ParameterWidgetsInfo::new(document_node, node_id, index, input_name, input_description, true),
31953195
TextInput::default().centered(true),
31963196
),

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

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ pub(crate) fn property_from_type(
121121
index: usize,
122122
ty: &Type,
123123
number_options: (Option<f64>, Option<f64>, Option<(f64, f64)>),
124+
unit: Option<&str>,
125+
display_decimal_places: Option<u32>,
126+
step: Option<f64>,
124127
context: &mut NodePropertiesContext,
125128
) -> Result<Vec<LayoutGroup>, Vec<LayoutGroup>> {
126129
let Some(network) = context.network_interface.nested_network(context.selection_network_path) else {
@@ -142,6 +145,15 @@ pub(crate) fn property_from_type(
142145
number_max = Some(range_end);
143146
number_input = number_input.mode_range().min(range_start).max(range_end);
144147
}
148+
if let Some(unit) = unit {
149+
number_input = number_input.unit(unit);
150+
}
151+
if let Some(display_decimal_places) = display_decimal_places {
152+
number_input = number_input.display_decimal_places(display_decimal_places);
153+
}
154+
if let Some(step) = step {
155+
number_input = number_input.step(step);
156+
}
145157

146158
let min = |x: f64| number_min.unwrap_or(x);
147159
let max = |x: f64| number_max.unwrap_or(x);
@@ -155,15 +167,15 @@ pub(crate) fn property_from_type(
155167
// Aliased types (ambiguous values)
156168
Some("Percentage") => number_widget(default_info, number_input.percentage().min(min(0.)).max(max(100.))).into(),
157169
Some("SignedPercentage") => number_widget(default_info, number_input.percentage().min(min(-100.)).max(max(100.))).into(),
158-
Some("Angle") => number_widget(default_info, number_input.mode_range().min(min(-180.)).max(max(180.)).unit("°")).into(),
159-
Some("Multiplier") => number_widget(default_info, number_input.unit("x")).into(),
160-
Some("PixelLength") => number_widget(default_info, number_input.min(min(0.)).unit(" px")).into(),
170+
Some("Angle") => number_widget(default_info, number_input.mode_range().min(min(-180.)).max(max(180.)).unit(unit.unwrap_or("°"))).into(),
171+
Some("Multiplier") => number_widget(default_info, number_input.unit(unit.unwrap_or("x"))).into(),
172+
Some("PixelLength") => number_widget(default_info, number_input.min(min(0.)).unit(unit.unwrap_or(" px"))).into(),
161173
Some("Length") => number_widget(default_info, number_input.min(min(0.))).into(),
162174
Some("Fraction") => number_widget(default_info, number_input.mode_range().min(min(0.)).max(max(1.))).into(),
163175
Some("IntegerCount") => number_widget(default_info, number_input.int().min(min(1.))).into(),
164176
Some("SeedValue") => number_widget(default_info, number_input.int().min(min(0.))).into(),
165-
Some("Resolution") => vector2_widget(default_info, "W", "H", " px", Some(64.)),
166-
Some("PixelSize") => vector2_widget(default_info, "X", "Y", " px", None),
177+
Some("Resolution") => coordinate_widget(default_info, "W", "H", unit.unwrap_or(" px"), Some(64.)),
178+
Some("PixelSize") => coordinate_widget(default_info, "X", "Y", unit.unwrap_or(" px"), None),
167179

168180
// For all other types, use TypeId-based matching
169181
_ => {
@@ -177,14 +189,14 @@ pub(crate) fn property_from_type(
177189
Some(x) if x == TypeId::of::<u64>() => number_widget(default_info, number_input.int().min(min(0.))).into(),
178190
Some(x) if x == TypeId::of::<bool>() => bool_widget(default_info, CheckboxInput::default()).into(),
179191
Some(x) if x == TypeId::of::<String>() => text_widget(default_info).into(),
180-
Some(x) if x == TypeId::of::<DVec2>() => vector2_widget(default_info, "X", "Y", "", None),
181-
Some(x) if x == TypeId::of::<UVec2>() => vector2_widget(default_info, "X", "Y", "", Some(0.)),
182-
Some(x) if x == TypeId::of::<IVec2>() => vector2_widget(default_info, "X", "Y", "", None),
192+
Some(x) if x == TypeId::of::<DVec2>() => coordinate_widget(default_info, "X", "Y", "", None),
193+
Some(x) if x == TypeId::of::<UVec2>() => coordinate_widget(default_info, "X", "Y", "", Some(0.)),
194+
Some(x) if x == TypeId::of::<IVec2>() => coordinate_widget(default_info, "X", "Y", "", None),
183195
// ==========================
184196
// PRIMITIVE COLLECTION TYPES
185197
// ==========================
186198
Some(x) if x == TypeId::of::<Vec<f64>>() => array_of_number_widget(default_info, TextInput::default()).into(),
187-
Some(x) if x == TypeId::of::<Vec<DVec2>>() => array_of_vector2_widget(default_info, TextInput::default()).into(),
199+
Some(x) if x == TypeId::of::<Vec<DVec2>>() => array_of_coordinates_widget(default_info, TextInput::default()).into(),
188200
// ====================
189201
// GRAPHICAL DATA TYPES
190202
// ====================
@@ -249,8 +261,8 @@ pub(crate) fn property_from_type(
249261
}
250262
}
251263
Type::Generic(_) => vec![TextLabel::new("Generic type (not supported)").widget_holder()].into(),
252-
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, context),
253-
Type::Future(out) => return property_from_type(node_id, index, out, number_options, context),
264+
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
265+
Type::Future(out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
254266
};
255267

256268
extra_widgets.push(widgets);
@@ -498,7 +510,7 @@ pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
498510
last.clone()
499511
}
500512

501-
pub fn vector2_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &str, unit: &str, min: Option<f64>) -> LayoutGroup {
513+
pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &str, unit: &str, min: Option<f64>) -> LayoutGroup {
502514
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
503515

504516
let mut widgets = start_widgets(parameter_widgets_info, FrontendGraphDataType::Number);
@@ -641,7 +653,7 @@ pub fn array_of_number_widget(parameter_widgets_info: ParameterWidgetsInfo, text
641653
widgets
642654
}
643655

644-
pub fn array_of_vector2_widget(parameter_widgets_info: ParameterWidgetsInfo, text_props: TextInput) -> Vec<WidgetHolder> {
656+
pub fn array_of_coordinates_widget(parameter_widgets_info: ParameterWidgetsInfo, text_props: TextInput) -> Vec<WidgetHolder> {
645657
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
646658

647659
let mut widgets = start_widgets(parameter_widgets_info, FrontendGraphDataType::Number);
@@ -1181,7 +1193,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
11811193
if let Some(&TaggedValue::GridType(grid_type)) = grid_type_input.as_non_exposed_value() {
11821194
match grid_type {
11831195
GridType::Rectangular => {
1184-
let spacing = vector2_widget(ParameterWidgetsInfo::from_index(document_node, node_id, spacing_index, true, context), "W", "H", " px", Some(0.));
1196+
let spacing = coordinate_widget(ParameterWidgetsInfo::from_index(document_node, node_id, spacing_index, true, context), "W", "H", " px", Some(0.));
11851197
widgets.push(spacing);
11861198
}
11871199
GridType::Isometric => {
@@ -1191,7 +1203,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
11911203
NumberInput::default().label("H").min(0.).unit(" px"),
11921204
),
11931205
};
1194-
let angles = vector2_widget(ParameterWidgetsInfo::from_index(document_node, node_id, angles_index, true, context), "", "", "°", None);
1206+
let angles = coordinate_widget(ParameterWidgetsInfo::from_index(document_node, node_id, angles_index, true, context), "", "", "°", None);
11951207
widgets.extend([spacing, angles]);
11961208
}
11971209
}
@@ -1395,6 +1407,9 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
13951407
};
13961408

13971409
let mut number_options = (None, None, None);
1410+
let mut display_decimal_places = None;
1411+
let mut step = None;
1412+
let mut unit_suffix = None;
13981413
let input_type = match implementation {
13991414
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => 'early_return: {
14001415
if let Some(field) = graphene_core::registry::NODE_METADATA
@@ -1404,6 +1419,9 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14041419
.and_then(|metadata| metadata.fields.get(input_index))
14051420
{
14061421
number_options = (field.number_min, field.number_max, field.number_mode_range);
1422+
display_decimal_places = field.number_display_decimal_places;
1423+
unit_suffix = field.unit;
1424+
step = field.number_step;
14071425
if let Some(ref default) = field.default_type {
14081426
break 'early_return default.clone();
14091427
}
@@ -1417,7 +1435,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14171435
let mut input_types = implementations
14181436
.keys()
14191437
.filter_map(|item| item.inputs.get(input_index))
1420-
.filter(|ty| property_from_type(node_id, input_index, ty, number_options, context).is_ok())
1438+
.filter(|ty| property_from_type(node_id, input_index, ty, number_options, unit_suffix, display_decimal_places, step, context).is_ok())
14211439
.collect::<Vec<_>>();
14221440
input_types.sort_by_key(|ty| ty.type_name());
14231441
let input_type = input_types.first().cloned();
@@ -1431,7 +1449,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14311449
_ => context.network_interface.input_type(&InputConnector::node(node_id, input_index), context.selection_network_path).0,
14321450
};
14331451

1434-
property_from_type(node_id, input_index, &input_type, number_options, context).unwrap_or_else(|value| value)
1452+
property_from_type(node_id, input_index, &input_type, number_options, unit_suffix, display_decimal_places, step, context).unwrap_or_else(|value| value)
14351453
});
14361454

14371455
layout.extend(row);

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,15 +467,16 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
467467
}
468468
};
469469

470-
const REPLACEMENTS: [(&str, &str); 37] = [
470+
const REPLACEMENTS: [(&str, &str); 40] = [
471471
("graphene_core::AddArtboardNode", "graphene_core::graphic_element::AppendArtboardNode"),
472472
("graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"),
473473
("graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"),
474474
("graphene_core::ToGraphicGroupNode", "graphene_core::graphic_element::ToGroupNode"),
475475
("graphene_core::logic::LogicAndNode", "graphene_core::ops::LogicAndNode"),
476476
("graphene_core::logic::LogicNotNode", "graphene_core::ops::LogicNotNode"),
477477
("graphene_core::logic::LogicOrNode", "graphene_core::ops::LogicOrNode"),
478-
("graphene_core::ops::ConstructVector2", "graphene_core::ops::Vector2ValueNode"),
478+
("graphene_core::ops::ConstructVector2", "graphene_core::ops::CoordinateValueNode"),
479+
("graphene_core::ops::Vector2ValueNode", "graphene_core::ops::CoordinateValueNode"),
479480
("graphene_core::raster::BlackAndWhiteNode", "graphene_core::raster::adjustments::BlackAndWhiteNode"),
480481
("graphene_core::raster::BlendNode", "graphene_core::raster::adjustments::BlendNode"),
481482
("graphene_core::raster::ChannelMixerNode", "graphene_core::raster::adjustments::ChannelMixerNode"),
@@ -484,6 +485,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
484485
("graphene_core::raster::ExtractChannelNode", "graphene_core::raster::adjustments::ExtractChannelNode"),
485486
("graphene_core::raster::GradientMapNode", "graphene_core::raster::adjustments::GradientMapNode"),
486487
("graphene_core::raster::HueSaturationNode", "graphene_core::raster::adjustments::HueSaturationNode"),
488+
("graphene_core::vector::GenerateHandlesNode", "graphene_core::vector::AutoTangentsNode"),
489+
("graphene_core::vector::RemoveHandlesNode", "graphene_core::vector::AutoTangentsNode"),
487490
("graphene_core::raster::InvertNode", "graphene_core::raster::adjustments::InvertNode"),
488491
("graphene_core::raster::InvertRGBNode", "graphene_core::raster::adjustments::InvertNode"),
489492
("graphene_core::raster::LevelsNode", "graphene_core::raster::adjustments::LevelsNode"),
@@ -963,6 +966,48 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
963966

964967
document.network_interface.replace_reference_name(node_id, network_path, "Flatten Path".to_string());
965968
}
969+
970+
if reference == "Remove Handles" {
971+
let node_definition = resolve_document_node_type("Auto-Tangents").unwrap();
972+
let new_node_template = node_definition.default_node_template();
973+
let document_node = new_node_template.document_node;
974+
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
975+
document
976+
.network_interface
977+
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
978+
979+
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
980+
981+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
982+
document
983+
.network_interface
984+
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::F64(0.), false), network_path);
985+
document
986+
.network_interface
987+
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path);
988+
989+
document.network_interface.replace_reference_name(node_id, network_path, "Auto-Tangents".to_string());
990+
}
991+
992+
if reference == "Generate Handles" {
993+
let node_definition = resolve_document_node_type("Auto-Tangents").unwrap();
994+
let new_node_template = node_definition.default_node_template();
995+
let document_node = new_node_template.document_node;
996+
document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone());
997+
document
998+
.network_interface
999+
.replace_implementation_metadata(node_id, network_path, new_node_template.persistent_node_metadata);
1000+
1001+
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path);
1002+
1003+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
1004+
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
1005+
document
1006+
.network_interface
1007+
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(true), false), network_path);
1008+
1009+
document.network_interface.replace_reference_name(node_id, network_path, "Auto-Tangents".to_string());
1010+
}
9661011
}
9671012

9681013
// TODO: Eventually remove this document upgrade code

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
5757
}
5858

5959
// Move the `second_layer` below the `first_layer` for positioning purposes
60-
let first_layer_parent = first_layer.parent(document.metadata()).unwrap();
61-
let first_layer_index = first_layer_parent.children(document.metadata()).position(|child| child == first_layer).unwrap();
60+
let Some(first_layer_parent) = first_layer.parent(document.metadata()) else { return };
61+
let Some(first_layer_index) = first_layer_parent.children(document.metadata()).position(|child| child == first_layer) else {
62+
return;
63+
};
6264
responses.add(NodeGraphMessage::MoveLayerToStack {
6365
layer: second_layer,
6466
parent: first_layer_parent,

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,7 @@ impl SelectedLayerState {
129129
}
130130

131131
pub fn selected_points_count(&self) -> usize {
132-
let count = self.selected_points.iter().fold(0, |acc, point| {
133-
let is_ignored = (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors);
134-
acc + if is_ignored { 0 } else { 1 }
135-
});
136-
count
132+
self.selected_points.len()
137133
}
138134
}
139135

@@ -194,7 +190,11 @@ impl ClosestSegment {
194190

195191
pub fn bezier(&self) -> Bezier {
196192
self.bezier
197-
}
193+
}
194+
195+
pub fn closest_point_document(&self) -> DVec2 {
196+
self.bezier.evaluate(TValue::Parametric(self.t))
197+
}
198198

199199
pub fn closest_point_to_viewport(&self) -> DVec2 {
200200
self.bezier_point_to_viewport
@@ -241,7 +241,7 @@ impl ClosestSegment {
241241
(first_handle, second_handle)
242242
}
243243

244-
pub fn adjusted_insert(&self, responses: &mut VecDeque<Message>) -> PointId {
244+
pub fn adjusted_insert(&self, responses: &mut VecDeque<Message>) -> (PointId, [SegmentId; 2]) {
245245
let layer = self.layer;
246246
let [first, second] = self.bezier.split(TValue::Parametric(self.t));
247247

@@ -286,11 +286,11 @@ impl ClosestSegment {
286286
responses.add(GraphOperationMessage::Vector { layer, modification_type });
287287
}
288288

289-
midpoint
289+
(midpoint, segment_ids)
290290
}
291291

292292
pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, extend_selection: bool) {
293-
let id = self.adjusted_insert(responses);
293+
let (id, _) = self.adjusted_insert(responses);
294294
shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection)
295295
}
296296

0 commit comments

Comments
 (0)