Rename graphic subtypes to remove their "data" and "group" suffixes (#2990)

* Rename VectorData to Vector

* Rename other VectorData* types to Vector*

* Move assorted data types out of vector_data.rs into misc.rs

* Rename vector_data.rs to vector_types.rs and remove the vector_types module folder

* Rename other references to "vector data"

* Remove label widgets for raster/vector/group to use "-" instead

* Rename RasterData to Raster

* Rename GraphicGroup to Group

* Fix migrations and rename graphic_element.rs -> graphic.rs

* Rename TaggedValue::ArtboardGroup -> TaggedValue::Artboard
This commit is contained in:
Keavon Chambers
2025-08-04 04:53:25 -07:00
committed by GitHub
parent 853c26cbc1
commit c98477d8ed
72 changed files with 1820 additions and 1901 deletions
@@ -692,7 +692,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
});
if layer_to_move.parent(self.metadata()) != Some(parent) {
// TODO: Fix this so it works when dragging a layer into a group parent which has a Transform node, which used to work before #2689 caused this regression by removing the empty VectorData table row.
// TODO: Fix this so it works when dragging a layer into a group parent which has a Transform node, which used to work before #2689 caused this regression by removing the empty vector table row.
// TODO: See #2688 for this issue.
let layer_local_transform = self.network_interface.document_metadata().transform_to_viewport(layer_to_move);
let undo_transform = self.network_interface.document_metadata().transform_to_viewport(parent).inverse();
@@ -2907,7 +2907,7 @@ impl DocumentMessageHandler {
/// Create a network interface with a single export
fn default_document_network_interface() -> NodeNetworkInterface {
let mut network_interface = NodeNetworkInterface::default();
network_interface.add_export(TaggedValue::ArtboardGroup(Default::default()), -1, "", &[]);
network_interface.add_export(TaggedValue::Artboard(Default::default()), -1, "", &[]);
network_interface
}
@@ -174,7 +174,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
let layer = modify_inputs.create_layer(id);
modify_inputs.insert_vector_data(subpaths, layer, true, true, true);
modify_inputs.insert_vector(subpaths, layer, true, true, true);
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
responses.add(NodeGraphMessage::RunDocumentGraph);
}
@@ -349,7 +349,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
let subpaths = convert_usvg_path(path);
let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box()).reduce(Quad::combine_bounds).unwrap_or_default();
modify_inputs.insert_vector_data(subpaths, layer, true, path.fill().is_some(), path.stroke().is_some());
modify_inputs.insert_vector(subpaths, layer, true, path.fill().is_some(), path.stroke().is_some());
if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) {
transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, transform * usvg_transform(node.abs_transform()));
@@ -14,7 +14,7 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, Raster};
use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::vector::style::{Fill, Stroke};
use graphene_std::vector::{PointId, VectorModificationType};
use graphene_std::{Graphic, NodeInputDecleration};
@@ -131,8 +131,8 @@ impl<'a> ModifyInputsContext<'a> {
/// Creates an artboard as the primary export for the document network
pub fn create_artboard(&mut self, new_id: NodeId, artboard: Artboard) -> LayerNodeIdentifier {
let artboard_node_template = resolve_document_node_type("Artboard").expect("Node").node_template_input_override([
Some(NodeInput::value(TaggedValue::ArtboardGroup(Default::default()), true)),
Some(NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true)),
Some(NodeInput::value(TaggedValue::Artboard(Default::default()), true)),
Some(NodeInput::value(TaggedValue::Group(Default::default()), true)),
Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)),
Some(NodeInput::value(TaggedValue::DVec2(artboard.dimensions.into()), false)),
Some(NodeInput::value(TaggedValue::Color(artboard.background), false)),
@@ -144,7 +144,7 @@ impl<'a> ModifyInputsContext<'a> {
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
let boolean = resolve_document_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([
Some(NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true)),
Some(NodeInput::value(TaggedValue::Group(Default::default()), true)),
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
]);
@@ -153,12 +153,12 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.move_node_to_chain_start(&boolean_id, layer, &[]);
}
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier, include_transform: bool, include_fill: bool, include_stroke: bool) {
let vector_data = Table::new_from_element(VectorData::from_subpaths(subpaths, true));
pub fn insert_vector(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier, include_transform: bool, include_fill: bool, include_stroke: bool) {
let vector = Table::new_from_element(Vector::from_subpaths(subpaths, true));
let shape = resolve_document_node_type("Path")
.expect("Path node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::VectorData(vector_data), false))]);
.node_template_input_override([Some(NodeInput::value(TaggedValue::Vector(vector), false))]);
let shape_id = NodeId::new();
self.network_interface.insert_node(shape_id, shape, &[]);
self.network_interface.move_node_to_chain_start(&shape_id, layer, &[]);
@@ -223,7 +223,7 @@ impl<'a> ModifyInputsContext<'a> {
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let image = resolve_document_node_type("Image Value")
.expect("ImageValue node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::RasterData(image_frame), false))]);
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Raster(image_frame), false))]);
let image_id = NodeId::new();
self.network_interface.insert_node(image_id, image, &[]);
@@ -296,11 +296,12 @@ impl<'a> ModifyInputsContext<'a> {
pub fn create_node(&mut self, reference: &str) -> Option<NodeId> {
let output_layer = self.get_output_layer()?;
let Some(node_definition) = resolve_document_node_type(reference) else {
log::error!("Node type {} does not exist in ModifyInputsContext::existing_node_id", reference);
log::error!("Node type {reference} does not exist in ModifyInputsContext::existing_node_id");
return None;
};
// If inserting a path node, insert a Flatten Path if the type is a graphic group.
// TODO: Allow the path node to operate on Graphic Group data by utilizing the reference for each vector data in a group.
// If inserting a path node, insert a Flatten Path if the type is Group.
// TODO: Allow the path node to operate on Group data by utilizing the reference for each Vector in a group.
if node_definition.identifier == "Path" {
let layer_input_type = self.network_interface.input_type(&InputConnector::node(output_layer.to_node(), 1), &[]).0.nested_type().clone();
if layer_input_type == concrete!(Table<Graphic>) {
@@ -24,7 +24,7 @@ use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
#[allow(unused_imports)]
use graphene_std::transform::Footprint;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::*;
use std::collections::{HashMap, HashSet, VecDeque};
@@ -232,14 +232,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
// Secondary (left) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 1)],
implementation: DocumentNodeImplementation::ProtoNode(graphic_element::to_element::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_element::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
// Primary (bottom) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic_element::to_group::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_group::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
..Default::default()
},
@@ -258,7 +258,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::node(NodeId(2), 0),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
implementation: DocumentNodeImplementation::ProtoNode(graphic_element::layer::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(graphic::layer::IDENTIFIER),
..Default::default()
},
]
@@ -269,8 +269,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true),
NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true),
NodeInput::value(TaggedValue::Group(Default::default()), true),
NodeInput::value(TaggedValue::Group(Default::default()), true),
],
..Default::default()
},
@@ -377,8 +377,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::ArtboardGroup(Default::default()), true),
NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true),
NodeInput::value(TaggedValue::Artboard(Default::default()), true),
NodeInput::value(TaggedValue::Group(Default::default()), true),
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false),
NodeInput::value(TaggedValue::Color(Color::WHITE), false),
@@ -628,7 +628,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::VectorData(Default::default()), true),
NodeInput::value(TaggedValue::Vector(Default::default()), true),
NodeInput::value(
TaggedValue::Footprint(Footprint {
transform: DAffine2::from_scale_angle_translation(DVec2::new(1000., 1000.), 0., DVec2::new(0., 0.)),
@@ -682,7 +682,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
},
description: Cow::Borrowed("Rasterizes the given vector data"),
description: Cow::Borrowed("TODO"),
properties: None,
},
DocumentNodeDefinition {
@@ -794,7 +794,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::RasterData(Default::default()), true)],
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -879,7 +879,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::RasterData(Default::default()), true)],
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -947,7 +947,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::RasterData(Default::default()), true),
NodeInput::value(TaggedValue::Raster(Default::default()), true),
NodeInput::value(TaggedValue::BrushStrokes(Vec::new()), false),
NodeInput::value(TaggedValue::BrushCache(BrushCache::default()), false),
],
@@ -986,7 +986,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::RasterData(Default::default()), true)],
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
..Default::default()
},
@@ -1005,7 +1005,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::RasterData(Default::default()), true)],
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
..Default::default()
},
@@ -1117,7 +1117,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::RasterData(Default::default()), true)],
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -1196,7 +1196,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
// document_node: DocumentNode {
// implementation: DocumentNodeImplementation::proto("graphene_core::raster::CurvesNode"),
// inputs: vec![
// NodeInput::value(TaggedValue::RasterData(Default::default()), true),
// NodeInput::value(TaggedValue::Raster(Default::default()), true),
// NodeInput::value(TaggedValue::Curve(Default::default()), false),
// ],
// ..Default::default()
@@ -1219,7 +1219,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
exports: vec![NodeInput::node(NodeId(1), 0)],
nodes: vec![
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<VectorData>), 0)],
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
skip_deduplication: true,
@@ -1243,14 +1243,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::VectorData(Default::default()), true),
NodeInput::value(TaggedValue::Vector(Default::default()), true),
NodeInput::value(TaggedValue::VectorModification(Default::default()), false),
],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Vector Data", "TODO").into(), ("Modification", "TODO").into()],
output_names: vec!["Vector Data".to_string()],
input_metadata: vec![("Content", "TODO").into(), ("Modification", "TODO").into()],
output_names: vec!["Modified".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
node_metadata: [
@@ -1374,7 +1374,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}),
),
InputMetadata::with_name_description_override("Align", "TODO", WidgetOverride::Custom("text_align".to_string())),
("Per-Glyph Instances", "Splits each text glyph into its own row in the table of vector data.").into(),
("Per-Glyph Instances", "Splits each text glyph into its own row in the table of vector geometry.").into(),
],
output_names: vec!["Vector".to_string()],
..Default::default()
@@ -1496,7 +1496,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
exports: vec![NodeInput::node(NodeId(3), 0)],
nodes: vec![
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<VectorData>), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
manual_composition: Some(generic!(T)),
..Default::default()
@@ -1527,7 +1527,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true),
NodeInput::value(TaggedValue::Group(Default::default()), true),
NodeInput::value(TaggedValue::BooleanOperation(path_bool::BooleanOperation::Union), false),
],
..Default::default()
@@ -1594,14 +1594,14 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
exports: vec![NodeInput::node(NodeId(4), 0)],
nodes: [
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<VectorData>), 0)],
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(vector::subpath_segment_lengths::IDENTIFIER),
manual_composition: Some(generic!(T)),
..Default::default()
},
DocumentNode {
inputs: vec![
NodeInput::network(concrete!(Table<VectorData>), 0),
NodeInput::network(concrete!(Table<Vector>), 0),
NodeInput::network(concrete!(vector::misc::PointSpacingType), 1),
NodeInput::network(concrete!(f64), 2),
NodeInput::network(concrete!(u32), 3),
@@ -1640,7 +1640,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::VectorData(Default::default()), true),
NodeInput::value(TaggedValue::Vector(Default::default()), true),
NodeInput::value(TaggedValue::PointSpacingType(Default::default()), false),
NodeInput::value(TaggedValue::F64(100.), false),
NodeInput::value(TaggedValue::U32(100), false),
@@ -1704,7 +1704,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
input_metadata: vec![
("Vector Data", "The shape to be resampled and converted into a polyline.").into(),
("Content", "The shape to be resampled and converted into a polyline.").into(),
("Spacing", node_properties::SAMPLE_POLYLINE_TOOLTIP_SPACING).into(),
InputMetadata::with_name_description_override(
"Separation",
@@ -1761,7 +1761,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
DocumentNode {
inputs: vec![
NodeInput::network(concrete!(Table<VectorData>), 0),
NodeInput::network(concrete!(Table<Vector>), 0),
NodeInput::network(concrete!(f64), 1),
NodeInput::network(concrete!(u32), 2),
],
@@ -1795,7 +1795,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::VectorData(Default::default()), true),
NodeInput::value(TaggedValue::Vector(Default::default()), true),
NodeInput::value(TaggedValue::F64(10.), false),
NodeInput::value(TaggedValue::U32(0), false),
],
@@ -1847,7 +1847,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
input_metadata: vec![
("Vector Data", "TODO").into(),
("Content", "TODO").into(),
InputMetadata::with_name_description_override(
"Separation Disk Diameter",
"TODO",
@@ -11,6 +11,7 @@ use glam::{DAffine2, DVec2};
use graph_craft::Type;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
use graphene_std::NodeInputDecleration;
use graphene_std::animation::RealTimeMode;
use graphene_std::extract_xy::XY;
use graphene_std::path_bool::BooleanOperation;
@@ -19,14 +20,10 @@ use graphene_std::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute,
SelectiveColorChoice,
};
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::table::Table;
use graphene_std::text::{Font, TextAlign};
use graphene_std::transform::{Footprint, ReferencePoint, Transform};
use graphene_std::vector::VectorData;
use graphene_std::vector::misc::{ArcType, CentroidType, GridType, MergeByDistanceAlgorithm, PointSpacingType};
use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
use graphene_std::{Graphic, NodeInputDecleration};
pub(crate) fn string_properties(text: &str) -> Vec<LayoutGroup> {
let widget = TextLabel::new(text).widget_holder();
@@ -180,12 +177,6 @@ pub(crate) fn property_from_type(
// ==========================
Some(x) if x == TypeId::of::<Vec<f64>>() => array_of_number_widget(default_info, TextInput::default()).into(),
Some(x) if x == TypeId::of::<Vec<DVec2>>() => array_of_vec2_widget(default_info, TextInput::default()).into(),
// ====================
// GRAPHICAL DATA TYPES
// ====================
Some(x) if x == TypeId::of::<Table<VectorData>>() => vector_data_widget(default_info).into(),
Some(x) if x == TypeId::of::<Table<Raster<CPU>>>() || x == TypeId::of::<Table<Raster<GPU>>>() => raster_widget(default_info).into(),
Some(x) if x == TypeId::of::<Table<Graphic>>() => group_widget(default_info).into(),
// ============
// STRUCT TYPES
// ============
@@ -794,33 +785,6 @@ pub fn font_inputs(parameter_widgets_info: ParameterWidgetsInfo) -> (Vec<WidgetH
(first_widgets, second_widgets)
}
pub fn vector_data_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(parameter_widgets_info);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Vector data is supplied through the node graph").widget_holder());
widgets
}
pub fn raster_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(parameter_widgets_info);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Raster data is supplied through the node graph").widget_holder());
widgets
}
pub fn group_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(parameter_widgets_info);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Group data is supplied through the node graph").widget_holder());
widgets
}
pub fn number_widget(parameter_widgets_info: ParameterWidgetsInfo, number_props: NumberInput) -> Vec<WidgetHolder> {
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
@@ -1880,7 +1844,7 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
let mut expression = x.value.trim().to_string();
if ["+", "-", "*", "/", "^", "%"].iter().any(|&infix| infix == expression) {
expression = format!("A {} B", expression);
expression = format!("A {expression} B");
} else if expression == "^" {
expression = String::from("A^B");
}
@@ -2078,7 +2042,7 @@ pub mod choice {
pub fn property_row(self) -> LayoutGroup {
let ParameterWidgetsInfo { document_node, node_id, index, .. } = self.parameter_info;
let Some(document_node) = document_node else {
log::error!("Could not get document node when building property row for node {:?}", node_id);
log::error!("Could not get document node when building property row for node {node_id:?}");
return LayoutGroup::Row { widgets: Vec::new() };
};
@@ -9,7 +9,7 @@ pub enum FrontendGraphDataType {
#[default]
General,
Raster,
VectorData,
Vector,
Number,
Group,
Artboard,
@@ -18,8 +18,8 @@ pub enum FrontendGraphDataType {
impl FrontendGraphDataType {
pub fn from_type(input: &Type) -> Self {
match TaggedValue::from_type_or_none(input) {
TaggedValue::RasterData(_) => Self::Raster,
TaggedValue::VectorData(_) => Self::VectorData,
TaggedValue::Raster(_) => Self::Raster,
TaggedValue::Vector(_) => Self::Vector,
TaggedValue::U32(_)
| TaggedValue::U64(_)
| TaggedValue::F64(_)
@@ -28,8 +28,8 @@ impl FrontendGraphDataType {
| TaggedValue::VecF64(_)
| TaggedValue::VecDVec2(_)
| TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::GraphicGroup(_) => Self::Group,
TaggedValue::ArtboardGroup(_) => Self::Artboard,
TaggedValue::Group(_) => Self::Group,
TaggedValue::Artboard(_) => Self::Artboard,
_ => Self::General,
}
}
@@ -5,7 +5,7 @@ use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerSta
use crate::messages::tool::tool_messages::tool_prelude::{DocumentMessageHandler, PreferencesMessageHandler};
use bezier_rs::{Bezier, BezierHandles};
use glam::{DAffine2, DVec2};
use graphene_std::vector::ManipulatorPointId;
use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::{PointId, SegmentId};
use wasm_bindgen::JsCast;
@@ -42,9 +42,9 @@ pub fn selected_segments(network_interface: &NodeNetworkInterface, shape_editor:
// TODO: Currently if there are two duplicate layers, both of their segments get overlays
// Adding segments which are are connected to selected anchors
for layer in network_interface.selected_nodes().selected_layers(network_interface.document_metadata()) {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for (segment_id, _bezier, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
if selected_anchors.contains(&start) || selected_anchors.contains(&end) {
selected_segments.push(segment_id);
}
@@ -118,14 +118,14 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
let display_anchors = overlay_context.visibility_settings.anchors();
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
if display_path {
overlay_context.outline_vector(&vector_data, transform);
overlay_context.outline_vector(&vector, transform);
}
// Get the selected segments and then add a bold line overlay on them
for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() {
for (segment_id, bezier, _, _) in vector.segment_bezier_iter() {
let Some(selected_shape_state) = shape_editor.selected_shape_state.get_mut(&layer) else {
continue;
};
@@ -139,30 +139,30 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
let is_selected = |point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
if display_handles {
let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector_data.adjacent_segment(point_id)).collect();
let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector.adjacent_segment(point_id)).collect();
match draw_handles {
DrawHandles::All => {
vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
vector.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
});
}
DrawHandles::SelectedAnchors(ref selected_segments) => {
vector_data
vector
.segment_bezier_iter()
.filter(|(segment_id, ..)| selected_segments.contains(segment_id))
.for_each(|(segment_id, bezier, _start, _end)| {
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
});
for (segment_id, bezier, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, bezier, start, end) in vector.segment_bezier_iter() {
if let Some((corresponding_anchor, _)) = opposite_handles_data.iter().find(|(_, adj_segment_id)| adj_segment_id == &segment_id) {
overlay_bezier_handle_specific_point(bezier, segment_id, (start, end), *corresponding_anchor, transform, is_selected, overlay_context);
}
}
}
DrawHandles::FrontierHandles(ref segment_endpoints) => {
vector_data
vector
.segment_bezier_iter()
.filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id))
.for_each(|(segment_id, bezier, start, end)| {
@@ -179,7 +179,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
}
if display_anchors {
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &position) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(ManipulatorPointId::Anchor(id)), None);
}
}
@@ -192,7 +192,7 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
}
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
@@ -200,8 +200,8 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
let selected = shape_editor.selected_shape_state.get(&layer);
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
for point in vector_data.extendable_points(preferences.vector_meshes) {
let Some(position) = vector_data.point_domain.position_from_id(point) else { continue };
for point in vector.extendable_points(preferences.vector_meshes) {
let Some(position) = vector.point_domain.position_from_id(point) else { continue };
let position = transform.transform_point2(position);
overlay_context.manipulator_anchor(position, is_selected(selected, ManipulatorPointId::Anchor(point)), None);
}
@@ -12,7 +12,7 @@ use glam::{DAffine2, DVec2};
use graphene_std::Color;
use graphene_std::math::quad::Quad;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::{PointId, SegmentId, VectorData};
use graphene_std::vector::{PointId, SegmentId, Vector};
use std::collections::HashMap;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{OffscreenCanvas, OffscreenCanvasRenderingContext2d};
@@ -757,12 +757,12 @@ impl OverlayContext {
}
/// 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) {
pub fn outline_vector(&mut self, vector: &Vector, transform: DAffine2) {
self.start_dpi_aware_transform();
self.render_context.begin_path();
let mut last_point = None;
for (_, bezier, start_id, end_id) in vector_data.segment_bezier_iter() {
for (_, bezier, start_id, end_id) in vector.segment_bezier_iter() {
let move_to = last_point != Some(start_id);
last_point = Some(end_id);
@@ -11,7 +11,7 @@ use glam::{DAffine2, DVec2};
use graphene_std::Color;
use graphene_std::math::quad::Quad;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::{PointId, SegmentId, VectorData};
use graphene_std::vector::{PointId, SegmentId, Vector};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use vello::Scene;
@@ -338,8 +338,8 @@ impl OverlayContext {
}
/// 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.internal().outline_vector(vector_data, transform);
pub fn outline_vector(&mut self, vector: &Vector, transform: DAffine2) {
self.internal().outline_vector(vector, transform);
}
/// Used by the Pen tool in order to show how the bezier curve would look like.
@@ -834,12 +834,12 @@ impl OverlayContextInternal {
}
/// Used by the Pen and Path tools to outline the path of the shape.
fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
fn outline_vector(&mut self, vector: &Vector, transform: DAffine2) {
let vello_transform = self.get_transform();
let mut path = BezPath::new();
let mut last_point = None;
for (_, bezier, start_id, end_id) in vector_data.segment_bezier_iter() {
for (_, bezier, start_id, end_id) in vector.segment_bezier_iter() {
let move_to = last_point != Some(start_id);
last_point = Some(end_id);
@@ -8,7 +8,7 @@ use graph_craft::document::NodeId;
use graphene_std::math::quad::Quad;
use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, VectorData};
use graphene_std::vector::{PointId, Vector};
use std::collections::{HashMap, HashSet};
use std::num::NonZeroU64;
@@ -26,7 +26,7 @@ pub struct DocumentMetadata {
pub structure: HashMap<LayerNodeIdentifier, NodeRelations>,
pub click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
pub clip_targets: HashSet<NodeId>,
pub vector_modify: HashMap<NodeId, VectorData>,
pub vector_modify: HashMap<NodeId, Vector>,
/// Transform from document space to viewport space.
pub document_to_viewport: DAffine2,
}
@@ -18,7 +18,7 @@ use graphene_std::math::quad::Quad;
use graphene_std::table::Table;
use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::{PointId, VectorData, VectorModificationType};
use graphene_std::vector::{PointId, Vector, VectorModificationType};
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
use interpreted_executor::node_registry::NODE_REGISTRY;
use serde_json::{Value, json};
@@ -675,7 +675,7 @@ impl NodeNetworkInterface {
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
let input_type = self.input_type(&InputConnector::node(*node_id, iterator_index), network_path).0;
// Value inputs are stored as concrete, so they are compared to the nested type. Node inputs are stored as fn, so they are compared to the entire type.
// For example a node input of (Footprint) -> VectorData would not be compatible with () -> VectorData
// For example a node input of (Footprint) -> Vector would not be compatible with () -> Vector
node_io.inputs.get(iterator_index).map(|ty| ty.nested_type().clone()).as_ref() == Some(&input_type) || node_io.inputs.get(iterator_index) == Some(&input_type)
});
if valid_implementation { node_io.inputs.get(*input_index).cloned() } else { None }
@@ -3444,12 +3444,12 @@ impl NodeNetworkInterface {
(layer_widths, chain_widths, has_left_input_wire)
}
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<VectorData> {
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path") {
if let Some(vector_data) = self.document_metadata.vector_modify.get(&path_node) {
let mut modified = vector_data.clone();
if let Some(vector) = self.document_metadata.vector_modify.get(&path_node) {
let mut modified = vector.clone();
let path_node = self.document_network().nodes.get(&path_node);
let modification_input = path_node.and_then(|node: &DocumentNode| node.inputs.get(1)).and_then(|input| input.as_value());
@@ -3464,7 +3464,7 @@ impl NodeNetworkInterface {
.click_targets
.get(&layer)
.map(|click| click.iter().map(ClickTarget::target_type))
.map(|target_types| VectorData::from_target_types(target_types, true))
.map(|target_types| Vector::from_target_types(target_types, true))
}
/// Loads the structure of layer nodes from a node graph.
@@ -3575,7 +3575,7 @@ impl NodeNetworkInterface {
}
/// Update the vector modify of the layers
pub fn update_vector_modify(&mut self, new_vector_modify: HashMap<NodeId, VectorData>) {
pub fn update_vector_modify(&mut self, new_vector_modify: HashMap<NodeId, Vector>) {
self.document_metadata.vector_modify = new_vector_modify;
}
}
@@ -6613,22 +6613,26 @@ fn migrate_output_names<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
const REPLACEMENTS: &[(&str, &str)] = &[
// Single to table data
("VectorData", "Table<VectorData>"),
("GraphicGroup", "Table<GraphicGroup>"),
("VectorData", "Table<Vector>"),
("GraphicGroup", "Table<Group>"),
("ImageFrame", "Table<Image>"),
// `ImageFrame` to `Image` rename
("Instances<ImageFrame>", "Table<Image>"),
// `Instances` to `Table` rename
("Instances<VectorData>", "Table<VectorData>"),
("Instances<GraphicGroup>", "Table<GraphicGroup>"),
("Instances<VectorData>", "Table<Vector>"),
("Instances<GraphicGroup>", "Table<Group>"),
("Instances<Image>", "Table<Image>"),
("Instances<GraphicElement>", "Table<Graphic>"),
("Table<GraphicElement>", "Table<Graphic>"),
("Future<Instances<VectorData>>", "Future<Table<VectorData>>"),
("Future<Instances<GraphicGroup>>", "Future<Table<GraphicGroup>>"),
("Future<Instances<Vector>>", "Future<Table<Vector>>"),
("Future<Instances<GraphicGroup>>", "Future<Table<Group>>"),
("Future<Instances<Image>>", "Future<Table<Image>>"),
("Future<Instances<GraphicElement>>", "Future<Table<Graphic>>"),
("Future<Table<GraphicElement>>", "Future<Table<Graphic>>"),
("Future<Table<VectorData>>", "Future<Table<Vector>>"),
("Table<VectorData>", "Table<Vector>"),
("Table<GraphicGroup>", "Table<Group>"),
("Future<Table<GraphicGroup>>", "Future<Table<Group>>"),
];
let mut names = Vec::<String>::deserialize(deserializer)?;
@@ -8,7 +8,8 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::utility_types::ToolType;
use glam::{DAffine2, DMat2, DVec2};
use graphene_std::renderer::Quad;
use graphene_std::vector::{HandleExt, HandleId, ManipulatorPointId, PointId, VectorModificationType};
use graphene_std::vector::misc::{HandleId, ManipulatorPointId};
use graphene_std::vector::{HandleExt, PointId, VectorModificationType};
use std::collections::{HashMap, VecDeque};
use std::f64::consts::PI;
@@ -79,7 +80,7 @@ impl OriginalTransforms {
if path_map.contains_key(&layer) {
continue;
}
let Some(vector_data) = network_interface.compute_modified_vector(layer) else {
let Some(vector) = network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(selected_points) = shape_editor.selected_points_in_layer(layer) else {
@@ -91,7 +92,7 @@ impl OriginalTransforms {
let mut selected_points = selected_points.clone();
for (segment_id, _, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
if selected_segments.contains(&segment_id) {
selected_points.insert(ManipulatorPointId::Anchor(start));
selected_points.insert(ManipulatorPointId::Anchor(end));
@@ -100,23 +101,23 @@ impl OriginalTransforms {
// Anchors also move their handles
let anchor_ids = selected_points.iter().filter_map(|point| point.as_anchor());
let anchors = anchor_ids.filter_map(|id| vector_data.point_domain.position_from_id(id).map(|pos| (id, AnchorPoint { initial: pos, current: pos })));
let anchors = anchor_ids.filter_map(|id| vector.point_domain.position_from_id(id).map(|pos| (id, AnchorPoint { initial: pos, current: pos })));
let anchors = anchors.collect();
let selected_handles = selected_points.iter().filter_map(|point| point.as_handle());
let anchor_ids = selected_points.iter().filter_map(|point| point.as_anchor());
let connected_handles = anchor_ids.flat_map(|point| vector_data.all_connected(point));
let connected_handles = anchor_ids.flat_map(|point| vector.all_connected(point));
let all_handles = selected_handles.chain(connected_handles);
let handles = all_handles
.filter_map(|id| {
let anchor = id.to_manipulator_point().get_anchor(&vector_data)?;
let initial = id.to_manipulator_point().get_position(&vector_data)?;
let relative = vector_data.point_domain.position_from_id(anchor)?;
let other_handle = vector_data
let anchor = id.to_manipulator_point().get_anchor(&vector)?;
let initial = id.to_manipulator_point().get_position(&vector)?;
let relative = vector.point_domain.position_from_id(anchor)?;
let other_handle = vector
.other_colinear_handle(id)
.filter(|other| !selected_points.contains(&other.to_manipulator_point()) && !selected_points.contains(&ManipulatorPointId::Anchor(anchor)));
let mirror = other_handle.and_then(|id| Some((id, id.to_manipulator_point().get_position(&vector_data)?)));
let mirror = other_handle.and_then(|id| Some((id, id.to_manipulator_point().get_position(&vector)?)));
Some((id, HandlePoint { initial, relative, anchor, mirror }))
})
@@ -13,7 +13,7 @@ use graphene_std::ProtoNodeIdentifier;
use graphene_std::table::Table;
use graphene_std::text::{TextAlign, TypesettingConfig};
use graphene_std::uuid::NodeId;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
use std::collections::HashMap;
@@ -38,12 +38,28 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
aliases: &["graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"],
},
NodeReplacement {
node: graphene_std::graphic_element::to_element::IDENTIFIER,
node: graphene_std::graphic::to_element::IDENTIFIER,
aliases: &["graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"],
},
NodeReplacement {
node: graphene_std::graphic_element::to_group::IDENTIFIER,
aliases: &["graphene_core::ToGraphicGroupNode"],
node: graphene_std::graphic::to_group::IDENTIFIER,
aliases: &["graphene_core::ToGraphicGroupNode", "graphene_core::graphic_element::ToGroupNode"],
},
NodeReplacement {
node: graphene_std::graphic::layer::IDENTIFIER,
aliases: &["graphene_core::graphic_element::LayerNode"],
},
NodeReplacement {
node: graphene_std::graphic::flatten_group::IDENTIFIER,
aliases: &["graphene_core::graphic_element::FlattenGroupNode"],
},
NodeReplacement {
node: graphene_std::graphic::flatten_vector::IDENTIFIER,
aliases: &["graphene_core::graphic_element::FlattenVectorNode"],
},
NodeReplacement {
node: graphene_std::graphic::index::IDENTIFIER,
aliases: &["graphene_core::graphic_element::IndexNode"],
},
// math_nodes
NodeReplacement {
@@ -456,6 +472,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
node: graphene_std::path_bool::boolean_operation::IDENTIFIER,
aliases: &["graphene_std::vector::BooleanOperationNode"],
},
NodeReplacement {
node: graphene_std::vector::path_modify::IDENTIFIER,
aliases: &["graphene_core::vector::vector_data::modification::PathModifyNode"],
},
// brush
NodeReplacement {
node: graphene_std::brush::brush::brush_stamp_generator::IDENTIFIER,
@@ -591,13 +611,13 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
return None;
}
// Obtain the document node for the given node ID, extract the vector points, and create vector data from the list of points
// Obtain the document node for the given node ID, extract the vector points, and create a Vector path from the list of points
let node = document.network_interface.document_node(node_id, network_path)?;
let Some(TaggedValue::VecDVec2(points)) = node.inputs.get(1).and_then(|tagged_value| tagged_value.as_value()) else {
log::error!("The old Spline node's input at index 1 is not a TaggedValue::VecDVec2");
return None;
};
let vector_data = VectorData::from_subpath(Subpath::from_anchors_linear(points.to_vec(), false));
let vector = Vector::from_subpath(Subpath::from_anchors_linear(points.to_vec(), false));
// Retrieve the output connectors linked to the "Spline" node's output port
let Some(spline_outputs) = document.network_interface.outward_wires(network_path)?.get(&OutputConnector::node(*node_id, 0)).cloned() else {
@@ -611,13 +631,13 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
return None;
};
// Get the "Path" node definition and fill it in with the vector data and default vector modification
// Get the "Path" node definition and fill it in with the Vector path and default vector modification
let Some(path_node_type) = resolve_document_node_type("Path") else {
log::error!("Path node does not exist.");
return None;
};
let path_node = path_node_type.node_template_input_override([
Some(NodeInput::value(TaggedValue::VectorData(Table::new_from_element(vector_data)), true)),
Some(NodeInput::value(TaggedValue::Vector(Table::new_from_element(vector)), true)),
Some(NodeInput::value(TaggedValue::VectorModification(Default::default()), false)),
]);
@@ -29,7 +29,8 @@ use graph_craft::document::NodeId;
use graphene_std::Color;
use graphene_std::renderer::Quad;
use graphene_std::text::Font;
use graphene_std::vector::{HandleId, PointId, SegmentId, VectorData, VectorModificationType};
use graphene_std::vector::misc::HandleId;
use graphene_std::vector::{PointId, SegmentId, Vector, VectorModificationType};
use std::vec;
#[derive(ExtractField)]
@@ -567,7 +568,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
// If not using Path tool, create new layers and add paths into those
if let Some(document) = self.active_document() {
let Ok(data) = serde_json::from_str::<Vec<(LayerNodeIdentifier, VectorData, DAffine2)>>(&data) else {
let Ok(data) = serde_json::from_str::<Vec<(LayerNodeIdentifier, Vector, DAffine2)>>(&data) else {
return;
};
@@ -603,7 +604,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
let stroke = graphene_std::vector::style::Stroke::new(Some(stroke_color.to_gamma_srgb()), DEFAULT_STROKE_WIDTH);
responses.add(GraphOperationMessage::StrokeSet { layer, stroke });
// Create new point ids and add those into the existing vector data
// Create new point ids and add those into the existing Vector path
let mut points_map = HashMap::new();
for (point, position) in new_vector.point_domain.iter() {
let new_point_id = PointId::generate();
@@ -612,7 +613,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
// Create new segment ids and add the segments into the existing vector data
// Create new segment ids and add the segments into the existing Vector path
let mut segments_map = HashMap::new();
for (segment_id, bezier, start, end) in new_vector.segment_bezier_iter() {
let new_segment_id = SegmentId::generate();
@@ -19,13 +19,13 @@ pub enum SpreadsheetMessage {
len: usize,
},
ViewVectorDataDomain {
domain: VectorDataDomain,
ViewVectorDomain {
domain: VectorDomain,
},
}
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum VectorDataDomain {
pub enum VectorDomain {
#[default]
Points,
Segments,
@@ -1,4 +1,4 @@
use super::VectorDataDomain;
use super::VectorDomain;
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, LayoutTarget, WidgetLayout};
use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::*;
@@ -8,7 +8,7 @@ use graphene_std::Context;
use graphene_std::memo::IORecord;
use graphene_std::raster::Image;
use graphene_std::table::Table;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::{Artboard, Graphic};
use std::any::Any;
use std::sync::Arc;
@@ -21,7 +21,7 @@ pub struct SpreadsheetMessageHandler {
inspect_node: Option<NodeId>,
introspected_data: Option<Arc<dyn Any + Send + Sync>>,
element_path: Vec<usize>,
viewing_vector_data_domain: VectorDataDomain,
viewing_vector_domain: VectorDomain,
}
#[message_handler_data]
@@ -54,8 +54,8 @@ impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
self.update_layout(responses);
}
SpreadsheetMessage::ViewVectorDataDomain { domain } => {
self.viewing_vector_data_domain = domain;
SpreadsheetMessage::ViewVectorDomain { domain } => {
self.viewing_vector_domain = domain;
self.update_layout(responses);
}
}
@@ -79,7 +79,7 @@ impl SpreadsheetMessageHandler {
current_depth: 0,
desired_path: &mut self.element_path,
breadcrumbs: Vec::new(),
vector_data_domain: self.viewing_vector_data_domain,
vector_domain: self.viewing_vector_domain,
};
let mut layout = self
.introspected_data
@@ -106,7 +106,7 @@ struct LayoutData<'a> {
current_depth: usize,
desired_path: &'a mut Vec<usize>,
breadcrumbs: Vec<String>,
vector_data_domain: VectorDataDomain,
vector_domain: VectorDomain,
}
fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
@@ -116,9 +116,9 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), Table<Artboard>>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<VectorData>>>() {
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), Table<VectorData>>>() {
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), Table<Vector>>>() {
Some(io.output.layout_with_breadcrumb(data))
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Graphic>>>() {
Some(io.output.layout_with_breadcrumb(data))
@@ -154,10 +154,10 @@ impl TableRowLayout for Graphic {
}
fn identifier(&self) -> String {
match self {
Self::GraphicGroup(table) => table.identifier(),
Self::VectorData(table) => table.identifier(),
Self::RasterDataCPU(_) => "RasterDataCPU".to_string(),
Self::RasterDataGPU(_) => "RasterDataGPU".to_string(),
Self::Group(group) => group.identifier(),
Self::Vector(vector) => vector.identifier(),
Self::RasterCPU(_) => "Raster (on CPU)".to_string(),
Self::RasterGPU(_) => "Raster (on GPU)".to_string(),
}
}
// Don't put a breadcrumb for Graphic
@@ -166,48 +166,48 @@ impl TableRowLayout for Graphic {
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
match self {
Self::GraphicGroup(table) => table.layout_with_breadcrumb(data),
Self::VectorData(table) => table.layout_with_breadcrumb(data),
Self::RasterDataCPU(_) => label("Raster is not supported"),
Self::RasterDataGPU(_) => label("Raster is not supported"),
Self::Group(table) => table.layout_with_breadcrumb(data),
Self::Vector(table) => table.layout_with_breadcrumb(data),
Self::RasterCPU(_) => label("Raster is not supported"),
Self::RasterGPU(_) => label("Raster is not supported"),
}
}
}
impl TableRowLayout for VectorData {
impl TableRowLayout for Vector {
fn type_name() -> &'static str {
"VectorData"
"Vector"
}
fn identifier(&self) -> String {
format!("Vector Data (points={}, segments={})", self.point_domain.ids().len(), self.segment_domain.ids().len())
format!("Vector ({} points, {} segments)", self.point_domain.ids().len(), self.segment_domain.ids().len())
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::<Vec<_>>().join(", ");
let colinear = if colinear.is_empty() { "None" } else { &colinear };
let style = vec![
TextLabel::new(format!(
"{}\n\nColinear Handle IDs: {}\n\nUpstream Graphic Group Table: {}",
"{}\n\nColinear Handle IDs: {}\n\nUpstream Group Table: {}",
self.style,
colinear,
if self.upstream_graphic_group.is_some() { "Yes" } else { "No" }
if self.upstream_group.is_some() { "Yes" } else { "No" }
))
.multiline(true)
.widget_holder(),
];
let domain_entries = [VectorDataDomain::Points, VectorDataDomain::Segments, VectorDataDomain::Regions]
let domain_entries = [VectorDomain::Points, VectorDomain::Segments, VectorDomain::Regions]
.into_iter()
.map(|domain| {
RadioEntryData::new(format!("{domain:?}"))
.label(format!("{domain:?}"))
.on_update(move |_| SpreadsheetMessage::ViewVectorDataDomain { domain }.into())
.on_update(move |_| SpreadsheetMessage::ViewVectorDomain { domain }.into())
})
.collect();
let domain = vec![RadioInput::new(domain_entries).selected_index(Some(data.vector_data_domain as u32)).widget_holder()];
let domain = vec![RadioInput::new(domain_entries).selected_index(Some(data.vector_domain as u32)).widget_holder()];
let mut table_rows = Vec::new();
match data.vector_data_domain {
VectorDataDomain::Points => {
match data.vector_domain {
VectorDomain::Points => {
table_rows.push(column_headings(&["", "position"]));
table_rows.extend(
self.point_domain
@@ -215,7 +215,7 @@ impl TableRowLayout for VectorData {
.map(|(id, position)| vec![TextLabel::new(format!("{}", id.inner())).widget_holder(), TextLabel::new(format!("{}", position)).widget_holder()]),
);
}
VectorDataDomain::Segments => {
VectorDomain::Segments => {
table_rows.push(column_headings(&["", "start_index", "end_index", "handles"]));
table_rows.extend(self.segment_domain.iter().map(|(id, start, end, handles)| {
vec![
@@ -226,7 +226,7 @@ impl TableRowLayout for VectorData {
]
}));
}
VectorDataDomain::Regions => {
VectorDomain::Regions => {
table_rows.push(column_headings(&["", "segment_range", "fill"]));
table_rows.extend(self.region_domain.iter().map(|(id, segment_range, fill)| {
vec![
@@ -263,7 +263,7 @@ impl TableRowLayout for Artboard {
self.label.clone()
}
fn compute_layout(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.graphic_group.compute_layout(data)
self.group.compute_layout(data)
}
}
@@ -14,8 +14,9 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::style::Gradient;
use graphene_std::vector::{ManipulatorPointId, PointId, SegmentId, VectorModificationType};
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
use std::collections::VecDeque;
/// Returns the ID of the first Spline node in the horizontal flow which is not followed by a `Path` node, or `None` if none exists.
@@ -34,7 +35,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
if first_layer == second_layer {
return;
}
// Calculate the downstream transforms in order to bring the other vector data into the same layer space
// Calculate the downstream transforms in order to bring the other vector geometry into the same layer space
let first_layer_transform = document.metadata().downstream_transform_to_document(first_layer);
let second_layer_transform = document.metadata().downstream_transform_to_document(second_layer);
@@ -162,24 +163,24 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde
/// Merge the `first_endpoint` with `second_endpoint`.
pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, first_endpoint: PointId, second_endpont: PointId, responses: &mut VecDeque<Message>) {
let transform = document.metadata().transform_to_document(layer);
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
let segment = vector_data.segment_bezier_iter().find(|(_, _, start, end)| *end == second_endpont || *start == second_endpont);
let segment = vector.segment_bezier_iter().find(|(_, _, start, end)| *end == second_endpont || *start == second_endpont);
let Some((segment, _, mut segment_start_point, mut segment_end_point)) = segment else {
log::error!("Could not get the segment for second_endpoint.");
return;
};
let mut handles = [None; 2];
if let Some(handle_position) = ManipulatorPointId::PrimaryHandle(segment).get_position(&vector_data) {
let anchor_position = ManipulatorPointId::Anchor(segment_start_point).get_position(&vector_data).unwrap();
if let Some(handle_position) = ManipulatorPointId::PrimaryHandle(segment).get_position(&vector) {
let anchor_position = ManipulatorPointId::Anchor(segment_start_point).get_position(&vector).unwrap();
let handle_position = transform.transform_point2(handle_position);
let anchor_position = transform.transform_point2(anchor_position);
let anchor_to_handle = handle_position - anchor_position;
handles[0] = Some(anchor_to_handle);
}
if let Some(handle_position) = ManipulatorPointId::EndHandle(segment).get_position(&vector_data) {
let anchor_position = ManipulatorPointId::Anchor(segment_end_point).get_position(&vector_data).unwrap();
if let Some(handle_position) = ManipulatorPointId::EndHandle(segment).get_position(&vector) {
let anchor_position = ManipulatorPointId::Anchor(segment_end_point).get_position(&vector).unwrap();
let handle_position = transform.transform_point2(handle_position);
let anchor_position = transform.transform_point2(anchor_position);
let anchor_to_handle = handle_position - anchor_position;
@@ -8,7 +8,8 @@ use crate::messages::tool::tool_messages::path_tool::PathOptionsUpdate;
use crate::messages::tool::tool_messages::select_tool::SelectOptionsUpdate;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{DAffine2, DVec2};
use graphene_std::{transform::ReferencePoint, vector::ManipulatorPointId};
use graphene_std::transform::ReferencePoint;
use graphene_std::vector::misc::ManipulatorPointId;
use std::fmt;
pub fn pin_pivot_widget(active: bool, enabled: bool, source: PivotToolSource) -> WidgetHolder {
@@ -13,8 +13,8 @@ use crate::messages::tool::common_functionality::utility_functions::{is_intersec
use crate::messages::tool::tool_messages::path_tool::{PathOverlayMode, PointSelectState};
use bezier_rs::{Bezier, BezierHandles, Subpath, TValue};
use glam::{DAffine2, DVec2};
use graphene_std::vector::{HandleExt, HandleId, SegmentId};
use graphene_std::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};
use graphene_std::vector::misc::{HandleId, ManipulatorPointId};
use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModificationType};
use std::f64::consts::TAU;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -164,13 +164,13 @@ pub struct ShapeState {
pub struct SelectedPointsInfo {
pub points: Vec<ManipulatorPointInfo>,
pub offset: DVec2,
pub vector_data: VectorData,
pub vector: Vector,
}
#[derive(Debug)]
pub struct SelectedSegmentsInfo {
pub segments: Vec<SegmentId>,
pub vector_data: VectorData,
pub vector: Vector,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -323,10 +323,10 @@ impl ClosestSegment {
(handle1 - handle2).try_normalize()
} else {
let [first_point, last_point] = self.points();
if let Some(vector_data) = document.network_interface.compute_modified_vector(self.layer()) {
if let Some(vector) = document.network_interface.compute_modified_vector(self.layer()) {
if let (Some(pos1), Some(pos2)) = (
ManipulatorPointId::Anchor(first_point).get_position(&vector_data),
ManipulatorPointId::Anchor(last_point).get_position(&vector_data),
ManipulatorPointId::Anchor(first_point).get_position(&vector),
ManipulatorPointId::Anchor(last_point).get_position(&vector),
) {
(pos1 - pos2).try_normalize()
} else {
@@ -373,13 +373,13 @@ impl ClosestSegment {
// If adjacent segments have colinear handles, their direction is changed but their handle lengths is preserved
// TODO: Find something which is more appropriate
let vector_data = document.network_interface.compute_modified_vector(self.layer())?;
let vector = document.network_interface.compute_modified_vector(self.layer())?;
if break_colinear_molding {
// Disable G1 continuity
let other_handles = [
restore_previous_handle_position(handle1, c1, start, &vector_data, layer, responses),
restore_previous_handle_position(handle2, c2, end, &vector_data, layer, responses),
restore_previous_handle_position(handle1, c1, start, &vector, layer, responses),
restore_previous_handle_position(handle2, c2, end, &vector, layer, responses),
];
// Store other HandleId in tool data to regain colinearity later
@@ -390,15 +390,15 @@ impl ClosestSegment {
}
} else {
// Move the colinear handles so that colinearity is maintained
adjust_handle_colinearity(handle1, start, nc1, &vector_data, layer, responses);
adjust_handle_colinearity(handle2, end, nc2, &vector_data, layer, responses);
adjust_handle_colinearity(handle1, start, nc1, &vector, layer, responses);
adjust_handle_colinearity(handle2, end, nc2, &vector, layer, responses);
if let Some(adjacent_handles) = temporary_adjacent_handles_while_molding {
if let Some(other_handle1) = adjacent_handles[0] {
restore_g1_continuity(handle1, other_handle1, nc1, start, &vector_data, layer, responses);
restore_g1_continuity(handle1, other_handle1, nc1, start, &vector, layer, responses);
}
if let Some(other_handle2) = adjacent_handles[1] {
restore_g1_continuity(handle2, other_handle2, nc2, end, &vector_data, layer, responses);
restore_g1_continuity(handle2, other_handle2, nc2, end, &vector, layer, responses);
}
}
None
@@ -441,10 +441,10 @@ impl ShapeState {
let (layer1, start_point) = all_selected_points[0];
let (layer2, end_point) = all_selected_points[1];
let Some(vector_data1) = document.network_interface.compute_modified_vector(layer1) else { return };
let Some(vector_data2) = document.network_interface.compute_modified_vector(layer2) else { return };
let Some(vector1) = document.network_interface.compute_modified_vector(layer1) else { return };
let Some(vector2) = document.network_interface.compute_modified_vector(layer2) else { return };
if vector_data1.all_connected(start_point).count() != 1 || vector_data2.all_connected(end_point).count() != 1 {
if vector1.all_connected(start_point).count() != 1 || vector2.all_connected(end_point).count() != 1 {
return;
}
@@ -477,15 +477,9 @@ impl ShapeState {
// If no points are selected, try to find a single continuous subpath in each layer to connect the endpoints of
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let endpoints: Vec<PointId> = vector_data
.point_domain
.ids()
.iter()
.copied()
.filter(|&point_id| vector_data.all_connected(point_id).count() == 1)
.collect();
let endpoints: Vec<PointId> = vector.point_domain.ids().iter().copied().filter(|&point_id| vector.all_connected(point_id).count() == 1).collect();
if endpoints.len() == 2 {
let start_point = endpoints[0];
@@ -515,29 +509,27 @@ impl ShapeState {
let mut offset = mouse_delta;
let mut best_snapped = SnappedPoint::infinite_snap(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
for (layer, state) in &self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(*layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(*layer) else { continue };
let to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
for &selected in &state.selected_points {
let source = match selected {
ManipulatorPointId::Anchor(_) if vector_data.colinear(selected) => SnapSource::Path(PathSnapSource::AnchorPointWithColinearHandles),
ManipulatorPointId::Anchor(_) if vector.colinear(selected) => SnapSource::Path(PathSnapSource::AnchorPointWithColinearHandles),
ManipulatorPointId::Anchor(_) => SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles),
// TODO: This doesn't actually work for handles, instead handles enter the arm above for free handles
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => SnapSource::Path(PathSnapSource::HandlePoint),
};
let Some(position) = selected.get_position(&vector_data) else { continue };
let Some(position) = selected.get_position(&vector) else { continue };
let mut point = SnapCandidatePoint::new_source(to_document.transform_point2(position) + mouse_delta, source);
if let Some(id) = selected.as_anchor() {
for neighbor in vector_data.connected_points(id) {
for neighbor in vector.connected_points(id) {
if state.is_point_selected(ManipulatorPointId::Anchor(neighbor)) {
continue;
}
let Some(position) = vector_data.point_domain.position_from_id(neighbor) else { continue };
let Some(position) = vector.point_domain.position_from_id(neighbor) else { continue };
point.neighbors.push(to_document.transform_point2(position));
}
}
@@ -569,8 +561,8 @@ impl ShapeState {
}
if let Some((layer, manipulator_point_id)) = self.find_nearest_visible_point_indices(network_interface, mouse_position, select_threshold, path_overlay_mode, frontier_handles_info) {
let vector_data = network_interface.compute_modified_vector(layer)?;
let point_position = manipulator_point_id.get_position(&vector_data)?;
let vector = network_interface.compute_modified_vector(layer)?;
let point_position = manipulator_point_id.get_position(&vector)?;
let selected_shape_state = self.selected_shape_state.get(&layer)?;
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
@@ -600,7 +592,7 @@ impl ShapeState {
.flat_map(|(layer, state)| state.selected_points.iter().map(|&point_id| ManipulatorPointInfo { layer: *layer, point_id }))
.collect();
return Some(Some(SelectedPointsInfo { points, offset, vector_data }));
return Some(Some(SelectedPointsInfo { points, offset, vector }));
}
None
}
@@ -623,13 +615,13 @@ impl ShapeState {
if !point_editing_mode && matches!(manipulator_point_id, ManipulatorPointId::Anchor(_)) {
return None;
}
let vector_data = network_interface.compute_modified_vector(layer)?;
let point_position = manipulator_point_id.get_position(&vector_data)?;
let vector = network_interface.compute_modified_vector(layer)?;
let point_position = manipulator_point_id.get_position(&vector)?;
// Check if point is visible under current overlay mode or not
let selected_segments = selected_segments(network_interface, self);
let selected_points = self.selected_points().cloned().collect::<HashSet<_>>();
if !is_visible_point(manipulator_point_id, &vector_data, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
if !is_visible_point(manipulator_point_id, &vector, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
return None;
}
@@ -650,7 +642,7 @@ impl ShapeState {
.flat_map(|(layer, state)| state.selected_points.iter().map(|&point_id| ManipulatorPointInfo { layer: *layer, point_id }))
.collect();
let selection_info = SelectedPointsInfo { points, offset, vector_data };
let selection_info = SelectedPointsInfo { points, offset, vector };
// Return the current selection state and info
return Some((already_selected, Some(selection_info)));
@@ -670,16 +662,14 @@ impl ShapeState {
/// Selects all anchors connected to the selected subpath, and deselects all handles, for the given layer.
pub fn select_connected(&mut self, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, mouse: DVec2, points: bool, segments: bool) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
return;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
let to_viewport = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
let layer_mouse = to_viewport.inverse().transform_point2(mouse);
let state = self.selected_shape_state.entry(layer).or_default();
let mut selected_stack = Vec::new();
// Find all subpaths that have been clicked
for stroke in vector_data.stroke_bezier_paths() {
for stroke in vector.stroke_bezier_paths() {
if stroke.contains_point(layer_mouse) {
if let Some(first) = stroke.manipulator_groups().first() {
selected_stack.push(first.id);
@@ -691,12 +681,12 @@ impl ShapeState {
if selected_stack.is_empty() {
// Fall back on just selecting all points/segments in the layer
if points {
for &point in vector_data.point_domain.ids() {
for &point in vector.point_domain.ids() {
state.select_point(ManipulatorPointId::Anchor(point));
}
}
if segments {
for &segment in vector_data.segment_domain.ids() {
for &segment in vector.segment_domain.ids() {
state.select_segment(segment);
}
}
@@ -708,7 +698,7 @@ impl ShapeState {
while let Some(point) = selected_stack.pop() {
if !connected_points.contains(&point) {
connected_points.insert(point);
selected_stack.extend(vector_data.connected_points(point));
selected_stack.extend(vector.connected_points(point));
}
}
@@ -717,7 +707,7 @@ impl ShapeState {
}
if segments {
for (id, _, start, end) in vector_data.segment_bezier_iter() {
for (id, _, start, end) in vector.segment_bezier_iter() {
if connected_points.contains(&start) || connected_points.contains(&end) {
state.select_segment(id);
}
@@ -740,13 +730,11 @@ impl ShapeState {
/// Internal helper function that selects all anchors, and deselects all handles, for a layer given its [`LayerNodeIdentifier`] and [`SelectedLayerState`].
fn select_all_anchors_in_layer_with_state(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, state: &mut SelectedLayerState) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
return;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
state.clear_points();
for &point in vector_data.point_domain.ids() {
for &point in vector.point_domain.ids() {
state.select_point(ManipulatorPointId::Anchor(point))
}
}
@@ -856,7 +844,7 @@ impl ShapeState {
});
}
pub fn move_anchor(&self, point: PointId, vector_data: &VectorData, delta: DVec2, layer: LayerNodeIdentifier, selected: Option<&SelectedLayerState>, responses: &mut VecDeque<Message>) {
pub fn move_anchor(&self, point: PointId, vector: &Vector, delta: DVec2, layer: LayerNodeIdentifier, selected: Option<&SelectedLayerState>, responses: &mut VecDeque<Message>) {
// Move anchor
responses.add(GraphOperationMessage::Vector {
layer,
@@ -864,8 +852,8 @@ impl ShapeState {
});
// Move the other handle for a quadratic bezier
for segment in vector_data.end_connected(point) {
let Some((start, _end, bezier)) = vector_data.segment_points_from_id(segment) else { continue };
for segment in vector.end_connected(point) {
let Some((start, _end, bezier)) = vector.segment_points_from_id(segment) else { continue };
if let BezierHandles::Quadratic { handle } = bezier.handles {
if selected.is_some_and(|selected| selected.is_point_selected(ManipulatorPointId::Anchor(start))) {
@@ -894,18 +882,18 @@ impl ShapeState {
return None;
}
let vector_data = network_interface.compute_modified_vector(layer)?;
let vector = network_interface.compute_modified_vector(layer)?;
let transform = network_interface.document_metadata().transform_to_document_if_feeds(layer, network_interface).inverse();
let position = transform.transform_point2(new_position);
let current_position = point.get_position(&vector_data)?;
let current_position = point.get_position(&vector)?;
let delta = position - current_position;
match *point {
ManipulatorPointId::Anchor(point) => self.move_anchor(point, &vector_data, delta, layer, None, responses),
ManipulatorPointId::Anchor(point) => self.move_anchor(point, &vector, delta, layer, None, responses),
ManipulatorPointId::PrimaryHandle(segment) => {
self.move_primary(segment, delta, layer, responses);
if let Some(handle) = point.as_handle() {
if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -914,7 +902,7 @@ impl ShapeState {
ManipulatorPointId::EndHandle(segment) => {
self.move_end(segment, delta, layer, responses);
if let Some(handle) = point.as_handle() {
if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -948,28 +936,26 @@ impl ShapeState {
if first_is_colinear { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free }
}
pub fn convert_manipulator_handles_to_colinear(&self, vector_data: &VectorData, point_id: PointId, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier) {
let Some(anchor_position) = ManipulatorPointId::Anchor(point_id).get_position(vector_data) else {
pub fn convert_manipulator_handles_to_colinear(&self, vector: &Vector, point_id: PointId, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier) {
let Some(anchor_position) = ManipulatorPointId::Anchor(point_id).get_position(vector) else {
return;
};
let handles = vector_data.all_connected(point_id).take(2).collect::<Vec<_>>();
let non_zero_handles = handles.iter().filter(|handle| handle.length(vector_data) > 1e-6).count();
let handles = vector.all_connected(point_id).take(2).collect::<Vec<_>>();
let non_zero_handles = handles.iter().filter(|handle| handle.length(vector) > 1e-6).count();
let handle_segments = handles.iter().map(|handles| handles.segment).collect::<Vec<_>>();
// Check if the anchor is connected to linear segments and has no handles
let linear_segments = vector_data.connected_linear_segments(point_id) != 0;
let linear_segments = vector.connected_linear_segments(point_id) != 0;
// Grab the next and previous manipulator groups by simply looking at the next / previous index
let points = handles.iter().map(|handle| vector_data.other_point(handle.segment, point_id));
let anchor_positions = points
.map(|point| point.and_then(|point| ManipulatorPointId::Anchor(point).get_position(vector_data)))
.collect::<Vec<_>>();
let points = handles.iter().map(|handle| vector.other_point(handle.segment, point_id));
let anchor_positions = points.map(|point| point.and_then(|point| ManipulatorPointId::Anchor(point).get_position(vector))).collect::<Vec<_>>();
let mut segment_angle = 0.;
let mut segment_count = 0.;
for segment in &handle_segments {
let Some(angle) = calculate_segment_angle(point_id, *segment, vector_data, false) else {
let Some(angle) = calculate_segment_angle(point_id, *segment, vector, false) else {
continue;
};
segment_angle += angle;
@@ -1002,15 +988,15 @@ impl ShapeState {
if non_zero_handles != 0 && !linear_segments {
let [a, b] = handles.as_slice() else { return };
let (non_zero_handle, zero_handle) = if a.length(vector_data) > 1e-6 { (a, b) } else { (b, a) };
let (non_zero_handle, zero_handle) = if a.length(vector) > 1e-6 { (a, b) } else { (b, a) };
let Some(direction) = non_zero_handle
.to_manipulator_point()
.get_position(vector_data)
.get_position(vector)
.and_then(|position| (position - anchor_position).try_normalize())
else {
return;
};
let new_position = -direction * non_zero_handle.length(vector_data);
let new_position = -direction * non_zero_handle.length(vector);
let modification_type = zero_handle.set_relative_position(new_position);
responses.add(GraphOperationMessage::Vector { layer, modification_type });
} else {
@@ -1031,7 +1017,7 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Create the opposite handle if it doesn't exist (if it is not a cubic segment)
if handle.opposite().to_manipulator_point().get_position(vector_data).is_none() {
if handle.opposite().to_manipulator_point().get_position(vector).is_none() {
let modification_type = handle.opposite().set_relative_position(DVec2::ZERO);
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1050,36 +1036,34 @@ impl ShapeState {
let mut skip_set = HashSet::new();
for (&layer, layer_state) in self.selected_shape_state.iter() {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
for &point in layer_state.selected_points.iter() {
// Skip a point which has more than 2 segments connected (vector meshes)
if let ManipulatorPointId::Anchor(anchor) = point {
if vector_data.all_connected(anchor).count() > 2 {
if vector.all_connected(anchor).count() > 2 {
continue;
}
}
// Here we take handles as the current handle and the most opposite non-colinear-handle
let is_handle_colinear = |handle: HandleId| -> bool { vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle) };
let is_handle_colinear = |handle: HandleId| -> bool { vector.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle) };
let other_handles = if matches!(point, ManipulatorPointId::Anchor(_)) {
point.get_handle_pair(&vector_data)
point.get_handle_pair(&vector)
} else {
point.get_all_connected_handles(&vector_data).and_then(|handles| {
point.get_all_connected_handles(&vector).and_then(|handles| {
let mut non_colinear_handles = handles.iter().filter(|&handle| !is_handle_colinear(*handle)).clone().collect::<Vec<_>>();
// Sort these by angle from the current handle
non_colinear_handles.sort_by(|&handle_a, &handle_b| {
let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle");
let orig_handle_pos = point.get_position(&vector_data).expect("No handle position");
let anchor = point.get_anchor_position(&vector).expect("No anchor position for handle");
let orig_handle_pos = point.get_position(&vector).expect("No handle position");
let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position");
let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position");
let a_pos = handle_a.to_manipulator_point().get_position(&vector).expect("No handle position");
let b_pos = handle_b.to_manipulator_point().get_position(&vector).expect("No handle position");
let v_orig = (orig_handle_pos - anchor).normalize_or_zero();
@@ -1112,13 +1096,13 @@ impl ShapeState {
skip_set.insert(handles);
let [selected0, selected1] = handles.map(|handle| layer_state.selected_points.contains(&handle.to_manipulator_point()));
let handle_positions = handles.map(|handle| handle.to_manipulator_point().get_position(&vector_data));
let handle_positions = handles.map(|handle| handle.to_manipulator_point().get_position(&vector));
let Some(anchor_id) = point.get_anchor(&vector_data) else { continue };
let Some(anchor) = vector_data.point_domain.position_from_id(anchor_id) else { continue };
let Some(anchor_id) = point.get_anchor(&vector) else { continue };
let Some(anchor) = vector.point_domain.position_from_id(anchor_id) else { continue };
let anchor_points = handles.map(|handle| vector_data.other_point(handle.segment, anchor_id));
let anchor_positions = anchor_points.map(|point| point.and_then(|point| vector_data.point_domain.position_from_id(point)));
let anchor_points = handles.map(|handle| vector.other_point(handle.segment, anchor_id));
let anchor_positions = anchor_points.map(|point| point.and_then(|point| vector.point_domain.position_from_id(point)));
// If one handle is selected (but both exist), only move the other handle
if let (true, [Some(pos0), Some(pos1)]) = ((selected0 ^ selected1), handle_positions) {
@@ -1160,7 +1144,7 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Create the opposite handle if it doesn't exist (if it is not a cubic segment)
if handles[index].opposite().to_manipulator_point().get_position(&vector_data).is_none() {
if handles[index].opposite().to_manipulator_point().get_position(&vector).is_none() {
let modification_type = handles[index].opposite().set_relative_position(DVec2::ZERO);
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1187,7 +1171,7 @@ impl ShapeState {
responses: &mut VecDeque<Message>,
) {
for (&layer, state) in &self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer));
@@ -1203,7 +1187,7 @@ impl ShapeState {
// Make a new collection of anchor points which needs to be moved
let mut affected_points = state.selected_points.clone();
for (segment_id, _, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
if state.is_segment_selected(segment_id) {
affected_points.insert(ManipulatorPointId::Anchor(start));
affected_points.insert(ManipulatorPointId::Anchor(end));
@@ -1217,28 +1201,28 @@ impl ShapeState {
let handle = match point {
ManipulatorPointId::Anchor(point) => {
self.move_anchor(point, &vector_data, delta, layer, Some(state), responses);
self.move_anchor(point, &vector, delta, layer, Some(state), responses);
continue;
}
ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment),
ManipulatorPointId::EndHandle(segment) => HandleId::end(segment),
};
let Some(anchor_id) = point.get_anchor(&vector_data) else { continue };
let Some(anchor_id) = point.get_anchor(&vector) else { continue };
if state.is_point_selected(ManipulatorPointId::Anchor(anchor_id)) {
continue;
}
let Some(anchor_position) = vector_data.point_domain.position_from_id(anchor_id) else { continue };
let Some(anchor_position) = vector.point_domain.position_from_id(anchor_id) else { continue };
let Some(handle_position) = point.get_position(&vector_data) else { continue };
let Some(handle_position) = point.get_position(&vector) else { continue };
let handle_position = handle_position + delta;
let modification_type = handle.set_relative_position(handle_position - anchor_position);
responses.add(GraphOperationMessage::Vector { layer, modification_type });
let Some(other) = vector_data.other_colinear_handle(handle) else { continue };
let Some(other) = vector.other_colinear_handle(handle) else { continue };
if skip_opposite_handle {
continue;
@@ -1262,7 +1246,7 @@ impl ShapeState {
} else {
// TODO: Is this equivalent to `transform_to_document_space`? If changed, the before and after should be tested.
let transform = document.metadata().document_to_viewport.inverse() * transform_to_viewport_space;
let Some(other_position) = other.to_manipulator_point().get_position(&vector_data) else {
let Some(other_position) = other.to_manipulator_point().get_position(&vector) else {
continue;
};
let direction = transform.transform_vector2(handle_position - anchor_position).try_normalize();
@@ -1284,9 +1268,9 @@ impl ShapeState {
self.selected_shape_state
.iter()
.filter_map(|(&layer, state)| {
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
let opposing_handle_lengths = vector_data
let opposing_handle_lengths = vector
.colinear_manipulators
.iter()
.filter_map(|&handles| {
@@ -1294,7 +1278,7 @@ impl ShapeState {
// i) Exactly one handle is selected.
// ii) The anchor is not selected.
let anchor = handles[0].to_manipulator_point().get_anchor(&vector_data)?;
let anchor = handles[0].to_manipulator_point().get_anchor(&vector)?;
let anchor_selected = state.is_point_selected(ManipulatorPointId::Anchor(anchor));
if anchor_selected {
return None;
@@ -1308,8 +1292,8 @@ impl ShapeState {
_ => return None,
};
let opposing_handle_position = other.to_manipulator_point().get_position(&vector_data)?;
let anchor_position = vector_data.point_domain.position_from_id(anchor)?;
let opposing_handle_position = other.to_manipulator_point().get_position(&vector)?;
let anchor_position = vector.point_domain.position_from_id(anchor)?;
let opposing_handle_length = transform.transform_vector2(opposing_handle_position - anchor_position).length();
Some((other, opposing_handle_length))
@@ -1320,15 +1304,15 @@ impl ShapeState {
.collect::<HashMap<_, _>>()
}
pub fn dissolve_segment(&self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector_data: &VectorData, segment: SegmentId, points: [PointId; 2]) {
pub fn dissolve_segment(&self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector: &Vector, segment: SegmentId, points: [PointId; 2]) {
// Checking which point is terminal point
let is_point1_terminal = vector_data.connected_count(points[0]) == 1;
let is_point2_terminal = vector_data.connected_count(points[1]) == 1;
let is_point1_terminal = vector.connected_count(points[0]) == 1;
let is_point2_terminal = vector.connected_count(points[1]) == 1;
// Delete the segment and terminal points
let modification_type = VectorModificationType::RemoveSegment { id: segment };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
for &handles in vector_data.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) {
for &handles in vector.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1343,27 +1327,27 @@ impl ShapeState {
}
}
fn dissolve_anchor(anchor: PointId, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector_data: &VectorData) -> Option<[(HandleId, PointId); 2]> {
fn dissolve_anchor(anchor: PointId, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector: &Vector) -> Option<[(HandleId, PointId); 2]> {
// Delete point
let modification_type = VectorModificationType::RemovePoint { id: anchor };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Delete connected segments
for HandleId { segment, .. } in vector_data.all_connected(anchor) {
for HandleId { segment, .. } in vector.all_connected(anchor) {
let modification_type = VectorModificationType::RemoveSegment { id: segment };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
for &handles in vector_data.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) {
for &handles in vector.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
}
// Add in new segment if possible
let mut handles = ManipulatorPointId::Anchor(anchor).get_handle_pair(vector_data)?;
let mut handles = ManipulatorPointId::Anchor(anchor).get_handle_pair(vector)?;
handles.reverse();
let opposites = handles.map(|handle| handle.opposite());
let [Some(start), Some(end)] = opposites.map(|opposite| opposite.to_manipulator_point().get_anchor(vector_data)) else {
let [Some(start), Some(end)] = opposites.map(|opposite| opposite.to_manipulator_point().get_anchor(vector)) else {
return None;
};
Some([(handles[0], start), (handles[1], end)])
@@ -1374,17 +1358,15 @@ impl ShapeState {
for (&layer, state) in &mut self.selected_shape_state {
let mut missing_anchors = HashMap::new();
let mut deleted_anchors = HashSet::new();
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let selected_segments = &state.selected_segments;
for point in std::mem::take(&mut state.selected_points) {
match point {
ManipulatorPointId::Anchor(anchor) => {
if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector_data) {
if !vector_data.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) && vector_data.all_connected(anchor).count() <= 2 {
if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector) {
if !vector.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) && vector.all_connected(anchor).count() <= 2 {
missing_anchors.insert(anchor, handles);
}
}
@@ -1398,7 +1380,7 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Disable the g1 continuous
for &handles in &vector_data.colinear_manipulators {
for &handles in &vector.colinear_manipulators {
if handles.contains(&handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1436,11 +1418,8 @@ impl ShapeState {
// Grab the handles from the opposite side of the segment(s) being deleted and make it relative to the anchor
let [handle_start, handle_end] = [start, end].map(|(handle, _)| {
let handle = handle.opposite();
let handle_position = handle.to_manipulator_point().get_position(&vector_data);
let relative_position = handle
.to_manipulator_point()
.get_anchor(&vector_data)
.and_then(|anchor| vector_data.point_domain.position_from_id(anchor));
let handle_position = handle.to_manipulator_point().get_position(&vector);
let relative_position = handle.to_manipulator_point().get_anchor(&vector).and_then(|anchor| vector.point_domain.position_from_id(anchor));
handle_position.and_then(|handle| relative_position.map(|relative| handle - relative)).unwrap_or_default()
});
@@ -1454,12 +1433,12 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
for &handles in vector_data.colinear_manipulators.iter() {
for &handles in vector.colinear_manipulators.iter() {
if !handles.iter().any(|&handle| handle == start.0.opposite() || handle == end.0.opposite()) {
continue;
}
let Some(anchor) = handles[0].to_manipulator_point().get_anchor(&vector_data) else { continue };
let Some(anchor) = handles[0].to_manipulator_point().get_anchor(&vector) else { continue };
let Some(other) = handles.iter().find(|&&handle| handle != start.0.opposite() && handle != end.0.opposite()) else {
continue;
};
@@ -1482,13 +1461,11 @@ impl ShapeState {
pub fn delete_selected_segments(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for (&layer, state) in &self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for (segment, _, start, end) in vector_data.segment_bezier_iter() {
for (segment, _, start, end) in vector.segment_bezier_iter() {
if state.selected_segments.contains(&segment) {
self.dissolve_segment(responses, layer, &vector_data, segment, [start, end]);
self.dissolve_segment(responses, layer, &vector, segment, [start, end]);
}
}
}
@@ -1496,13 +1473,11 @@ impl ShapeState {
pub fn delete_hanging_selected_anchors(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for (&layer, state) in &self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for point in &state.selected_points {
if let ManipulatorPointId::Anchor(anchor) = point {
if vector_data.all_connected(*anchor).all(|segment| state.is_segment_selected(segment.segment)) {
if vector.all_connected(*anchor).all(|segment| state.is_segment_selected(segment.segment)) {
let modification_type = VectorModificationType::RemovePoint { id: *anchor };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1513,16 +1488,16 @@ impl ShapeState {
pub fn break_path_at_selected_point(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for (&layer, state) in &self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for &delete in &state.selected_points {
let Some(point) = delete.get_anchor(&vector_data) else { continue };
let Some(pos) = vector_data.point_domain.position_from_id(point) else { continue };
let Some(point) = delete.get_anchor(&vector) else { continue };
let Some(pos) = vector.point_domain.position_from_id(point) else { continue };
let mut used_initial_point = false;
for handle in vector_data.all_connected(point) {
for handle in vector.all_connected(point) {
// Disable the g1 continuous
for &handles in &vector_data.colinear_manipulators {
for &handles in &vector.colinear_manipulators {
if handles.contains(&handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1544,8 +1519,8 @@ impl ShapeState {
// Update segment
let HandleId { ty, segment } = handle;
let modification_type = match ty {
graphene_std::vector::HandleType::Primary => VectorModificationType::SetStartPoint { segment, id },
graphene_std::vector::HandleType::End => VectorModificationType::SetEndPoint { segment, id },
graphene_std::vector::misc::HandleType::Primary => VectorModificationType::SetStartPoint { segment, id },
graphene_std::vector::misc::HandleType::End => VectorModificationType::SetEndPoint { segment, id },
};
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1557,19 +1532,17 @@ impl ShapeState {
/// Delete point(s) and adjacent segments.
pub fn delete_point_and_break_path(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for (&layer, state) in &mut self.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for delete in std::mem::take(&mut state.selected_points) {
let Some(point) = delete.get_anchor(&vector_data) else { continue };
let Some(point) = delete.get_anchor(&vector) else { continue };
// Delete point
let modification_type = VectorModificationType::RemovePoint { id: point };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Delete connected segments
for HandleId { segment, .. } in vector_data.all_connected(point) {
for HandleId { segment, .. } in vector.all_connected(point) {
let modification_type = VectorModificationType::RemoveSegment { id: segment };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1580,18 +1553,18 @@ impl ShapeState {
/// Disable colinear handles colinear.
pub fn disable_colinear_handles_state_on_selected(&self, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) {
for (&layer, state) in &self.selected_shape_state {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for &point in &state.selected_points {
if let ManipulatorPointId::Anchor(point) = point {
for connected in vector_data.all_connected(point) {
if let Some(&handles) = vector_data.colinear_manipulators.iter().find(|target| target.contains(&connected)) {
for connected in vector.all_connected(point) {
if let Some(&handles) = vector.colinear_manipulators.iter().find(|target| target.contains(&connected)) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
}
} else if let Some(handle) = point.as_handle() {
if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
@@ -1642,11 +1615,11 @@ impl ShapeState {
// Choose the first point under the threshold
if distance_squared < select_threshold_squared {
// Check if point is visible in current PathOverlayMode
let vector_data = network_interface.compute_modified_vector(layer)?;
let vector = network_interface.compute_modified_vector(layer)?;
let selected_segments = selected_segments(network_interface, self);
let selected_points = self.selected_points().cloned().collect::<HashSet<_>>();
if !is_visible_point(manipulator_point_id, &vector_data, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
if !is_visible_point(manipulator_point_id, &vector, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
return None;
}
@@ -1666,11 +1639,11 @@ impl ShapeState {
let mut closest_distance_squared: f64 = f64::MAX;
let mut manipulator_point = None;
let vector_data = network_interface.compute_modified_vector(layer)?;
let vector = network_interface.compute_modified_vector(layer)?;
let viewspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
// Handles
for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() {
for (segment_id, bezier, _, _) in vector.segment_bezier_iter() {
let bezier = bezier.apply_transformation(|point| viewspace.transform_point2(point));
let valid = |handle: DVec2, control: DVec2| handle.distance_squared(control) > crate::consts::HIDE_HANDLE_DISTANCE.powi(2);
@@ -1689,7 +1662,7 @@ impl ShapeState {
}
// Anchors
for (&id, &point) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &point) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
let point = viewspace.transform_point2(point);
if point.distance_squared(pos) <= closest_distance_squared {
@@ -1711,9 +1684,9 @@ impl ShapeState {
let mut closest = None;
let mut closest_distance_squared: f64 = tolerance * tolerance;
let vector_data = network_interface.compute_modified_vector(layer)?;
let vector = network_interface.compute_modified_vector(layer)?;
for (segment, mut bezier, start, end) in vector_data.segment_bezier_iter() {
for (segment, mut bezier, start, end) in vector.segment_bezier_iter() {
let t = bezier.project(layer_pos);
let layerspace = bezier.evaluate(TValue::Parametric(t));
@@ -1730,8 +1703,8 @@ impl ShapeState {
}
}
let primary_handle = vector_data.colinear_manipulators.iter().find(|handles| handles.contains(&HandleId::primary(segment)));
let end_handle = vector_data.colinear_manipulators.iter().find(|handles| handles.contains(&HandleId::end(segment)));
let primary_handle = vector.colinear_manipulators.iter().find(|handles| handles.contains(&HandleId::primary(segment)));
let end_handle = vector.colinear_manipulators.iter().find(|handles| handles.contains(&HandleId::end(segment)));
let primary_handle = primary_handle.and_then(|&handles| handles.into_iter().find(|handle| handle.segment != segment));
let end_handle = end_handle.and_then(|&handles| handles.into_iter().find(|handle| handle.segment != segment));
@@ -1761,13 +1734,13 @@ impl ShapeState {
}
pub fn get_dragging_state(&self, network_interface: &NodeNetworkInterface) -> PointSelectState {
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for point in self.selected_points() {
if point.as_anchor().is_some() {
return PointSelectState::Anchor;
}
if point.get_handle_pair(&vector_data).is_some() {
if point.get_handle_pair(&vector).is_some() {
return PointSelectState::HandleWithPair;
}
}
@@ -1778,13 +1751,13 @@ impl ShapeState {
/// Returns true if at least one handle with pair is selected
pub fn handle_with_pair_selected(&mut self, network_interface: &NodeNetworkInterface) -> bool {
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for point in self.selected_points() {
if point.as_anchor().is_some() {
return false;
}
if point.get_handle_pair(&vector_data).is_some() {
if point.get_handle_pair(&vector).is_some() {
return true;
}
}
@@ -1798,22 +1771,22 @@ impl ShapeState {
let mut handles_to_update = Vec::new();
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for point in self.selected_points() {
if point.as_anchor().is_some() {
continue;
}
if let Some(other_handles) = point.get_all_connected_handles(&vector_data) {
if let Some(other_handles) = point.get_all_connected_handles(&vector) {
// Find the next closest handle in the clockwise sense
let mut candidates = other_handles.clone();
candidates.sort_by(|&handle_a, &handle_b| {
let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle");
let orig_handle_pos = point.get_position(&vector_data).expect("No handle position");
let anchor = point.get_anchor_position(&vector).expect("No anchor position for handle");
let orig_handle_pos = point.get_position(&vector).expect("No handle position");
let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position");
let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position");
let a_pos = handle_a.to_manipulator_point().get_position(&vector).expect("No handle position");
let b_pos = handle_b.to_manipulator_point().get_position(&vector).expect("No handle position");
let v_orig = (orig_handle_pos - anchor).normalize_or_zero();
@@ -1861,13 +1834,11 @@ impl ShapeState {
let mut points_to_select: Vec<(LayerNodeIdentifier, Option<PointId>, Option<ManipulatorPointId>)> = Vec::new();
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for point in self.selected_points().filter(|point| point.as_handle().is_some()) {
let anchor = point.get_anchor(&vector_data);
match point.get_handle_pair(&vector_data) {
let anchor = point.get_anchor(&vector);
match point.get_handle_pair(&vector) {
Some(handles) => {
points_to_select.push((layer, anchor, Some(handles[1].to_manipulator_point())));
}
@@ -1907,14 +1878,14 @@ impl ShapeState {
/// This can can be activated by double clicking on an anchor with the Path tool.
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
let mut process_layer = |layer| {
let vector_data = network_interface.compute_modified_vector(layer)?;
let vector = network_interface.compute_modified_vector(layer)?;
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
let mut result = None;
let mut closest_distance_squared = tolerance * tolerance;
// Find the closest anchor point on the current layer
for (&id, &anchor) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &anchor) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
let screenspace = transform_to_screenspace.transform_point2(anchor);
let distance_squared = screenspace.distance_squared(target);
@@ -1925,23 +1896,23 @@ impl ShapeState {
}
let (id, anchor) = result?;
let handles = vector_data.all_connected(id);
let handles = vector.all_connected(id);
let positions = handles
.filter_map(|handle| handle.to_manipulator_point().get_position(&vector_data))
.filter_map(|handle| handle.to_manipulator_point().get_position(&vector))
.filter(|&handle| anchor.abs_diff_eq(handle, 1e-5))
.count();
// Check if the anchor is connected to linear segments.
let one_or_more_segment_linear = vector_data.connected_linear_segments(id) != 0;
let one_or_more_segment_linear = vector.connected_linear_segments(id) != 0;
// Check by comparing the handle positions to the anchor if this manipulator group is a point
for point in self.selected_points() {
let Some(point_id) = point.as_anchor() else { continue };
if positions != 0 || one_or_more_segment_linear {
self.convert_manipulator_handles_to_colinear(&vector_data, point_id, responses, layer);
self.convert_manipulator_handles_to_colinear(&vector, point_id, responses, layer);
} else {
for handle in vector_data.all_connected(point_id) {
let Some(bezier) = vector_data.segment_from_id(handle.segment) else { continue };
for handle in vector.all_connected(point_id) {
let Some(bezier) = vector.segment_from_id(handle.segment) else { continue };
match bezier.handles {
BezierHandles::Linear => {}
@@ -1952,7 +1923,7 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Set the manipulator to have non-colinear handles
for &handles in &vector_data.colinear_manipulators {
for &handles in &vector.colinear_manipulators {
if handles.contains(&HandleId::primary(segment)) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1965,7 +1936,7 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
// Set the manipulator to have non-colinear handles
for &handles in &vector_data.colinear_manipulators {
for &handles in &vector.colinear_manipulators {
if handles.contains(&handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -2019,14 +1990,14 @@ impl ShapeState {
for (layer, points) in points_inside {
let Some(state) = self.selected_shape_state.get_mut(&layer) else { continue };
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
for point in points {
match (point, selection_change) {
(_, SelectionChange::Shrink) => state.deselect_point(point),
(ManipulatorPointId::EndHandle(_) | ManipulatorPointId::PrimaryHandle(_), _) => {
let handle = point.as_handle().expect("Handle cannot be converted");
if handle.length(&vector_data) > 0. {
if handle.length(&vector) > 0. {
state.select_point(point);
}
}
@@ -2043,9 +2014,9 @@ impl ShapeState {
}
// Also select/deselect the endpoints of respective segments
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
if !select_points && select_segments {
vector_data
vector
.segment_bezier_iter()
.filter(|(segment, _, _, _)| segments.contains(segment))
.for_each(|(_, _, start, end)| match selection_change {
@@ -2081,17 +2052,17 @@ impl ShapeState {
let mut segments_inside: HashMap<LayerNodeIdentifier, HashSet<SegmentId>> = HashMap::new();
for &layer in self.selected_shape_state.keys() {
let vector_data = network_interface.compute_modified_vector(layer);
let Some(vector_data) = vector_data else { continue };
let vector = network_interface.compute_modified_vector(layer);
let Some(vector) = vector else { continue };
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.start_point().count());
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.end_point().count());
for start in vector_data.start_point() {
assert!(vector_data.point_domain.ids().contains(&start));
assert_eq!(vector.segment_domain.ids().len(), vector.start_point().count());
assert_eq!(vector.segment_domain.ids().len(), vector.end_point().count());
for start in vector.start_point() {
assert!(vector.point_domain.ids().contains(&start));
}
for end in vector_data.end_point() {
assert!(vector_data.point_domain.ids().contains(&end));
for end in vector.end_point() {
assert!(vector.point_domain.ids().contains(&end));
}
let polygon_subpath = if let SelectionShape::Lasso(polygon) = selection_shape {
@@ -2105,7 +2076,7 @@ impl ShapeState {
};
// Selection segments
for (id, bezier, _, _) in vector_data.segment_bezier_iter() {
for (id, bezier, _, _) in vector.segment_bezier_iter() {
if select_segments {
// Select segments if they lie inside the bounding box or lasso polygon
let segment_bbox = calculate_bezier_bbox(bezier);
@@ -2154,7 +2125,7 @@ impl ShapeState {
};
if select && select_points {
let is_visible_handle = is_visible_point(id, &vector_data, path_overlay_mode, frontier_handles_info, selected_segments.clone(), &selected_points);
let is_visible_handle = is_visible_point(id, &vector, path_overlay_mode, frontier_handles_info, selected_segments.clone(), &selected_points);
if is_visible_handle {
points_inside.entry(layer).or_default().insert(id);
@@ -2164,7 +2135,7 @@ impl ShapeState {
}
// Checking for selection of anchor points
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &position) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
let transformed_position = transform.transform_point2(position);
let select = match selection_shape {
@@ -205,12 +205,12 @@ pub fn transform_cage_overlays(document: &DocumentMessageHandler, tool_data: &mu
pub fn anchor_overlays(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) {
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_viewport(layer);
overlay_context.outline_vector(&vector_data, transform);
overlay_context.outline_vector(&vector, transform);
for (_, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (_, &position) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
overlay_context.manipulator_anchor(transform.transform_point2(position), false, None);
}
}
@@ -16,7 +16,8 @@ use graph_craft::document::value::TaggedValue;
use graphene_std::renderer::Quad;
use graphene_std::table::Table;
use graphene_std::text::{FontCache, load_font};
use graphene_std::vector::{HandleExt, HandleId, ManipulatorPointId, PointId, SegmentId, VectorData, VectorModification, VectorModificationType};
use graphene_std::vector::misc::{HandleId, ManipulatorPointId};
use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModification, VectorModificationType};
use kurbo::{CubicBez, Line, ParamCurveExtrema, PathSeg, Point, QuadBez};
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
@@ -47,14 +48,12 @@ where
let mut best_distance_squared = max_distance * max_distance;
for layer in layers {
let viewspace = document.metadata().transform_to_viewport(layer);
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
for id in vector_data.extendable_points(preferences.vector_meshes) {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for id in vector.extendable_points(preferences.vector_meshes) {
if exclude(id) {
continue;
}
let Some(point) = vector_data.point_domain.position_from_id(id) else { continue };
let Some(point) = vector.point_domain.position_from_id(id) else { continue };
let distance_squared = viewspace.transform_point2(point).distance_squared(goal);
@@ -88,16 +87,16 @@ pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageH
Quad::from_box([DVec2::ZERO + vertical_offset, far + vertical_offset])
}
pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData, prefer_handle_direction: bool) -> Option<f64> {
let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point);
let anchor_position = vector_data.point_domain.position_from_id(anchor)?;
let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector_data);
let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data);
pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector: &Vector, prefer_handle_direction: bool) -> Option<f64> {
let is_start = |point: PointId, segment: SegmentId| vector.segment_start_from_id(segment) == Some(point);
let anchor_position = vector.point_domain.position_from_id(anchor)?;
let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector);
let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector);
let start_point = if is_start(anchor, segment) {
vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id))
vector.segment_end_from_id(segment).and_then(|id| vector.point_domain.position_from_id(id))
} else {
vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id))
vector.segment_start_from_id(segment).and_then(|id| vector.point_domain.position_from_id(id))
};
let required_handle = if is_start(anchor, segment) {
@@ -115,9 +114,9 @@ pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data:
required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X))
}
pub fn adjust_handle_colinearity(handle: HandleId, anchor_position: DVec2, target_control_point: DVec2, vector_data: &VectorData, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
let Some(other_handle) = vector_data.other_colinear_handle(handle) else { return };
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector_data) else {
pub fn adjust_handle_colinearity(handle: HandleId, anchor_position: DVec2, target_control_point: DVec2, vector: &Vector, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
let Some(other_handle) = vector.other_colinear_handle(handle) else { return };
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector) else {
return;
};
let Some(direction) = (anchor_position - target_control_point).try_normalize() else { return };
@@ -132,12 +131,12 @@ pub fn restore_previous_handle_position(
handle: HandleId,
original_c: DVec2,
anchor_position: DVec2,
vector_data: &VectorData,
vector: &Vector,
layer: LayerNodeIdentifier,
responses: &mut VecDeque<Message>,
) -> Option<HandleId> {
let other_handle = vector_data.other_colinear_handle(handle)?;
let handle_position = other_handle.to_manipulator_point().get_position(vector_data)?;
let other_handle = vector.other_colinear_handle(handle)?;
let handle_position = other_handle.to_manipulator_point().get_position(vector)?;
let direction = (anchor_position - original_c).try_normalize()?;
let old_relative_position = (handle_position - anchor_position).length() * direction;
@@ -151,16 +150,8 @@ pub fn restore_previous_handle_position(
Some(other_handle)
}
pub fn restore_g1_continuity(
handle: HandleId,
other_handle: HandleId,
control_point: DVec2,
anchor_position: DVec2,
vector_data: &VectorData,
layer: LayerNodeIdentifier,
responses: &mut VecDeque<Message>,
) {
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector_data) else {
pub fn restore_g1_continuity(handle: HandleId, other_handle: HandleId, control_point: DVec2, anchor_position: DVec2, vector: &Vector, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
let Some(handle_position) = other_handle.to_manipulator_point().get_position(vector) else {
return;
};
let Some(direction) = (anchor_position - control_point).try_normalize() else { return };
@@ -177,7 +168,7 @@ pub fn restore_g1_continuity(
/// Check whether a point is visible in the current overlay mode.
pub fn is_visible_point(
manipulator_point_id: ManipulatorPointId,
vector_data: &VectorData,
vector: &Vector,
path_overlay_mode: PathOverlayMode,
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
selected_segments: Vec<SegmentId>,
@@ -194,14 +185,14 @@ pub fn is_visible_point(
}
// Either the segment is a part of selected segments or the opposite handle is a part of existing selection
let Some(handle_pair) = manipulator_point_id.get_handle_pair(vector_data) else { return false };
let Some(handle_pair) = manipulator_point_id.get_handle_pair(vector) else { return false };
let other_handle = handle_pair[1].to_manipulator_point();
// Return whether the list of selected points contain the other handle
selected_points.contains(&other_handle)
}
(PathOverlayMode::FrontierHandles, false) => {
let Some(anchor) = manipulator_point_id.get_anchor(vector_data) else {
let Some(anchor) = manipulator_point_id.get_anchor(vector) else {
warn!("No anchor for selected handle");
return false;
};
@@ -600,13 +591,13 @@ pub fn make_path_editable_is_allowed(network_interface: &NodeNetworkInterface, m
return None;
}
// Must be a layer of type Table<VectorData>
// Must be a layer of type Table<Vector>
let compatible_type = NodeGraphLayer::new(first_layer, network_interface)
.horizontal_layer_flow()
.nth(1)
.map(|node_id| {
let (output_type, _) = network_interface.output_type(&node_id, 0, &[]);
output_type.nested_type() == concrete!(Table<VectorData>).nested_type()
output_type.nested_type() == concrete!(Table<Vector>).nested_type()
})
.unwrap_or_default();
if !compatible_type {
@@ -11,6 +11,7 @@ use crate::messages::tool::common_functionality::snapping::SnapData;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::transformation_cage::*;
use graph_craft::document::NodeId;
use graphene_std::Artboard;
use graphene_std::renderer::Quad;
use graphene_std::table::Table;
@@ -337,8 +338,8 @@ impl Fsm for ArtboardToolFsmState {
responses.add(GraphOperationMessage::NewArtboard {
id,
artboard: graphene_std::Artboard {
graphic_group: Table::new(),
artboard: Artboard {
group: Table::new(),
label: String::from("Artboard"),
location: start.min(end).round().as_ivec2(),
dimensions: (start.round() - end.round()).abs().as_ivec2(),
@@ -360,9 +360,9 @@ mod test_freehand {
use crate::messages::tool::tool_messages::freehand_tool::FreehandOptionsUpdate;
use crate::test_utils::test_prelude::*;
use glam::{DAffine2, DVec2};
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
async fn get_vector_data(editor: &mut EditorTestUtils) -> Vec<(VectorData, DAffine2)> {
async fn get_vector_and_transform_list(editor: &mut EditorTestUtils) -> Vec<(Vector, DAffine2)> {
let document = editor.active_document();
let layers = document.metadata().all_layers();
@@ -372,23 +372,22 @@ mod test_freehand {
// Only get layers with path nodes
let _ = graph_layer.upstream_visible_node_id_from_name_in_layer("Path")?;
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().transform_to_viewport(layer);
Some((vector_data, transform))
Some((vector, transform))
})
.collect()
}
fn verify_path_points(vector_data_list: &[(VectorData, DAffine2)], expected_captured_points: &[DVec2], tolerance: f64) -> Result<(), String> {
assert_eq!(vector_data_list.len(), 1, "there should be one vector data");
fn verify_path_points(vector_and_transform_list: &[(Vector, DAffine2)], expected_captured_points: &[DVec2], tolerance: f64) -> Result<(), String> {
assert_eq!(vector_and_transform_list.len(), 1, "There should be one row of Vector geometry");
let path_data = vector_data_list.iter().find(|(data, _)| data.point_domain.ids().len() > 0).ok_or("Could not find path data")?;
let (vector, transform) = vector_and_transform_list.iter().find(|(data, _)| data.point_domain.ids().len() > 0).ok_or("Could not find path data")?;
let (vector_data, transform) = path_data;
let point_count = vector_data.point_domain.ids().len();
let segment_count = vector_data.segment_domain.ids().len();
let point_count = vector.point_domain.ids().len();
let segment_count = vector.segment_domain.ids().len();
let actual_positions: Vec<DVec2> = vector_data.point_domain.positions().iter().map(|&position| transform.transform_point2(position)).collect();
let actual_positions: Vec<DVec2> = vector.point_domain.positions().iter().map(|&position| transform.transform_point2(position)).collect();
if segment_count != point_count - 1 {
return Err(format!("Expected segments to be one less than points, got {} segments for {} points", segment_count, point_count));
@@ -435,8 +434,8 @@ mod test_freehand {
let expected_captured_points = &mouse_points[1..];
editor.drag_path(&mouse_points, ModifierKeys::empty()).await;
let vector_data_list = get_vector_data(&mut editor).await;
verify_path_points(&vector_data_list, expected_captured_points, 1.).expect("Path points verification failed");
let vector_and_transform_list = get_vector_and_transform_list(&mut editor).await;
verify_path_points(&vector_and_transform_list, expected_captured_points, 1.).expect("Path points verification failed");
}
#[tokio::test]
@@ -468,12 +467,12 @@ mod test_freehand {
)
.await;
let initial_vector_data = get_vector_data(&mut editor).await;
assert!(!initial_vector_data.is_empty(), "No vector data found after initial drawing");
let initial_vector_and_transform_list = get_vector_and_transform_list(&mut editor).await;
assert!(!initial_vector_and_transform_list.is_empty(), "No Vector geometry found after initial drawing");
let (initial_data, transform) = &initial_vector_data[0];
let initial_point_count = initial_data.point_domain.ids().len();
let initial_segment_count = initial_data.segment_domain.ids().len();
let (initial_vector, initial_transform) = &initial_vector_and_transform_list[0];
let initial_point_count = initial_vector.point_domain.ids().len();
let initial_segment_count = initial_vector.segment_domain.ids().len();
assert!(initial_point_count >= 2, "Expected at least 2 points in initial path, found {}", initial_point_count);
assert_eq!(
@@ -484,15 +483,15 @@ mod test_freehand {
initial_segment_count
);
let extendable_points = initial_data.extendable_points(false).collect::<Vec<_>>();
let extendable_points = initial_vector.extendable_points(false).collect::<Vec<_>>();
assert!(!extendable_points.is_empty(), "No extendable points found in the path");
let endpoint_id = extendable_points[0];
let endpoint_pos_option = initial_data.point_domain.position_from_id(endpoint_id);
let endpoint_pos_option = initial_vector.point_domain.position_from_id(endpoint_id);
assert!(endpoint_pos_option.is_some(), "Could not find position for endpoint");
let endpoint_pos = endpoint_pos_option.unwrap();
let endpoint_viewport_pos = transform.transform_point2(endpoint_pos);
let endpoint_viewport_pos = initial_transform.transform_point2(endpoint_pos);
assert!(endpoint_viewport_pos.is_finite(), "Endpoint position is not finite");
@@ -527,12 +526,12 @@ mod test_freehand {
)
.await;
let extended_vector_data = get_vector_data(&mut editor).await;
assert!(!extended_vector_data.is_empty(), "No vector data found after extension");
let extended_vector_and_transform = get_vector_and_transform_list(&mut editor).await;
assert!(!extended_vector_and_transform.is_empty(), "No Vector geometry found after extension");
let (extended_data, _) = &extended_vector_data[0];
let extended_point_count = extended_data.point_domain.ids().len();
let extended_segment_count = extended_data.segment_domain.ids().len();
let (extended_vector, _) = &extended_vector_and_transform[0];
let extended_point_count = extended_vector.point_domain.ids().len();
let extended_segment_count = extended_vector.segment_domain.ids().len();
assert!(
extended_point_count > initial_point_count,
@@ -585,12 +584,12 @@ mod test_freehand {
)
.await;
let initial_vector_data = get_vector_data(&mut editor).await;
assert!(!initial_vector_data.is_empty(), "No vector data found after initial drawing");
let initial_vector_and_transform = get_vector_and_transform_list(&mut editor).await;
assert!(!initial_vector_and_transform.is_empty(), "No vector geometry found after initial drawing");
let (initial_data, _) = &initial_vector_data[0];
let initial_point_count = initial_data.point_domain.ids().len();
let initial_segment_count = initial_data.segment_domain.ids().len();
let (initial_vector, _) = &initial_vector_and_transform[0];
let initial_point_count = initial_vector.point_domain.ids().len();
let initial_segment_count = initial_vector.segment_domain.ids().len();
let existing_layer_id = {
let document = editor.active_document();
@@ -636,8 +635,8 @@ mod test_freehand {
)
.await;
let final_vector_data = get_vector_data(&mut editor).await;
assert!(!final_vector_data.is_empty(), "No vector data found after second drawing");
let final_vector_and_transform = get_vector_and_transform_list(&mut editor).await;
assert!(!final_vector_and_transform.is_empty(), "No vector geometry found after second drawing");
// Verify we still have only one layer
let layer_count = {
@@ -646,9 +645,9 @@ mod test_freehand {
};
assert_eq!(layer_count, 1, "Expected only one layer after drawing with Shift key");
let (final_data, _) = &final_vector_data[0];
let final_point_count = final_data.point_domain.ids().len();
let final_segment_count = final_data.segment_domain.ids().len();
let (final_vector, _) = &final_vector_and_transform[0];
let final_point_count = final_vector.point_domain.ids().len();
let final_segment_count = final_vector.segment_domain.ids().len();
assert!(
final_point_count > initial_point_count,
@@ -27,8 +27,8 @@ use graphene_std::renderer::Quad;
use graphene_std::transform::ReferencePoint;
use graphene_std::uuid::NodeId;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, VectorData};
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
use graphene_std::vector::misc::{HandleId, ManipulatorPointId};
use graphene_std::vector::{HandleExt, NoHashBuilder, PointId, SegmentId, Vector, VectorModificationType};
use std::vec;
#[derive(Default, ExtractField)]
@@ -285,7 +285,7 @@ impl LayoutHolder for PathTool {
.selected_index(Some(self.options.path_overlay_mode as u32))
.widget_holder();
// Works only if a single layer is selected and its type is vectordata
// Works only if a single layer is selected and its type is Vector
let path_node_button = TextButton::new("Make Path Editable")
.icon(Some("NodeShape".into()))
.tooltip("Make Path Editable")
@@ -619,10 +619,10 @@ impl PathToolData {
self.can_toggle_colinearity = match &selection_status {
SelectionStatus::None => false,
SelectionStatus::One(single_selected_point) => {
let vector_data = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
if single_selected_point.id.get_handle_pair(&vector_data).is_some() {
let anchor = single_selected_point.id.get_anchor(&vector_data).expect("Cannot find connected anchor");
vector_data.all_connected(anchor).count() <= 2
let vector = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
if single_selected_point.id.get_handle_pair(&vector).is_some() {
let anchor = single_selected_point.id.get_anchor(&vector).expect("Cannot find connected anchor");
vector.all_connected(anchor).count() <= 2
} else {
false
}
@@ -761,13 +761,13 @@ impl PathToolData {
if handle_drag_from_anchor {
if let Some((layer, point)) = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) {
// Check that selected point is an anchor
if let (Some(point_id), Some(vector_data)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) {
let handles = vector_data.all_connected(point_id).collect::<Vec<_>>();
if let (Some(point_id), Some(vector)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) {
let handles = vector.all_connected(point_id).collect::<Vec<_>>();
self.alt_clicked_on_anchor = true;
for handle in &handles {
let modification_type = handle.set_relative_position(DVec2::ZERO);
responses.add(GraphOperationMessage::Vector { layer, modification_type });
for &handles in &vector_data.colinear_manipulators {
for &handles in &vector.colinear_manipulators {
if handles.contains(handle) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -783,16 +783,16 @@ impl PathToolData {
}
}
if let Some((Some(point), Some(vector_data))) = shape_editor
if let Some((Some(point), Some(vector))) = shape_editor
.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD)
.map(|(layer, point)| (point.as_anchor(), document.network_interface.compute_modified_vector(layer)))
{
let handles = vector_data
let handles = vector
.all_connected(point)
.filter(|handle| handle.length(&vector_data) < 1e-6)
.filter(|handle| handle.length(&vector) < 1e-6)
.map(|handle| handle.to_manipulator_point())
.collect::<Vec<_>>();
let endpoint = vector_data.extendable_points(false).any(|anchor| point == anchor);
let endpoint = vector.extendable_points(false).any(|anchor| point == anchor);
if drag_zero_handle && (handles.len() == 1 && !endpoint) {
shape_editor.deselect_all_points();
@@ -883,27 +883,25 @@ impl PathToolData {
let mut manipulators = HashMap::with_hasher(NoHashBuilder);
let mut unselected = Vec::new();
for (&layer, state) in &shape_editor.selected_shape_state {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
for point in state.selected_points() {
let Some(anchor) = point.get_anchor(&vector_data) else { continue };
let Some(anchor) = point.get_anchor(&vector) else { continue };
layer_manipulators.insert(anchor);
let Some([handle1, handle2]) = point.get_handle_pair(&vector_data) else { continue };
let Some([handle1, handle2]) = point.get_handle_pair(&vector) else { continue };
let Some(handle) = point.as_handle() else { continue };
// Check which handle is selected and which is opposite
let opposite = if handle == handle1 { handle2 } else { handle1 };
self.opposite_handle_position = if self.opposite_handle_position.is_none() {
opposite.to_manipulator_point().get_position(&vector_data)
opposite.to_manipulator_point().get_position(&vector)
} else {
self.opposite_handle_position
};
}
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &position) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
if layer_manipulators.contains(&id) {
continue;
}
@@ -956,17 +954,17 @@ impl PathToolData {
return false;
};
let Some(vector_data) = document.network_interface.compute_modified_vector(*layer) else {
let Some(vector) = document.network_interface.compute_modified_vector(*layer) else {
self.opposing_handle_lengths = Some(shape_editor.opposing_handle_lengths(document));
return false;
};
// Check if handle has a pair (to ignore handles of edges of open paths)
if let Some(handle_pair) = selected_handle_id.get_handle_pair(&vector_data) {
if let Some(handle_pair) = selected_handle_id.get_handle_pair(&vector) {
let opposite_handle_length = handle_pair.iter().filter(|&&h| h.to_manipulator_point() != selected_handle_id).find_map(|&h| {
let opp_handle_pos = h.to_manipulator_point().get_position(&vector_data)?;
let opp_anchor_id = h.to_manipulator_point().get_anchor(&vector_data)?;
let opp_anchor_pos = vector_data.point_domain.position_from_id(opp_anchor_id)?;
let opp_handle_pos = h.to_manipulator_point().get_position(&vector)?;
let opp_anchor_id = h.to_manipulator_point().get_anchor(&vector)?;
let opp_anchor_pos = vector.point_domain.position_from_id(opp_anchor_id)?;
Some((opp_handle_pos - opp_anchor_pos).length())
});
@@ -997,11 +995,11 @@ impl PathToolData {
let handle_id = selected_handle.to_manipulator_point();
let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
let vector_data = document.network_interface.compute_modified_vector(*layer)?;
let vector = document.network_interface.compute_modified_vector(*layer)?;
let handle_position_local = selected_handle.to_manipulator_point().get_position(&vector_data)?;
let anchor_id = selected_handle.to_manipulator_point().get_anchor(&vector_data)?;
let anchor_position_local = vector_data.point_domain.position_from_id(anchor_id)?;
let handle_position_local = selected_handle.to_manipulator_point().get_position(&vector)?;
let anchor_id = selected_handle.to_manipulator_point().get_anchor(&vector)?;
let anchor_position_local = vector.point_domain.position_from_id(anchor_id)?;
let handle_position_document = layer_to_document.transform_point2(handle_position_local);
let anchor_position_document = layer_to_document.transform_point2(anchor_position_local);
@@ -1024,24 +1022,24 @@ impl PathToolData {
) -> f64 {
let current_angle = -handle_vector.angle_to(DVec2::X);
if let Some((vector_data, layer)) = shape_editor
if let Some((vector, layer)) = shape_editor
.selected_shape_state
.iter()
.next()
.and_then(|(layer, _)| document.network_interface.compute_modified_vector(*layer).map(|vector_data| (vector_data, layer)))
.and_then(|(layer, _)| document.network_interface.compute_modified_vector(*layer).map(|vector| (vector, layer)))
{
let adjacent_anchor = check_handle_over_adjacent_anchor(handle_id, &vector_data);
let adjacent_anchor = check_handle_over_adjacent_anchor(handle_id, &vector);
let mut required_angle = None;
// If the handle is dragged over one of its adjacent anchors while holding down the Ctrl key, compute the angle based on the tangent formed with the neighboring anchor points.
if adjacent_anchor.is_some() && lock_angle && !self.angle_locked {
let anchor = handle_id.get_anchor(&vector_data);
let (angle, anchor_position) = calculate_adjacent_anchor_tangent(handle_id, anchor, adjacent_anchor, &vector_data);
let anchor = handle_id.get_anchor(&vector);
let (angle, anchor_position) = calculate_adjacent_anchor_tangent(handle_id, anchor, adjacent_anchor, &vector);
let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
self.adjacent_anchor_offset = handle_id
.get_anchor_position(&vector_data)
.get_anchor_position(&vector)
.and_then(|handle_anchor| anchor_position.map(|adjacent_anchor| layer_to_document.transform_point2(adjacent_anchor) - layer_to_document.transform_point2(handle_anchor)));
required_angle = angle;
@@ -1049,7 +1047,7 @@ impl PathToolData {
// If the handle is dragged near its adjacent anchors while holding down the Ctrl key, compute the angle using the tangent direction of neighboring segments.
if relative_vector.length() < 25. && lock_angle && !self.angle_locked {
required_angle = calculate_lock_angle(self, shape_editor, responses, document, &vector_data, handle_id, tangent_to_neighboring_tangents);
required_angle = calculate_lock_angle(self, shape_editor, responses, document, &vector, handle_id, tangent_to_neighboring_tangents);
}
// Finalize and apply angle locking if a valid target angle was determined.
@@ -1158,17 +1156,17 @@ impl PathToolData {
self.snapping_axis = None;
}
fn get_normalized_tangent(&mut self, point: PointId, segment: SegmentId, vector_data: &VectorData) -> Option<DVec2> {
let other_point = vector_data.other_point(segment, point)?;
let position = ManipulatorPointId::Anchor(point).get_position(vector_data)?;
fn get_normalized_tangent(&mut self, point: PointId, segment: SegmentId, vector: &Vector) -> Option<DVec2> {
let other_point = vector.other_point(segment, point)?;
let position = ManipulatorPointId::Anchor(point).get_position(vector)?;
let mut handles = vector_data.all_connected(other_point);
let mut handles = vector.all_connected(other_point);
let other_handle = handles.find(|handle| handle.segment == segment)?;
let target_position = if other_handle.length(vector_data) == 0. {
ManipulatorPointId::Anchor(other_point).get_position(vector_data)?
let target_position = if other_handle.length(vector) == 0. {
ManipulatorPointId::Anchor(other_point).get_position(vector)?
} else {
other_handle.to_manipulator_point().get_position(vector_data)?
other_handle.to_manipulator_point().get_position(vector)?
};
let tangent_vector = target_position - position;
@@ -1207,19 +1205,17 @@ impl PathToolData {
let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else {
return false;
};
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
return false;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return false };
// Check that the handles of anchor point are also colinear
if !vector_data.colinear(*anchor) {
if !vector.colinear(*anchor) {
return false;
};
let Some(point_id) = anchor.as_anchor() else { return false };
let mut connected_segments = [None, None];
for (segment, bezier, start, end) in vector_data.segment_bezier_iter() {
for (segment, bezier, start, end) in vector.segment_bezier_iter() {
if start == point_id || end == point_id {
match (connected_segments[0], connected_segments[1]) {
(None, None) => connected_segments[0] = Some(SlidingSegmentData { segment_id: segment, bezier, start }),
@@ -1253,7 +1249,7 @@ impl PathToolData {
let anchor = sliding_point_info.anchor;
let layer = sliding_point_info.layer;
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { return };
let Some(vector) = network_interface.compute_modified_vector(layer) else { return };
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
let layer_pos = transform.inverse().transform_point2(target_position);
@@ -1272,12 +1268,12 @@ impl PathToolData {
};
// Move the anchor to the new position
let Some(current_position) = ManipulatorPointId::Anchor(anchor).get_position(&vector_data) else {
let Some(current_position) = ManipulatorPointId::Anchor(anchor).get_position(&vector) else {
return;
};
let delta = new_position - current_position;
shape_editor.move_anchor(anchor, &vector_data, delta, layer, None, responses);
shape_editor.move_anchor(anchor, &vector, delta, layer, None, responses);
// Make a split at the t_value
let [first, second] = closer_segment.bezier.split(TValue::Parametric(t_value));
@@ -1419,19 +1415,19 @@ impl PathToolData {
let Some(layer) = document.network_interface.selected_nodes().selected_layers(document.metadata()).next() else {
return;
};
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return };
let Some(point_id) = shape_editor.selected_points().next().unwrap().get_anchor(&vector_data) else {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
let Some(point_id) = shape_editor.selected_points().next().unwrap().get_anchor(&vector) else {
return;
};
if vector_data.connected_count(point_id) == 2 {
let connected_segments: Vec<HandleId> = vector_data.all_connected(point_id).collect();
if vector.connected_count(point_id) == 2 {
let connected_segments: Vec<HandleId> = vector.all_connected(point_id).collect();
let segment1 = connected_segments[0];
let Some(tangent1) = self.get_normalized_tangent(point_id, segment1.segment, &vector_data) else {
let Some(tangent1) = self.get_normalized_tangent(point_id, segment1.segment, &vector) else {
return;
};
let segment2 = connected_segments[1];
let Some(tangent2) = self.get_normalized_tangent(point_id, segment2.segment, &vector_data) else {
let Some(tangent2) = self.get_normalized_tangent(point_id, segment2.segment, &vector) else {
return;
};
@@ -1565,11 +1561,11 @@ impl Fsm for PathToolFsmState {
let selected_layers = shape_editor.selected_layers().cloned().collect::<Vec<_>>();
for layer in selected_layers {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let selected_state = shape_editor.selected_shape_state.entry(layer).or_default();
for (segment, _, start, end) in vector_data.segment_bezier_iter() {
for (segment, _, start, end) in vector.segment_bezier_iter() {
if selected_state.is_segment_selected(segment) {
selected_state.select_point(ManipulatorPointId::Anchor(start));
selected_state.select_point(ManipulatorPointId::Anchor(end));
@@ -1610,11 +1606,11 @@ impl Fsm for PathToolFsmState {
let selected_layers = shape_editor.selected_layers().cloned().collect::<Vec<_>>();
for layer in selected_layers {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let selected_state = shape_editor.selected_shape_state.entry(layer).or_default();
for (segment, _, start, end) in vector_data.segment_bezier_iter() {
for (segment, _, start, end) in vector.segment_bezier_iter() {
let first_selected = selected_state.is_point_selected(ManipulatorPointId::Anchor(start));
let second_selected = selected_state.is_point_selected(ManipulatorPointId::Anchor(end));
if first_selected && second_selected {
@@ -1663,12 +1659,12 @@ impl Fsm for PathToolFsmState {
let mut segment_endpoints: HashMap<SegmentId, Vec<PointId>> = HashMap::new();
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
// The points which are part of only one segment will be rendered
let mut selected_segments_by_point: HashMap<PointId, Vec<SegmentId>> = HashMap::new();
for (segment_id, _bezier, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
if selected_segments.contains(&segment_id) {
selected_segments_by_point.entry(start).or_default().push(segment_id);
selected_segments_by_point.entry(end).or_default().push(segment_id);
@@ -1721,8 +1717,8 @@ impl Fsm for PathToolFsmState {
);
let Some((layer, manipulator_point_id)) = nearest_visible_point_indices else { return };
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return };
let Some(position) = manipulator_point_id.get_position(&vector_data) else {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
let Some(position) = manipulator_point_id.get_position(&vector) else {
error!("No position for hovered point");
return;
};
@@ -1847,10 +1843,10 @@ impl Fsm for PathToolFsmState {
};
for (layer, points) in points_inside {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for point in points {
let Some(position) = point.get_position(&vector_data) else { continue };
let Some(position) = point.get_position(&vector) else { continue };
let transform = document.metadata().transform_to_viewport(layer);
let position = transform.transform_point2(position);
@@ -1865,11 +1861,11 @@ impl Fsm for PathToolFsmState {
}
for (layer, segments) in segments_inside {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
for (segment, bezier, _, _) in vector_data.segment_bezier_iter() {
for (segment, bezier, _, _) in vector.segment_bezier_iter() {
if segments.contains(&segment) {
overlay_context.outline_overlay_bezier(bezier, transform);
}
@@ -2392,8 +2388,8 @@ impl Fsm for PathToolFsmState {
if !drag_occurred && !tool_data.molding_segment && ((point_mode && !segment_mode) || (segment_mode && tool_data.segment_editing_modifier)) {
if tool_data.delete_segment_pressed {
if let Some(vector_data) = document.network_interface.compute_modified_vector(segment.layer()) {
shape_editor.dissolve_segment(responses, segment.layer(), &vector_data, segment.segment(), segment.points());
if let Some(vector) = document.network_interface.compute_modified_vector(segment.layer()) {
shape_editor.dissolve_segment(responses, segment.layer(), &vector, segment.segment(), segment.points());
}
} else {
let is_segment_selected = shape_editor
@@ -2580,17 +2576,15 @@ impl Fsm for PathToolFsmState {
continue;
}
let Some(old_vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let Some(old_vector) = document.network_interface.compute_modified_vector(layer) else { continue };
// Also get the transform node that is applied on the layer if it exists
let transform = document.metadata().transform_to_document(layer);
let mut new_vector_data = VectorData::default();
let mut new_vector = Vector::default();
let mut selected_points_by_segment = HashSet::new();
old_vector_data
old_vector
.segment_bezier_iter()
.filter(|(segment, _, _, _)| layer_selection_state.is_segment_selected(*segment))
.for_each(|(_, _, start, end)| {
@@ -2599,16 +2593,16 @@ impl Fsm for PathToolFsmState {
});
// Add all the selected points
for (point, position) in old_vector_data.point_domain.iter() {
for (point, position) in old_vector.point_domain.iter() {
if layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(point)) || selected_points_by_segment.contains(&point) {
new_vector_data.point_domain.push(point, position);
new_vector.point_domain.push(point, position);
}
}
let find_index = |id: PointId| new_vector_data.point_domain.iter().enumerate().find(|(_, (point_id, _))| *point_id == id).map(|(index, _)| index);
let find_index = |id: PointId| new_vector.point_domain.iter().enumerate().find(|(_, (point_id, _))| *point_id == id).map(|(index, _)| index);
// Add segments which have selected ends
for ((segment_id, bezier, start, end), stroke) in old_vector_data.segment_bezier_iter().zip(old_vector_data.segment_domain.stroke().iter()) {
for ((segment_id, bezier, start, end), stroke) in old_vector.segment_bezier_iter().zip(old_vector.segment_domain.stroke().iter()) {
let both_ends_selected = layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(start)) && layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(end));
let segment_selected = layer_selection_state.is_segment_selected(segment_id);
@@ -2618,17 +2612,17 @@ impl Fsm for PathToolFsmState {
error!("Point does not exist in point domain");
return PathToolFsmState::Ready;
};
new_vector_data.segment_domain.push(segment_id, start_index, end_index, bezier.handles, *stroke);
new_vector.segment_domain.push(segment_id, start_index, end_index, bezier.handles, *stroke);
}
}
for handles in old_vector_data.colinear_manipulators {
if new_vector_data.segment_domain.ids().contains(&handles[0].segment) && new_vector_data.segment_domain.ids().contains(&handles[1].segment) {
new_vector_data.colinear_manipulators.push(handles);
for handles in old_vector.colinear_manipulators {
if new_vector.segment_domain.ids().contains(&handles[0].segment) && new_vector.segment_domain.ids().contains(&handles[1].segment) {
new_vector.colinear_manipulators.push(handles);
}
}
buffer.push((layer, new_vector_data, transform));
buffer.push((layer, new_vector, transform));
}
if clipboard == Clipboard::Device {
@@ -2650,7 +2644,7 @@ impl Fsm for PathToolFsmState {
}
(_, PathToolMessage::Paste { data }) => {
// Deserialize the data
if let Ok(data) = serde_json::from_str::<Vec<(LayerNodeIdentifier, VectorData, DAffine2)>>(&data) {
if let Ok(data) = serde_json::from_str::<Vec<(LayerNodeIdentifier, Vector, DAffine2)>>(&data) {
shape_editor.deselect_all_points();
responses.add(DocumentMessage::AddTransaction);
let mut new_layers = Vec::new();
@@ -2690,7 +2684,7 @@ impl Fsm for PathToolFsmState {
layer
};
// Create new point ids and add those into the existing vector data
// Create new point ids and add those into the existing vector content
let mut points_map = HashMap::new();
for (point, position) in new_vector.point_domain.iter() {
let new_point_id = PointId::generate();
@@ -2701,7 +2695,7 @@ impl Fsm for PathToolFsmState {
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
// Create new segment ids and add the segments into the existing vector data
// Create new segment ids and add the segments into the existing vector content
let mut segments_map = HashMap::new();
for (segment_id, bezier, start, end) in new_vector.segment_bezier_iter() {
let new_segment_id = SegmentId::generate();
@@ -2777,13 +2771,13 @@ impl Fsm for PathToolFsmState {
if layer_selection_state.is_empty() {
continue;
}
let Some(old_vector_data) = document.network_interface.compute_modified_vector(layer) else {
let Some(old_vector) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
// Add all the selected points
let mut selected_points_by_segment = HashSet::new();
old_vector_data
old_vector
.segment_bezier_iter()
.filter(|(segment, _, _, _)| layer_selection_state.is_segment_selected(*segment))
.for_each(|(_, _, start, end)| {
@@ -2792,7 +2786,7 @@ impl Fsm for PathToolFsmState {
});
let mut points_map = HashMap::new();
for (point, position) in old_vector_data.point_domain.iter() {
for (point, position) in old_vector.point_domain.iter() {
// TODO: Either the point is selected or it is an endpoint of a selected segment
if layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(point)) || selected_points_by_segment.contains(&point) {
@@ -2808,7 +2802,7 @@ impl Fsm for PathToolFsmState {
let mut segments_map = HashMap::new();
for (segment_id, bezier, start, end) in old_vector_data.segment_bezier_iter() {
for (segment_id, bezier, start, end) in old_vector.segment_bezier_iter() {
let both_ends_selected = layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(start)) && layer_selection_state.is_point_selected(ManipulatorPointId::Anchor(end));
let segment_selected = layer_selection_state.is_segment_selected(segment_id);
@@ -2830,7 +2824,7 @@ impl Fsm for PathToolFsmState {
}
}
for handles in old_vector_data.colinear_manipulators {
for handles in old_vector.colinear_manipulators {
let to_new_handle = |handle: HandleId| -> HandleId {
HandleId {
ty: handle.ty,
@@ -3104,13 +3098,13 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: &
let Some(layer) = selection_layers.find(|(_, v)| *v > 0).map(|(k, _)| k) else {
return SelectionStatus::None;
};
let Some(vector_data) = network_interface.compute_modified_vector(layer) else {
let Some(vector) = network_interface.compute_modified_vector(layer) else {
return SelectionStatus::None;
};
let Some(&point) = shape_state.selected_points().next() else {
return SelectionStatus::None;
};
let Some(local_position) = point.get_position(&vector_data) else {
let Some(local_position) = point.get_position(&vector) else {
return SelectionStatus::None;
};
@@ -3118,7 +3112,7 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: &
.document_metadata()
.transform_to_document_if_feeds(layer, network_interface)
.transform_point2(local_position);
let manipulator_angle = if vector_data.colinear(point) { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free };
let manipulator_angle = if vector.colinear(point) { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free };
return SelectionStatus::One(SingleSelectedPoint {
coordinates,
@@ -3143,40 +3137,40 @@ fn calculate_lock_angle(
shape_state: &mut ShapeState,
responses: &mut VecDeque<Message>,
document: &DocumentMessageHandler,
vector_data: &VectorData,
vector: &Vector,
handle_id: ManipulatorPointId,
tangent_to_neighboring_tangents: bool,
) -> Option<f64> {
let anchor = handle_id.get_anchor(vector_data)?;
let anchor_position = vector_data.point_domain.position_from_id(anchor);
let anchor = handle_id.get_anchor(vector)?;
let anchor_position = vector.point_domain.position_from_id(anchor);
let current_segment = handle_id.get_segment();
let points_connected = vector_data.connected_count(anchor);
let points_connected = vector.connected_count(anchor);
let (anchor_position, segment) = anchor_position.zip(current_segment)?;
if points_connected == 1 {
calculate_segment_angle(anchor, segment, vector_data, false)
calculate_segment_angle(anchor, segment, vector, false)
} else {
let opposite_handle = handle_id
.get_handle_pair(vector_data)
.get_handle_pair(vector)
.iter()
.flatten()
.find(|&h| h.to_manipulator_point() != handle_id)
.copied()
.map(|h| h.to_manipulator_point());
let opposite_handle_position = opposite_handle.and_then(|h| h.get_position(vector_data)).filter(|pos| (pos - anchor_position).length() > 1e-6);
let opposite_handle_position = opposite_handle.and_then(|h| h.get_position(vector)).filter(|pos| (pos - anchor_position).length() > 1e-6);
if let Some(opposite_pos) = opposite_handle_position {
if !vector_data.colinear_manipulators.iter().flatten().map(|h| h.to_manipulator_point()).any(|h| h == handle_id) {
if !vector.colinear_manipulators.iter().flatten().map(|h| h.to_manipulator_point()).any(|h| h == handle_id) {
shape_state.convert_selected_manipulators_to_colinear_handles(responses, document);
tool_data.temporary_colinear_handles = true;
}
Some(-(opposite_pos - anchor_position).angle_to(DVec2::X))
} else {
let angle_1 = vector_data
let angle_1 = vector
.adjacent_segment(&handle_id)
.and_then(|(_, adjacent_segment)| calculate_segment_angle(anchor, adjacent_segment, vector_data, false));
.and_then(|(_, adjacent_segment)| calculate_segment_angle(anchor, adjacent_segment, vector, false));
let angle_2 = calculate_segment_angle(anchor, segment, vector_data, false);
let angle_2 = calculate_segment_angle(anchor, segment, vector, false);
match (angle_1, angle_2) {
(Some(angle_1), Some(angle_2)) => {
@@ -3195,37 +3189,32 @@ fn calculate_lock_angle(
}
}
fn check_handle_over_adjacent_anchor(handle_id: ManipulatorPointId, vector_data: &VectorData) -> Option<PointId> {
let (anchor, handle_position) = handle_id.get_anchor(vector_data).zip(handle_id.get_position(vector_data))?;
fn check_handle_over_adjacent_anchor(handle_id: ManipulatorPointId, vector: &Vector) -> Option<PointId> {
let (anchor, handle_position) = handle_id.get_anchor(vector).zip(handle_id.get_position(vector))?;
let check_if_close = |point_id: &PointId| {
let Some(anchor_position) = vector_data.point_domain.position_from_id(*point_id) else {
let Some(anchor_position) = vector.point_domain.position_from_id(*point_id) else {
return false;
};
(anchor_position - handle_position).length() < 10.
};
vector_data.connected_points(anchor).find(check_if_close)
vector.connected_points(anchor).find(check_if_close)
}
fn calculate_adjacent_anchor_tangent(
currently_dragged_handle: ManipulatorPointId,
anchor: Option<PointId>,
adjacent_anchor: Option<PointId>,
vector_data: &VectorData,
) -> (Option<f64>, Option<DVec2>) {
fn calculate_adjacent_anchor_tangent(currently_dragged_handle: ManipulatorPointId, anchor: Option<PointId>, adjacent_anchor: Option<PointId>, vector: &Vector) -> (Option<f64>, Option<DVec2>) {
// Early return if no anchor or no adjacent anchors
let Some((dragged_handle_anchor, adjacent_anchor)) = anchor.zip(adjacent_anchor) else {
return (None, None);
};
let adjacent_anchor_position = vector_data.point_domain.position_from_id(adjacent_anchor);
let adjacent_anchor_position = vector.point_domain.position_from_id(adjacent_anchor);
let handles: Vec<_> = vector_data.all_connected(adjacent_anchor).filter(|handle| handle.length(vector_data) > 1e-6).collect();
let handles: Vec<_> = vector.all_connected(adjacent_anchor).filter(|handle| handle.length(vector) > 1e-6).collect();
match handles.len() {
0 => {
// Find non-shared segments
let non_shared_segment: Vec<_> = vector_data
let non_shared_segment: Vec<_> = vector
.segment_bezier_iter()
.filter_map(|(segment_id, _, start, end)| {
let touches_adjacent = start == adjacent_anchor || end == adjacent_anchor;
@@ -3237,7 +3226,7 @@ fn calculate_adjacent_anchor_tangent(
match non_shared_segment.first() {
Some(&segment) => {
let angle = calculate_segment_angle(adjacent_anchor, segment, vector_data, true);
let angle = calculate_segment_angle(adjacent_anchor, segment, vector, true);
(angle, adjacent_anchor_position)
}
None => (None, None),
@@ -3246,7 +3235,7 @@ fn calculate_adjacent_anchor_tangent(
1 => {
let segment = handles[0].segment;
let angle = calculate_segment_angle(adjacent_anchor, segment, vector_data, true);
let angle = calculate_segment_angle(adjacent_anchor, segment, vector, true);
(angle, adjacent_anchor_position)
}
@@ -3261,7 +3250,7 @@ fn calculate_adjacent_anchor_tangent(
};
let angle = shared_segment_handle
.get_position(vector_data)
.get_position(vector)
.zip(adjacent_anchor_position)
.map(|(handle, anchor)| -(handle - anchor).angle_to(DVec2::X));
@@ -3296,8 +3285,8 @@ fn update_dynamic_hints(
shape_editor.selected_points().next(),
document.network_interface.selected_nodes().selected_layers(document.metadata()).next(),
) {
if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) {
single_colinear_anchor_selected = vector_data.colinear(*anchor)
if let Some(vector) = document.network_interface.compute_modified_vector(layer) {
single_colinear_anchor_selected = vector.colinear(*anchor)
}
}
}
@@ -3448,9 +3437,9 @@ fn update_dynamic_hints(
let handle1 = HandleId::primary(segment.segment());
let handle2 = HandleId::end(segment.segment());
if let Some(vector_data) = document.network_interface.compute_modified_vector(segment.layer()) {
let other_handle1 = vector_data.other_colinear_handle(handle1);
let other_handle2 = vector_data.other_colinear_handle(handle2);
if let Some(vector) = document.network_interface.compute_modified_vector(segment.layer()) {
let other_handle1 = vector.other_colinear_handle(handle1);
let other_handle2 = vector.other_colinear_handle(handle2);
if other_handle1.is_some() || other_handle2.is_some() {
has_colinear_anchors = true;
}
+109 -120
View File
@@ -14,8 +14,8 @@ use crate::messages::tool::common_functionality::utility_functions::{calculate_s
use bezier_rs::{Bezier, BezierHandles};
use graph_craft::document::NodeId;
use graphene_std::Color;
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
use graphene_std::vector::{PointId, VectorModificationType};
use graphene_std::vector::misc::{HandleId, ManipulatorPointId};
use graphene_std::vector::{NoHashBuilder, PointId, SegmentId, StrokeId, Vector, VectorModificationType};
#[derive(Default, ExtractField)]
pub struct PenTool {
@@ -395,11 +395,11 @@ impl PenToolData {
}
/// Check whether target handle is primary, end, or `self.handle_end`
fn check_end_handle_type(&self, vector_data: &VectorData) -> TargetHandle {
fn check_end_handle_type(&self, vector: &Vector) -> TargetHandle {
match (self.handle_end, self.prior_segment_endpoint, self.prior_segment, self.path_closed) {
(Some(_), _, _, false) => TargetHandle::PreviewInHandle,
(None, Some(point), Some(segment), false) | (Some(_), Some(point), Some(segment), true) => {
if vector_data.segment_start_from_id(segment) == Some(point) {
if vector.segment_start_from_id(segment) == Some(point) {
TargetHandle::PriorOutHandle(segment)
} else {
TargetHandle::PriorInHandle(segment)
@@ -409,23 +409,23 @@ impl PenToolData {
}
}
fn check_grs_end_handle(&self, vector_data: &VectorData) -> TargetHandle {
fn check_grs_end_handle(&self, vector: &Vector) -> TargetHandle {
let Some(point) = self.latest_point().map(|point| point.id) else { return TargetHandle::None };
let Some(segment) = self.prior_segment else { return TargetHandle::None };
if vector_data.segment_start_from_id(segment) == Some(point) {
if vector.segment_start_from_id(segment) == Some(point) {
TargetHandle::PriorOutHandle(segment)
} else {
TargetHandle::PriorInHandle(segment)
}
}
fn get_opposite_handle_type(&self, handle_type: TargetHandle, vector_data: &VectorData) -> TargetHandle {
fn get_opposite_handle_type(&self, handle_type: TargetHandle, vector: &Vector) -> TargetHandle {
match handle_type {
TargetHandle::FuturePreviewOutHandle => self.check_end_handle_type(vector_data),
TargetHandle::FuturePreviewOutHandle => self.check_end_handle_type(vector),
TargetHandle::PreviewInHandle => match (self.path_closed, self.prior_segment_endpoint, self.prior_segment) {
(true, Some(point), Some(segment)) => {
if vector_data.segment_start_from_id(segment) == Some(point) {
if vector.segment_start_from_id(segment) == Some(point) {
TargetHandle::PriorOutHandle(segment)
} else {
TargetHandle::PriorInHandle(segment)
@@ -472,10 +472,10 @@ impl PenToolData {
}
}
fn target_handle_position(&self, handle_type: TargetHandle, vector_data: &VectorData) -> Option<DVec2> {
fn target_handle_position(&self, handle_type: TargetHandle, vector: &Vector) -> Option<DVec2> {
match handle_type {
TargetHandle::PriorOutHandle(segment) => ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data),
TargetHandle::PriorInHandle(segment) => ManipulatorPointId::EndHandle(segment).get_position(vector_data),
TargetHandle::PriorOutHandle(segment) => ManipulatorPointId::PrimaryHandle(segment).get_position(vector),
TargetHandle::PriorInHandle(segment) => ManipulatorPointId::EndHandle(segment).get_position(vector),
TargetHandle::PreviewInHandle => self.handle_end,
TargetHandle::FuturePreviewOutHandle => Some(self.next_handle_start),
TargetHandle::None => None,
@@ -488,11 +488,11 @@ impl PenToolData {
return;
};
let Some(vector_data) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
let Some(vector) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
return;
};
match self.check_end_handle_type(&vector_data) {
match self.check_end_handle_type(&vector) {
TargetHandle::PriorInHandle(segment) => shape_state.deselect_point(ManipulatorPointId::EndHandle(segment)),
TargetHandle::PriorOutHandle(segment) => shape_state.deselect_point(ManipulatorPointId::PrimaryHandle(segment)),
_ => {}
@@ -518,18 +518,14 @@ impl PenToolData {
self.latest_points.len() == 1 && self.latest_point().is_some_and(|point| point.pos == self.next_point)
}
// When the vector data transform changes, the positions of the points must be recalculated.
// When the vector transform changes, the positions of the points must be recalculated.
fn recalculate_latest_points_position(&mut self, document: &DocumentMessageHandler) {
let selected_nodes = document.network_interface.selected_nodes();
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
if let (Some(layer), None) = (selected_layers.next(), selected_layers.next()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
return;
};
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
for point in &mut self.latest_points {
let Some(pos) = vector_data.point_domain.position_from_id(point.id) else {
continue;
};
let Some(pos) = vector.point_domain.position_from_id(point.id) else { continue };
point.pos = pos;
point.handle_start = point.pos;
}
@@ -549,7 +545,7 @@ impl PenToolData {
self.g1_continuous = true;
let document = snap_data.document;
self.next_handle_start = self.next_point;
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
self.update_handle_type(TargetHandle::FuturePreviewOutHandle);
self.handle_mode = HandleMode::ColinearLocked;
@@ -565,7 +561,7 @@ impl PenToolData {
self.store_clicked_endpoint(document, &transform, snap_data.input, preferences);
if self.modifiers.lock_angle {
self.set_lock_angle(&vector_data, id, self.prior_segment);
self.set_lock_angle(&vector, id, self.prior_segment);
let last_segment = self.prior_segment;
let Some(point) = self.latest_point_mut() else { return };
point.in_segment = last_segment;
@@ -579,7 +575,7 @@ impl PenToolData {
}
// Closing path
let closing_path_on_point = self.close_path_on_point(snap_data, &vector_data, document, preferences, id, &transform);
let closing_path_on_point = self.close_path_on_point(snap_data, &vector, document, preferences, id, &transform);
if !closing_path_on_point && preferences.vector_meshes {
// Attempt to find nearest segment and close path on segment by creating an anchor point on it
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
@@ -600,24 +596,16 @@ impl PenToolData {
self.handle_mode = HandleMode::Free;
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
self.set_lock_angle(&vector_data, prior_endpoint, self.prior_segment);
self.set_lock_angle(&vector, prior_endpoint, self.prior_segment);
self.switch_to_free_on_ctrl_release = true;
}
}
}
}
fn close_path_on_point(
&mut self,
snap_data: SnapData,
vector_data: &VectorData,
document: &DocumentMessageHandler,
preferences: &PreferencesMessageHandler,
id: PointId,
transform: &DAffine2,
) -> bool {
for id in vector_data.extendable_points(preferences.vector_meshes).filter(|&point| point != id) {
let Some(pos) = vector_data.point_domain.position_from_id(id) else { continue };
fn close_path_on_point(&mut self, snap_data: SnapData, vector: &Vector, document: &DocumentMessageHandler, preferences: &PreferencesMessageHandler, id: PointId, transform: &DAffine2) -> bool {
for id in vector.extendable_points(preferences.vector_meshes).filter(|&point| point != id) {
let Some(pos) = vector.point_domain.position_from_id(id) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(self.next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
@@ -629,7 +617,7 @@ impl PenToolData {
self.store_clicked_endpoint(document, transform, snap_data.input, preferences);
self.handle_mode = HandleMode::Free;
if let (true, Some(prior_endpoint)) = (self.modifiers.lock_angle, self.prior_segment_endpoint) {
self.set_lock_angle(vector_data, prior_endpoint, self.prior_segment);
self.set_lock_angle(vector, prior_endpoint, self.prior_segment);
self.switch_to_free_on_ctrl_release = true;
}
return true;
@@ -662,11 +650,11 @@ impl PenToolData {
let selected_nodes = document.network_interface.selected_nodes();
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none()).or(self.current_layer)?;
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let start = self.latest_point()?.id;
let transform = document.metadata().document_to_viewport * transform;
for id in vector_data.extendable_points(preferences.vector_meshes).filter(|&point| point != start) {
let Some(pos) = vector_data.point_domain.position_from_id(id) else { continue };
for id in vector.extendable_points(preferences.vector_meshes).filter(|&point| point != start) {
let Some(pos) = vector.point_domain.position_from_id(id) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
if transformed_distance_between_squared < snap_point_tolerance_squared {
@@ -687,15 +675,15 @@ impl PenToolData {
// Store the segment
let id = SegmentId::generate();
if self.path_closed {
if let Some((handles, handle1_pos)) = match self.get_opposite_handle_type(TargetHandle::PreviewInHandle, &vector_data) {
if let Some((handles, handle1_pos)) = match self.get_opposite_handle_type(TargetHandle::PreviewInHandle, &vector) {
TargetHandle::PriorOutHandle(segment) => {
let handles = [HandleId::end(id), HandleId::primary(segment)];
let handle1_pos = handles[1].to_manipulator_point().get_position(&vector_data);
let handle1_pos = handles[1].to_manipulator_point().get_position(&vector);
handle1_pos.map(|pos| (handles, pos))
}
TargetHandle::PriorInHandle(segment) => {
let handles = [HandleId::end(id), HandleId::end(segment)];
let handle1_pos = handles[1].to_manipulator_point().get_position(&vector_data);
let handle1_pos = handles[1].to_manipulator_point().get_position(&vector);
handle1_pos.map(|pos| (handles, pos))
}
_ => None,
@@ -719,14 +707,14 @@ impl PenToolData {
// Mirror
if let Some((last_segment, last_point)) = self.latest_point().and_then(|point| point.in_segment).zip(self.latest_point()) {
let end = vector_data.segment_end_from_id(last_segment) == Some(last_point.id);
let end = vector.segment_end_from_id(last_segment) == Some(last_point.id);
let handles = if end {
[HandleId::end(last_segment), HandleId::primary(id)]
} else {
[HandleId::primary(last_segment), HandleId::primary(id)]
};
if let Some(h1) = handles[0].to_manipulator_point().get_position(&vector_data) {
if let Some(h1) = handles[0].to_manipulator_point().get_position(&vector) {
let angle = (h1 - last_point.pos).angle_to(last_point.handle_start - last_point.pos);
let pi = std::f64::consts::PI;
let colinear = (angle - pi).abs() < 1e-6 || (angle + pi).abs() < 1e-6;
@@ -758,13 +746,13 @@ impl PenToolData {
transform: &DAffine2,
snap_data: &SnapData<'_>,
mouse: &DVec2,
vector_data: &VectorData,
vector: &Vector,
input: &InputPreprocessorMessageHandler,
) -> Option<DVec2> {
let reference_handle = if self.path_closed { TargetHandle::PreviewInHandle } else { TargetHandle::FuturePreviewOutHandle };
let end_handle = self.get_opposite_handle_type(reference_handle, vector_data);
let end_handle_pos = self.target_handle_position(end_handle, vector_data);
let ref_pos = self.target_handle_position(reference_handle, vector_data)?;
let end_handle = self.get_opposite_handle_type(reference_handle, vector);
let end_handle_pos = self.target_handle_position(end_handle, vector);
let ref_pos = self.target_handle_position(reference_handle, vector)?;
let snap = &mut self.snap_manager;
let snap_data = SnapData::new_snap_cache(snap_data.document, input, &self.snap_cache);
@@ -826,7 +814,7 @@ impl PenToolData {
responses: &mut VecDeque<Message>,
) {
// Validate necessary data exists
let Some(vector_data) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
let Some(vector) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
return;
};
@@ -842,9 +830,9 @@ impl PenToolData {
let should_swap_to_start = !self.path_closed && !matches!(self.handle_type, TargetHandle::None | TargetHandle::FuturePreviewOutHandle);
if should_swap_to_opposite {
let opposite_type = self.get_opposite_handle_type(self.handle_type, &vector_data);
let opposite_type = self.get_opposite_handle_type(self.handle_type, &vector);
// Update offset
let Some(handle_pos) = self.target_handle_position(opposite_type, &vector_data) else {
let Some(handle_pos) = self.target_handle_position(opposite_type, &vector) else {
self.handle_swapped = false;
return;
};
@@ -894,7 +882,7 @@ impl PenToolData {
Some(PenToolFsmState::DraggingHandle(self.handle_mode))
}
fn move_anchor_and_handles(&mut self, delta: DVec2, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>, vector_data: &VectorData) {
fn move_anchor_and_handles(&mut self, delta: DVec2, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>, vector: &Vector) {
if self.handle_end.is_none() {
if let Some(latest_pt) = self.latest_point_mut() {
latest_pt.pos += delta;
@@ -912,10 +900,10 @@ impl PenToolData {
let reference_handle = if self.path_closed { TargetHandle::PreviewInHandle } else { TargetHandle::FuturePreviewOutHandle };
// Move the end handle
let end_handle_type = self.get_opposite_handle_type(reference_handle, vector_data);
let end_handle_type = self.get_opposite_handle_type(reference_handle, vector);
match end_handle_type {
TargetHandle::PriorInHandle(..) | TargetHandle::PriorOutHandle(..) => {
let Some(handle_pos) = self.target_handle_position(end_handle_type, vector_data) else { return };
let Some(handle_pos) = self.target_handle_position(end_handle_type, vector) else { return };
self.update_target_handle_pos(end_handle_type, self.next_point, responses, handle_pos + delta, layer);
}
_ => {}
@@ -934,13 +922,13 @@ impl PenToolData {
let colinear = (self.handle_mode == HandleMode::ColinearEquidistant && self.modifiers.break_handle) || (self.handle_mode == HandleMode::ColinearLocked && !self.modifiers.break_handle);
let document = snap_data.document;
let Some(layer) = layer else { return Some(PenToolFsmState::DraggingHandle(self.handle_mode)) };
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let viewport_to_document = document.metadata().document_to_viewport.inverse();
let mut mouse_pos = mouse;
// Handles pressing Space to drag anchor and its handles
if self.modifiers.move_anchor_with_handles {
let Some(delta) = self.space_anchor_handle_snap(&viewport_to_document, &transform, &snap_data, &mouse, &vector_data, input) else {
let Some(delta) = self.space_anchor_handle_snap(&viewport_to_document, &transform, &snap_data, &mouse, &vector, input) else {
return Some(PenToolFsmState::DraggingHandle(self.handle_mode));
};
@@ -959,7 +947,7 @@ impl PenToolData {
};
}
self.move_anchor_and_handles(delta, layer, responses, &vector_data);
self.move_anchor_and_handles(delta, layer, responses, &vector);
responses.add(OverlaysMessage::Draw);
return Some(PenToolFsmState::DraggingHandle(self.handle_mode));
@@ -994,8 +982,8 @@ impl PenToolData {
match self.handle_mode {
HandleMode::ColinearLocked | HandleMode::ColinearEquidistant => {
self.g1_continuous = true;
self.apply_colinear_constraint(responses, layer, self.next_point, &vector_data);
self.adjust_handle_length(responses, layer, &vector_data);
self.apply_colinear_constraint(responses, layer, self.next_point, &vector);
self.adjust_handle_length(responses, layer, &vector);
}
HandleMode::Free => {
self.g1_continuous = false;
@@ -1006,7 +994,7 @@ impl PenToolData {
let Some(endpoint) = self.prior_segment_endpoint else {
return Some(PenToolFsmState::DraggingHandle(self.handle_mode));
};
self.set_lock_angle(&vector_data, endpoint, self.prior_segment);
self.set_lock_angle(&vector, endpoint, self.prior_segment);
self.switch_to_free_on_ctrl_release = true;
let last_segment = self.prior_segment;
if let Some(latest) = self.latest_point_mut() {
@@ -1020,19 +1008,19 @@ impl PenToolData {
}
/// Makes the opposite handle equidistant or locks its length.
fn adjust_handle_length(&mut self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector_data: &VectorData) {
let opposite_handle_type = self.get_opposite_handle_type(self.handle_type, vector_data);
fn adjust_handle_length(&mut self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector: &Vector) {
let opposite_handle_type = self.get_opposite_handle_type(self.handle_type, vector);
match self.handle_mode {
HandleMode::ColinearEquidistant => {
if self.modifiers.break_handle {
// Store handle for later restoration only when Alt is first pressed
if !self.alt_pressed {
self.previous_handle_end_pos = self.target_handle_position(opposite_handle_type, vector_data);
self.previous_handle_end_pos = self.target_handle_position(opposite_handle_type, vector);
self.alt_pressed = true;
}
// Set handle to opposite position of the other handle
let Some(new_position) = self.target_handle_position(self.handle_type, vector_data).map(|handle| self.next_point * 2. - handle) else {
let Some(new_position) = self.target_handle_position(self.handle_type, vector).map(|handle| self.next_point * 2. - handle) else {
return;
};
self.update_target_handle_pos(opposite_handle_type, self.next_point, responses, new_position, layer);
@@ -1047,7 +1035,7 @@ impl PenToolData {
}
HandleMode::ColinearLocked => {
if !self.modifiers.break_handle {
let Some(new_position) = self.target_handle_position(self.handle_type, vector_data).map(|handle| self.next_point * 2. - handle) else {
let Some(new_position) = self.target_handle_position(self.handle_type, vector).map(|handle| self.next_point * 2. - handle) else {
return;
};
self.update_target_handle_pos(opposite_handle_type, self.next_point, responses, new_position, layer);
@@ -1057,23 +1045,23 @@ impl PenToolData {
}
}
fn apply_colinear_constraint(&mut self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, anchor_pos: DVec2, vector_data: &VectorData) {
let Some(handle) = self.target_handle_position(self.handle_type, vector_data) else {
return;
};
fn apply_colinear_constraint(&mut self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, anchor_pos: DVec2, vector: &Vector) {
let Some(handle) = self.target_handle_position(self.handle_type, vector) else { return };
if (anchor_pos - handle).length() < 1e-6 && self.modifiers.lock_angle {
return;
}
let Some(direction) = (anchor_pos - handle).try_normalize() else {
return;
};
let opposite_handle = self.get_opposite_handle_type(self.handle_type, vector_data);
let Some(handle_offset) = self.target_handle_position(opposite_handle, vector_data).map(|handle| (handle - anchor_pos).length()) else {
let Some(direction) = (anchor_pos - handle).try_normalize() else { return };
let opposite_handle = self.get_opposite_handle_type(self.handle_type, vector);
let Some(handle_offset) = self.target_handle_position(opposite_handle, vector).map(|handle| (handle - anchor_pos).length()) else {
return;
};
let new_handle_position = anchor_pos + handle_offset * direction;
self.update_target_handle_pos(opposite_handle, self.next_point, responses, new_handle_position, layer);
}
@@ -1086,10 +1074,10 @@ impl PenToolData {
let selected_nodes = document.network_interface.selected_nodes();
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none()).or(self.current_layer)?;
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let vector = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().document_to_viewport * transform;
for point in vector_data.extendable_points(preferences.vector_meshes) {
let Some(pos) = vector_data.point_domain.position_from_id(point) else { continue };
for point in vector.extendable_points(preferences.vector_meshes) {
let Some(pos) = vector.point_domain.position_from_id(point) else { continue };
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(self.next_point));
let snap_point_tolerance_squared = crate::consts::SNAP_POINT_TOLERANCE.powi(2);
if transformed_distance_between_squared < snap_point_tolerance_squared {
@@ -1217,11 +1205,11 @@ impl PenToolData {
if append {
if let Some((layer, point, _)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector_data.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
if self.modifiers.lock_angle {
self.set_lock_angle(&vector_data, point, segment);
self.set_lock_angle(&vector, point, segment);
self.switch_to_free_on_ctrl_release = true;
}
}
@@ -1235,11 +1223,11 @@ impl PenToolData {
}
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector_data.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
self.handle_mode = HandleMode::Free;
if self.modifiers.lock_angle {
self.set_lock_angle(&vector_data, point, segment);
self.set_lock_angle(&vector, point, segment);
self.switch_to_free_on_ctrl_release = true;
}
}
@@ -1265,9 +1253,9 @@ impl PenToolData {
/// Perform extension of an existing path
fn extend_existing_path(&mut self, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, point: PointId, position: DVec2) {
let vector_data = document.network_interface.compute_modified_vector(layer);
let (handle_start, in_segment) = if let Some(vector_data) = &vector_data {
vector_data
let vector = document.network_interface.compute_modified_vector(layer);
let (handle_start, in_segment) = if let Some(vector) = &vector {
vector
.segment_bezier_iter()
.find_map(|(segment_id, bezier, start, end)| {
let is_end = point == end;
@@ -1310,12 +1298,12 @@ impl PenToolData {
self.next_point = position;
self.next_handle_start = handle_start;
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector_data.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
self.handle_mode = HandleMode::Free;
if self.modifiers.lock_angle {
self.set_lock_angle(&vector_data, point, segment);
self.set_lock_angle(&vector, point, segment);
self.switch_to_free_on_ctrl_release = true;
}
}
@@ -1340,11 +1328,11 @@ impl PenToolData {
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
self.prior_segment_endpoint = Some(point);
self.prior_segment_layer = Some(layer);
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector_data.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
self.prior_segment = segment;
layer_manipulators.insert(point);
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
for (&id, &position) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
if id == point {
continue;
}
@@ -1355,8 +1343,8 @@ impl PenToolData {
}
}
fn set_lock_angle(&mut self, vector_data: &VectorData, anchor: PointId, segment: Option<SegmentId>) {
let anchor_position = vector_data.point_domain.position_from_id(anchor);
fn set_lock_angle(&mut self, vector: &Vector, anchor: PointId, segment: Option<SegmentId>) {
let anchor_position = vector.point_domain.position_from_id(anchor);
let Some((anchor_position, segment)) = anchor_position.zip(segment) else {
self.handle_mode = HandleMode::Free;
@@ -1365,7 +1353,7 @@ impl PenToolData {
match (self.handle_type, self.path_closed) {
(TargetHandle::FuturePreviewOutHandle, _) | (TargetHandle::PreviewInHandle, true) => {
if let Some(required_handle) = calculate_segment_angle(anchor, segment, vector_data, true) {
if let Some(required_handle) = calculate_segment_angle(anchor, segment, vector, true) {
self.angle = required_handle;
self.handle_mode = HandleMode::ColinearEquidistant;
}
@@ -1460,12 +1448,12 @@ impl Fsm for PenToolFsmState {
responses.add(TransformLayerMessage::BeginScalePen { last_point, handle });
}
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
tool_data.previous_handle_start_pos = latest.handle_start;
let opposite_handle = tool_data.check_grs_end_handle(&vector_data);
tool_data.previous_handle_end_pos = tool_data.target_handle_position(opposite_handle, &vector_data);
let opposite_handle = tool_data.check_grs_end_handle(&vector);
tool_data.previous_handle_end_pos = tool_data.target_handle_position(opposite_handle, &vector);
let handle1 = latest_handle_start - latest_pos;
let Some(opposite_handle_pos) = tool_data.target_handle_position(opposite_handle, &vector_data) else {
let Some(opposite_handle_pos) = tool_data.target_handle_position(opposite_handle, &vector) else {
return PenToolFsmState::GRSHandle;
};
let handle2 = opposite_handle_pos - latest_pos;
@@ -1476,8 +1464,8 @@ impl Fsm for PenToolFsmState {
}
(PenToolFsmState::GRSHandle, PenToolMessage::FinalPosition { final_position }) => {
let Some(layer) = layer else { return PenToolFsmState::GRSHandle };
let vector_data = document.network_interface.compute_modified_vector(layer);
let Some(vector_data) = vector_data else { return PenToolFsmState::GRSHandle };
let vector = document.network_interface.compute_modified_vector(layer);
let Some(vector) = vector else { return PenToolFsmState::GRSHandle };
if let Some(latest_pt) = tool_data.latest_point_mut() {
let layer_space_to_viewport = document.metadata().transform_to_viewport(layer);
@@ -1489,8 +1477,8 @@ impl Fsm for PenToolFsmState {
let Some(latest) = tool_data.latest_point() else {
return PenToolFsmState::GRSHandle;
};
let opposite_handle = tool_data.check_grs_end_handle(&vector_data);
let Some(opposite_handle_pos) = tool_data.target_handle_position(opposite_handle, &vector_data) else {
let opposite_handle = tool_data.check_grs_end_handle(&vector);
let Some(opposite_handle_pos) = tool_data.target_handle_position(opposite_handle, &vector) else {
return PenToolFsmState::GRSHandle;
};
@@ -1531,8 +1519,8 @@ impl Fsm for PenToolFsmState {
tool_data.next_handle_start = input.mouse.position;
let Some(layer) = layer else { return PenToolFsmState::GRSHandle };
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let opposite_handle = tool_data.check_grs_end_handle(&vector_data);
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
let opposite_handle = tool_data.check_grs_end_handle(&vector);
let previous = tool_data.previous_handle_start_pos;
if let Some(latest) = tool_data.latest_point_mut() {
@@ -1722,10 +1710,10 @@ impl Fsm for PenToolFsmState {
let start = latest_point.id;
if let Some(layer) = layer
&& let Some(mut vector_data) = document.network_interface.compute_modified_vector(layer)
&& let Some(mut vector) = document.network_interface.compute_modified_vector(layer)
{
let closest_point = vector_data.extendable_points(preferences.vector_meshes).filter(|&id| id != start).find(|&id| {
vector_data.point_domain.position_from_id(id).map_or(false, |pos| {
let closest_point = vector.extendable_points(preferences.vector_meshes).filter(|&id| id != start).find(|&id| {
vector.point_domain.position_from_id(id).is_some_and(|pos| {
let dist_sq = transform.transform_point2(pos).distance_squared(transform.transform_point2(next_point));
dist_sq < crate::consts::SNAP_POINT_TOLERANCE.powi(2)
})
@@ -1734,21 +1722,21 @@ impl Fsm for PenToolFsmState {
// We have the point. Join the 2 vertices and check if any path is closed.
if let Some(end) = closest_point {
let segment_id = SegmentId::generate();
vector_data.push(segment_id, start, end, BezierHandles::Cubic { handle_start, handle_end }, StrokeId::ZERO);
vector.push(segment_id, start, end, BezierHandles::Cubic { handle_start, handle_end }, StrokeId::ZERO);
let grouped_segments = vector_data.auto_join_paths();
let grouped_segments = vector.auto_join_paths();
let closed_paths = grouped_segments.iter().filter(|path| path.is_closed() && path.contains(segment_id));
let subpaths: Vec<_> = closed_paths
.filter_map(|path| {
let segments = path.edges.iter().filter_map(|edge| {
vector_data
vector
.segment_domain
.iter()
.find(|(id, _, _, _)| id == &edge.id)
.map(|(_, start, end, bezier)| if start == edge.start { (bezier, start, end) } else { (bezier.reversed(), end, start) })
});
vector_data.subpath_from_segments_ignore_discontinuities(segments)
vector.subpath_from_segments_ignore_discontinuities(segments)
})
.collect();
@@ -1817,7 +1805,8 @@ impl Fsm for PenToolFsmState {
}
// Merge two layers if the point is connected to the end point of another path
// This might not be the correct solution to artboards being included as the other layer, which occurs due to the compute_modified_vector call in should_extend using the click targets for a layer instead of vector data.
// This might not be the correct solution to artboards being included as the other layer,
// which occurs due to the `compute_modified_vector` call in `should_extend` using the click targets for a layer instead of vector.
let layers = LayerNodeIdentifier::ROOT_PARENT
.descendants(document.metadata())
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
@@ -1887,7 +1876,7 @@ impl Fsm for PenToolFsmState {
tool_data.toggle_colinear_debounce = true;
}
let Some(vector_data) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
let Some(vector) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) else {
return self;
};
@@ -1901,7 +1890,7 @@ impl Fsm for PenToolFsmState {
document
.metadata()
.transform_to_viewport(layer)
.transform_point2(tool_data.target_handle_position(reference_handle, &vector_data).unwrap())
.transform_point2(tool_data.target_handle_position(reference_handle, &vector).unwrap())
});
tool_data.handle_start_offset = handle_start.map(|start| start - input.mouse.position);
tool_data.space_pressed = true;
@@ -2075,8 +2064,8 @@ impl Fsm for PenToolFsmState {
}
(PenToolFsmState::DraggingHandle(..), PenToolMessage::Confirm) => {
// Confirm to end path
if let Some((vector_data, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
let single_point_in_layer = vector_data.point_domain.ids().len() == 1;
if let Some((vector, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
let single_point_in_layer = vector.point_domain.ids().len() == 1;
tool_data.finish_placing_handle(SnapData::new(document, input), transform, preferences, responses);
let latest_points = tool_data.latest_points.len() == 1;
@@ -2129,8 +2118,8 @@ impl Fsm for PenToolFsmState {
}
}
(PenToolFsmState::PlacingAnchor, PenToolMessage::Abort) => {
let should_delete_layer = if let Some(vector_data) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) {
vector_data.point_domain.ids().len() == 1
let should_delete_layer = if let Some(vector) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)) {
vector.point_domain.ids().len() == 1
} else {
false
};
@@ -548,15 +548,15 @@ mod test_spline_tool {
use crate::test_utils::test_prelude::*;
use glam::DAffine2;
use graphene_std::vector::PointId;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
fn assert_point_positions(vector_data: &VectorData, layer_to_viewport: DAffine2, expected_points: &[DVec2], epsilon: f64) {
let points_in_viewport: Vec<DVec2> = vector_data
fn assert_point_positions(vector: &Vector, layer_to_viewport: DAffine2, expected_points: &[DVec2], epsilon: f64) {
let points_in_viewport: Vec<DVec2> = vector
.point_domain
.ids()
.iter()
.filter_map(|&point_id| {
let position = vector_data.point_domain.position_from_id(point_id)?;
let position = vector.point_domain.position_from_id(point_id)?;
Some(layer_to_viewport.transform_point2(position))
})
.collect();
@@ -601,19 +601,19 @@ mod test_spline_tool {
let first_spline_node = find_spline(document, spline_layer).expect("Spline node not found in the layer");
let first_vector_data = document.network_interface.compute_modified_vector(spline_layer).expect("Vector data not found for the spline layer");
let first_vector = document.network_interface.compute_modified_vector(spline_layer).expect("Vector not found for the spline layer");
// Verify initial spline has correct number of points and segments
let initial_point_count = first_vector_data.point_domain.ids().len();
let initial_segment_count = first_vector_data.segment_domain.ids().len();
let initial_point_count = first_vector.point_domain.ids().len();
let initial_segment_count = first_vector.segment_domain.ids().len();
assert_eq!(initial_point_count, 3, "Expected 3 points in initial spline, found {}", initial_point_count);
assert_eq!(initial_segment_count, 2, "Expected 2 segments in initial spline, found {}", initial_segment_count);
let layer_to_viewport = document.metadata().transform_to_viewport(spline_layer);
let endpoints: Vec<(PointId, DVec2)> = first_vector_data
let endpoints: Vec<(PointId, DVec2)> = first_vector
.extendable_points(false)
.filter_map(|point_id| first_vector_data.point_domain.position_from_id(point_id).map(|pos| (point_id, layer_to_viewport.transform_point2(pos))))
.filter_map(|point_id| first_vector.point_domain.position_from_id(point_id).map(|pos| (point_id, layer_to_viewport.transform_point2(pos))))
.collect();
assert_eq!(endpoints.len(), 2, "Expected 2 endpoints in the initial spline");
@@ -632,14 +632,14 @@ mod test_spline_tool {
editor.press(Key::Enter, ModifierKeys::empty()).await;
let document = editor.active_document();
let extended_vector_data = document
let extended_vector = document
.network_interface
.compute_modified_vector(spline_layer)
.expect("Vector data not found for the extended spline layer");
.expect("Vector not found for the extended spline layer");
// Verify extended spline has correct number of points and segments
let extended_point_count = extended_vector_data.point_domain.ids().len();
let extended_segment_count = extended_vector_data.segment_domain.ids().len();
let extended_point_count = extended_vector.point_domain.ids().len();
let extended_segment_count = extended_vector.segment_domain.ids().len();
assert_eq!(extended_point_count, 5, "Expected 5 points in extended spline, found {}", extended_point_count);
assert_eq!(extended_segment_count, 4, "Expected 4 segments in extended spline, found {}", extended_segment_count);
@@ -653,7 +653,7 @@ mod test_spline_tool {
let all_expected_points = [initial_points[0], initial_points[1], initial_points[2], continuation_points[0], continuation_points[1]];
assert_point_positions(&extended_vector_data, layer_to_viewport, &all_expected_points, 1e-10);
assert_point_positions(&extended_vector, layer_to_viewport, &all_expected_points, 1e-10);
}
#[tokio::test]
@@ -688,14 +688,14 @@ mod test_spline_tool {
.selected_visible_and_unlocked_layers(network_interface)
.next()
.expect("Should have a selected layer");
let vector_data = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let vector = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let layer_to_viewport = document.metadata().transform_to_viewport(layer);
// Expected points in viewport coordinates
let expected_points = vec![DVec2::new(50., 50.), DVec2::new(100., 50.), DVec2::new(150., 100.)];
// Assert all points are correctly positioned
assert_point_positions(&vector_data, layer_to_viewport, &expected_points, 1e-10);
assert_point_positions(&vector, layer_to_viewport, &expected_points, 1e-10);
}
#[tokio::test]
@@ -728,14 +728,14 @@ mod test_spline_tool {
.selected_visible_and_unlocked_layers(network_interface)
.next()
.expect("Should have a selected layer");
let vector_data = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let vector = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let layer_to_viewport = document.metadata().transform_to_viewport(layer);
// Expected points in viewport coordinates
let expected_points = vec![DVec2::new(50., 50.), DVec2::new(100., 50.), DVec2::new(150., 100.)];
// Assert all points are correctly positioned
assert_point_positions(&vector_data, layer_to_viewport, &expected_points, 1e-10);
assert_point_positions(&vector, layer_to_viewport, &expected_points, 1e-10);
}
#[tokio::test]
@@ -766,14 +766,14 @@ mod test_spline_tool {
.selected_visible_and_unlocked_layers(network_interface)
.next()
.expect("Should have a selected layer");
let vector_data = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let vector = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let layer_to_viewport = document.metadata().transform_to_viewport(layer);
// Expected points in viewport coordinates
let expected_points = vec![DVec2::new(50., 50.), DVec2::new(100., 50.), DVec2::new(150., 100.)];
// Assert all points are correctly positioned
assert_point_positions(&vector_data, layer_to_viewport, &expected_points, 1e-10);
assert_point_positions(&vector, layer_to_viewport, &expected_points, 1e-10);
}
#[tokio::test]
@@ -805,14 +805,14 @@ mod test_spline_tool {
.selected_visible_and_unlocked_layers(network_interface)
.next()
.expect("Should have a selected layer");
let vector_data = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let vector = network_interface.compute_modified_vector(layer).expect("Should have vector data");
let layer_to_viewport = document.metadata().transform_to_viewport(layer);
// Expected points in viewport coordinates
let expected_points = vec![DVec2::new(50., 50.), DVec2::new(100., 50.), DVec2::new(150., 100.)];
// Assert all points are correctly positioned
assert_point_positions(&vector_data, layer_to_viewport, &expected_points, 1e-10);
assert_point_positions(&vector, layer_to_viewport, &expected_points, 1e-10);
}
#[tokio::test]
@@ -845,17 +845,17 @@ mod test_spline_tool {
let spline_layer = layers.next().expect("Failed to find the spline layer");
assert!(find_spline(document, spline_layer).is_some(), "Spline node not found in the layer");
let vector_data = document.network_interface.compute_modified_vector(spline_layer).expect("Vector data not found for the spline layer");
let vector = document.network_interface.compute_modified_vector(spline_layer).expect("Vector not found for the spline layer");
// Verify we have the correct number of points and segments
let point_count = vector_data.point_domain.ids().len();
let segment_count = vector_data.segment_domain.ids().len();
let point_count = vector.point_domain.ids().len();
let segment_count = vector.segment_domain.ids().len();
assert_eq!(point_count, 3, "Expected 3 points in the spline, found {}", point_count);
assert_eq!(segment_count, 2, "Expected 2 segments in the spline, found {}", segment_count);
let layer_to_viewport = document.metadata().transform_to_viewport(spline_layer);
assert_point_positions(&vector_data, layer_to_viewport, &spline_points, 1e-10);
assert_point_positions(&vector, layer_to_viewport, &spline_points, 1e-10);
}
}
@@ -11,9 +11,9 @@ use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::{ToolData, ToolType};
use glam::{DAffine2, DVec2};
use graphene_std::renderer::Quad;
use graphene_std::vector::ManipulatorPointId;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::{VectorData, VectorModificationType};
use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::{Vector, VectorModificationType};
use std::f64::consts::{PI, TAU};
const TRANSFORM_GRS_OVERLAY_PROVIDER: OverlayProvider = |context| TransformLayerMessage::Overlays(context).into();
@@ -123,14 +123,14 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
self.grab_target = self.local_pivot;
}
// Here vector data from all layers is not considered which can be a problem in pivot calculation
else if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
else if let Some(vector) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
*selected.original_transforms = OriginalTransforms::default();
let viewspace = document.metadata().transform_to_viewport(selected_layers[0]);
let selected_segments = shape_editor.selected_segments().collect::<HashSet<_>>();
let mut affected_points = shape_editor.selected_points().copied().collect::<Vec<_>>();
for (segment_id, _, start, end) in vector_data.segment_bezier_iter() {
for (segment_id, _, start, end) in vector.segment_bezier_iter() {
if selected_segments.contains(&segment_id) {
affected_points.push(ManipulatorPointId::Anchor(start));
affected_points.push(ManipulatorPointId::Anchor(end));
@@ -139,11 +139,11 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
let affected_point_refs = affected_points.iter().collect();
let get_location = |point: &&ManipulatorPointId| point.get_position(&vector_data).map(|position| viewspace.transform_point2(position));
let get_location = |point: &&ManipulatorPointId| point.get_position(&vector).map(|position| viewspace.transform_point2(position));
if let (Some((new_pivot, grab_target)), bounds) = calculate_pivot(
document,
&affected_point_refs,
&vector_data,
&vector,
viewspace,
|point: &ManipulatorPointId| get_location(&point),
&mut self.pivot_gizmo,
@@ -362,12 +362,12 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
}
}
if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
if let Some(vector) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
if let [point] = selected_points.as_slice() {
if matches!(point, ManipulatorPointId::Anchor(_)) {
if let Some([handle1, handle2]) = point.get_handle_pair(&vector_data) {
let handle1_length = handle1.length(&vector_data);
let handle2_length = handle2.length(&vector_data);
if let Some([handle1, handle2]) = point.get_handle_pair(&vector) {
let handle1_length = handle1.length(&vector);
let handle2_length = handle2.length(&vector);
if (handle1_length == 0. && handle2_length == 0. && !using_select_tool) || (handle1_length == f64::MAX && handle2_length == f64::MAX && !using_select_tool) {
// G should work for this point but not R and S
@@ -378,7 +378,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
}
}
} else {
let handle_length = point.as_handle().map(|handle| handle.length(&vector_data));
let handle_length = point.as_handle().map(|handle| handle.length(&vector));
if handle_length == Some(0.) {
selected.original_transforms.clear();
@@ -693,7 +693,7 @@ impl TransformLayerMessageHandler {
fn calculate_pivot(
document: &DocumentMessageHandler,
selected_points: &Vec<&ManipulatorPointId>,
vector_data: &VectorData,
vector: &Vector,
viewspace: DAffine2,
get_location: impl Fn(&ManipulatorPointId) -> Option<DVec2>,
gizmo: &mut PivotGizmo,
@@ -736,8 +736,8 @@ fn calculate_pivot(
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => {
// Get the anchor position and transform it to the pivot
let (Some(pivot_position), Some(position)) = (
point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position)),
point.get_position(vector_data),
point.get_anchor_position(vector).map(|anchor_position| viewspace.transform_point2(anchor_position)),
point.get_position(vector),
) else {
return (None, None);
};
@@ -774,15 +774,15 @@ fn project_edge_to_quad(edge: DVec2, quad: &Quad, local: bool, axis_constraint:
fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for &layer in selected_layers {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for [handle1, handle2] in &vector_data.colinear_manipulators {
for [handle1, handle2] in &vector.colinear_manipulators {
let manipulator1 = handle1.to_manipulator_point();
let manipulator2 = handle2.to_manipulator_point();
let Some(anchor) = manipulator1.get_anchor_position(&vector_data) else { continue };
let Some(pos1) = manipulator1.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
let Some(pos2) = manipulator2.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
let Some(anchor) = manipulator1.get_anchor_position(&vector) else { continue };
let Some(pos1) = manipulator1.get_position(&vector).map(|pos| pos - anchor) else { continue };
let Some(pos2) = manipulator2.get_position(&vector).map(|pos| pos - anchor) else { continue };
let angle = pos1.angle_to(pos2);
+5 -5
View File
@@ -13,7 +13,7 @@ use graphene_std::renderer::{Render, RenderParams, SvgRender};
use graphene_std::renderer::{RenderMetadata, format_transform_matrix};
use graphene_std::text::FontCache;
use graphene_std::transform::Footprint;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::vector::style::ViewMode;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
@@ -34,7 +34,7 @@ pub struct ExecutionResponse {
result: Result<TaggedValue, String>,
responses: VecDeque<FrontendMessage>,
transform: DAffine2,
vector_modify: HashMap<NodeId, VectorData>,
vector_modify: HashMap<NodeId, Vector>,
/// The resulting value from the temporary inspected during execution
inspect_result: Option<InspectResult>,
}
@@ -377,9 +377,9 @@ impl NodeGraphExecutor {
TaggedValue::F64(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::DVec2(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::OptionalColor(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::VectorData(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::GraphicGroup(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::RasterData(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::Vector(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::Group(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::Raster(render_object) => Self::debug_render(render_object, transform, responses),
TaggedValue::Palette(render_object) => Self::debug_render(render_object, transform, responses),
_ => {
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
+11 -12
View File
@@ -14,7 +14,7 @@ use graphene_std::renderer::{Render, RenderParams, SvgRender};
use graphene_std::renderer::{RenderSvgSegmentList, SvgSegment};
use graphene_std::table::{Table, TableRow};
use graphene_std::text::FontCache;
use graphene_std::vector::VectorData;
use graphene_std::vector::Vector;
use graphene_std::vector::style::ViewMode;
use graphene_std::wasm_application_io::{RenderOutputType, WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
@@ -51,7 +51,7 @@ pub struct NodeRuntime {
// TODO: Remove, it doesn't need to be persisted anymore
/// The current renders of the thumbnails for layer nodes.
thumbnail_renders: HashMap<NodeId, Vec<SvgSegment>>,
vector_modify: HashMap<NodeId, VectorData>,
vector_modify: HashMap<NodeId, Vector>,
}
/// Messages passed from the editor thread to the node runtime thread.
@@ -309,11 +309,11 @@ impl NodeRuntime {
};
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, graphene_std::Graphic>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, graphene_std::Artboard>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
// Insert the vector modify if we are dealing with vector data
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Context, Table<VectorData>>>() {
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {
let default = TableRow::default();
self.vector_modify
.insert(parent_network_node_id, record.output.iter_ref().next().unwrap_or_else(|| default.as_ref()).element.clone());
@@ -323,12 +323,11 @@ impl NodeRuntime {
}
}
// If this is `Graphic` data:
// Regenerate click targets and thumbnails for the layers in the graph, modifying the state and updating the UI.
fn process_graphic_element(
// If this is `Graphic` data, regenerate click targets and thumbnails for the layers in the graph, modifying the state and updating the UI.
fn process_graphic(
thumbnail_renders: &mut HashMap<NodeId, Vec<SvgSegment>>,
parent_network_node_id: NodeId,
graphic_element: &impl Render,
graphic: &impl Render,
responses: &mut VecDeque<FrontendMessage>,
update_thumbnails: bool,
) {
@@ -339,7 +338,7 @@ impl NodeRuntime {
}
// Skip thumbnails if the layer is too complex (for performance)
if graphic_element.render_complexity() > 1000 {
if graphic.render_complexity() > 1000 {
let old = thumbnail_renders.insert(parent_network_node_id, Vec::new());
if old.is_none_or(|v| !v.is_empty()) {
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
@@ -350,7 +349,7 @@ impl NodeRuntime {
return;
}
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true);
let bounds = graphic.bounding_box(DAffine2::IDENTITY, true);
// Render the thumbnail from a `Graphic` into an SVG string
let render_params = RenderParams {
@@ -363,7 +362,7 @@ impl NodeRuntime {
alignment_parent_transform: None,
};
let mut render = SvgRender::new();
graphic_element.render_svg(&mut render, &render_params);
graphic.render_svg(&mut render, &render_params);
// And give the SVG a viewbox and outer <svg>...</svg> wrapper tag
let [min, max] = bounds.unwrap_or_default();