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:
parent
853c26cbc1
commit
c98477d8ed
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:#?}"));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@
|
|||
--color-data-general-dim: #8a8a8a;
|
||||
--color-data-raster: #e4bb72;
|
||||
--color-data-raster-dim: #8b7752;
|
||||
--color-data-vectordata: #65bbe5;
|
||||
--color-data-vectordata-dim: #4b778c;
|
||||
--color-data-vector: #65bbe5;
|
||||
--color-data-vector-dim: #4b778c;
|
||||
--color-data-group: #66b195;
|
||||
--color-data-group-dim: #3d725e;
|
||||
--color-data-artboard: #fbf9eb;
|
||||
|
|
|
|||
|
|
@ -679,7 +679,7 @@
|
|||
}
|
||||
|
||||
.color-vector {
|
||||
fill: var(--color-data-vectordata);
|
||||
fill: var(--color-data-vector);
|
||||
}
|
||||
|
||||
.color-raster {
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export type ContextMenuInformation = {
|
|||
contextMenuData: "CreateNode" | { type: "CreateNode"; compatibleType: string } | { nodeId: bigint; currentlyIsNode: boolean };
|
||||
};
|
||||
|
||||
export type FrontendGraphDataType = "General" | "Raster" | "VectorData" | "Number" | "Group" | "Artboard";
|
||||
export type FrontendGraphDataType = "General" | "Raster" | "Vector" | "Number" | "Group" | "Artboard";
|
||||
|
||||
export class Node {
|
||||
readonly index!: bigint;
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ impl EditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Paste vector data into a new layer from a serialized JSON representation
|
||||
/// Paste vector into a new layer from a serialized JSON representation
|
||||
#[wasm_bindgen(js_name = pasteSerializedVector)]
|
||||
pub fn paste_serialized_vector(&self, data: String) {
|
||||
let message = PortfolioMessage::PasteSerializedVector { data };
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
|
|||
|
||||
There is also the more general `register_node!` for nodes that do not need to run per pixel.
|
||||
```rs
|
||||
register_node!(graphene_core::transform_nodes::SetTransformNode<_>, input: VectorData, params: [DAffine2]),
|
||||
register_node!(graphene_core::transform_nodes::SetTransformNode<_>, input: Vector, params: [DAffine2]),
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::raster_types::{CPU, GPU, Raster};
|
|||
use crate::table::{Table, TableRow};
|
||||
use crate::transform::TransformMut;
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, Graphic, OwnedContextImpl};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
|
|
@ -14,7 +14,7 @@ use std::hash::Hash;
|
|||
/// Some [`ArtboardData`] with some optional clipping bounds that can be exported.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Artboard {
|
||||
pub graphic_group: Table<Graphic>,
|
||||
pub group: Table<Graphic>,
|
||||
pub label: String,
|
||||
pub location: IVec2,
|
||||
pub dimensions: IVec2,
|
||||
|
|
@ -31,7 +31,7 @@ impl Default for Artboard {
|
|||
impl Artboard {
|
||||
pub fn new(location: IVec2, dimensions: IVec2) -> Self {
|
||||
Self {
|
||||
graphic_group: Table::new(),
|
||||
group: Table::new(),
|
||||
label: "Artboard".to_string(),
|
||||
location: location.min(location + dimensions),
|
||||
dimensions: dimensions.abs(),
|
||||
|
|
@ -47,7 +47,7 @@ impl BoundingBox for Artboard {
|
|||
if self.clip {
|
||||
Some(artboard_bounds)
|
||||
} else {
|
||||
[self.graphic_group.bounding_box(transform, include_stroke), Some(artboard_bounds)]
|
||||
[self.group.bounding_box(transform, include_stroke), Some(artboard_bounds)]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.reduce(Quad::combine_bounds)
|
||||
|
|
@ -68,7 +68,7 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
ArtboardGroup(ArtboardGroup),
|
||||
ArtboardGroupTable(Table<Artboard>),
|
||||
ArtboardTable(Table<Artboard>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
|
|
@ -84,7 +84,7 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
}
|
||||
table
|
||||
}
|
||||
EitherFormat::ArtboardGroupTable(artboard_group_table) => artboard_group_table,
|
||||
EitherFormat::ArtboardTable(artboard_group_table) => artboard_group_table,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
|||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> DAffine2,
|
||||
|
|
@ -112,7 +112,6 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
|||
clip: bool,
|
||||
) -> Artboard {
|
||||
let location = location.as_ivec2();
|
||||
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
|
||||
|
||||
let footprint = ctx.try_footprint().copied();
|
||||
let mut new_ctx = OwnedContextImpl::from(ctx);
|
||||
|
|
@ -120,13 +119,19 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
|||
footprint.translate(location.as_dvec2());
|
||||
new_ctx = new_ctx.with_footprint(footprint);
|
||||
}
|
||||
let graphic_group = contents.eval(new_ctx.into_context()).await;
|
||||
let group = contents.eval(new_ctx.into_context()).await.into();
|
||||
|
||||
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
|
||||
|
||||
let location = location.min(location + dimensions);
|
||||
|
||||
let dimensions = dimensions.abs();
|
||||
|
||||
Artboard {
|
||||
graphic_group: graphic_group.into(),
|
||||
group,
|
||||
label,
|
||||
location: location.min(location + dimensions),
|
||||
dimensions: dimensions.abs(),
|
||||
location,
|
||||
dimensions,
|
||||
background,
|
||||
clip,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::raster_types::{CPU, Raster};
|
||||
use crate::registry::types::Percentage;
|
||||
use crate::table::Table;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{BlendMode, Color, Ctx, Graphic};
|
||||
|
||||
pub(super) trait MultiplyAlpha {
|
||||
|
|
@ -13,7 +13,7 @@ impl MultiplyAlpha for Color {
|
|||
*self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.))
|
||||
}
|
||||
}
|
||||
impl MultiplyAlpha for Table<VectorData> {
|
||||
impl MultiplyAlpha for Table<Vector> {
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for row in self.iter_mut() {
|
||||
row.alpha_blending.opacity *= factor as f32;
|
||||
|
|
@ -43,7 +43,7 @@ impl MultiplyFill for Color {
|
|||
*self = Color::from_rgbaf32_unchecked(self.r(), self.g(), self.b(), (self.a() * factor as f32).clamp(0., 1.))
|
||||
}
|
||||
}
|
||||
impl MultiplyFill for Table<VectorData> {
|
||||
impl MultiplyFill for Table<Vector> {
|
||||
fn multiply_fill(&mut self, factor: f64) {
|
||||
for row in self.iter_mut() {
|
||||
row.alpha_blending.fill *= factor as f32;
|
||||
|
|
@ -69,7 +69,7 @@ trait SetBlendMode {
|
|||
fn set_blend_mode(&mut self, blend_mode: BlendMode);
|
||||
}
|
||||
|
||||
impl SetBlendMode for Table<VectorData> {
|
||||
impl SetBlendMode for Table<Vector> {
|
||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||
for row in self.iter_mut() {
|
||||
row.alpha_blending.blend_mode = blend_mode;
|
||||
|
|
@ -95,7 +95,7 @@ trait SetClip {
|
|||
fn set_clip(&mut self, clip: bool);
|
||||
}
|
||||
|
||||
impl SetClip for Table<VectorData> {
|
||||
impl SetClip for Table<Vector> {
|
||||
fn set_clip(&mut self, clip: bool) {
|
||||
for row in self.iter_mut() {
|
||||
row.alpha_blending.clip = clip;
|
||||
|
|
@ -122,7 +122,7 @@ fn blend_mode<T: SetBlendMode>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
)]
|
||||
mut value: T,
|
||||
|
|
@ -138,7 +138,7 @@ fn opacity<T: MultiplyAlpha>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
)]
|
||||
mut value: T,
|
||||
|
|
@ -154,7 +154,7 @@ fn blending<T: SetBlendMode + MultiplyAlpha + MultiplyFill + SetClip>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
)]
|
||||
mut value: T,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use crate::raster_types::{CPU, Raster};
|
||||
use crate::table::Table;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Color, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[node_macro::node(category("Debug"), name("Log to Console"))]
|
||||
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, Table<VectorData>, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, Table<Vector>, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
log::debug!("{:#?}", value);
|
||||
value
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::math::quad::Quad;
|
|||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Color, Ctx};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
|
@ -13,90 +13,88 @@ use std::hash::Hash;
|
|||
/// The possible forms of graphical content that can be rendered by the Render node into either an image or SVG syntax.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Graphic {
|
||||
/// Equivalent to the SVG <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
|
||||
GraphicGroup(Table<Graphic>),
|
||||
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
|
||||
VectorData(Table<VectorData>),
|
||||
RasterDataCPU(Table<Raster<CPU>>),
|
||||
RasterDataGPU(Table<Raster<GPU>>),
|
||||
Group(Table<Graphic>),
|
||||
Vector(Table<Vector>),
|
||||
RasterCPU(Table<Raster<CPU>>),
|
||||
RasterGPU(Table<Raster<GPU>>),
|
||||
}
|
||||
|
||||
impl Default for Graphic {
|
||||
fn default() -> Self {
|
||||
Self::GraphicGroup(Default::default())
|
||||
Self::Group(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
// GraphicGroup
|
||||
// Group
|
||||
impl From<Table<Graphic>> for Graphic {
|
||||
fn from(graphic_group: Table<Graphic>) -> Self {
|
||||
Graphic::GraphicGroup(graphic_group)
|
||||
fn from(group: Table<Graphic>) -> Self {
|
||||
Graphic::Group(group)
|
||||
}
|
||||
}
|
||||
|
||||
// VectorData
|
||||
impl From<VectorData> for Graphic {
|
||||
fn from(vector_data: VectorData) -> Self {
|
||||
Graphic::VectorData(Table::new_from_element(vector_data))
|
||||
// Vector
|
||||
impl From<Vector> for Graphic {
|
||||
fn from(vector: Vector) -> Self {
|
||||
Graphic::Vector(Table::new_from_element(vector))
|
||||
}
|
||||
}
|
||||
impl From<Table<VectorData>> for Graphic {
|
||||
fn from(vector_data: Table<VectorData>) -> Self {
|
||||
Graphic::VectorData(vector_data)
|
||||
impl From<Table<Vector>> for Graphic {
|
||||
fn from(vector: Table<Vector>) -> Self {
|
||||
Graphic::Vector(vector)
|
||||
}
|
||||
}
|
||||
impl From<VectorData> for Table<Graphic> {
|
||||
fn from(vector_data: VectorData) -> Self {
|
||||
Table::new_from_element(Graphic::VectorData(Table::new_from_element(vector_data)))
|
||||
impl From<Vector> for Table<Graphic> {
|
||||
fn from(vector: Vector) -> Self {
|
||||
Table::new_from_element(Graphic::Vector(Table::new_from_element(vector)))
|
||||
}
|
||||
}
|
||||
impl From<Table<VectorData>> for Table<Graphic> {
|
||||
fn from(vector_data: Table<VectorData>) -> Self {
|
||||
Table::new_from_element(Graphic::VectorData(vector_data))
|
||||
impl From<Table<Vector>> for Table<Graphic> {
|
||||
fn from(vector: Table<Vector>) -> Self {
|
||||
Table::new_from_element(Graphic::Vector(vector))
|
||||
}
|
||||
}
|
||||
|
||||
// Raster<CPU>
|
||||
impl From<Raster<CPU>> for Graphic {
|
||||
fn from(raster_data: Raster<CPU>) -> Self {
|
||||
Graphic::RasterDataCPU(Table::new_from_element(raster_data))
|
||||
fn from(raster: Raster<CPU>) -> Self {
|
||||
Graphic::RasterCPU(Table::new_from_element(raster))
|
||||
}
|
||||
}
|
||||
impl From<Table<Raster<CPU>>> for Graphic {
|
||||
fn from(raster_data: Table<Raster<CPU>>) -> Self {
|
||||
Graphic::RasterDataCPU(raster_data)
|
||||
fn from(raster: Table<Raster<CPU>>) -> Self {
|
||||
Graphic::RasterCPU(raster)
|
||||
}
|
||||
}
|
||||
impl From<Raster<CPU>> for Table<Graphic> {
|
||||
fn from(raster_data: Raster<CPU>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterDataCPU(Table::new_from_element(raster_data)))
|
||||
fn from(raster: Raster<CPU>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterCPU(Table::new_from_element(raster)))
|
||||
}
|
||||
}
|
||||
impl From<Table<Raster<CPU>>> for Table<Graphic> {
|
||||
fn from(raster_data_table: Table<Raster<CPU>>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterDataCPU(raster_data_table))
|
||||
fn from(raster: Table<Raster<CPU>>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterCPU(raster))
|
||||
}
|
||||
}
|
||||
|
||||
// Raster<GPU>
|
||||
impl From<Raster<GPU>> for Graphic {
|
||||
fn from(raster_data: Raster<GPU>) -> Self {
|
||||
Graphic::RasterDataGPU(Table::new_from_element(raster_data))
|
||||
fn from(raster: Raster<GPU>) -> Self {
|
||||
Graphic::RasterGPU(Table::new_from_element(raster))
|
||||
}
|
||||
}
|
||||
impl From<Table<Raster<GPU>>> for Graphic {
|
||||
fn from(raster_data: Table<Raster<GPU>>) -> Self {
|
||||
Graphic::RasterDataGPU(raster_data)
|
||||
fn from(raster: Table<Raster<GPU>>) -> Self {
|
||||
Graphic::RasterGPU(raster)
|
||||
}
|
||||
}
|
||||
impl From<Raster<GPU>> for Table<Graphic> {
|
||||
fn from(raster_data: Raster<GPU>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterDataGPU(Table::new_from_element(raster_data)))
|
||||
fn from(raster: Raster<GPU>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterGPU(Table::new_from_element(raster)))
|
||||
}
|
||||
}
|
||||
impl From<Table<Raster<GPU>>> for Table<Graphic> {
|
||||
fn from(raster_data_table: Table<Raster<GPU>>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterDataGPU(raster_data_table))
|
||||
fn from(raster: Table<Raster<GPU>>) -> Self {
|
||||
Table::new_from_element(Graphic::RasterGPU(raster))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,58 +113,58 @@ impl From<DAffine2> for Table<Graphic> {
|
|||
impl Graphic {
|
||||
pub fn as_group(&self) -> Option<&Table<Graphic>> {
|
||||
match self {
|
||||
Graphic::GraphicGroup(group) => Some(group),
|
||||
Graphic::Group(group) => Some(group),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_group_mut(&mut self) -> Option<&mut Table<Graphic>> {
|
||||
match self {
|
||||
Graphic::GraphicGroup(group) => Some(group),
|
||||
Graphic::Group(group) => Some(group),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vector_data(&self) -> Option<&Table<VectorData>> {
|
||||
pub fn as_vector(&self) -> Option<&Table<Vector>> {
|
||||
match self {
|
||||
Graphic::VectorData(data) => Some(data),
|
||||
Graphic::Vector(vector) => Some(vector),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vector_data_mut(&mut self) -> Option<&mut Table<VectorData>> {
|
||||
pub fn as_vector_mut(&mut self) -> Option<&mut Table<Vector>> {
|
||||
match self {
|
||||
Graphic::VectorData(data) => Some(data),
|
||||
Graphic::Vector(vector) => Some(vector),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_raster(&self) -> Option<&Table<Raster<CPU>>> {
|
||||
match self {
|
||||
Graphic::RasterDataCPU(raster) => Some(raster),
|
||||
Graphic::RasterCPU(raster) => Some(raster),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_raster_mut(&mut self) -> Option<&mut Table<Raster<CPU>>> {
|
||||
match self {
|
||||
Graphic::RasterDataCPU(raster) => Some(raster),
|
||||
Graphic::RasterCPU(raster) => Some(raster),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn had_clip_enabled(&self) -> bool {
|
||||
match self {
|
||||
Graphic::VectorData(data) => data.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::GraphicGroup(data) => data.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterDataCPU(data) => data.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterDataGPU(data) => data.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::Vector(vector) => vector.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::Group(group) => group.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterCPU(raster) => raster.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterGPU(raster) => raster.iter_ref().all(|row| row.alpha_blending.clip),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_reduce_to_clip_path(&self) -> bool {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data_table) => vector_data_table.iter_ref().all(|row| {
|
||||
Graphic::Vector(vector) => vector.iter_ref().all(|row| {
|
||||
let style = &row.element.style;
|
||||
let alpha_blending = &row.alpha_blending;
|
||||
(alpha_blending.opacity > 1. - f32::EPSILON) && style.fill().is_opaque() && style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||
|
|
@ -179,10 +177,10 @@ impl Graphic {
|
|||
impl BoundingBox for Graphic {
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterDataCPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterDataGPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform, include_stroke),
|
||||
Graphic::Vector(vector) => vector.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterCPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterGPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::Group(group) => group.bounding_box(transform, include_stroke),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -198,8 +196,8 @@ impl BoundingBox for Table<Graphic> {
|
|||
#[node_macro::node(category(""))]
|
||||
async fn layer<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Graphic>, Table<VectorData>, Table<Raster<CPU>>, Table<Raster<GPU>>)] mut stack: Table<I>,
|
||||
#[implementations(Graphic, VectorData, Raster<CPU>, Raster<GPU>)] element: I,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] mut stack: Table<I>,
|
||||
#[implementations(Graphic, Vector, Raster<CPU>, Raster<GPU>)] element: I,
|
||||
node_path: Vec<NodeId>,
|
||||
) -> Table<I> {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
|
|
@ -220,7 +218,7 @@ async fn to_element<Data: Into<Graphic> + 'n>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
DAffine2,
|
||||
|
|
@ -235,7 +233,7 @@ async fn to_group<Data: Into<Table<Graphic>> + 'n>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
)]
|
||||
|
|
@ -255,16 +253,16 @@ async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool)
|
|||
let recurse = fully_flatten || recursion_depth == 0;
|
||||
|
||||
match current_element {
|
||||
// If we're allowed to recurse, flatten any GraphicGroups we encounter
|
||||
Graphic::GraphicGroup(mut current_element) if recurse => {
|
||||
// If we're allowed to recurse, flatten any groups we encounter
|
||||
Graphic::Group(mut current_element) if recurse => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
for graphic_element in current_element.iter_mut() {
|
||||
*graphic_element.transform = *current_row.transform * *graphic_element.transform;
|
||||
for graphic in current_element.iter_mut() {
|
||||
*graphic.transform = *current_row.transform * *graphic.transform;
|
||||
}
|
||||
|
||||
flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten
|
||||
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
|
||||
_ => {
|
||||
output_group_table.push(TableRow {
|
||||
element: current_element,
|
||||
|
|
@ -284,36 +282,36 @@ async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool)
|
|||
}
|
||||
|
||||
#[node_macro::node(category("Vector"))]
|
||||
async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<VectorData> {
|
||||
async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<Vector> {
|
||||
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
|
||||
fn flatten_group(output_group_table: &mut Table<VectorData>, current_group_table: Table<Graphic>) {
|
||||
for current_graphic_element_row in current_group_table.iter_ref() {
|
||||
let current_element = current_graphic_element_row.element.clone();
|
||||
let reference = *current_graphic_element_row.source_node_id;
|
||||
fn flatten_group(output_group_table: &mut Table<Vector>, current_group_table: Table<Graphic>) {
|
||||
for current_graphic_row in current_group_table.iter_ref() {
|
||||
let current_graphic = current_graphic_row.element.clone();
|
||||
let source_node_id = *current_graphic_row.source_node_id;
|
||||
|
||||
match current_element {
|
||||
// If we're allowed to recurse, flatten any GraphicGroups we encounter
|
||||
Graphic::GraphicGroup(mut current_element) => {
|
||||
match current_graphic {
|
||||
// If we're allowed to recurse, flatten any groups we encounter
|
||||
Graphic::Group(mut current_graphic_table) => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
for graphic_element in current_element.iter_mut() {
|
||||
*graphic_element.transform = *current_graphic_element_row.transform * *graphic_element.transform;
|
||||
for graphic in current_graphic_table.iter_mut() {
|
||||
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
|
||||
}
|
||||
|
||||
flatten_group(output_group_table, current_element);
|
||||
flatten_group(output_group_table, current_graphic_table);
|
||||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten
|
||||
Graphic::VectorData(vector_table) => {
|
||||
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
|
||||
Graphic::Vector(vector_table) => {
|
||||
for current_vector_row in vector_table.iter_ref() {
|
||||
output_group_table.push(TableRow {
|
||||
element: current_vector_row.element.clone(),
|
||||
transform: *current_graphic_element_row.transform * *current_vector_row.transform,
|
||||
transform: *current_graphic_row.transform * *current_vector_row.transform,
|
||||
alpha_blending: AlphaBlending {
|
||||
blend_mode: current_vector_row.alpha_blending.blend_mode,
|
||||
opacity: current_graphic_element_row.alpha_blending.opacity * current_vector_row.alpha_blending.opacity,
|
||||
opacity: current_graphic_row.alpha_blending.opacity * current_vector_row.alpha_blending.opacity,
|
||||
fill: current_vector_row.alpha_blending.fill,
|
||||
clip: current_vector_row.alpha_blending.clip,
|
||||
},
|
||||
source_node_id: reference,
|
||||
source_node_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -339,7 +337,7 @@ fn index<T: AtIndex + Clone + Default>(
|
|||
Vec<Option<Color>>,
|
||||
Vec<f64>, Vec<u64>,
|
||||
Vec<DVec2>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Graphic>,
|
||||
)]
|
||||
|
|
@ -379,7 +377,7 @@ impl<T: Clone> AtIndex for Table<T> {
|
|||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
|
||||
pub fn migrate_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
|
||||
|
|
@ -402,32 +400,32 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
|||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::OldGraphicGroup(old) => {
|
||||
let mut graphic_group_table = Table::new();
|
||||
for (graphic_element, source_node_id) in old.elements {
|
||||
graphic_group_table.push(TableRow {
|
||||
element: graphic_element,
|
||||
let mut group_table = Table::new();
|
||||
for (graphic, source_node_id) in old.elements {
|
||||
group_table.push(TableRow {
|
||||
element: graphic,
|
||||
transform: old.transform,
|
||||
alpha_blending: old.alpha_blending,
|
||||
source_node_id,
|
||||
});
|
||||
}
|
||||
graphic_group_table
|
||||
group_table
|
||||
}
|
||||
EitherFormat::Table(value) => {
|
||||
// Try to deserialize as either table format
|
||||
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
|
||||
let mut graphic_group_table = Table::new();
|
||||
let mut group_table = Table::new();
|
||||
for row in old_table.iter_ref() {
|
||||
for (graphic_element, source_node_id) in &row.element.elements {
|
||||
graphic_group_table.push(TableRow {
|
||||
element: graphic_element.clone(),
|
||||
for (graphic, source_node_id) in &row.element.elements {
|
||||
group_table.push(TableRow {
|
||||
element: graphic.clone(),
|
||||
transform: *row.transform,
|
||||
alpha_blending: *row.alpha_blending,
|
||||
source_node_id: *source_node_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
graphic_group_table
|
||||
group_table
|
||||
} else if let Ok(new_table) = serde_json::from_value::<Table<Graphic>>(value) {
|
||||
new_table
|
||||
} else {
|
||||
|
|
@ -11,7 +11,7 @@ pub mod debug;
|
|||
pub mod extract_xy;
|
||||
pub mod generic;
|
||||
pub mod gradient;
|
||||
pub mod graphic_element;
|
||||
pub mod graphic;
|
||||
pub mod logic;
|
||||
pub mod math;
|
||||
pub mod memo;
|
||||
|
|
@ -41,7 +41,7 @@ pub use graphene_core_shaders::AsU32;
|
|||
pub use graphene_core_shaders::blending;
|
||||
pub use graphene_core_shaders::choice_type;
|
||||
pub use graphene_core_shaders::color;
|
||||
pub use graphic_element::Graphic;
|
||||
pub use graphic::Graphic;
|
||||
pub use memo::MemoHash;
|
||||
pub use num_traits;
|
||||
use std::any::TypeId;
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@ use crate::gradient::GradientStops;
|
|||
use crate::graphene_core::registry::types::TextArea;
|
||||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::Table;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Context, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<VectorData>)] value: T) -> String {
|
||||
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<Vector>)] value: T) -> String {
|
||||
format!("{:?}", value)
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
fn serialize<T: serde::Serialize>(
|
||||
_: impl Ctx,
|
||||
#[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Color, Option<Color>, Table<Graphic>, Table<VectorData>, Table<Raster<CPU>>)] value: T,
|
||||
#[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Color, Option<Color>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>)] value: T,
|
||||
) -> String {
|
||||
serde_json::to_string(&value).unwrap_or_else(|_| "Serialization Error".to_string())
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ async fn switch<T, C: Send + 'n + Clone>(
|
|||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
|
|
@ -81,7 +81,7 @@ async fn switch<T, C: Send + 'n + Clone>(
|
|||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::AlphaBlending;
|
|||
use crate::color::float_to_srgb_u8;
|
||||
use crate::raster_types::Raster;
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
|
@ -239,7 +239,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
/// Equivalent to the SVG <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
|
||||
GraphicGroup(Table<GraphicElement>),
|
||||
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
|
||||
VectorData(Table<VectorData>),
|
||||
VectorData(Table<Vector>),
|
||||
RasterFrame(RasterFrame),
|
||||
}
|
||||
|
||||
|
|
@ -283,21 +283,20 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
enum FormatVersions {
|
||||
Image(Image<Color>),
|
||||
OldImageFrame(OldImageFrame<Color>),
|
||||
ImageFrame(Table<ImageFrame<Color>>),
|
||||
ImageFrameTable(Table<Image<Color>>),
|
||||
RasterDataTable(Table<Raster<CPU>>),
|
||||
ImageFrameTable(Table<ImageFrame<Color>>),
|
||||
ImageTable(Table<Image<Color>>),
|
||||
RasterTable(Table<Raster<CPU>>),
|
||||
}
|
||||
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => Table::new_from_element(Raster::new_cpu(image)),
|
||||
FormatVersions::OldImageFrame(image_frame_with_transform_and_blending) => {
|
||||
let OldImageFrame { image, transform, alpha_blending } = image_frame_with_transform_and_blending;
|
||||
FormatVersions::OldImageFrame(OldImageFrame { image, transform, alpha_blending }) => {
|
||||
let mut image_frame_table = Table::new_from_element(Raster::new_cpu(image));
|
||||
*image_frame_table.iter_mut().next().unwrap().transform = transform;
|
||||
*image_frame_table.iter_mut().next().unwrap().alpha_blending = alpha_blending;
|
||||
image_frame_table
|
||||
}
|
||||
FormatVersions::ImageFrame(image_frame) => Table::new_from_element(Raster::new_cpu(
|
||||
FormatVersions::ImageFrameTable(image_frame) => Table::new_from_element(Raster::new_cpu(
|
||||
image_frame
|
||||
.iter_ref()
|
||||
.next()
|
||||
|
|
@ -306,8 +305,8 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
.image
|
||||
.clone(),
|
||||
)),
|
||||
FormatVersions::ImageFrameTable(image_frame_table) => Table::new_from_element(Raster::new_cpu(image_frame_table.iter_ref().next().unwrap().element.clone())),
|
||||
FormatVersions::RasterDataTable(raster_data_table) => raster_data_table,
|
||||
FormatVersions::ImageTable(table) => Table::new_from_element(Raster::new_cpu(table.iter_ref().next().unwrap().element.clone())),
|
||||
FormatVersions::RasterTable(table) => table,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +337,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
|||
/// Equivalent to the SVG <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
|
||||
GraphicGroup(Table<GraphicElement>),
|
||||
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
|
||||
VectorData(Table<VectorData>),
|
||||
VectorData(Table<Vector>),
|
||||
RasterFrame(RasterFrame),
|
||||
}
|
||||
|
||||
|
|
@ -382,9 +381,9 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
|||
enum FormatVersions {
|
||||
Image(Image<Color>),
|
||||
OldImageFrame(OldImageFrame<Color>),
|
||||
ImageFrame(Table<ImageFrame<Color>>),
|
||||
RasterDataTable(Table<Raster<CPU>>),
|
||||
ImageTableRow(TableRow<Raster<CPU>>),
|
||||
ImageFrameTable(Table<ImageFrame<Color>>),
|
||||
RasterTable(Table<Raster<CPU>>),
|
||||
RasterTableRow(TableRow<Raster<CPU>>),
|
||||
}
|
||||
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
|
|
@ -398,12 +397,12 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
|||
alpha_blending: image_frame_with_transform_and_blending.alpha_blending,
|
||||
source_node_id: None,
|
||||
},
|
||||
FormatVersions::ImageFrame(image_frame) => TableRow {
|
||||
FormatVersions::ImageFrameTable(image_frame) => TableRow {
|
||||
element: Raster::new_cpu(image_frame.iter_ref().next().unwrap().element.image.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
FormatVersions::RasterDataTable(image_frame_table) => image_frame_table.iter().next().unwrap_or_default(),
|
||||
FormatVersions::ImageTableRow(image_table_row) => image_table_row,
|
||||
FormatVersions::RasterTable(image_frame_table) => image_frame_table.iter().next().unwrap_or_default(),
|
||||
FormatVersions::RasterTableRow(image_table_row) => image_table_row,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::Table;
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Artboard, Color, Graphic};
|
||||
use glam::DVec2;
|
||||
|
||||
|
|
@ -18,22 +18,22 @@ impl<T: RenderComplexity> RenderComplexity for Table<T> {
|
|||
|
||||
impl RenderComplexity for Artboard {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.graphic_group.render_complexity()
|
||||
self.group.render_complexity()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Graphic {
|
||||
fn render_complexity(&self) -> usize {
|
||||
match self {
|
||||
Self::GraphicGroup(table) => table.render_complexity(),
|
||||
Self::VectorData(table) => table.render_complexity(),
|
||||
Self::RasterDataCPU(table) => table.render_complexity(),
|
||||
Self::RasterDataGPU(table) => table.render_complexity(),
|
||||
Self::Group(table) => table.render_complexity(),
|
||||
Self::Vector(table) => table.render_complexity(),
|
||||
Self::RasterCPU(table) => table.render_complexity(),
|
||||
Self::RasterGPU(table) => table.render_complexity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for VectorData {
|
||||
impl RenderComplexity for Vector {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.segment_domain.ids().len()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::TextAlign;
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::vector::{PointId, VectorData};
|
||||
use crate::vector::{PointId, Vector};
|
||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use core::cell::RefCell;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
|
@ -24,7 +24,7 @@ struct PathBuilder {
|
|||
current_subpath: Subpath<PointId>,
|
||||
origin: DVec2,
|
||||
glyph_subpaths: Vec<Subpath<PointId>>,
|
||||
vector_table: Table<VectorData>,
|
||||
vector_table: Table<Vector>,
|
||||
scale: f64,
|
||||
id: PointId,
|
||||
}
|
||||
|
|
@ -52,13 +52,13 @@ impl PathBuilder {
|
|||
|
||||
if per_glyph_instances {
|
||||
self.vector_table.push(TableRow {
|
||||
element: VectorData::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false),
|
||||
element: Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false),
|
||||
transform: DAffine2::from_translation(glyph_offset),
|
||||
..Default::default()
|
||||
});
|
||||
} else {
|
||||
for subpath in self.glyph_subpaths.drain(..) {
|
||||
// Unwrapping here is ok because `self.vector_table` is initialized with a single `VectorData`
|
||||
// Unwrapping here is ok because `self.vector_table` is initialized with a single `Vector` table element
|
||||
self.vector_table.get_mut(0).unwrap().element.append_subpath(subpath, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -205,15 +205,15 @@ fn layout_text(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingC
|
|||
Some(layout)
|
||||
}
|
||||
|
||||
pub fn to_path(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingConfig, per_glyph_instances: bool) -> Table<VectorData> {
|
||||
pub fn to_path(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingConfig, per_glyph_instances: bool) -> Table<Vector> {
|
||||
let Some(layout) = layout_text(str, font_data, typesetting) else {
|
||||
return Table::new_from_element(VectorData::default());
|
||||
return Table::new_from_element(Vector::default());
|
||||
};
|
||||
|
||||
let mut path_builder = PathBuilder {
|
||||
current_subpath: Subpath::new(Vec::new(), false),
|
||||
glyph_subpaths: Vec::new(),
|
||||
vector_table: if per_glyph_instances { Table::new() } else { Table::new_from_element(VectorData::default()) },
|
||||
vector_table: if per_glyph_instances { Table::new() } else { Table::new_from_element(Vector::default()) },
|
||||
scale: layout.scale() as f64,
|
||||
id: PointId::ZERO,
|
||||
origin: DVec2::default(),
|
||||
|
|
@ -228,7 +228,7 @@ pub fn to_path(str: &str, font_data: Option<Blob<u8>>, typesetting: TypesettingC
|
|||
}
|
||||
|
||||
if path_builder.vector_table.is_empty() {
|
||||
path_builder.vector_table = Table::new_from_element(VectorData::default());
|
||||
path_builder.vector_table = Table::new_from_element(Vector::default());
|
||||
}
|
||||
|
||||
path_builder.vector_table
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::Table;
|
||||
use crate::transform::{ApplyTransform, Footprint, Transform};
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, Graphic, OwnedContextImpl};
|
||||
use core::f64;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
|
@ -12,7 +12,7 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
|
|||
#[implementations(
|
||||
Context -> DAffine2,
|
||||
Context -> DVec2,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
|
|
@ -43,7 +43,7 @@ async fn transform<T: ApplyTransform + 'n + 'static>(
|
|||
#[node_macro::node(category(""))]
|
||||
fn replace_transform<Data, TransformInput: Transform>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<VectorData>, Table<Raster<CPU>>, Table<Graphic>)] mut data: Table<Data>,
|
||||
#[implementations(Table<Vector>, Table<Raster<CPU>>, Table<Graphic>)] mut data: Table<Data>,
|
||||
#[implementations(DAffine2)] transform: TransformInput,
|
||||
) -> Table<Data> {
|
||||
for data_transform in data.iter_mut() {
|
||||
|
|
@ -57,13 +57,13 @@ async fn extract_transform<T>(
|
|||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
)]
|
||||
vector_data: Table<T>,
|
||||
vector: Table<T>,
|
||||
) -> DAffine2 {
|
||||
vector_data.iter_ref().next().map(|vector_data| *vector_data.transform).unwrap_or_default()
|
||||
vector.iter_ref().next().map(|row| *row.transform).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Math: Transform"))]
|
||||
|
|
@ -90,7 +90,7 @@ fn decompose_scale(_: impl Ctx, transform: DAffine2) -> DVec2 {
|
|||
async fn boundless_footprint<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
|
|
@ -108,7 +108,7 @@ async fn boundless_footprint<T: 'n + 'static>(
|
|||
async fn freeze_real_time<T: 'n + 'static>(
|
||||
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||
#[implementations(
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
|
|
|
|||
|
|
@ -167,8 +167,7 @@ fn migrate_type_descriptor_names<'de, D: serde::Deserializer<'de>>(deserializer:
|
|||
let name = match name.as_str() {
|
||||
"f32" => "f64".to_string(),
|
||||
"graphene_core::transform::Footprint" => "std::option::Option<std::sync::Arc<graphene_core::context::OwnedContextImpl>>".to_string(),
|
||||
"graphene_core::graphic_element::GraphicGroup" => "graphene_core::table::Table<graphene_core::graphic_element::GraphicGroup>".to_string(),
|
||||
"graphene_core::vector::vector_data::VectorData" => "graphene_core::table::Table<graphene_core::vector::vector_data::VectorData>".to_string(),
|
||||
"graphene_core::graphic_element::GraphicGroup" => "graphene_core::table::Table<graphene_core::graphic::Graphic>".to_string(),
|
||||
"graphene_core::raster::image::ImageFrame<Color>"
|
||||
| "graphene_core::raster::image::ImageFrame<graphene_core::raster::color::Color>"
|
||||
| "graphene_core::instances::Instances<graphene_core::raster::image::ImageFrame<Color>>"
|
||||
|
|
@ -176,8 +175,13 @@ fn migrate_type_descriptor_names<'de, D: serde::Deserializer<'de>>(deserializer:
|
|||
| "graphene_core::instances::Instances<graphene_core::raster::image::Image<graphene_core::raster::color::Color>>" => {
|
||||
"graphene_core::table::Table<graphene_core::raster::image::Image<graphene_core::raster::color::Color>>".to_string()
|
||||
}
|
||||
"graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>" => "graphene_core::table::Table<graphene_core::vector::vector_data::VectorData>".to_string(),
|
||||
"graphene_core::vector::vector_data::VectorData"
|
||||
| "graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>"
|
||||
| "graphene_core::table::Table<graphene_core::vector::vector_data::VectorData>"
|
||||
| "graphene_core::table::Table<graphene_core::vector::vector_data::Vector>" => "graphene_core::table::Table<graphene_core::vector::vector_types::Vector>".to_string(),
|
||||
"graphene_core::instances::Instances<graphene_core::graphic_element::Artboard>" => "graphene_core::table::Table<graphene_core::artboard::Artboard>".to_string(),
|
||||
"graphene_core::vector::vector_data::modification::VectorModification" => "graphene_core::vector::vector_modification::VectorModification".to_string(),
|
||||
"graphene_core::table::Table<graphene_core::graphic_element::Graphic>" => "graphene_core::table::Table<graphene_core::graphic::Graphic>".to_string(),
|
||||
_ => name,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use crate::raster_types::{CPU, Raster};
|
||||
use crate::table::{Table, TableRowRef};
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, Graphic, OwnedContextImpl};
|
||||
use glam::DVec2;
|
||||
|
||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||
async fn instance_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
|
||||
points: Table<VectorData>,
|
||||
points: Table<Vector>,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
|
|
@ -51,7 +51,7 @@ async fn instance_repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
|||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>
|
||||
)]
|
||||
instance: impl Node<'n, Context<'static>, Output = Table<T>>,
|
||||
|
|
@ -99,7 +99,7 @@ mod test {
|
|||
use super::*;
|
||||
use crate::Node;
|
||||
use crate::extract_xy::{ExtractXyNode, XY};
|
||||
use crate::vector::VectorData;
|
||||
use crate::vector::Vector;
|
||||
use bezier_rs::Subpath;
|
||||
use glam::DVec2;
|
||||
use std::pin::Pin;
|
||||
|
|
@ -128,7 +128,7 @@ mod test {
|
|||
);
|
||||
|
||||
let positions = [DVec2::new(40., 20.), DVec2::ONE, DVec2::new(-42., 9.), DVec2::new(10., 345.)];
|
||||
let points = Table::new_from_element(VectorData::from_subpath(Subpath::from_anchors_linear(positions, false)));
|
||||
let points = Table::new_from_element(Vector::from_subpath(Subpath::from_anchors_linear(positions, false)));
|
||||
let generated = super::instance_on_points(owned, points, &rect, false).await;
|
||||
assert_eq!(generated.len(), positions.len());
|
||||
for (position, generated_row) in positions.into_iter().zip(generated.iter_ref()) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use crate::vector::{PointDomain, PointId, SegmentDomain, VectorData, VectorDataIndex};
|
||||
use crate::vector::{PointDomain, PointId, SegmentDomain, SegmentId, Vector};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use petgraph::graph::{EdgeIndex, NodeIndex, UnGraph};
|
||||
use petgraph::prelude::UnGraphMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
pub trait MergeByDistanceExt {
|
||||
|
|
@ -9,10 +11,10 @@ pub trait MergeByDistanceExt {
|
|||
fn merge_by_distance_spatial(&mut self, transform: DAffine2, distance: f64);
|
||||
}
|
||||
|
||||
impl MergeByDistanceExt for VectorData {
|
||||
impl MergeByDistanceExt for Vector {
|
||||
fn merge_by_distance_topological(&mut self, distance: f64) {
|
||||
// Treat self as an undirected graph
|
||||
let indices = VectorDataIndex::build_from(self);
|
||||
let indices = VectorIndex::build_from(self);
|
||||
|
||||
// TODO: We lose information on the winding order by using an undirected graph. Switch to a directed graph and fix the algorithm to handle that.
|
||||
// Graph containing only short edges, referencing the data graph
|
||||
|
|
@ -207,8 +209,94 @@ impl MergeByDistanceExt for VectorData {
|
|||
}
|
||||
}
|
||||
|
||||
// Create new vector data
|
||||
// Create new vector geometry
|
||||
self.point_domain = new_point_domain;
|
||||
self.segment_domain = new_segment_domain;
|
||||
}
|
||||
}
|
||||
|
||||
/// All the fixed fields of a point from the point domain.
|
||||
pub(crate) struct Point {
|
||||
pub id: PointId,
|
||||
pub position: DVec2,
|
||||
}
|
||||
|
||||
/// Useful indexes to speed up various operations on [`Vector`].
|
||||
///
|
||||
/// Important: It is the user's responsibility to ensure the indexes remain valid after mutations to the data.
|
||||
pub struct VectorIndex {
|
||||
/// Points and segments form a graph. Store it here in a form amenable to graph algorithms.
|
||||
///
|
||||
/// Currently, segment data is not stored as it is not used, but it could easily be added.
|
||||
pub(crate) point_graph: UnGraph<Point, ()>,
|
||||
pub(crate) segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
||||
/// Get the offset from the point ID.
|
||||
pub(crate) point_to_offset: FxHashMap<PointId, usize>,
|
||||
// TODO: faces
|
||||
}
|
||||
|
||||
impl VectorIndex {
|
||||
/// Construct a [`VectorIndex`] by building indexes from the given [`Vector`]. Takes `O(n)` time.
|
||||
pub fn build_from(data: &Vector) -> Self {
|
||||
let point_to_offset = data.point_domain.ids().iter().copied().enumerate().map(|(a, b)| (b, a)).collect::<FxHashMap<_, _>>();
|
||||
|
||||
let mut point_to_node = FxHashMap::default();
|
||||
let mut segment_to_edge = FxHashMap::default();
|
||||
|
||||
let mut graph = UnGraph::new_undirected();
|
||||
|
||||
for (point_id, position) in data.point_domain.iter() {
|
||||
let idx = graph.add_node(Point { id: point_id, position });
|
||||
point_to_node.insert(point_id, idx);
|
||||
}
|
||||
|
||||
for (segment_id, start_offset, end_offset, ..) in data.segment_domain.iter() {
|
||||
let start_id = data.point_domain.ids()[start_offset];
|
||||
let end_id = data.point_domain.ids()[end_offset];
|
||||
let edge = graph.add_edge(point_to_node[&start_id], point_to_node[&end_id], ());
|
||||
|
||||
segment_to_edge.insert(segment_id, edge);
|
||||
}
|
||||
|
||||
Self {
|
||||
point_graph: graph,
|
||||
segment_to_edge,
|
||||
point_to_offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the length of given segment's chord. Takes `O(1)` time.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if no segment with the given ID is found.
|
||||
pub fn segment_chord_length(&self, id: SegmentId) -> f64 {
|
||||
let edge_idx = self.segment_to_edge[&id];
|
||||
let (start, end) = self.point_graph.edge_endpoints(edge_idx).unwrap();
|
||||
let start_position = self.point_graph.node_weight(start).unwrap().position;
|
||||
let end_position = self.point_graph.node_weight(end).unwrap().position;
|
||||
(start_position - end_position).length()
|
||||
}
|
||||
|
||||
/// Get the ends of a segment. Takes `O(1)` time.
|
||||
///
|
||||
/// The IDs will be ordered [smallest, largest] so they can be used to find other segments with the same endpoints, regardless of direction.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the ID is not present.
|
||||
pub fn segment_ends(&self, id: SegmentId) -> [NodeIndex; 2] {
|
||||
let (start, end) = self.point_graph.edge_endpoints(self.segment_to_edge[&id]).unwrap();
|
||||
if start < end { [start, end] } else { [end, start] }
|
||||
}
|
||||
|
||||
/// Get the physical location of a point. Takes `O(1)` time.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `id` isn't in the data.
|
||||
pub fn point_position(&self, id: PointId, data: &Vector) -> DVec2 {
|
||||
let offset = self.point_to_offset[&id];
|
||||
data.point_domain.positions()[offset]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,21 +3,22 @@ use super::{PointId, SegmentId, StrokeId};
|
|||
use crate::Ctx;
|
||||
use crate::registry::types::{Angle, PixelSize};
|
||||
use crate::table::Table;
|
||||
use crate::vector::{HandleId, VectorData};
|
||||
use crate::vector::Vector;
|
||||
use crate::vector::misc::HandleId;
|
||||
use bezier_rs::Subpath;
|
||||
use glam::DVec2;
|
||||
|
||||
trait CornerRadius {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<VectorData>;
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector>;
|
||||
}
|
||||
impl CornerRadius for f64 {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<VectorData> {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector> {
|
||||
let clamped_radius = if clamped { self.clamp(0., size.x.min(size.y).max(0.) / 2.) } else { self };
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_rounded_rect(size / -2., size / 2., [clamped_radius; 4])))
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_rounded_rect(size / -2., size / 2., [clamped_radius; 4])))
|
||||
}
|
||||
}
|
||||
impl CornerRadius for [f64; 4] {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<VectorData> {
|
||||
fn generate(self, size: DVec2, clamped: bool) -> Table<Vector> {
|
||||
let clamped_radius = if clamped {
|
||||
// Algorithm follows the CSS spec: <https://drafts.csswg.org/css-backgrounds/#corner-overlap>
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ impl CornerRadius for [f64; 4] {
|
|||
} else {
|
||||
self
|
||||
};
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_rounded_rect(size / -2., size / 2., clamped_radius)))
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_rounded_rect(size / -2., size / 2., clamped_radius)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,9 +45,9 @@ fn circle(
|
|||
#[unit(" px")]
|
||||
#[default(50.)]
|
||||
radius: f64,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let radius = radius.abs();
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
|
|
@ -61,8 +62,8 @@ fn arc(
|
|||
#[range((0., 360.))]
|
||||
sweep_angle: Angle,
|
||||
arc_type: ArcType,
|
||||
) -> Table<VectorData> {
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_arc(
|
||||
) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_arc(
|
||||
radius,
|
||||
start_angle / 360. * std::f64::consts::TAU,
|
||||
sweep_angle / 360. * std::f64::consts::TAU,
|
||||
|
|
@ -84,12 +85,12 @@ fn ellipse(
|
|||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_y: f64,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let radius = DVec2::new(radius_x, radius_y);
|
||||
let corner1 = -radius;
|
||||
let corner2 = radius;
|
||||
|
||||
let mut ellipse = VectorData::from_subpath(Subpath::new_ellipse(corner1, corner2));
|
||||
let mut ellipse = Vector::from_subpath(Subpath::new_ellipse(corner1, corner2));
|
||||
|
||||
let len = ellipse.segment_domain.ids().len();
|
||||
for i in 0..len {
|
||||
|
|
@ -114,7 +115,7 @@ fn rectangle<T: CornerRadius>(
|
|||
_individual_corner_radii: bool, // TODO: Move this to the bottom once we have a migration capability
|
||||
#[implementations(f64, [f64; 4])] corner_radius: T,
|
||||
#[default(true)] clamped: bool,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
corner_radius.generate(DVec2::new(width, height), clamped)
|
||||
}
|
||||
|
||||
|
|
@ -129,10 +130,10 @@ fn regular_polygon<T: AsU64>(
|
|||
#[unit(" px")]
|
||||
#[default(50)]
|
||||
radius: f64,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let points = sides.as_u64();
|
||||
let radius: f64 = radius * 2.;
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_regular_polygon(DVec2::splat(-radius), points, radius)))
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_regular_polygon(DVec2::splat(-radius), points, radius)))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
|
|
@ -149,17 +150,17 @@ fn star<T: AsU64>(
|
|||
#[unit(" px")]
|
||||
#[default(25)]
|
||||
radius_2: f64,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let points = sides.as_u64();
|
||||
let diameter: f64 = radius_1 * 2.;
|
||||
let inner_diameter = radius_2 * 2.;
|
||||
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_star_polygon(DVec2::splat(-diameter), points, diameter, inner_diameter)))
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_star_polygon(DVec2::splat(-diameter), points, diameter, inner_diameter)))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn line(_: impl Ctx, _primary: (), #[default(0., 0.)] start: PixelSize, #[default(100., 100.)] end: PixelSize) -> Table<VectorData> {
|
||||
Table::new_from_element(VectorData::from_subpath(Subpath::new_line(start, end)))
|
||||
fn line(_: impl Ctx, _primary: (), #[default(0., 0.)] start: PixelSize, #[default(100., 100.)] end: PixelSize) -> Table<Vector> {
|
||||
Table::new_from_element(Vector::from_subpath(Subpath::new_line(start, end)))
|
||||
}
|
||||
|
||||
trait GridSpacing {
|
||||
|
|
@ -189,11 +190,11 @@ fn grid<T: GridSpacing>(
|
|||
#[default(10)] columns: u32,
|
||||
#[default(10)] rows: u32,
|
||||
#[default(30., 30.)] angles: DVec2,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let (x_spacing, y_spacing) = spacing.as_dvec2().into();
|
||||
let (angle_a, angle_b) = angles.into();
|
||||
|
||||
let mut vector_data = VectorData::default();
|
||||
let mut vector = Vector::default();
|
||||
let mut segment_id = SegmentId::ZERO;
|
||||
let mut point_id = PointId::ZERO;
|
||||
|
||||
|
|
@ -203,13 +204,13 @@ fn grid<T: GridSpacing>(
|
|||
for y in 0..rows {
|
||||
for x in 0..columns {
|
||||
// Add current point to the grid
|
||||
let current_index = vector_data.point_domain.ids().len();
|
||||
vector_data.point_domain.push(point_id.next_id(), DVec2::new(x_spacing * x as f64, y_spacing * y as f64));
|
||||
let current_index = vector.point_domain.ids().len();
|
||||
vector.point_domain.push(point_id.next_id(), DVec2::new(x_spacing * x as f64, y_spacing * y as f64));
|
||||
|
||||
// Helper function to connect points with line segments
|
||||
let mut push_segment = |to_index: Option<usize>| {
|
||||
if let Some(other_index) = to_index {
|
||||
vector_data
|
||||
vector
|
||||
.segment_domain
|
||||
.push(segment_id.next_id(), other_index, current_index, bezier_rs::BezierHandles::Linear, StrokeId::ZERO);
|
||||
}
|
||||
|
|
@ -233,7 +234,7 @@ fn grid<T: GridSpacing>(
|
|||
for y in 0..rows {
|
||||
for x in 0..columns {
|
||||
// Add current point to the grid with offset for odd columns
|
||||
let current_index = vector_data.point_domain.ids().len();
|
||||
let current_index = vector.point_domain.ids().len();
|
||||
|
||||
let a_angles_eaten = x.div_ceil(2) as f64;
|
||||
let b_angles_eaten = (x / 2) as f64;
|
||||
|
|
@ -241,12 +242,12 @@ fn grid<T: GridSpacing>(
|
|||
let offset_y_fraction = b_angles_eaten * tan_b - a_angles_eaten * tan_a;
|
||||
|
||||
let position = DVec2::new(spacing.x * x as f64, spacing.y * y as f64 + offset_y_fraction * spacing.x);
|
||||
vector_data.point_domain.push(point_id.next_id(), position);
|
||||
vector.point_domain.push(point_id.next_id(), position);
|
||||
|
||||
// Helper function to connect points with line segments
|
||||
let mut push_segment = |to_index: Option<usize>| {
|
||||
if let Some(other_index) = to_index {
|
||||
vector_data
|
||||
vector
|
||||
.segment_domain
|
||||
.push(segment_id.next_id(), other_index, current_index, bezier_rs::BezierHandles::Linear, StrokeId::ZERO);
|
||||
}
|
||||
|
|
@ -271,7 +272,7 @@ fn grid<T: GridSpacing>(
|
|||
}
|
||||
}
|
||||
|
||||
Table::new_from_element(vector_data)
|
||||
Table::new_from_element(vector)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::PointId;
|
||||
use super::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
|
||||
use crate::vector::{SegmentId, Vector};
|
||||
use bezier_rs::{BezierHandles, ManipulatorGroup, Subpath};
|
||||
use dyn_any::DynAny;
|
||||
use glam::DVec2;
|
||||
|
|
@ -136,9 +137,9 @@ pub fn handles_to_segment(start: DVec2, handles: BezierHandles, end: DVec2) -> P
|
|||
}
|
||||
|
||||
pub fn subpath_to_kurbo_bezpath(subpath: Subpath<PointId>) -> BezPath {
|
||||
let maniputor_groups = subpath.manipulator_groups();
|
||||
let manipulator_groups = subpath.manipulator_groups();
|
||||
let closed = subpath.closed();
|
||||
bezpath_from_manipulator_groups(maniputor_groups, closed)
|
||||
bezpath_from_manipulator_groups(manipulator_groups, closed)
|
||||
}
|
||||
|
||||
pub fn bezpath_from_manipulator_groups(manipulator_groups: &[ManipulatorGroup<PointId>], closed: bool) -> BezPath {
|
||||
|
|
@ -181,8 +182,8 @@ pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec<ManipulatorGroup
|
|||
kurbo::PathEl::LineTo(point) => ManipulatorGroup::new(point_to_dvec2(point), None, None),
|
||||
kurbo::PathEl::QuadTo(point, point1) => ManipulatorGroup::new(point_to_dvec2(point1), Some(point_to_dvec2(point)), None),
|
||||
kurbo::PathEl::CurveTo(point, point1, point2) => {
|
||||
if let Some(last_maipulator_group) = manipulator_groups.last_mut() {
|
||||
last_maipulator_group.out_handle = Some(point_to_dvec2(point));
|
||||
if let Some(last_manipulator_group) = manipulator_groups.last_mut() {
|
||||
last_manipulator_group.out_handle = Some(point_to_dvec2(point));
|
||||
}
|
||||
ManipulatorGroup::new(point_to_dvec2(point2), Some(point_to_dvec2(point1)), None)
|
||||
}
|
||||
|
|
@ -237,3 +238,182 @@ pub fn pathseg_abs_diff_eq(seg1: PathSeg, seg2: PathSeg, max_abs_diff: f64) -> b
|
|||
|
||||
seg1_points.len() == seg2_points.len() && seg1_points.into_iter().zip(seg2_points).all(|(a, b)| cmp(a.x, b.x) && cmp(a.y, b.y))
|
||||
}
|
||||
|
||||
/// A selectable part of a curve, either an anchor (start or end of a bézier) or a handle (doesn't necessarily go through the bézier but influences curvature).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ManipulatorPointId {
|
||||
/// A control anchor - the start or end point of a bézier.
|
||||
Anchor(PointId),
|
||||
/// The handle for a bézier - the first handle on a cubic and the only handle on a quadratic.
|
||||
PrimaryHandle(SegmentId),
|
||||
/// The end handle on a cubic bézier.
|
||||
EndHandle(SegmentId),
|
||||
}
|
||||
|
||||
impl ManipulatorPointId {
|
||||
/// Attempt to retrieve the manipulator position in layer space (no transformation applied).
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn get_position(&self, vector: &Vector) -> Option<DVec2> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(id) => vector.point_domain.position_from_id(*id),
|
||||
ManipulatorPointId::PrimaryHandle(id) => vector.segment_from_id(*id).and_then(|bezier| bezier.handle_start()),
|
||||
ManipulatorPointId::EndHandle(id) => vector.segment_from_id(*id).and_then(|bezier| bezier.handle_end()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_anchor_position(&self, vector: &Vector) -> Option<DVec2> {
|
||||
match self {
|
||||
ManipulatorPointId::EndHandle(_) | ManipulatorPointId::PrimaryHandle(_) => self.get_anchor(vector).and_then(|id| vector.point_domain.position_from_id(id)),
|
||||
_ => self.get_position(vector),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get a pair of handles. For an anchor this is the first two handles connected. For a handle it is self and the first opposing handle.
|
||||
#[must_use]
|
||||
pub fn get_handle_pair(self, vector: &Vector) -> Option<[HandleId; 2]> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => vector.all_connected(point).take(2).collect::<Vec<_>>().try_into().ok(),
|
||||
ManipulatorPointId::PrimaryHandle(segment) => {
|
||||
let point = vector.segment_domain.segment_start_from_id(segment)?;
|
||||
let current = HandleId::primary(segment);
|
||||
let other = vector.segment_domain.all_connected(point).find(|&value| value != current);
|
||||
other.map(|other| [current, other])
|
||||
}
|
||||
ManipulatorPointId::EndHandle(segment) => {
|
||||
let point = vector.segment_domain.segment_end_from_id(segment)?;
|
||||
let current = HandleId::end(segment);
|
||||
let other = vector.segment_domain.all_connected(point).find(|&value| value != current);
|
||||
other.map(|other| [current, other])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds all the connected handles of a point.
|
||||
/// For an anchor it is all the connected handles.
|
||||
/// For a handle it is all the handles connected to its corresponding anchor other than the current handle.
|
||||
pub fn get_all_connected_handles(self, vector: &Vector) -> Option<Vec<HandleId>> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => {
|
||||
let connected = vector.all_connected(point).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
ManipulatorPointId::PrimaryHandle(segment) => {
|
||||
let point = vector.segment_domain.segment_start_from_id(segment)?;
|
||||
let current = HandleId::primary(segment);
|
||||
let connected = vector.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
ManipulatorPointId::EndHandle(segment) => {
|
||||
let point = vector.segment_domain.segment_end_from_id(segment)?;
|
||||
let current = HandleId::end(segment);
|
||||
let connected = vector.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to find the closest anchor. If self is already an anchor then it is just self. If it is a start or end handle, then the start or end point is chosen.
|
||||
#[must_use]
|
||||
pub fn get_anchor(self, vector: &Vector) -> Option<PointId> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => Some(point),
|
||||
ManipulatorPointId::PrimaryHandle(segment) => vector.segment_start_from_id(segment),
|
||||
ManipulatorPointId::EndHandle(segment) => vector.segment_end_from_id(segment),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert self to a [`HandleId`], returning none for an anchor.
|
||||
#[must_use]
|
||||
pub fn as_handle(self) -> Option<HandleId> {
|
||||
match self {
|
||||
ManipulatorPointId::PrimaryHandle(segment) => Some(HandleId::primary(segment)),
|
||||
ManipulatorPointId::EndHandle(segment) => Some(HandleId::end(segment)),
|
||||
ManipulatorPointId::Anchor(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert self to an anchor, returning None for a handle.
|
||||
#[must_use]
|
||||
pub fn as_anchor(self) -> Option<PointId> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => Some(point),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_segment(self) -> Option<SegmentId> {
|
||||
match self {
|
||||
ManipulatorPointId::PrimaryHandle(segment) | ManipulatorPointId::EndHandle(segment) => Some(segment),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of handle found on a bézier curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum HandleType {
|
||||
/// The first handle on a cubic bézier or the only handle on a quadratic bézier.
|
||||
Primary,
|
||||
/// The second handle on a cubic bézier.
|
||||
End,
|
||||
}
|
||||
|
||||
/// Represents a primary or end handle found in a particular segment.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HandleId {
|
||||
pub ty: HandleType,
|
||||
pub segment: SegmentId,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HandleId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.ty {
|
||||
// I haven't checked if "out" and "in" are reversed, or are accurate translations of the "primary" and "end" terms used in the `HandleType` enum, so this naming is an assumption.
|
||||
HandleType::Primary => write!(f, "{} out", self.segment.inner()),
|
||||
HandleType::End => write!(f, "{} in", self.segment.inner()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HandleId {
|
||||
/// Construct a handle for the first handle on a cubic bézier or the only handle on a quadratic bézier.
|
||||
#[must_use]
|
||||
pub const fn primary(segment: SegmentId) -> Self {
|
||||
Self { ty: HandleType::Primary, segment }
|
||||
}
|
||||
|
||||
/// Construct a handle for the end handle on a cubic bézier.
|
||||
#[must_use]
|
||||
pub const fn end(segment: SegmentId) -> Self {
|
||||
Self { ty: HandleType::End, segment }
|
||||
}
|
||||
|
||||
/// Convert to [`ManipulatorPointId`].
|
||||
#[must_use]
|
||||
pub fn to_manipulator_point(self) -> ManipulatorPointId {
|
||||
match self.ty {
|
||||
HandleType::Primary => ManipulatorPointId::PrimaryHandle(self.segment),
|
||||
HandleType::End => ManipulatorPointId::EndHandle(self.segment),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the magnitude of the handle from the anchor.
|
||||
pub fn length(self, vector: &Vector) -> f64 {
|
||||
let Some(anchor_position) = self.to_manipulator_point().get_anchor_position(vector) else {
|
||||
// TODO: This was previously an unwrap which was encountered, so this is a temporary way to avoid a crash
|
||||
return 0.;
|
||||
};
|
||||
let handle_position = self.to_manipulator_point().get_position(vector);
|
||||
handle_position.map(|pos| (pos - anchor_position).length()).unwrap_or(f64::MAX)
|
||||
}
|
||||
|
||||
/// Convert an end handle to the primary handle and a primary handle to an end handle. Note that the new handle may not exist (e.g. for a quadratic bézier).
|
||||
#[must_use]
|
||||
pub fn opposite(self) -> Self {
|
||||
match self.ty {
|
||||
HandleType::Primary => Self::end(self.segment),
|
||||
HandleType::End => Self::primary(self.segment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ pub mod generator_nodes;
|
|||
pub mod misc;
|
||||
mod reference_point;
|
||||
pub mod style;
|
||||
mod vector_data;
|
||||
mod vector_attributes;
|
||||
mod vector_modification;
|
||||
mod vector_nodes;
|
||||
mod vector_types;
|
||||
|
||||
pub use bezier_rs;
|
||||
pub use reference_point::*;
|
||||
pub use style::PathStyle;
|
||||
pub use vector_data::*;
|
||||
pub use vector_nodes::*;
|
||||
pub use vector_types::*;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl std::fmt::Display for Fill {
|
|||
match self {
|
||||
Self::None => write!(f, "None"),
|
||||
Self::Solid(color) => write!(f, "#{} (Alpha: {}%)", color.to_rgb_hex_srgb(), color.a() * 100.),
|
||||
Self::Gradient(gradient) => write!(f, "{}", gradient),
|
||||
Self::Gradient(gradient) => write!(f, "{gradient}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::vector::misc::dvec2_to_point;
|
||||
use crate::vector::vector_data::{HandleId, VectorData};
|
||||
use crate::vector::misc::{HandleId, dvec2_to_point};
|
||||
use crate::vector::vector_types::Vector;
|
||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
|
@ -673,7 +673,7 @@ impl FoundSubpath {
|
|||
}
|
||||
}
|
||||
|
||||
impl VectorData {
|
||||
impl Vector {
|
||||
/// Construct a [`kurbo::PathSeg`] by resolving the points from their ids.
|
||||
fn path_segment_from_index(&self, start: usize, end: usize, handles: BezierHandles) -> PathSeg {
|
||||
let start = dvec2_to_point(self.point_domain.positions()[start]);
|
||||
|
|
@ -896,7 +896,7 @@ impl VectorData {
|
|||
}
|
||||
|
||||
StrokePathIter {
|
||||
vector_data: self,
|
||||
vector: self,
|
||||
points,
|
||||
skip: 0,
|
||||
done_one: false,
|
||||
|
|
@ -952,13 +952,11 @@ impl VectorData {
|
|||
self.stroke_bezier_paths().flat_map(|mut path| std::mem::take(path.manipulator_groups_mut()))
|
||||
}
|
||||
|
||||
/// Get manipulator by id
|
||||
pub fn manipulator_group_id(&self, id: impl Into<PointId>) -> Option<ManipulatorGroup<PointId>> {
|
||||
let id = id.into();
|
||||
self.manipulator_groups().find(|group| group.id == id)
|
||||
}
|
||||
|
||||
/// Transforms this vector data
|
||||
pub fn transform(&mut self, transform: DAffine2) {
|
||||
self.point_domain.transform(transform);
|
||||
self.segment_domain.transform(transform);
|
||||
|
|
@ -1026,7 +1024,7 @@ impl StrokePathIterPointMetadata {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct StrokePathIter<'a> {
|
||||
vector_data: &'a VectorData,
|
||||
vector: &'a Vector,
|
||||
points: Vec<StrokePathIterPointMetadata>,
|
||||
skip: usize,
|
||||
done_one: bool,
|
||||
|
|
@ -1056,29 +1054,29 @@ impl Iterator for StrokePathIter<'_> {
|
|||
let Some(val) = self.points[point_index].take_first() else {
|
||||
// Dead end
|
||||
groups.push(ManipulatorGroup {
|
||||
anchor: self.vector_data.point_domain.positions()[point_index],
|
||||
anchor: self.vector.point_domain.positions()[point_index],
|
||||
in_handle,
|
||||
out_handle: None,
|
||||
id: self.vector_data.point_domain.ids()[point_index],
|
||||
id: self.vector.point_domain.ids()[point_index],
|
||||
});
|
||||
|
||||
break;
|
||||
};
|
||||
|
||||
let mut handles = self.vector_data.segment_domain.handles()[val.segment_index];
|
||||
let mut handles = self.vector.segment_domain.handles()[val.segment_index];
|
||||
if val.start_from_end {
|
||||
handles = handles.reversed();
|
||||
}
|
||||
let next_point_index = if val.start_from_end {
|
||||
self.vector_data.segment_domain.start_point()[val.segment_index]
|
||||
self.vector.segment_domain.start_point()[val.segment_index]
|
||||
} else {
|
||||
self.vector_data.segment_domain.end_point()[val.segment_index]
|
||||
self.vector.segment_domain.end_point()[val.segment_index]
|
||||
};
|
||||
groups.push(ManipulatorGroup {
|
||||
anchor: self.vector_data.point_domain.positions()[point_index],
|
||||
anchor: self.vector.point_domain.positions()[point_index],
|
||||
in_handle,
|
||||
out_handle: handles.start(),
|
||||
id: self.vector_data.point_domain.ids()[point_index],
|
||||
id: self.vector.point_domain.ids()[point_index],
|
||||
});
|
||||
|
||||
in_handle = handles.end();
|
||||
|
|
@ -1102,7 +1100,7 @@ impl bezier_rs::Identifier for PointId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents the conversion of ids used when concatenating vector data with conflicting ids.
|
||||
/// Represents the conversion of IDs used when concatenating vector paths with conflicting IDs.
|
||||
pub struct IdMap {
|
||||
pub point_offset: usize,
|
||||
pub point_map: HashMap<PointId, PointId>,
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
use super::{PointId, SegmentId, VectorData};
|
||||
use glam::DVec2;
|
||||
use petgraph::graph::{EdgeIndex, NodeIndex, UnGraph};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
/// All the fixed fields of a point from the point domain.
|
||||
pub struct Point {
|
||||
pub id: PointId,
|
||||
pub position: DVec2,
|
||||
}
|
||||
|
||||
/// Useful indexes to speed up various operations on `VectorData`.
|
||||
///
|
||||
/// Important: It is the user's responsibility to ensure the indexes remain valid after mutations to the data.
|
||||
pub struct VectorDataIndex {
|
||||
/// Points and segments form a graph. Store it here in a form amenable to graph algorithms.
|
||||
///
|
||||
/// Currently, segment data is not stored as it is not used, but it could easily be added.
|
||||
pub(crate) point_graph: UnGraph<Point, ()>,
|
||||
pub(crate) segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
||||
/// Get the offset from the point ID.
|
||||
pub(crate) point_to_offset: FxHashMap<PointId, usize>,
|
||||
// TODO: faces
|
||||
}
|
||||
|
||||
impl VectorDataIndex {
|
||||
/// Construct a [`VectorDataIndex`] by building indexes from the given [`VectorData`]. Takes `O(n)` time.
|
||||
pub fn build_from(data: &VectorData) -> Self {
|
||||
let point_to_offset = data.point_domain.ids().iter().copied().enumerate().map(|(a, b)| (b, a)).collect::<FxHashMap<_, _>>();
|
||||
|
||||
let mut point_to_node = FxHashMap::default();
|
||||
let mut segment_to_edge = FxHashMap::default();
|
||||
|
||||
let mut graph = UnGraph::new_undirected();
|
||||
|
||||
for (point_id, position) in data.point_domain.iter() {
|
||||
let idx = graph.add_node(Point { id: point_id, position });
|
||||
point_to_node.insert(point_id, idx);
|
||||
}
|
||||
|
||||
for (segment_id, start_offset, end_offset, ..) in data.segment_domain.iter() {
|
||||
let start_id = data.point_domain.ids()[start_offset];
|
||||
let end_id = data.point_domain.ids()[end_offset];
|
||||
let edge = graph.add_edge(point_to_node[&start_id], point_to_node[&end_id], ());
|
||||
|
||||
segment_to_edge.insert(segment_id, edge);
|
||||
}
|
||||
|
||||
Self {
|
||||
point_graph: graph,
|
||||
segment_to_edge,
|
||||
point_to_offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the length of given segment's chord. Takes `O(1)` time.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if no segment with the given ID is found.
|
||||
pub fn segment_chord_length(&self, id: SegmentId) -> f64 {
|
||||
let edge_idx = self.segment_to_edge[&id];
|
||||
let (start, end) = self.point_graph.edge_endpoints(edge_idx).unwrap();
|
||||
let start_position = self.point_graph.node_weight(start).unwrap().position;
|
||||
let end_position = self.point_graph.node_weight(end).unwrap().position;
|
||||
(start_position - end_position).length()
|
||||
}
|
||||
|
||||
/// Get the ends of a segment. Takes `O(1)` time.
|
||||
///
|
||||
/// The IDs will be ordered [smallest, largest] so they can be used to find other segments with the same endpoints, regardless of direction.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the ID is not present.
|
||||
pub fn segment_ends(&self, id: SegmentId) -> [NodeIndex; 2] {
|
||||
let (start, end) = self.point_graph.edge_endpoints(self.segment_to_edge[&id]).unwrap();
|
||||
if start < end { [start, end] } else { [end, start] }
|
||||
}
|
||||
|
||||
/// Get the physical location of a point. Takes `O(1)` time.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if `id` isn't in the data.
|
||||
pub fn point_position(&self, id: PointId, data: &VectorData) -> DVec2 {
|
||||
let offset = self.point_to_offset[&id];
|
||||
data.point_domain.positions()[offset]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
use super::*;
|
||||
use crate::Ctx;
|
||||
use crate::table::TableRow;
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::uuid::{NodeId, generate_uuid};
|
||||
use crate::vector::misc::{HandleId, HandleType, point_to_dvec2};
|
||||
use bezier_rs::BezierHandles;
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use kurbo::{BezPath, PathEl, Point};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::BuildHasher;
|
||||
|
||||
/// Represents a procedural change to the [`PointDomain`] in [`VectorData`].
|
||||
/// Represents a procedural change to the [`PointDomain`] in [`Vector`].
|
||||
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PointModification {
|
||||
add: Vec<PointId>,
|
||||
|
|
@ -58,12 +60,12 @@ impl PointModification {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a new modification that will convert an empty [`VectorData`] into the target [`VectorData`].
|
||||
pub fn create_from_vector(vector_data: &VectorData) -> Self {
|
||||
/// Create a new modification that will convert an empty [`Vector`] into the target [`Vector`].
|
||||
pub fn create_from_vector(vector: &Vector) -> Self {
|
||||
Self {
|
||||
add: vector_data.point_domain.ids().to_vec(),
|
||||
add: vector.point_domain.ids().to_vec(),
|
||||
remove: HashSet::new(),
|
||||
delta: vector_data.point_domain.ids().iter().copied().zip(vector_data.point_domain.positions().iter().cloned()).collect(),
|
||||
delta: vector.point_domain.ids().iter().copied().zip(vector.point_domain.positions().iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ impl PointModification {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a procedural change to the [`SegmentDomain`] in [`VectorData`].
|
||||
/// Represents a procedural change to the [`SegmentDomain`] in [`Vector`].
|
||||
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SegmentModification {
|
||||
add: Vec<SegmentId>,
|
||||
|
|
@ -177,11 +179,11 @@ impl SegmentModification {
|
|||
let Some(&stroke) = self.stroke.get(&add_id) else { continue };
|
||||
|
||||
let Some(start_index) = point_domain.resolve_id(start) else {
|
||||
warn!("invalid start id: {:#?}", start);
|
||||
warn!("invalid start id: {start:#?}");
|
||||
continue;
|
||||
};
|
||||
let Some(end_index) = point_domain.resolve_id(end) else {
|
||||
warn!("invalid end id: {:#?}", end);
|
||||
warn!("invalid end id: {end:#?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
@ -206,27 +208,25 @@ impl SegmentModification {
|
|||
|
||||
assert!(
|
||||
segment_domain.start_point().iter().all(|&index| index < point_domain.ids().len()),
|
||||
"index should be in range {:#?}",
|
||||
segment_domain
|
||||
"index should be in range {segment_domain:#?}"
|
||||
);
|
||||
assert!(
|
||||
segment_domain.end_point().iter().all(|&index| index < point_domain.ids().len()),
|
||||
"index should be in range {:#?}",
|
||||
segment_domain
|
||||
"index should be in range {segment_domain:#?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a new modification that will convert an empty [`VectorData`] into the target [`VectorData`].
|
||||
pub fn create_from_vector(vector_data: &VectorData) -> Self {
|
||||
let point_id = |(&segment, &index)| (segment, vector_data.point_domain.ids()[index]);
|
||||
/// Create a new modification that will convert an empty [`Vector`] into the target [`Vector`].
|
||||
pub fn create_from_vector(vector: &Vector) -> Self {
|
||||
let point_id = |(&segment, &index)| (segment, vector.point_domain.ids()[index]);
|
||||
Self {
|
||||
add: vector_data.segment_domain.ids().to_vec(),
|
||||
add: vector.segment_domain.ids().to_vec(),
|
||||
remove: HashSet::new(),
|
||||
start_point: vector_data.segment_domain.ids().iter().zip(vector_data.segment_domain.start_point()).map(point_id).collect(),
|
||||
end_point: vector_data.segment_domain.ids().iter().zip(vector_data.segment_domain.end_point()).map(point_id).collect(),
|
||||
handle_primary: vector_data.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_start().map(|handle| handle - b.start))).collect(),
|
||||
handle_end: vector_data.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_end().map(|handle| handle - b.end))).collect(),
|
||||
stroke: vector_data.segment_domain.ids().iter().copied().zip(vector_data.segment_domain.stroke().iter().cloned()).collect(),
|
||||
start_point: vector.segment_domain.ids().iter().zip(vector.segment_domain.start_point()).map(point_id).collect(),
|
||||
end_point: vector.segment_domain.ids().iter().zip(vector.segment_domain.end_point()).map(point_id).collect(),
|
||||
handle_primary: vector.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_start().map(|handle| handle - b.start))).collect(),
|
||||
handle_end: vector.segment_bezier_iter().map(|(id, b, _, _)| (id, b.handle_end().map(|handle| handle - b.end))).collect(),
|
||||
stroke: vector.segment_domain.ids().iter().copied().zip(vector.segment_domain.stroke().iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ impl SegmentModification {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a procedural change to the [`RegionDomain`] in [`VectorData`].
|
||||
/// Represents a procedural change to the [`RegionDomain`] in [`Vector`].
|
||||
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RegionModification {
|
||||
add: Vec<RegionId>,
|
||||
|
|
@ -284,18 +284,18 @@ impl RegionModification {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a new modification that will convert an empty [`VectorData`] into the target [`VectorData`].
|
||||
pub fn create_from_vector(vector_data: &VectorData) -> Self {
|
||||
/// Create a new modification that will convert an empty [`Vector`] into the target [`Vector`].
|
||||
pub fn create_from_vector(vector: &Vector) -> Self {
|
||||
Self {
|
||||
add: vector_data.region_domain.ids().to_vec(),
|
||||
add: vector.region_domain.ids().to_vec(),
|
||||
remove: HashSet::new(),
|
||||
segment_range: vector_data.region_domain.ids().iter().copied().zip(vector_data.region_domain.segment_range().iter().cloned()).collect(),
|
||||
fill: vector_data.region_domain.ids().iter().copied().zip(vector_data.region_domain.fill().iter().cloned()).collect(),
|
||||
segment_range: vector.region_domain.ids().iter().copied().zip(vector.region_domain.segment_range().iter().cloned()).collect(),
|
||||
fill: vector.region_domain.ids().iter().copied().zip(vector.region_domain.fill().iter().cloned()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a procedural change to the [`VectorData`].
|
||||
/// Represents a procedural change to the [`Vector`].
|
||||
#[derive(Clone, Debug, Default, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct VectorModification {
|
||||
points: PointModification,
|
||||
|
|
@ -327,27 +327,27 @@ pub enum VectorModificationType {
|
|||
}
|
||||
|
||||
impl VectorModification {
|
||||
/// Apply this modification to the specified [`VectorData`].
|
||||
pub fn apply(&self, vector_data: &mut VectorData) {
|
||||
self.points.apply(&mut vector_data.point_domain, &mut vector_data.segment_domain);
|
||||
self.segments.apply(&mut vector_data.segment_domain, &vector_data.point_domain);
|
||||
self.regions.apply(&mut vector_data.region_domain);
|
||||
/// Apply this modification to the specified [`Vector`].
|
||||
pub fn apply(&self, vector: &mut Vector) {
|
||||
self.points.apply(&mut vector.point_domain, &mut vector.segment_domain);
|
||||
self.segments.apply(&mut vector.segment_domain, &vector.point_domain);
|
||||
self.regions.apply(&mut vector.region_domain);
|
||||
|
||||
let valid = |val: &[HandleId; 2]| vector_data.segment_domain.ids().contains(&val[0].segment) && vector_data.segment_domain.ids().contains(&val[1].segment);
|
||||
vector_data
|
||||
let valid = |val: &[HandleId; 2]| vector.segment_domain.ids().contains(&val[0].segment) && vector.segment_domain.ids().contains(&val[1].segment);
|
||||
vector
|
||||
.colinear_manipulators
|
||||
.retain(|val| !self.remove_g1_continuous.contains(val) && !self.remove_g1_continuous.contains(&[val[1], val[0]]) && valid(val));
|
||||
|
||||
for handles in &self.add_g1_continuous {
|
||||
if !vector_data.colinear_manipulators.iter().any(|test| test == handles || test == &[handles[1], handles[0]]) && valid(handles) {
|
||||
vector_data.colinear_manipulators.push(*handles);
|
||||
if !vector.colinear_manipulators.iter().any(|test| test == handles || test == &[handles[1], handles[0]]) && valid(handles) {
|
||||
vector.colinear_manipulators.push(*handles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a [`VectorModificationType`] to this modification.
|
||||
pub fn modify(&mut self, vector_data_modification: &VectorModificationType) {
|
||||
match vector_data_modification {
|
||||
pub fn modify(&mut self, vector_modification: &VectorModificationType) {
|
||||
match vector_modification {
|
||||
VectorModificationType::InsertSegment { id, points, handles } => self.segments.push(*id, *points, *handles, StrokeId::ZERO),
|
||||
VectorModificationType::InsertPoint { id, position } => self.points.push(*id, *position),
|
||||
|
||||
|
|
@ -400,13 +400,13 @@ impl VectorModification {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a new modification that will convert an empty [`VectorData`] into the target [`VectorData`].
|
||||
pub fn create_from_vector(vector_data: &VectorData) -> Self {
|
||||
/// Create a new modification that will convert an empty [`Vector`] into the target [`Vector`].
|
||||
pub fn create_from_vector(vector: &Vector) -> Self {
|
||||
Self {
|
||||
points: PointModification::create_from_vector(vector_data),
|
||||
segments: SegmentModification::create_from_vector(vector_data),
|
||||
regions: RegionModification::create_from_vector(vector_data),
|
||||
add_g1_continuous: vector_data.colinear_manipulators.iter().copied().collect(),
|
||||
points: PointModification::create_from_vector(vector),
|
||||
segments: SegmentModification::create_from_vector(vector),
|
||||
regions: RegionModification::create_from_vector(vector),
|
||||
add_g1_continuous: vector.colinear_manipulators.iter().copied().collect(),
|
||||
remove_g1_continuous: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -420,38 +420,38 @@ impl Hash for VectorModification {
|
|||
|
||||
/// Applies a diff modification to a vector path.
|
||||
#[node_macro::node(category(""))]
|
||||
async fn path_modify(_ctx: impl Ctx, mut vector_data: Table<VectorData>, modification: Box<VectorModification>, node_path: Vec<NodeId>) -> Table<VectorData> {
|
||||
if vector_data.is_empty() {
|
||||
vector_data.push(TableRow::default());
|
||||
async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Box<VectorModification>, node_path: Vec<NodeId>) -> Table<Vector> {
|
||||
if vector.is_empty() {
|
||||
vector.push(TableRow::default());
|
||||
}
|
||||
let row = vector_data.get_mut(0).expect("push should give one item");
|
||||
let row = vector.get_mut(0).expect("push should give one item");
|
||||
modification.apply(row.element);
|
||||
|
||||
// Update the source node id
|
||||
let this_node_path = node_path.iter().rev().nth(1).copied();
|
||||
*row.source_node_id = row.source_node_id.or(this_node_path);
|
||||
|
||||
if vector_data.len() > 1 {
|
||||
warn!("The path modify ran on {} rows of vector data. Only the first can be modified.", vector_data.len());
|
||||
if vector.len() > 1 {
|
||||
warn!("The path modify ran on {} vector rows. Only the first can be modified.", vector.len());
|
||||
}
|
||||
vector_data
|
||||
vector
|
||||
}
|
||||
|
||||
/// Applies the vector path's local transformation to its geometry and resets it to the identity.
|
||||
#[node_macro::node(category("Vector"))]
|
||||
async fn apply_transform(_ctx: impl Ctx, mut vector_data: Table<VectorData>) -> Table<VectorData> {
|
||||
for row in vector_data.iter_mut() {
|
||||
let vector_data = row.element;
|
||||
async fn apply_transform(_ctx: impl Ctx, mut vector: Table<Vector>) -> Table<Vector> {
|
||||
for row in vector.iter_mut() {
|
||||
let vector = row.element;
|
||||
let transform = *row.transform;
|
||||
|
||||
for (_, point) in vector_data.point_domain.positions_mut() {
|
||||
for (_, point) in vector.point_domain.positions_mut() {
|
||||
*point = transform.transform_point2(*point);
|
||||
}
|
||||
|
||||
*row.transform = DAffine2::IDENTITY;
|
||||
}
|
||||
|
||||
vector_data
|
||||
vector
|
||||
}
|
||||
|
||||
// Do we want to enforce that all serialized/deserialized hashmaps are a vec of tuples?
|
||||
|
|
@ -524,11 +524,11 @@ pub struct AppendBezpath<'a> {
|
|||
last_segment_id: Option<SegmentId>,
|
||||
point_id: PointId,
|
||||
segment_id: SegmentId,
|
||||
vector_data: &'a mut VectorData,
|
||||
vector: &'a mut Vector,
|
||||
}
|
||||
|
||||
impl<'a> AppendBezpath<'a> {
|
||||
fn new(vector_data: &'a mut VectorData) -> Self {
|
||||
fn new(vector: &'a mut Vector) -> Self {
|
||||
Self {
|
||||
first_point: None,
|
||||
last_point: None,
|
||||
|
|
@ -536,9 +536,9 @@ impl<'a> AppendBezpath<'a> {
|
|||
last_point_index: None,
|
||||
first_segment_id: None,
|
||||
last_segment_id: None,
|
||||
point_id: vector_data.point_domain.next_id(),
|
||||
segment_id: vector_data.segment_domain.next_id(),
|
||||
vector_data,
|
||||
point_id: vector.point_domain.next_id(),
|
||||
segment_id: vector.segment_domain.next_id(),
|
||||
vector,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -555,28 +555,28 @@ impl<'a> AppendBezpath<'a> {
|
|||
|
||||
// Create a new segment.
|
||||
let next_segment_id = self.segment_id.next_id();
|
||||
self.vector_data
|
||||
self.vector
|
||||
.segment_domain
|
||||
.push(next_segment_id, self.last_point_index.unwrap(), self.first_point_index.unwrap(), handle, StrokeId::ZERO);
|
||||
|
||||
// Create a new region.
|
||||
let next_region_id = self.vector_data.region_domain.next_id();
|
||||
let next_region_id = self.vector.region_domain.next_id();
|
||||
let first_segment_id = self.first_segment_id.unwrap_or(next_segment_id);
|
||||
let last_segment_id = next_segment_id;
|
||||
|
||||
self.vector_data.region_domain.push(next_region_id, first_segment_id..=last_segment_id, FillId::ZERO);
|
||||
self.vector.region_domain.push(next_region_id, first_segment_id..=last_segment_id, FillId::ZERO);
|
||||
}
|
||||
|
||||
fn append_segment(&mut self, end_point: Point, handle: BezierHandles) {
|
||||
// Append the point.
|
||||
let next_point_index = self.vector_data.point_domain.ids().len();
|
||||
let next_point_index = self.vector.point_domain.ids().len();
|
||||
let next_point_id = self.point_id.next_id();
|
||||
|
||||
self.vector_data.point_domain.push(next_point_id, point_to_dvec2(end_point));
|
||||
self.vector.point_domain.push(next_point_id, point_to_dvec2(end_point));
|
||||
|
||||
// Append the segment.
|
||||
let next_segment_id = self.segment_id.next_id();
|
||||
self.vector_data
|
||||
self.vector
|
||||
.segment_domain
|
||||
.push(next_segment_id, self.last_point_index.unwrap(), next_point_index, handle, StrokeId::ZERO);
|
||||
|
||||
|
|
@ -593,8 +593,8 @@ impl<'a> AppendBezpath<'a> {
|
|||
self.last_point = Some(point);
|
||||
|
||||
// Append the first point.
|
||||
let next_point_index = self.vector_data.point_domain.ids().len();
|
||||
self.vector_data.point_domain.push(self.point_id.next_id(), point_to_dvec2(point));
|
||||
let next_point_index = self.vector.point_domain.ids().len();
|
||||
self.vector.point_domain.push(self.point_id.next_id(), point_to_dvec2(point));
|
||||
|
||||
// Update the state.
|
||||
self.first_point_index = Some(next_point_index);
|
||||
|
|
@ -610,8 +610,8 @@ impl<'a> AppendBezpath<'a> {
|
|||
self.last_segment_id = None;
|
||||
}
|
||||
|
||||
pub fn append_bezpath(vector_data: &'a mut VectorData, bezpath: BezPath) {
|
||||
let mut this = Self::new(vector_data);
|
||||
pub fn append_bezpath(vector: &'a mut Vector, bezpath: BezPath) {
|
||||
let mut this = Self::new(vector);
|
||||
let mut elements = bezpath.elements().iter().peekable();
|
||||
|
||||
while let Some(element) = elements.next() {
|
||||
|
|
@ -656,12 +656,11 @@ impl<'a> AppendBezpath<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait VectorDataExt {
|
||||
/// Appends a Kurbo BezPath to the vector data.
|
||||
pub trait VectorExt {
|
||||
fn append_bezpath(&mut self, bezpath: BezPath);
|
||||
}
|
||||
|
||||
impl VectorDataExt for VectorData {
|
||||
impl VectorExt for Vector {
|
||||
fn append_bezpath(&mut self, bezpath: BezPath) {
|
||||
AppendBezpath::append_bezpath(self, bezpath);
|
||||
}
|
||||
|
|
@ -689,16 +688,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn modify_new() {
|
||||
let vector_data = VectorData::from_subpaths(
|
||||
let vector = Vector::from_subpaths(
|
||||
[bezier_rs::Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE), bezier_rs::Subpath::new_rect(DVec2::NEG_ONE, DVec2::ZERO)],
|
||||
false,
|
||||
);
|
||||
|
||||
let modify = VectorModification::create_from_vector(&vector_data);
|
||||
let modify = VectorModification::create_from_vector(&vector);
|
||||
|
||||
let mut new = VectorData::default();
|
||||
let mut new = Vector::default();
|
||||
modify.apply(&mut new);
|
||||
assert_eq!(vector_data, new);
|
||||
assert_eq!(vector, new);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -715,32 +714,32 @@ mod tests {
|
|||
false,
|
||||
),
|
||||
];
|
||||
let mut vector_data = VectorData::from_subpaths(subpaths, false);
|
||||
let mut vector = Vector::from_subpaths(subpaths, false);
|
||||
|
||||
let mut modify_new = VectorModification::create_from_vector(&vector_data);
|
||||
let mut modify_new = VectorModification::create_from_vector(&vector);
|
||||
let mut modify_original = VectorModification::default();
|
||||
|
||||
for modification in [&mut modify_new, &mut modify_original] {
|
||||
let point = vector_data.point_domain.ids()[0];
|
||||
let point = vector.point_domain.ids()[0];
|
||||
modification.modify(&VectorModificationType::ApplyPointDelta { point, delta: DVec2::X * 0.5 });
|
||||
let point = vector_data.point_domain.ids()[9];
|
||||
let point = vector.point_domain.ids()[9];
|
||||
modification.modify(&VectorModificationType::ApplyPointDelta { point, delta: DVec2::X });
|
||||
}
|
||||
|
||||
let mut new = VectorData::default();
|
||||
let mut new = Vector::default();
|
||||
modify_new.apply(&mut new);
|
||||
|
||||
modify_original.apply(&mut vector_data);
|
||||
modify_original.apply(&mut vector);
|
||||
|
||||
assert_eq!(vector_data, new);
|
||||
assert_eq!(vector_data.point_domain.positions()[0], DVec2::X);
|
||||
assert_eq!(vector_data.point_domain.positions()[9], DVec2::new(11., 0.));
|
||||
assert_eq!(vector, new);
|
||||
assert_eq!(vector.point_domain.positions()[0], DVec2::X);
|
||||
assert_eq!(vector.point_domain.positions()[9], DVec2::new(11., 0.));
|
||||
assert_eq!(
|
||||
vector_data.segment_bezier_iter().nth(8).unwrap().1,
|
||||
vector.segment_bezier_iter().nth(8).unwrap().1,
|
||||
Bezier::from_quadratic_dvec2(DVec2::new(0., 0.), DVec2::new(5., 10.), DVec2::new(11., 0.))
|
||||
);
|
||||
assert_eq!(
|
||||
vector_data.segment_bezier_iter().nth(9).unwrap().1,
|
||||
vector.segment_bezier_iter().nth(9).unwrap().1,
|
||||
Bezier::from_quadratic_dvec2(DVec2::new(11., 0.), DVec2::new(16., 10.), DVec2::new(20., 0.))
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,83 +1,24 @@
|
|||
mod attributes;
|
||||
mod indexed;
|
||||
mod modification;
|
||||
|
||||
use super::misc::{dvec2_to_point, point_to_dvec2};
|
||||
use super::misc::dvec2_to_point;
|
||||
use super::style::{PathStyle, Stroke};
|
||||
pub use super::vector_attributes::*;
|
||||
pub use super::vector_modification::*;
|
||||
use crate::bounds::BoundingBox;
|
||||
use crate::math::quad::Quad;
|
||||
use crate::table::Table;
|
||||
use crate::transform::Transform;
|
||||
use crate::vector::click_target::{ClickTargetType, FreePoint};
|
||||
use crate::vector::misc::{HandleId, ManipulatorPointId};
|
||||
use crate::{AlphaBlending, Color, Graphic};
|
||||
pub use attributes::*;
|
||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||
use core::borrow::Borrow;
|
||||
use core::hash::Hash;
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
pub use indexed::VectorDataIndex;
|
||||
use kurbo::{Affine, BezPath, Rect, Shape};
|
||||
pub use modification::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_vector_data<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<VectorData>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldVectorData {
|
||||
pub transform: DAffine2,
|
||||
pub alpha_blending: AlphaBlending,
|
||||
|
||||
pub style: PathStyle,
|
||||
|
||||
/// A list of all manipulator groups (referenced in `subpaths`) that have colinear handles (where they're locked at 180° angles from one another).
|
||||
/// This gets read in `graph_operation_message_handler.rs` by calling `inputs.as_mut_slice()` (search for the string `"Shape does not have both `subpath` and `colinear_manipulators` inputs"` to find it).
|
||||
pub colinear_manipulators: Vec<[HandleId; 2]>,
|
||||
|
||||
pub point_domain: PointDomain,
|
||||
pub segment_domain: SegmentDomain,
|
||||
pub region_domain: RegionDomain,
|
||||
|
||||
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_graphic_group: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum EitherFormat {
|
||||
VectorData(VectorData),
|
||||
OldVectorData(OldVectorData),
|
||||
VectorDataTable(Table<VectorData>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::VectorData(vector_data) => Table::new_from_element(vector_data),
|
||||
EitherFormat::OldVectorData(old) => {
|
||||
let mut vector_data_table = Table::new_from_element(VectorData {
|
||||
style: old.style,
|
||||
colinear_manipulators: old.colinear_manipulators,
|
||||
point_domain: old.point_domain,
|
||||
segment_domain: old.segment_domain,
|
||||
region_domain: old.region_domain,
|
||||
upstream_graphic_group: old.upstream_graphic_group,
|
||||
});
|
||||
*vector_data_table.iter_mut().next().unwrap().transform = old.transform;
|
||||
*vector_data_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;
|
||||
vector_data_table
|
||||
}
|
||||
EitherFormat::VectorDataTable(vector_data_table) => vector_data_table,
|
||||
})
|
||||
}
|
||||
|
||||
/// [VectorData] is passed between nodes.
|
||||
/// It contains a list of subpaths (that may be open or closed), a transform, and some style information.
|
||||
///
|
||||
/// Segments are connected if they share endpoints.
|
||||
/// Represents vector graphics data, composed of Bézier curves in a path or mesh arrangement.
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct VectorData {
|
||||
pub struct Vector {
|
||||
pub style: PathStyle,
|
||||
|
||||
/// A list of all manipulator groups (referenced in `subpaths`) that have colinear handles (where they're locked at 180° angles from one another).
|
||||
|
|
@ -88,11 +29,11 @@ pub struct VectorData {
|
|||
pub segment_domain: SegmentDomain,
|
||||
pub region_domain: RegionDomain,
|
||||
|
||||
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_graphic_group: Option<Table<Graphic>>,
|
||||
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_group: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
impl Default for VectorData {
|
||||
impl Default for Vector {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
style: PathStyle::new(Some(Stroke::new(Some(Color::BLACK), 0.)), super::style::Fill::None),
|
||||
|
|
@ -100,12 +41,12 @@ impl Default for VectorData {
|
|||
point_domain: PointDomain::new(),
|
||||
segment_domain: SegmentDomain::new(),
|
||||
region_domain: RegionDomain::new(),
|
||||
upstream_graphic_group: None,
|
||||
upstream_group: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for VectorData {
|
||||
impl std::hash::Hash for Vector {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.point_domain.hash(state);
|
||||
self.segment_domain.hash(state);
|
||||
|
|
@ -115,8 +56,8 @@ impl std::hash::Hash for VectorData {
|
|||
}
|
||||
}
|
||||
|
||||
impl VectorData {
|
||||
/// Push a subpath to the vector data
|
||||
impl Vector {
|
||||
/// Add a Bezier-rs subpath to this path.
|
||||
pub fn append_subpath(&mut self, subpath: impl Borrow<bezier_rs::Subpath<PointId>>, preserve_id: bool) {
|
||||
let subpath: &bezier_rs::Subpath<PointId> = subpath.borrow();
|
||||
let stroke_id = StrokeId::ZERO;
|
||||
|
|
@ -188,40 +129,40 @@ impl VectorData {
|
|||
self.point_domain.push(id, point.position);
|
||||
}
|
||||
|
||||
/// Construct some new vector data from a single subpath with an identity transform and black fill.
|
||||
/// Construct some new vector path from a single Bezier-rs subpath with an identity transform and black fill.
|
||||
pub fn from_subpath(subpath: impl Borrow<bezier_rs::Subpath<PointId>>) -> Self {
|
||||
Self::from_subpaths([subpath], false)
|
||||
}
|
||||
|
||||
/// Construct some new vector data from a single [`BezPath`] with an identity transform and black fill.
|
||||
/// Construct some new vector path from a single [`BezPath`] with an identity transform and black fill.
|
||||
pub fn from_bezpath(bezpath: BezPath) -> Self {
|
||||
let mut vector_data = Self::default();
|
||||
vector_data.append_bezpath(bezpath);
|
||||
vector_data
|
||||
let mut vector = Self::default();
|
||||
vector.append_bezpath(bezpath);
|
||||
vector
|
||||
}
|
||||
|
||||
/// Construct some new vector data from subpaths with an identity transform and black fill.
|
||||
/// Construct some new vector path from Bezier-rs subpaths with an identity transform and black fill.
|
||||
pub fn from_subpaths(subpaths: impl IntoIterator<Item = impl Borrow<bezier_rs::Subpath<PointId>>>, preserve_id: bool) -> Self {
|
||||
let mut vector_data = Self::default();
|
||||
let mut vector = Self::default();
|
||||
|
||||
for subpath in subpaths.into_iter() {
|
||||
vector_data.append_subpath(subpath, preserve_id);
|
||||
vector.append_subpath(subpath, preserve_id);
|
||||
}
|
||||
|
||||
vector_data
|
||||
vector
|
||||
}
|
||||
|
||||
pub fn from_target_types(target_types: impl IntoIterator<Item = impl Borrow<ClickTargetType>>, preserve_id: bool) -> Self {
|
||||
let mut vector_data = Self::default();
|
||||
let mut vector = Self::default();
|
||||
|
||||
for target_type in target_types.into_iter() {
|
||||
match target_type.borrow() {
|
||||
ClickTargetType::Subpath(subpath) => vector_data.append_subpath(subpath, preserve_id),
|
||||
ClickTargetType::FreePoint(point) => vector_data.append_free_point(point, preserve_id),
|
||||
ClickTargetType::Subpath(subpath) => vector.append_subpath(subpath, preserve_id),
|
||||
ClickTargetType::FreePoint(point) => vector.append_free_point(point, preserve_id),
|
||||
}
|
||||
}
|
||||
|
||||
vector_data
|
||||
vector
|
||||
}
|
||||
|
||||
/// Compute the bounding boxes of the bezpaths without any transform
|
||||
|
|
@ -374,12 +315,12 @@ impl VectorData {
|
|||
self.point_domain.resolve_id(point).map_or(0, |point| self.segment_domain.connected_count(point))
|
||||
}
|
||||
|
||||
pub fn check_point_inside_shape(&self, vector_data_transform: DAffine2, point: DVec2) -> bool {
|
||||
pub fn check_point_inside_shape(&self, transform: DAffine2, point: DVec2) -> bool {
|
||||
let number = self
|
||||
.stroke_bezpath_iter()
|
||||
.map(|mut bezpath| {
|
||||
// TODO: apply transform to points instead of modifying the paths
|
||||
bezpath.apply_affine(Affine::new(vector_data_transform.to_cols_array()));
|
||||
bezpath.apply_affine(Affine::new(transform.to_cols_array()));
|
||||
bezpath.close_path();
|
||||
let bbox = bezpath.bounding_box();
|
||||
(bezpath, bbox)
|
||||
|
|
@ -493,7 +434,7 @@ impl VectorData {
|
|||
}
|
||||
}
|
||||
|
||||
impl BoundingBox for Table<VectorData> {
|
||||
impl BoundingBox for Table<Vector> {
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> {
|
||||
self.iter_ref()
|
||||
.flat_map(|row| {
|
||||
|
|
@ -516,212 +457,84 @@ impl BoundingBox for Table<VectorData> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A selectable part of a curve, either an anchor (start or end of a bézier) or a handle (doesn't necessarily go through the bézier but influences curvature).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ManipulatorPointId {
|
||||
/// A control anchor - the start or end point of a bézier.
|
||||
Anchor(PointId),
|
||||
/// The handle for a bézier - the first handle on a cubic and the only handle on a quadratic.
|
||||
PrimaryHandle(SegmentId),
|
||||
/// The end handle on a cubic bézier.
|
||||
EndHandle(SegmentId),
|
||||
}
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Vector>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
impl ManipulatorPointId {
|
||||
/// Attempt to retrieve the manipulator position in layer space (no transformation applied).
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn get_position(&self, vector_data: &VectorData) -> Option<DVec2> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(id) => vector_data.point_domain.position_from_id(*id),
|
||||
ManipulatorPointId::PrimaryHandle(id) => vector_data.segment_from_id(*id).and_then(|bezier| bezier.handle_start()),
|
||||
ManipulatorPointId::EndHandle(id) => vector_data.segment_from_id(*id).and_then(|bezier| bezier.handle_end()),
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldVectorData {
|
||||
pub transform: DAffine2,
|
||||
pub alpha_blending: AlphaBlending,
|
||||
|
||||
pub style: PathStyle,
|
||||
|
||||
/// A list of all manipulator groups (referenced in `subpaths`) that have colinear handles (where they're locked at 180° angles from one another).
|
||||
/// This gets read in `graph_operation_message_handler.rs` by calling `inputs.as_mut_slice()` (search for the string `"Shape does not have both `subpath` and `colinear_manipulators` inputs"` to find it).
|
||||
pub colinear_manipulators: Vec<[HandleId; 2]>,
|
||||
|
||||
pub point_domain: PointDomain,
|
||||
pub segment_domain: SegmentDomain,
|
||||
pub region_domain: RegionDomain,
|
||||
|
||||
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_graphic_group: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum EitherFormat {
|
||||
Vector(Vector),
|
||||
OldVectorData(OldVectorData),
|
||||
VectorTable(Table<Vector>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::Vector(vector) => Table::new_from_element(vector),
|
||||
EitherFormat::OldVectorData(old) => {
|
||||
let mut vector_table = Table::new_from_element(Vector {
|
||||
style: old.style,
|
||||
colinear_manipulators: old.colinear_manipulators,
|
||||
point_domain: old.point_domain,
|
||||
segment_domain: old.segment_domain,
|
||||
region_domain: old.region_domain,
|
||||
upstream_group: old.upstream_graphic_group,
|
||||
});
|
||||
*vector_table.iter_mut().next().unwrap().transform = old.transform;
|
||||
*vector_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;
|
||||
vector_table
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_anchor_position(&self, vector_data: &VectorData) -> Option<DVec2> {
|
||||
match self {
|
||||
ManipulatorPointId::EndHandle(_) | ManipulatorPointId::PrimaryHandle(_) => self.get_anchor(vector_data).and_then(|id| vector_data.point_domain.position_from_id(id)),
|
||||
_ => self.get_position(vector_data),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get a pair of handles. For an anchor this is the first two handles connected. For a handle it is self and the first opposing handle.
|
||||
#[must_use]
|
||||
pub fn get_handle_pair(self, vector_data: &VectorData) -> Option<[HandleId; 2]> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => vector_data.all_connected(point).take(2).collect::<Vec<_>>().try_into().ok(),
|
||||
ManipulatorPointId::PrimaryHandle(segment) => {
|
||||
let point = vector_data.segment_domain.segment_start_from_id(segment)?;
|
||||
let current = HandleId::primary(segment);
|
||||
let other = vector_data.segment_domain.all_connected(point).find(|&value| value != current);
|
||||
other.map(|other| [current, other])
|
||||
}
|
||||
ManipulatorPointId::EndHandle(segment) => {
|
||||
let point = vector_data.segment_domain.segment_end_from_id(segment)?;
|
||||
let current = HandleId::end(segment);
|
||||
let other = vector_data.segment_domain.all_connected(point).find(|&value| value != current);
|
||||
other.map(|other| [current, other])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds all the connected handles of a point.
|
||||
/// For an anchor it is all the connected handles.
|
||||
/// For a handle it is all the handles connected to its corresponding anchor other than the current handle.
|
||||
pub fn get_all_connected_handles(self, vector_data: &VectorData) -> Option<Vec<HandleId>> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => {
|
||||
let connected = vector_data.all_connected(point).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
ManipulatorPointId::PrimaryHandle(segment) => {
|
||||
let point = vector_data.segment_domain.segment_start_from_id(segment)?;
|
||||
let current = HandleId::primary(segment);
|
||||
let connected = vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
ManipulatorPointId::EndHandle(segment) => {
|
||||
let point = vector_data.segment_domain.segment_end_from_id(segment)?;
|
||||
let current = HandleId::end(segment);
|
||||
let connected = vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>();
|
||||
Some(connected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to find the closest anchor. If self is already an anchor then it is just self. If it is a start or end handle, then the start or end point is chosen.
|
||||
#[must_use]
|
||||
pub fn get_anchor(self, vector_data: &VectorData) -> Option<PointId> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => Some(point),
|
||||
ManipulatorPointId::PrimaryHandle(segment) => vector_data.segment_start_from_id(segment),
|
||||
ManipulatorPointId::EndHandle(segment) => vector_data.segment_end_from_id(segment),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert self to a [`HandleId`], returning none for an anchor.
|
||||
#[must_use]
|
||||
pub fn as_handle(self) -> Option<HandleId> {
|
||||
match self {
|
||||
ManipulatorPointId::PrimaryHandle(segment) => Some(HandleId::primary(segment)),
|
||||
ManipulatorPointId::EndHandle(segment) => Some(HandleId::end(segment)),
|
||||
ManipulatorPointId::Anchor(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert self to an anchor, returning None for a handle.
|
||||
#[must_use]
|
||||
pub fn as_anchor(self) -> Option<PointId> {
|
||||
match self {
|
||||
ManipulatorPointId::Anchor(point) => Some(point),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_segment(self) -> Option<SegmentId> {
|
||||
match self {
|
||||
ManipulatorPointId::PrimaryHandle(segment) | ManipulatorPointId::EndHandle(segment) => Some(segment),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of handle found on a bézier curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum HandleType {
|
||||
/// The first handle on a cubic bézier or the only handle on a quadratic bézier.
|
||||
Primary,
|
||||
/// The second handle on a cubic bézier.
|
||||
End,
|
||||
}
|
||||
|
||||
/// Represents a primary or end handle found in a particular segment.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct HandleId {
|
||||
pub ty: HandleType,
|
||||
pub segment: SegmentId,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HandleId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.ty {
|
||||
// I haven't checked if "out" and "in" are reversed, or are accurate translations of the "primary" and "end" terms used in the `HandleType` enum, so this naming is an assumption.
|
||||
HandleType::Primary => write!(f, "{} out", self.segment.inner()),
|
||||
HandleType::End => write!(f, "{} in", self.segment.inner()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HandleId {
|
||||
/// Construct a handle for the first handle on a cubic bézier or the only handle on a quadratic bézier.
|
||||
#[must_use]
|
||||
pub const fn primary(segment: SegmentId) -> Self {
|
||||
Self { ty: HandleType::Primary, segment }
|
||||
}
|
||||
|
||||
/// Construct a handle for the end handle on a cubic bézier.
|
||||
#[must_use]
|
||||
pub const fn end(segment: SegmentId) -> Self {
|
||||
Self { ty: HandleType::End, segment }
|
||||
}
|
||||
|
||||
/// Convert to [`ManipulatorPointId`].
|
||||
#[must_use]
|
||||
pub fn to_manipulator_point(self) -> ManipulatorPointId {
|
||||
match self.ty {
|
||||
HandleType::Primary => ManipulatorPointId::PrimaryHandle(self.segment),
|
||||
HandleType::End => ManipulatorPointId::EndHandle(self.segment),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the magnitude of the handle from the anchor.
|
||||
pub fn length(self, vector_data: &VectorData) -> f64 {
|
||||
let Some(anchor_position) = self.to_manipulator_point().get_anchor_position(vector_data) else {
|
||||
// TODO: This was previously an unwrap which was encountered, so this is a temporary way to avoid a crash
|
||||
return 0.;
|
||||
};
|
||||
let handle_position = self.to_manipulator_point().get_position(vector_data);
|
||||
handle_position.map(|pos| (pos - anchor_position).length()).unwrap_or(f64::MAX)
|
||||
}
|
||||
|
||||
/// Convert an end handle to the primary handle and a primary handle to an end handle. Note that the new handle may not exist (e.g. for a quadratic bézier).
|
||||
#[must_use]
|
||||
pub fn opposite(self) -> Self {
|
||||
match self.ty {
|
||||
HandleType::Primary => Self::end(self.segment),
|
||||
HandleType::End => Self::primary(self.segment),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn assert_subpath_eq(generated: &[bezier_rs::Subpath<PointId>], expected: &[bezier_rs::Subpath<PointId>]) {
|
||||
assert_eq!(generated.len(), expected.len());
|
||||
for (generated, expected) in generated.iter().zip(expected) {
|
||||
assert_eq!(generated.manipulator_groups().len(), expected.manipulator_groups().len());
|
||||
assert_eq!(generated.closed(), expected.closed());
|
||||
for (generated, expected) in generated.manipulator_groups().iter().zip(expected.manipulator_groups()) {
|
||||
assert_eq!(generated.in_handle, expected.in_handle);
|
||||
assert_eq!(generated.out_handle, expected.out_handle);
|
||||
assert_eq!(generated.anchor, expected.anchor);
|
||||
}
|
||||
}
|
||||
EitherFormat::VectorTable(vector_table) => vector_table,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_subpath_eq(generated: &[bezier_rs::Subpath<PointId>], expected: &[bezier_rs::Subpath<PointId>]) {
|
||||
assert_eq!(generated.len(), expected.len());
|
||||
for (generated, expected) in generated.iter().zip(expected) {
|
||||
assert_eq!(generated.manipulator_groups().len(), expected.manipulator_groups().len());
|
||||
assert_eq!(generated.closed(), expected.closed());
|
||||
for (generated, expected) in generated.manipulator_groups().iter().zip(expected.manipulator_groups()) {
|
||||
assert_eq!(generated.in_handle, expected.in_handle);
|
||||
assert_eq!(generated.out_handle, expected.out_handle);
|
||||
assert_eq!(generated.anchor, expected.anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_closed_subpath() {
|
||||
let circle = bezier_rs::Subpath::new_ellipse(DVec2::NEG_ONE, DVec2::ONE);
|
||||
let vector_data = VectorData::from_subpath(&circle);
|
||||
assert_eq!(vector_data.point_domain.ids().len(), 4);
|
||||
let bezier_paths = vector_data.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
let vector = Vector::from_subpath(&circle);
|
||||
assert_eq!(vector.point_domain.ids().len(), 4);
|
||||
let bezier_paths = vector.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
assert_eq!(bezier_paths.len(), 4);
|
||||
assert!(bezier_paths.iter().all(|&bezier| circle.iter().any(|original_bezier| original_bezier == bezier)));
|
||||
|
||||
let generated = vector_data.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
let generated = vector.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
assert_subpath_eq(&generated, &[circle]);
|
||||
}
|
||||
|
||||
|
|
@ -729,12 +542,12 @@ mod tests {
|
|||
fn construct_open_subpath() {
|
||||
let bezier = bezier_rs::Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::NEG_ONE, DVec2::ONE, DVec2::X);
|
||||
let subpath = bezier_rs::Subpath::from_bezier(&bezier);
|
||||
let vector_data = VectorData::from_subpath(&subpath);
|
||||
assert_eq!(vector_data.point_domain.ids().len(), 2);
|
||||
let bezier_paths = vector_data.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
let vector = Vector::from_subpath(&subpath);
|
||||
assert_eq!(vector.point_domain.ids().len(), 2);
|
||||
let bezier_paths = vector.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
assert_eq!(bezier_paths, vec![bezier]);
|
||||
|
||||
let generated = vector_data.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
let generated = vector.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
assert_subpath_eq(&generated, &[subpath]);
|
||||
}
|
||||
|
||||
|
|
@ -744,14 +557,14 @@ mod tests {
|
|||
let curve = bezier_rs::Subpath::from_bezier(&curve);
|
||||
let circle = bezier_rs::Subpath::new_ellipse(DVec2::NEG_ONE, DVec2::ONE);
|
||||
|
||||
let vector_data = VectorData::from_subpaths([&curve, &circle], false);
|
||||
assert_eq!(vector_data.point_domain.ids().len(), 6);
|
||||
let vector = Vector::from_subpaths([&curve, &circle], false);
|
||||
assert_eq!(vector.point_domain.ids().len(), 6);
|
||||
|
||||
let bezier_paths = vector_data.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
let bezier_paths = vector.segment_bezier_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
assert_eq!(bezier_paths.len(), 5);
|
||||
assert!(bezier_paths.iter().all(|&bezier| circle.iter().chain(curve.iter()).any(|original_bezier| original_bezier == bezier)));
|
||||
|
||||
let generated = vector_data.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
let generated = vector.stroke_bezier_paths().collect::<Vec<_>>();
|
||||
assert_subpath_eq(&generated, &[curve, circle]);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ use glam::{DAffine2, DVec2};
|
|||
use graphene_core::table::{Table, TableRow, TableRowRef};
|
||||
use graphene_core::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::{PointId, VectorData};
|
||||
use graphene_core::vector::{PointId, Vector};
|
||||
use graphene_core::{Color, Ctx, Graphic};
|
||||
pub use path_bool as path_bool_lib;
|
||||
use path_bool::{FillRule, PathBooleanOperation};
|
||||
|
|
@ -35,7 +35,7 @@ pub enum BooleanOperation {
|
|||
async fn boolean_operation<I: Into<Table<Graphic>> + 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The group of paths to perform the boolean operation on. Nested groups are automatically flattened.
|
||||
#[implementations(Table<Graphic>, Table<VectorData>)]
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
group_of_paths: I,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
///
|
||||
|
|
@ -44,55 +44,55 @@ async fn boolean_operation<I: Into<Table<Graphic>> + 'n + Send + Clone>(
|
|||
/// Intersection cuts away all but the overlapping areas shared by every path.
|
||||
/// Difference cuts away the overlapping areas shared by every path, leaving only the non-overlapping areas.
|
||||
operation: BooleanOperation,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let group_of_paths = group_of_paths.into();
|
||||
|
||||
// The first index is the bottom of the stack
|
||||
let mut result_vector_data_table = boolean_operation_on_vector_data_table(flatten_vector_data(&group_of_paths).iter_ref(), operation);
|
||||
let mut result_vector_table = boolean_operation_on_vector_table(flatten_vector(&group_of_paths).iter_ref(), operation);
|
||||
|
||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||
if let Some(result_vector_data) = result_vector_data_table.iter_mut().next() {
|
||||
let transform = *result_vector_data.transform;
|
||||
*result_vector_data.transform = DAffine2::IDENTITY;
|
||||
if let Some(result_vector) = result_vector_table.iter_mut().next() {
|
||||
let transform = *result_vector.transform;
|
||||
*result_vector.transform = DAffine2::IDENTITY;
|
||||
|
||||
VectorData::transform(result_vector_data.element, transform);
|
||||
result_vector_data.element.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector_data.element.upstream_graphic_group = Some(group_of_paths.clone());
|
||||
Vector::transform(result_vector.element, transform);
|
||||
result_vector.element.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector.element.upstream_group = Some(group_of_paths.clone());
|
||||
|
||||
// Clean up the boolean operation result by merging duplicated points
|
||||
result_vector_data.element.merge_by_distance_spatial(*result_vector_data.transform, 0.0001);
|
||||
result_vector.element.merge_by_distance_spatial(*result_vector.transform, 0.0001);
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
result_vector_table
|
||||
}
|
||||
|
||||
fn boolean_operation_on_vector_data_table<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>> + Clone, boolean_operation: BooleanOperation) -> Table<VectorData> {
|
||||
fn boolean_operation_on_vector_table<'a>(vector: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector>> + Clone, boolean_operation: BooleanOperation) -> Table<Vector> {
|
||||
match boolean_operation {
|
||||
BooleanOperation::Union => union(vector_data),
|
||||
BooleanOperation::SubtractFront => subtract(vector_data),
|
||||
BooleanOperation::SubtractBack => subtract(vector_data.rev()),
|
||||
BooleanOperation::Intersect => intersect(vector_data),
|
||||
BooleanOperation::Difference => difference(vector_data),
|
||||
BooleanOperation::Union => union(vector),
|
||||
BooleanOperation::SubtractFront => subtract(vector),
|
||||
BooleanOperation::SubtractBack => subtract(vector.rev()),
|
||||
BooleanOperation::Intersect => intersect(vector),
|
||||
BooleanOperation::Difference => difference(vector),
|
||||
}
|
||||
}
|
||||
|
||||
fn union<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
// Reverse vector data so that the result style is the style of the first vector data
|
||||
let mut vector_data_reversed = vector_data.rev();
|
||||
fn union<'a>(vector: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector>>) -> Table<Vector> {
|
||||
// Reverse the vector table rows so that the result style is the style of the first vector row
|
||||
let mut vector_reversed = vector.rev();
|
||||
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data_reversed.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
let mut result_vector_table = Table::new_from_row(vector_reversed.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
// Loop over all vector data and union it with the result
|
||||
// Loop over all vector table rows and union it with the result
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data_reversed.next().unwrap_or(default.as_ref()));
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
let mut second_vector = Some(vector_reversed.next().unwrap_or(default.as_ref()));
|
||||
while let Some(lower_vector) = second_vector {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector.transform;
|
||||
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_union(upper_path_string, lower_path_string) };
|
||||
|
|
@ -103,27 +103,27 @@ fn union<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector
|
|||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
second_vector_data = vector_data_reversed.next();
|
||||
second_vector = vector_reversed.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
result_vector_table
|
||||
}
|
||||
|
||||
fn subtract<'a>(vector_data: impl Iterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
let mut vector_data = vector_data.into_iter();
|
||||
fn subtract<'a>(vector: impl Iterator<Item = TableRowRef<'a, Vector>>) -> Table<Vector> {
|
||||
let mut vector = vector.into_iter();
|
||||
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
let mut result_vector_table = Table::new_from_row(vector.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
let mut next_vector_data = vector_data.next();
|
||||
let mut next_vector = vector.next();
|
||||
|
||||
while let Some(lower_vector_data) = next_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
while let Some(lower_vector) = next_vector {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector.transform;
|
||||
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||
|
|
@ -134,29 +134,29 @@ fn subtract<'a>(vector_data: impl Iterator<Item = TableRowRef<'a, VectorData>>)
|
|||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
next_vector_data = vector_data.next();
|
||||
next_vector = vector.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
result_vector_table
|
||||
}
|
||||
|
||||
fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
let mut vector_data = vector_data.rev();
|
||||
fn intersect<'a>(vector: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector>>) -> Table<Vector> {
|
||||
let mut vector = vector.rev();
|
||||
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
let mut result_vector_table = Table::new_from_row(vector.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data.next().unwrap_or(default.as_ref()));
|
||||
let mut second_vector = Some(vector.next().unwrap_or(default.as_ref()));
|
||||
|
||||
// For each vector data, set the result to the intersection of that data and the result
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
// For each vector table row, set the result to the intersection of that path and the current result
|
||||
while let Some(lower_vector) = second_vector {
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector.transform;
|
||||
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
|
|
@ -166,28 +166,28 @@ fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, Ve
|
|||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
second_vector_data = vector_data.next();
|
||||
second_vector = vector.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
result_vector_table
|
||||
}
|
||||
|
||||
fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>> + Clone) -> Table<VectorData> {
|
||||
let mut vector_data_iter = vector_data.clone().rev();
|
||||
fn difference<'a>(vector: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector>> + Clone) -> Table<Vector> {
|
||||
let mut vector_iter = vector.clone().rev();
|
||||
let mut any_intersection = TableRow::default();
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data_iter.next().unwrap_or(default.as_ref()));
|
||||
let mut second_vector = Some(vector_iter.next().unwrap_or(default.as_ref()));
|
||||
|
||||
// Find where all vector data intersect at least once
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let filtered_vector_data = vector_data.clone().filter(|v| *v != lower_vector_data).collect::<Vec<_>>().into_iter();
|
||||
let unioned = boolean_operation_on_vector_data_table(filtered_vector_data, BooleanOperation::Union);
|
||||
// Find where all vector table row paths intersect at least once
|
||||
while let Some(lower_vector) = second_vector {
|
||||
let filtered_vector = vector.clone().filter(|v| *v != lower_vector).collect::<Vec<_>>().into_iter();
|
||||
let unioned = boolean_operation_on_vector_table(filtered_vector, BooleanOperation::Union);
|
||||
let first_row = unioned.iter_ref().next().expect("Expected at least one row after the boolean union");
|
||||
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector.transform;
|
||||
|
||||
let upper_path_string = to_path(first_row.element, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_intersection_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
|
|
@ -213,70 +213,70 @@ fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, V
|
|||
any_intersection.element.style = boolean_intersection_result.element.style.clone();
|
||||
any_intersection.alpha_blending = boolean_intersection_result.alpha_blending;
|
||||
|
||||
second_vector_data = vector_data_iter.next();
|
||||
second_vector = vector_iter.next();
|
||||
}
|
||||
|
||||
// Subtract the area where they intersect at least once from the union of all vector data
|
||||
let union = boolean_operation_on_vector_data_table(vector_data, BooleanOperation::Union);
|
||||
boolean_operation_on_vector_data_table(union.iter_ref().chain(std::iter::once(any_intersection.as_ref())), BooleanOperation::SubtractFront)
|
||||
// Subtract the area where they intersect at least once from the union of all vector paths
|
||||
let union = boolean_operation_on_vector_table(vector, BooleanOperation::Union);
|
||||
boolean_operation_on_vector_table(union.iter_ref().chain(std::iter::once(any_intersection.as_ref())), BooleanOperation::SubtractFront)
|
||||
}
|
||||
|
||||
fn flatten_vector_data(graphic_group_table: &Table<Graphic>) -> Table<VectorData> {
|
||||
graphic_group_table
|
||||
fn flatten_vector(group_table: &Table<Graphic>) -> Table<Vector> {
|
||||
group_table
|
||||
.iter_ref()
|
||||
.flat_map(|element| {
|
||||
match element.element.clone() {
|
||||
Graphic::VectorData(vector_data) => {
|
||||
// Apply the parent group's transform to each element of vector data
|
||||
vector_data
|
||||
Graphic::Vector(vector) => {
|
||||
// Apply the parent group's transform to each element of the vector table
|
||||
vector
|
||||
.iter()
|
||||
.map(|mut sub_vector_data| {
|
||||
sub_vector_data.transform = *element.transform * sub_vector_data.transform;
|
||||
.map(|mut sub_vector| {
|
||||
sub_vector.transform = *element.transform * sub_vector.transform;
|
||||
|
||||
sub_vector_data
|
||||
sub_vector
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::RasterDataCPU(image) => {
|
||||
Graphic::RasterCPU(image) => {
|
||||
let make_row = |transform| {
|
||||
// Convert the image frame into a rectangular subpath with the image's transform
|
||||
let mut subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
|
||||
// Create a vector data table row from the rectangular subpath, with a default black fill
|
||||
let mut element = VectorData::from_subpath(subpath);
|
||||
// Create a vector table row from the rectangular subpath, with a default black fill
|
||||
let mut element = Vector::from_subpath(subpath);
|
||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
// Apply the parent group's transform to each raster element
|
||||
image.iter_ref().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::RasterDataGPU(image) => {
|
||||
Graphic::RasterGPU(image) => {
|
||||
let make_row = |transform| {
|
||||
// Convert the image frame into a rectangular subpath with the image's transform
|
||||
let mut subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
|
||||
// Create a vector data table row from the rectangular subpath, with a default black fill
|
||||
let mut element = VectorData::from_subpath(subpath);
|
||||
// Create a vector table row from the rectangular subpath, with a default black fill
|
||||
let mut element = Vector::from_subpath(subpath);
|
||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
// Apply the parent group's transform to each raster element
|
||||
image.iter_ref().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::GraphicGroup(mut graphic_group) => {
|
||||
Graphic::Group(mut group) => {
|
||||
// Apply the parent group's transform to each element of inner group
|
||||
for sub_element in graphic_group.iter_mut() {
|
||||
for sub_element in group.iter_mut() {
|
||||
*sub_element.transform = *element.transform * *sub_element.transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner group into vector data
|
||||
let unioned = boolean_operation_on_vector_data_table(flatten_vector_data(&graphic_group).iter_ref(), BooleanOperation::Union);
|
||||
// Recursively flatten the inner group into the vector table
|
||||
let unioned = boolean_operation_on_vector_table(flatten_vector(&group).iter_ref(), BooleanOperation::Union);
|
||||
|
||||
unioned.iter().collect::<Vec<_>>()
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ fn flatten_vector_data(graphic_group_table: &Table<Graphic>) -> Table<VectorData
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn to_path(vector: &VectorData, transform: DAffine2) -> Vec<path_bool::PathSegment> {
|
||||
fn to_path(vector: &Vector, transform: DAffine2) -> Vec<path_bool::PathSegment> {
|
||||
let mut path = Vec::new();
|
||||
for subpath in vector.stroke_bezier_paths() {
|
||||
to_path_segments(&mut path, &subpath, transform);
|
||||
|
|
@ -318,7 +318,7 @@ fn to_path_segments(path: &mut Vec<path_bool::PathSegment>, subpath: &Subpath<Po
|
|||
}
|
||||
}
|
||||
|
||||
fn from_path(path_data: &[Path]) -> VectorData {
|
||||
fn from_path(path_data: &[Path]) -> Vector {
|
||||
const EPSILON: f64 = 1e-5;
|
||||
|
||||
fn is_close(a: DVec2, b: DVec2) -> bool {
|
||||
|
|
@ -362,7 +362,7 @@ fn from_path(path_data: &[Path]) -> VectorData {
|
|||
}
|
||||
}
|
||||
|
||||
VectorData::from_subpaths(all_subpaths, false)
|
||||
Vector::from_subpaths(all_subpaths, false)
|
||||
}
|
||||
|
||||
type Path = Vec<path_bool::PathSegment>;
|
||||
|
|
|
|||
|
|
@ -920,7 +920,7 @@ impl NodeNetwork {
|
|||
if !node.visible && node.implementation != identity_node {
|
||||
node.implementation = identity_node;
|
||||
|
||||
// Connect layer node to the graphic group below
|
||||
// Connect layer node to the group below
|
||||
node.inputs.drain(1..);
|
||||
node.manual_composition = None;
|
||||
self.nodes.insert(id, node);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use graphene_core::raster_types::{CPU, Raster};
|
|||
use graphene_core::table::Table;
|
||||
use graphene_core::transform::ReferencePoint;
|
||||
use graphene_core::uuid::NodeId;
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::vector::Vector;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::{Artboard, Color, Graphic, MemoHash, Node, Type};
|
||||
use graphene_svg_renderer::RenderMetadata;
|
||||
|
|
@ -180,17 +180,19 @@ tagged_value! {
|
|||
// ===========
|
||||
// TABLE TYPES
|
||||
// ===========
|
||||
#[serde(alias = "GraphicElement")]
|
||||
Graphic(Graphic),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::vector::migrate_vector_data"))] // TODO: Eventually remove this migration document upgrade code
|
||||
VectorData(Table<VectorData>),
|
||||
GraphicUnused(Graphic), // TODO: This is unused but removing it causes `cargo test` to infinitely recurse its type solving; figure out why and then remove this
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::vector::migrate_vector"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "VectorData")]
|
||||
Vector(Table<Vector>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "ImageFrame")]
|
||||
RasterData(Table<Raster<CPU>>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::graphic_element::migrate_graphic_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||
GraphicGroup(Table<Graphic>),
|
||||
#[serde(alias = "ImageFrame", alias = "RasterData")]
|
||||
Raster(Table<Raster<CPU>>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::graphic::migrate_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "GraphicGroup")]
|
||||
Group(Table<Graphic>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||
ArtboardGroup(Table<Artboard>),
|
||||
#[serde(alias = "ArtboardGroup")]
|
||||
Artboard(Table<Artboard>),
|
||||
// ============
|
||||
// STRUCT TYPES
|
||||
// ============
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use graph_craft::wasm_application_io::WasmEditorApi;
|
||||
pub use graphene_core::text::*;
|
||||
use graphene_core::{Ctx, table::Table, vector::VectorData};
|
||||
use graphene_core::{Ctx, table::Table, vector::Vector};
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
fn text<'i: 'n>(
|
||||
|
|
@ -28,10 +28,10 @@ fn text<'i: 'n>(
|
|||
#[default(0.)]
|
||||
tilt: f64,
|
||||
align: TextAlign,
|
||||
/// Splits each text glyph into its own row in the table of vector data.
|
||||
/// Splits each text glyph into its own row in the table of vector geometry.
|
||||
#[default(false)]
|
||||
per_glyph_instances: bool,
|
||||
) -> Table<VectorData> {
|
||||
) -> Table<Vector> {
|
||||
let typesetting = TypesettingConfig {
|
||||
font_size,
|
||||
line_height_ratio,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use graphene_core::raster::image::Image;
|
|||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
use graphene_core::transform::Footprint;
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::vector::Vector;
|
||||
use graphene_core::{Color, Context, Ctx, ExtractFootprint, Graphic, OwnedContextImpl, WasmNotSend};
|
||||
use graphene_svg_renderer::RenderMetadata;
|
||||
use graphene_svg_renderer::{Render, RenderParams, RenderSvgSegmentList, SvgRender, format_transform_matrix};
|
||||
|
|
@ -215,7 +215,7 @@ async fn render_canvas(render_config: RenderConfig, data: impl Render, editor: &
|
|||
async fn rasterize<T: WasmNotSend + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<VectorData>,
|
||||
Table<Vector>,
|
||||
Table<Raster<CPU>>,
|
||||
Table<Graphic>,
|
||||
)]
|
||||
|
|
@ -283,7 +283,7 @@ async fn render<'a: 'n, T: 'n + Render + WasmNotSend>(
|
|||
render_config: RenderConfig,
|
||||
editor_api: impl Node<Context<'static>, Output = &'a WasmEditorApi>,
|
||||
#[implementations(
|
||||
Context -> Table<VectorData>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Artboard>,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use graphene_core::render_complexity::RenderComplexity;
|
|||
use graphene_core::table::{Table, TableRow};
|
||||
use graphene_core::transform::{Footprint, Transform};
|
||||
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_core::vector::Vector;
|
||||
use graphene_core::vector::click_target::{ClickTarget, FreePoint};
|
||||
use graphene_core::vector::style::{Fill, Stroke, StrokeAlign, ViewMode};
|
||||
use graphene_core::{Artboard, Graphic};
|
||||
|
|
@ -365,7 +365,7 @@ impl Render for Table<Graphic> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(graphic_group_id) = element_id {
|
||||
if let Some(group_id) = element_id {
|
||||
let mut all_upstream_click_targets = Vec::new();
|
||||
|
||||
for row in self.iter_ref() {
|
||||
|
|
@ -379,7 +379,7 @@ impl Render for Table<Graphic> {
|
|||
all_upstream_click_targets.extend(new_click_targets);
|
||||
}
|
||||
|
||||
metadata.click_targets.insert(graphic_group_id, all_upstream_click_targets);
|
||||
metadata.click_targets.insert(group_id, all_upstream_click_targets);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -408,20 +408,20 @@ impl Render for Table<Graphic> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Render for Table<VectorData> {
|
||||
impl Render for Table<Vector> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
for row in self.iter_ref() {
|
||||
let multiplied_transform = *row.transform;
|
||||
let vector_data = &row.element;
|
||||
let vector = &row.element;
|
||||
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
||||
let has_real_stroke = vector_data.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
let has_real_stroke = vector.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
|
||||
let applied_stroke_transform = set_stroke_transform.unwrap_or(*row.transform);
|
||||
let applied_stroke_transform = render_params.alignment_parent_transform.unwrap_or(applied_stroke_transform);
|
||||
let element_transform = set_stroke_transform.map(|stroke_transform| multiplied_transform * stroke_transform.inverse());
|
||||
let element_transform = element_transform.unwrap_or(DAffine2::IDENTITY);
|
||||
let layer_bounds = vector_data.bounding_box().unwrap_or_default();
|
||||
let transformed_bounds = vector_data.bounding_box_with_transform(applied_stroke_transform).unwrap_or_default();
|
||||
let layer_bounds = vector.bounding_box().unwrap_or_default();
|
||||
let transformed_bounds = vector.bounding_box_with_transform(applied_stroke_transform).unwrap_or_default();
|
||||
|
||||
let mut path = String::new();
|
||||
|
||||
|
|
@ -429,12 +429,12 @@ impl Render for Table<VectorData> {
|
|||
let _ = subpath.subpath_to_svg(&mut path, applied_stroke_transform);
|
||||
}
|
||||
|
||||
let connected = vector_data.stroke_bezier_paths().all(|path| path.closed());
|
||||
let can_draw_aligned_stroke = vector_data.style.stroke().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered()) && connected;
|
||||
let connected = vector.stroke_bezier_paths().all(|path| path.closed());
|
||||
let can_draw_aligned_stroke = vector.style.stroke().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered()) && connected;
|
||||
let mut push_id = None;
|
||||
|
||||
if can_draw_aligned_stroke {
|
||||
let mask_type = if vector_data.style.stroke().unwrap().align == StrokeAlign::Inside {
|
||||
let mask_type = if vector.style.stroke().unwrap().align == StrokeAlign::Inside {
|
||||
MaskType::Clip
|
||||
} else {
|
||||
MaskType::Mask
|
||||
|
|
@ -566,7 +566,7 @@ impl Render for Table<VectorData> {
|
|||
element.style.clear_stroke();
|
||||
element.style.set_fill(Fill::solid(Color::BLACK));
|
||||
|
||||
let vector_data = Table::new_from_row(TableRow {
|
||||
let vector_table = Table::new_from_row(TableRow {
|
||||
element,
|
||||
alpha_blending: *row.alpha_blending,
|
||||
transform: *row.transform,
|
||||
|
|
@ -580,7 +580,7 @@ impl Render for Table<VectorData> {
|
|||
let rect = kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y);
|
||||
|
||||
scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect);
|
||||
vector_data.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform));
|
||||
vector_table.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform));
|
||||
scene.push_layer(peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcIn), 1., kurbo::Affine::IDENTITY, &rect);
|
||||
}
|
||||
|
||||
|
|
@ -730,11 +730,11 @@ impl Render for Table<VectorData> {
|
|||
fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option<NodeId>) {
|
||||
for row in self.iter_ref() {
|
||||
let transform = *row.transform;
|
||||
let vector_data = row.element;
|
||||
let vector = row.element;
|
||||
|
||||
if let Some(element_id) = element_id {
|
||||
let stroke_width = vector_data.style.stroke().as_ref().map_or(0., Stroke::weight);
|
||||
let filled = vector_data.style.fill() != &Fill::None;
|
||||
let stroke_width = vector.style.stroke().as_ref().map_or(0., Stroke::weight);
|
||||
let filled = vector.style.fill() != &Fill::None;
|
||||
let fill = |mut subpath: Subpath<_>| {
|
||||
if filled {
|
||||
subpath.set_closed(true);
|
||||
|
|
@ -743,9 +743,9 @@ impl Render for Table<VectorData> {
|
|||
};
|
||||
|
||||
// For free-floating anchors, we need to add a click target for each
|
||||
let single_anchors_targets = vector_data.point_domain.ids().iter().filter_map(|&point_id| {
|
||||
if vector_data.connected_count(point_id) == 0 {
|
||||
let anchor = vector_data.point_domain.position_from_id(point_id).unwrap_or_default();
|
||||
let single_anchors_targets = vector.point_domain.ids().iter().filter_map(|&point_id| {
|
||||
if vector.connected_count(point_id) == 0 {
|
||||
let anchor = vector.point_domain.position_from_id(point_id).unwrap_or_default();
|
||||
let point = FreePoint::new(point_id, anchor);
|
||||
|
||||
Some(ClickTarget::new_with_free_point(point))
|
||||
|
|
@ -754,7 +754,7 @@ impl Render for Table<VectorData> {
|
|||
}
|
||||
});
|
||||
|
||||
let click_targets = vector_data
|
||||
let click_targets = vector
|
||||
.stroke_bezier_paths()
|
||||
.map(fill)
|
||||
.map(|subpath| ClickTarget::new_with_subpath(subpath, stroke_width))
|
||||
|
|
@ -764,9 +764,9 @@ impl Render for Table<VectorData> {
|
|||
metadata.click_targets.entry(element_id).or_insert(click_targets);
|
||||
}
|
||||
|
||||
if let Some(upstream_graphic_group) = &vector_data.upstream_graphic_group {
|
||||
if let Some(upstream_group) = &vector.upstream_group {
|
||||
footprint.transform *= transform;
|
||||
upstream_graphic_group.collect_metadata(metadata, footprint, None);
|
||||
upstream_group.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -853,7 +853,7 @@ impl Render for Artboard {
|
|||
},
|
||||
// Artboard contents
|
||||
|render| {
|
||||
self.graphic_group.render_svg(render, render_params);
|
||||
self.group.render_svg(render, render_params);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -875,9 +875,9 @@ impl Render for Artboard {
|
|||
let blend_mode = peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcOver);
|
||||
scene.push_layer(blend_mode, 1., kurbo::Affine::new(transform.to_cols_array()), &rect);
|
||||
}
|
||||
// Since the graphic group's transform is right multiplied in when rendering the graphic group, we just need to right multiply by the offset here.
|
||||
// Since the group's transform is right multiplied in when rendering the group, we just need to right multiply by the offset here.
|
||||
let child_transform = transform * DAffine2::from_translation(self.location.as_dvec2());
|
||||
self.graphic_group.render_to_vello(scene, child_transform, context, render_params);
|
||||
self.group.render_to_vello(scene, child_transform, context, render_params);
|
||||
if self.clip {
|
||||
scene.pop_layer();
|
||||
}
|
||||
|
|
@ -894,7 +894,7 @@ impl Render for Artboard {
|
|||
}
|
||||
}
|
||||
footprint.transform *= self.transform();
|
||||
self.graphic_group.collect_metadata(metadata, footprint, None);
|
||||
self.group.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
|
|
@ -1140,38 +1140,38 @@ impl Render for Table<Raster<GPU>> {
|
|||
impl Render for Graphic {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.render_svg(render, render_params),
|
||||
Graphic::RasterDataCPU(raster) => raster.render_svg(render, render_params),
|
||||
Graphic::RasterDataGPU(_raster) => (),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params),
|
||||
Graphic::Vector(vector) => vector.render_svg(render, render_params),
|
||||
Graphic::RasterCPU(raster) => raster.render_svg(render, render_params),
|
||||
Graphic::RasterGPU(_) => (),
|
||||
Graphic::Group(group) => group.render_svg(render, render_params),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterDataCPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterDataGPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::Vector(vector) => vector.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterCPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterGPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::Group(group) => group.render_to_vello(scene, transform, context, render_params),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
if let Some(element_id) = element_id {
|
||||
match self {
|
||||
Graphic::GraphicGroup(_) => {
|
||||
Graphic::Group(_) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
}
|
||||
Graphic::VectorData(vector_data) => {
|
||||
Graphic::Vector(vector) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
// TODO: Find a way to handle more than one row of the graphical data table
|
||||
if let Some(vector_data) = vector_data.iter_ref().next() {
|
||||
metadata.first_element_source_id.insert(element_id, *vector_data.source_node_id);
|
||||
metadata.local_transforms.insert(element_id, *vector_data.transform);
|
||||
if let Some(vector) = vector.iter_ref().next() {
|
||||
metadata.first_element_source_id.insert(element_id, *vector.source_node_id);
|
||||
metadata.local_transforms.insert(element_id, *vector.transform);
|
||||
}
|
||||
}
|
||||
Graphic::RasterDataCPU(raster_frame) => {
|
||||
Graphic::RasterCPU(raster_frame) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than one row of images
|
||||
|
|
@ -1179,7 +1179,7 @@ impl Render for Graphic {
|
|||
metadata.local_transforms.insert(element_id, *image.transform);
|
||||
}
|
||||
}
|
||||
Graphic::RasterDataGPU(raster_frame) => {
|
||||
Graphic::RasterGPU(raster_frame) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
|
||||
// TODO: Find a way to handle more than one row of images
|
||||
|
|
@ -1191,37 +1191,37 @@ impl Render for Graphic {
|
|||
}
|
||||
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterDataCPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterDataGPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::Vector(vector) => vector.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterCPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterGPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::Group(group) => group.collect_metadata(metadata, footprint, element_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterDataCPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterDataGPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.add_upstream_click_targets(click_targets),
|
||||
Graphic::Vector(vector) => vector.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterCPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterGPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::Group(group) => group.add_upstream_click_targets(click_targets),
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.contains_artboard(),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.contains_artboard(),
|
||||
Graphic::RasterDataCPU(raster) => raster.contains_artboard(),
|
||||
Graphic::RasterDataGPU(raster) => raster.contains_artboard(),
|
||||
Graphic::Vector(vector) => vector.contains_artboard(),
|
||||
Graphic::Group(group) => group.contains_artboard(),
|
||||
Graphic::RasterCPU(raster) => raster.contains_artboard(),
|
||||
Graphic::RasterGPU(raster) => raster.contains_artboard(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_ids_from_hash(&mut self, reference: Option<NodeId>) {
|
||||
match self {
|
||||
Graphic::VectorData(vector_data) => vector_data.new_ids_from_hash(reference),
|
||||
Graphic::GraphicGroup(graphic_group) => graphic_group.new_ids_from_hash(reference),
|
||||
Graphic::RasterDataCPU(_) => (),
|
||||
Graphic::RasterDataGPU(_) => (),
|
||||
Graphic::Vector(vector) => vector.new_ids_from_hash(reference),
|
||||
Graphic::Group(group) => group.new_ids_from_hash(reference),
|
||||
Graphic::RasterCPU(_) => (),
|
||||
Graphic::RasterGPU(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use graphene_std::any::DowncastBothNode;
|
|||
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
||||
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
||||
use graphene_std::table::Table;
|
||||
use graphene_std::vector::VectorData;
|
||||
use graphene_std::vector::Vector;
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::wasm_application_io::{WasmEditorApi, WasmSurfaceHandle};
|
||||
use node_registry_macros::{async_node, convert_node, into_node};
|
||||
|
|
@ -31,9 +31,9 @@ use wgpu_executor::{WgpuSurface, WindowHandle};
|
|||
// TODO: turn into hashmap
|
||||
fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
|
||||
let mut node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
|
||||
into_node!(from: Table<VectorData>, to: Table<VectorData>),
|
||||
into_node!(from: Table<VectorData>, to: Graphic),
|
||||
into_node!(from: Table<VectorData>, to: Table<Graphic>),
|
||||
into_node!(from: Table<Vector>, to: Table<Vector>),
|
||||
into_node!(from: Table<Vector>, to: Graphic),
|
||||
into_node!(from: Table<Vector>, to: Table<Graphic>),
|
||||
into_node!(from: Table<Graphic>, to: Table<Graphic>),
|
||||
into_node!(from: Table<Graphic>, to: Graphic),
|
||||
into_node!(from: Table<Raster<CPU>>, to: Table<Raster<CPU>>),
|
||||
|
|
@ -43,7 +43,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
into_node!(from: Table<Raster<CPU>>, to: Table<Graphic>),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<VectorData>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Artboard]),
|
||||
|
|
@ -78,7 +78,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
|
||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<VectorData>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Vector>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Graphic>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec<DVec2>]),
|
||||
|
|
@ -94,7 +94,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<VectorData>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => WgpuSurface]),
|
||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@ mod tests {
|
|||
let attr = quote!(category("Vector: Shape"));
|
||||
let input = quote!(
|
||||
/// Test
|
||||
fn circle(_: impl Ctx, #[default(50.)] radius: f64) -> VectorData {
|
||||
fn circle(_: impl Ctx, #[default(50.)] radius: f64) -> Vector {
|
||||
// Implementation details...
|
||||
}
|
||||
);
|
||||
|
|
@ -937,7 +937,7 @@ mod tests {
|
|||
ty: parse_quote!(impl Ctx),
|
||||
implementations: Punctuated::new(),
|
||||
},
|
||||
output_type: parse_quote!(VectorData),
|
||||
output_type: parse_quote!(Vector),
|
||||
is_async: false,
|
||||
fields: vec![ParsedField::Regular {
|
||||
pat_ident: pat_ident("radius"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue